@ohhwells/bridge 0.1.71-next.218 → 0.1.72

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  // src/OhhwellsBridge.tsx
4
- import React12, { useCallback as useCallback8, useEffect as useEffect13, useLayoutEffect as useLayoutEffect5, useRef as useRef10, useState as useState13 } from "react";
4
+ import React12, { useCallback as useCallback7, useEffect as useEffect12, useLayoutEffect as useLayoutEffect5, useRef as useRef9, useState as useState12 } from "react";
5
5
  import { createRoot as createRoot2 } from "react-dom/client";
6
6
  import { flushSync as flushSync2 } from "react-dom";
7
7
 
@@ -83,12 +83,7 @@ function parseAiSectionsState(raw) {
83
83
  media: entry.media && typeof entry.media === "object" ? entry.media : {}
84
84
  }));
85
85
  const removed = Array.isArray(parsed.removed) ? parsed.removed.filter((id) => typeof id === "string" && id.length > 0) : [];
86
- return {
87
- v: 1,
88
- sections,
89
- ...removed.length ? { removed } : {},
90
- ...parsed.hideTemplate === true ? { hideTemplate: true } : {}
91
- };
86
+ return { v: 1, sections, ...removed.length ? { removed } : {} };
92
87
  } catch {
93
88
  return EMPTY_AI_SECTIONS;
94
89
  }
@@ -101,7 +96,6 @@ function applyTreeToState(state, payload) {
101
96
  const entry = {
102
97
  id: payload.id,
103
98
  label: payload.label ?? "Generated section",
104
- ...typeof payload.path === "string" && payload.path ? { path: payload.path } : {},
105
99
  afterSection: payload.mode === "insert" && !insertBefore ? payload.targetSectionId ?? null : null,
106
100
  ...insertBefore && payload.targetSectionId ? { beforeSection: payload.targetSectionId } : {},
107
101
  ...payload.mode === "replace" && payload.targetSectionId ? { replaces: payload.targetSectionId } : {},
@@ -124,317 +118,6 @@ function deleteSectionFromState(state, sectionId) {
124
118
  return { ...state, v: 1, sections: state.sections, removed: [...removed, sectionId] };
125
119
  }
126
120
 
127
- // src/lib/brand-chrome.ts
128
- var BRAND_NAME_KEY = "__ohw_brand_name";
129
- var BRAND_TITLE_KEY = "__ohw_site_title";
130
- var BRAND_FAVICON_LETTER_KEY = "__ohw_favicon_letter";
131
- var BRAND_CHROME_KEYS = /* @__PURE__ */ new Set([
132
- BRAND_NAME_KEY,
133
- BRAND_TITLE_KEY,
134
- BRAND_FAVICON_LETTER_KEY
135
- ]);
136
- function upsertMeta(selector, attr, token, value) {
137
- let el = document.head.querySelector(selector);
138
- if (!el) {
139
- el = document.createElement("meta");
140
- el.setAttribute(attr, token);
141
- document.head.appendChild(el);
142
- }
143
- if (el.getAttribute("content") !== value) el.setAttribute("content", value);
144
- }
145
- function escapeXml(value) {
146
- return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
147
- }
148
- function applyLetterFavicon(letter) {
149
- 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>`;
150
- const href = `data:image/svg+xml,${encodeURIComponent(svg)}`;
151
- let link = document.head.querySelector('link[rel="icon"]');
152
- if (!link) {
153
- link = document.createElement("link");
154
- link.rel = "icon";
155
- document.head.appendChild(link);
156
- }
157
- link.type = "image/svg+xml";
158
- if (link.href !== href) link.href = href;
159
- }
160
- function applyBrandChrome(content) {
161
- const name = content[BRAND_NAME_KEY];
162
- if (typeof name === "string" && name.length > 0) {
163
- document.querySelectorAll("[data-ohw-wordmark]").forEach((el) => {
164
- if (el.textContent !== name) el.textContent = name;
165
- if (el.getAttribute("title") !== name) el.setAttribute("title", name);
166
- });
167
- }
168
- const title = content[BRAND_TITLE_KEY];
169
- if (typeof title === "string" && title.length > 0) {
170
- if (document.title !== title) document.title = title;
171
- upsertMeta('meta[property="og:title"]', "property", "og:title", title);
172
- upsertMeta('meta[name="twitter:title"]', "name", "twitter:title", title);
173
- }
174
- const letter = content[BRAND_FAVICON_LETTER_KEY];
175
- if (typeof letter === "string" && letter.length > 0) {
176
- applyLetterFavicon(letter);
177
- }
178
- }
179
-
180
- // src/lib/brand-kit.ts
181
- var BRAND_KIT_KEY = "__ohw_brand";
182
- var BRAND_VAR_PREFIX = "--ohw-brand-";
183
- var BRAND_VAR_NAMES = ["primary", "accent", "light", "dark", "surface", "border", "muted"].map(
184
- (role) => `${BRAND_VAR_PREFIX}${role}`
185
- );
186
- var FONT_VARS = {
187
- heading: ["--font-heading", "--font-display", "--brand-font-heading"],
188
- body: ["--font-body", "--brand-font-body"]
189
- };
190
- var BRAND_FONT_LINK_ID = "ohw-brand-fonts";
191
- function brandColorVars(kit) {
192
- const { dark, primary, accent, light } = kit.palette;
193
- const mix = (a, aPct, b) => `color-mix(in srgb, ${a} ${aPct}%, ${b})`;
194
- return {
195
- [`${BRAND_VAR_PREFIX}primary`]: primary,
196
- [`${BRAND_VAR_PREFIX}accent`]: accent,
197
- [`${BRAND_VAR_PREFIX}light`]: light,
198
- [`${BRAND_VAR_PREFIX}dark`]: dark,
199
- [`${BRAND_VAR_PREFIX}surface`]: mix(light, 95, dark),
200
- [`${BRAND_VAR_PREFIX}border`]: mix(light, 85, dark),
201
- [`${BRAND_VAR_PREFIX}muted`]: mix(dark, 62, light)
202
- };
203
- }
204
- function parseBrandKit(raw) {
205
- if (!raw) return null;
206
- try {
207
- const parsed = JSON.parse(raw);
208
- const p = parsed?.palette;
209
- const f = parsed?.fonts;
210
- 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") {
211
- return null;
212
- }
213
- return {
214
- palette: { dark: p.dark, primary: p.primary, accent: p.accent, light: p.light },
215
- fonts: { heading: f.heading, body: f.body }
216
- };
217
- } catch {
218
- return null;
219
- }
220
- }
221
- function familyOf(stack) {
222
- const first = stack.split(",")[0]?.trim() ?? "";
223
- return first.replace(/^['"]|['"]$/g, "");
224
- }
225
- function loadBrandFonts(families) {
226
- const unique = [...new Set(families.map((f) => f.trim()).filter(Boolean))];
227
- if (unique.length === 0) return;
228
- const spec = unique.map((f) => `${f.replace(/ /g, "+")}:400,500,600,700`).join("|");
229
- const href = `https://fonts.googleapis.com/css?family=${spec}&display=swap`;
230
- let link = document.getElementById(BRAND_FONT_LINK_ID);
231
- if (!link) {
232
- link = document.createElement("link");
233
- link.id = BRAND_FONT_LINK_ID;
234
- link.rel = "stylesheet";
235
- document.head.appendChild(link);
236
- }
237
- if (link.href !== href) link.href = href;
238
- }
239
- function applyBrandToDom(kit) {
240
- const root = document.documentElement;
241
- if (!kit) {
242
- for (const name of BRAND_VAR_NAMES) root.style.removeProperty(name);
243
- for (const name of [...FONT_VARS.heading, ...FONT_VARS.body]) root.style.removeProperty(name);
244
- document.getElementById(BRAND_FONT_LINK_ID)?.remove();
245
- return;
246
- }
247
- for (const [name, value] of Object.entries(brandColorVars(kit))) root.style.setProperty(name, value);
248
- for (const name of FONT_VARS.heading) root.style.setProperty(name, kit.fonts.heading);
249
- for (const name of FONT_VARS.body) root.style.setProperty(name, kit.fonts.body);
250
- loadBrandFonts([familyOf(kit.fonts.heading), familyOf(kit.fonts.body)]);
251
- }
252
-
253
- // src/lib/section-styles.ts
254
- var STYLE_STORE_KEY = "__ohw_styles";
255
- var STYLE_SHEET_ID = "ohw-section-styles";
256
- function parseStyleStore(raw) {
257
- if (!raw) return null;
258
- try {
259
- const parsed = JSON.parse(raw);
260
- if (parsed?.v !== 1) return null;
261
- return {
262
- v: 1,
263
- sections: typeof parsed.sections === "object" && parsed.sections ? parsed.sections : {},
264
- nodes: typeof parsed.nodes === "object" && parsed.nodes ? parsed.nodes : {}
265
- };
266
- } catch {
267
- return null;
268
- }
269
- }
270
- var BG_VALUES = {
271
- surface: "color-mix(in srgb, var(--ohw-brand-light, var(--color-light, #ECECEB)) 94%, var(--ohw-brand-dark, var(--color-dark, #0C0A09)))",
272
- accent: "var(--ohw-brand-primary, var(--color-primary, #0078E5))",
273
- "accent-soft": "color-mix(in srgb, var(--ohw-brand-primary, var(--color-primary, #0078E5)) 12%, var(--ohw-brand-light, var(--color-light, #FFFFFF)))"
274
- };
275
- var HEADLINE_SIZES = { md: 24, lg: 32, xl: 40, display: 56 };
276
- function styleSheetCss() {
277
- const rules = [];
278
- for (const [tone, value] of Object.entries(BG_VALUES)) {
279
- rules.push(`[data-ohw-style-bg="${tone}"] { background: ${value} !important; }`);
280
- }
281
- rules.push(
282
- `[data-ohw-style-bg="accent"] { color: var(--ohw-brand-light, var(--color-light, #FFFFFF)) !important; }`
283
- );
284
- rules.push(
285
- `[data-ohw-style-distribution="space-between"] { display: flex !important; flex-direction: column; justify-content: space-between; }`,
286
- `[data-ohw-style-distribution="center"] { display: flex !important; flex-direction: column; justify-content: center; }`
287
- );
288
- for (const [scale, size] of Object.entries(HEADLINE_SIZES)) {
289
- rules.push(
290
- `[data-ohw-style-headline="${scale}"] :is(h1, h2, h3) { font-size: ${size}px !important; line-height: 1.15 !important; }`
291
- );
292
- }
293
- rules.push(
294
- `[data-ohw-style-aspect="fill-height"] img { height: 100% !important; aspect-ratio: auto !important; object-fit: cover; }`
295
- );
296
- for (const aspect of ["1:1", "4:5", "3:4", "3:2", "16:9", "2:1", "3:1"]) {
297
- rules.push(
298
- `[data-ohw-style-aspect="${aspect.replace(":", "-")}"] img { aspect-ratio: ${aspect.replace(":", " / ")} !important; height: auto !important; object-fit: cover; }`
299
- );
300
- }
301
- const pad = { tight: 40, balanced: 64, airy: 96 };
302
- for (const [spacing, px] of Object.entries(pad)) {
303
- rules.push(
304
- `[data-ohw-style-spacing="${spacing}"] { padding-top: ${px}px !important; padding-bottom: ${px}px !important; }`
305
- );
306
- }
307
- return rules.join("\n");
308
- }
309
- var STYLE_FONT_LINK_ID = "ohw-style-fonts";
310
- function loadStyleFonts(families) {
311
- const unique = [...new Set(families.map((f) => f.trim()).filter(Boolean))];
312
- const existing = document.getElementById(STYLE_FONT_LINK_ID);
313
- if (unique.length === 0) {
314
- existing?.remove();
315
- return;
316
- }
317
- const spec = unique.map((f) => `${f.replace(/ /g, "+")}:400,500,600,700`).join("|");
318
- const href = `https://fonts.googleapis.com/css?family=${spec}&display=swap`;
319
- let link = existing;
320
- if (!link) {
321
- link = document.createElement("link");
322
- link.id = STYLE_FONT_LINK_ID;
323
- link.rel = "stylesheet";
324
- document.head.appendChild(link);
325
- }
326
- if (link.href !== href) link.href = href;
327
- }
328
- var SECTION_ATTRS = {
329
- sectionBackground: "data-ohw-style-bg",
330
- textDistribution: "data-ohw-style-distribution",
331
- headlineScale: "data-ohw-style-headline",
332
- imageAspect: "data-ohw-style-aspect",
333
- spacing: "data-ohw-style-spacing"
334
- };
335
- var NODE_WROTE_ATTR = "data-ohw-style-node";
336
- var NODE_PROPS = ["color", "font-family", "font-size", "background"];
337
- function saveInline(el, prop) {
338
- const attr = `data-ohw-style-prev-${prop}`;
339
- if (!el.hasAttribute(attr)) el.setAttribute(attr, el.style.getPropertyValue(prop));
340
- }
341
- function restoreInline(el, prop) {
342
- const attr = `data-ohw-style-prev-${prop}`;
343
- if (!el.hasAttribute(attr)) return;
344
- const prev = el.getAttribute(attr) ?? "";
345
- if (prev) el.style.setProperty(prop, prev);
346
- else el.style.removeProperty(prop);
347
- el.removeAttribute(attr);
348
- }
349
- function ensureStyleSheet() {
350
- let el = document.getElementById(STYLE_SHEET_ID);
351
- if (!el) {
352
- el = document.createElement("style");
353
- el.id = STYLE_SHEET_ID;
354
- document.head.appendChild(el);
355
- }
356
- const css = styleSheetCss();
357
- if (el.textContent !== css) el.textContent = css;
358
- }
359
- function clearSectionAttrs(root) {
360
- for (const attr of Object.values(SECTION_ATTRS)) {
361
- for (const el of Array.from(root.querySelectorAll(`[${attr}]`))) el.removeAttribute(attr);
362
- }
363
- for (const el of Array.from(root.querySelectorAll("[data-ohw-style-bgcolor]"))) {
364
- restoreInline(el, "background");
365
- el.removeAttribute("data-ohw-style-bgcolor");
366
- }
367
- }
368
- function clearNodeProps(root) {
369
- for (const el of Array.from(root.querySelectorAll(`[${NODE_WROTE_ATTR}]`))) {
370
- const h = el;
371
- for (const prop of NODE_PROPS) restoreInline(h, prop);
372
- h.removeAttribute(NODE_WROTE_ATTR);
373
- }
374
- }
375
- function buttonSurfaceOf(el) {
376
- return el.closest("a, button") ?? el;
377
- }
378
- function applyStylesToDom(store) {
379
- ensureStyleSheet();
380
- clearSectionAttrs(document);
381
- clearNodeProps(document);
382
- loadStyleFonts(
383
- store ? Object.values(store.nodes).flatMap((n) => n.fontFamily ? [n.fontFamily] : []) : []
384
- );
385
- if (!store) return;
386
- for (const [sectionId, override] of Object.entries(store.sections)) {
387
- const sections = document.querySelectorAll(
388
- `[data-ohw-section="${CSS.escape(sectionId)}"]`
389
- );
390
- for (const section of Array.from(sections)) {
391
- for (const [prop, attr] of Object.entries(SECTION_ATTRS)) {
392
- const value = override[prop];
393
- if (value === void 0) continue;
394
- if (prop === "sectionBackground" && override.sectionBackgroundColor !== void 0) continue;
395
- section.setAttribute(attr, String(value).replace(":", "-"));
396
- }
397
- if (override.sectionBackgroundColor !== void 0) {
398
- saveInline(section, "background");
399
- section.style.setProperty("background", override.sectionBackgroundColor, "important");
400
- section.setAttribute("data-ohw-style-bgcolor", "");
401
- }
402
- }
403
- }
404
- for (const [key, override] of Object.entries(store.nodes)) {
405
- const nodes = document.querySelectorAll(`[data-ohw-key="${CSS.escape(key)}"]`);
406
- for (const el of Array.from(nodes)) {
407
- if (override.color !== void 0) {
408
- saveInline(el, "color");
409
- el.style.setProperty("color", override.color, "important");
410
- el.setAttribute(NODE_WROTE_ATTR, "");
411
- }
412
- if (override.fontFamily !== void 0) {
413
- saveInline(el, "font-family");
414
- el.style.setProperty("font-family", `'${override.fontFamily}'`, "important");
415
- el.setAttribute(NODE_WROTE_ATTR, "");
416
- }
417
- if (override.fontSize !== void 0) {
418
- saveInline(el, "font-size");
419
- el.style.setProperty("font-size", `${override.fontSize}px`, "important");
420
- el.setAttribute(NODE_WROTE_ATTR, "");
421
- }
422
- if (override.buttonBackground !== void 0 || override.buttonText !== void 0) {
423
- const surface = buttonSurfaceOf(el);
424
- if (override.buttonBackground !== void 0) {
425
- saveInline(surface, "background");
426
- surface.style.setProperty("background", override.buttonBackground, "important");
427
- }
428
- if (override.buttonText !== void 0) {
429
- saveInline(surface, "color");
430
- surface.style.setProperty("color", override.buttonText, "important");
431
- }
432
- surface.setAttribute(NODE_WROTE_ATTR, "");
433
- }
434
- }
435
- }
436
- }
437
-
438
121
  // src/ui/ai-tree/aiSectionsManager.tsx
439
122
  import { flushSync } from "react-dom";
440
123
  import { createRoot } from "react-dom/client";
@@ -449,8 +132,7 @@ function lucideByName(name) {
449
132
  }
450
133
  var typeStyle = (spec, font) => ({
451
134
  fontFamily: font,
452
- // Headings shrink with the viewport (reaching full size around ~900px wide); body copy stays put.
453
- 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,
135
+ fontSize: spec.size,
454
136
  lineHeight: spec.line,
455
137
  fontWeight: spec.weight
456
138
  });
@@ -465,36 +147,6 @@ var FEATURE_LINE_CSS = [
465
147
  `background-color:currentColor;-webkit-mask:url("${CHECK_MASK}") no-repeat 0 0/24px 24px;`,
466
148
  `mask:url("${CHECK_MASK}") no-repeat 0 0/24px 24px}`
467
149
  ].join("");
468
- function hexLuminance(color) {
469
- const m = /^#([0-9a-f]{6})$/i.exec(color.trim());
470
- if (!m) return null;
471
- const [r2, g, b] = [0, 2, 4].map((i) => {
472
- const c = parseInt(m[1].slice(i, i + 2), 16) / 255;
473
- return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
474
- });
475
- return 0.2126 * r2 + 0.7152 * g + 0.0722 * b;
476
- }
477
- function hexContrast(a, b) {
478
- const la = hexLuminance(a);
479
- const lb = hexLuminance(b);
480
- if (la === null || lb === null) return null;
481
- const [hi, lo] = la > lb ? [la, lb] : [lb, la];
482
- return (hi + 0.05) / (lo + 0.05);
483
- }
484
- function accentBandContext(brand) {
485
- const p = brand.palette;
486
- const lightWins = (hexContrast(p.light, p.primary) ?? 99) >= (hexContrast(p.dark, p.primary) ?? 0);
487
- if (lightWins) {
488
- return {
489
- brand: { ...brand, palette: { dark: p.light, primary: p.light, accent: p.light, light: p.primary } },
490
- buttonLabel: p.primary
491
- };
492
- }
493
- return {
494
- brand: { ...brand, palette: { dark: p.dark, primary: p.dark, accent: p.dark, light: p.light } },
495
- buttonLabel: p.light
496
- };
497
- }
498
150
  function textAttrs(ctx, path) {
499
151
  return ctx.keyFor ? { "data-ohw-key": ctx.keyFor(path), "data-ohw-editable": "text" } : {};
500
152
  }
@@ -506,12 +158,7 @@ var AI_RESPONSIVE_CSS = [
506
158
  "@media (max-width: 640px) {",
507
159
  " [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
508
160
  " [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
509
- // Group containers flatten to a column on phones; span placements come along for free.
510
- " [data-ai-responsive] [data-ai-group] { display: flex !important; flex-direction: column !important; }",
511
- " [data-ai-responsive] [data-ai-group] > * { grid-column: auto !important; }",
512
161
  " [data-ai-responsive] [data-ai-section-inner] { padding-left: 20px !important; padding-right: 20px !important; }",
513
- " [data-ai-responsive] { overflow-x: hidden; }",
514
- " [data-ai-responsive] img { max-width: 100%; }",
515
162
  "}"
516
163
  ].join("\n");
517
164
  var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
@@ -589,7 +236,7 @@ function ButtonEl({
589
236
  }) {
590
237
  const secondary = slots.variant === "secondary";
591
238
  const href = str(slots.href);
592
- const linkAttrs = ctx.keyFor && path ? { "data-ohw-href-key": ctx.keyFor(`${path}.href`), "data-ohw-role": "button" } : {};
239
+ const linkAttrs = ctx.keyFor && path ? { "data-ohw-href-key": ctx.keyFor(`${path}.href`) } : {};
593
240
  return /* @__PURE__ */ jsx(
594
241
  "a",
595
242
  {
@@ -605,7 +252,7 @@ function ButtonEl({
605
252
  textDecoration: "none",
606
253
  cursor: "pointer",
607
254
  ...typeStyle(AI_TREE_TOKENS.type.button, ctx.brand.fonts.body),
608
- ...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 }
255
+ ...secondary ? { border: `1px solid ${ctx.brand.palette.dark}`, color: ctx.brand.palette.dark } : { background: ctx.brand.palette.primary, color: AI_TREE_TOKENS.textPrimaryForeground }
609
256
  },
610
257
  children: /* @__PURE__ */ jsx("span", { ...path ? textAttrs(ctx, `${path}.label`) : {}, children: str(slots.label) })
611
258
  }
@@ -1111,24 +758,7 @@ function CardBlock({ node, ctx, path }) {
1111
758
  minWidth: 0
1112
759
  },
1113
760
  children: [
1114
- media && (horizontal ? /* @__PURE__ */ jsx(
1115
- "div",
1116
- {
1117
- style: (
1118
- // An icon hugs its glyph — flex:1 gave a 48px icon half the card and pushed the
1119
- // text to the far side. Photos keep the half-and-half split. The inset has no
1120
- // inner padding (the photo split absorbed that), so the icon carries its own gap.
1121
- /^(lucide|simple):/.test(mediaRef) ? {
1122
- flexShrink: 0,
1123
- display: "flex",
1124
- alignItems: "center",
1125
- padding: mediaInset,
1126
- [mediaPosition === "right" ? "marginLeft" : "marginRight"]: 20
1127
- } : { flex: 1, minWidth: 0, display: "flex", alignItems: "center", padding: mediaInset }
1128
- ),
1129
- children: media
1130
- }
1131
- ) : /* @__PURE__ */ jsx(
761
+ media && (horizontal ? /* @__PURE__ */ jsx("div", { style: { flex: 1, minWidth: 0, display: "flex", alignItems: "center", padding: mediaInset }, children: media }) : /* @__PURE__ */ jsx(
1132
762
  "div",
1133
763
  {
1134
764
  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" },
@@ -1219,44 +849,13 @@ function AccordionBlock({ node, ctx, path }) {
1219
849
  ) })
1220
850
  ] }, i)) });
1221
851
  }
1222
- function useIsMobile() {
1223
- const [mobile, setMobile] = React.useState(
1224
- () => typeof window !== "undefined" && window.matchMedia("(max-width: 768px)").matches
1225
- );
1226
- React.useEffect(() => {
1227
- const mq = window.matchMedia("(max-width: 768px)");
1228
- const update = () => setMobile(mq.matches);
1229
- update();
1230
- mq.addEventListener("change", update);
1231
- return () => mq.removeEventListener("change", update);
1232
- }, []);
1233
- return mobile;
1234
- }
1235
852
  function Carousel({ items, itemsPerRow, ctx }) {
1236
- const isMobile = useIsMobile();
1237
- const perPage = isMobile ? 1 : itemsPerRow;
1238
- const pages = Math.max(1, Math.ceil(items.length / perPage));
1239
853
  const [page, setPage] = React.useState(0);
854
+ const pages = Math.max(1, Math.ceil(items.length / itemsPerRow));
1240
855
  const current = Math.min(page, pages - 1);
1241
- if (pages <= 1) {
1242
- const cols = Math.max(1, Math.min(items.length, itemsPerRow));
1243
- return /* @__PURE__ */ jsx(
1244
- "div",
1245
- {
1246
- "data-ai-grid": String(cols),
1247
- style: {
1248
- display: "grid",
1249
- gridTemplateColumns: `repeat(${cols}, 1fr)`,
1250
- gap: AI_TREE_TOKENS.spacing8,
1251
- alignItems: "start"
1252
- },
1253
- children: items
1254
- }
1255
- );
1256
- }
1257
856
  const pageGroups = Array.from(
1258
857
  { length: pages },
1259
- (_, p) => items.slice(p * perPage, (p + 1) * perPage)
858
+ (_, p) => items.slice(p * itemsPerRow, (p + 1) * itemsPerRow)
1260
859
  );
1261
860
  const chrome = (enabled) => ({
1262
861
  border: `1px solid ${ctx.brand.palette.dark}`,
@@ -1281,69 +880,55 @@ function Carousel({ items, itemsPerRow, ctx }) {
1281
880
  cursor: "pointer",
1282
881
  padding: 0
1283
882
  });
1284
- const viewport = /* @__PURE__ */ jsx("div", { style: { flex: isMobile ? "0 0 auto" : 1, minWidth: 0, width: "100%", overflow: "hidden" }, children: /* @__PURE__ */ jsx(
1285
- "div",
1286
- {
1287
- style: {
1288
- display: "flex",
1289
- transform: `translateX(-${current * 100}%)`,
1290
- transition: "transform 0.4s ease"
1291
- },
1292
- children: pageGroups.map((group, p) => /* @__PURE__ */ jsx(
883
+ return /* @__PURE__ */ jsxs("div", { "data-ai-carousel": "", style: { display: "flex", flexDirection: "column", gap: AI_TREE_TOKENS.spacing8, alignItems: "center" }, children: [
884
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: AI_TREE_TOKENS.spacing6, width: "100%" }, children: [
885
+ /* @__PURE__ */ jsx(
886
+ "button",
887
+ {
888
+ type: "button",
889
+ "aria-label": "Previous",
890
+ onClick: () => setPage((p) => Math.max(0, p - 1)),
891
+ style: chrome(current > 0),
892
+ children: /* @__PURE__ */ jsx(ArrowLeft, { size: 24, strokeWidth: 1.5, "aria-hidden": true })
893
+ }
894
+ ),
895
+ /* @__PURE__ */ jsx("div", { style: { flex: 1, minWidth: 0, overflow: "hidden" }, children: /* @__PURE__ */ jsx(
1293
896
  "div",
1294
897
  {
1295
- "data-ai-grid": String(perPage),
1296
898
  style: {
1297
- flex: "0 0 100%",
1298
- display: "grid",
1299
- gridTemplateColumns: `repeat(${perPage}, 1fr)`,
1300
- gap: AI_TREE_TOKENS.spacing8,
1301
- alignItems: "start"
899
+ display: "flex",
900
+ transform: `translateX(-${current * 100}%)`,
901
+ transition: "transform 0.4s ease"
1302
902
  },
1303
- children: group
1304
- },
1305
- p
1306
- ))
1307
- }
1308
- ) });
1309
- const prevBtn = /* @__PURE__ */ jsx(
1310
- "button",
1311
- {
1312
- type: "button",
1313
- "aria-label": "Previous",
1314
- onClick: () => setPage((p) => Math.max(0, p - 1)),
1315
- style: chrome(current > 0),
1316
- children: /* @__PURE__ */ jsx(ArrowLeft, { size: 24, strokeWidth: 1.5, "aria-hidden": true })
1317
- }
1318
- );
1319
- const nextBtn = /* @__PURE__ */ jsx(
1320
- "button",
1321
- {
1322
- type: "button",
1323
- "aria-label": "Next",
1324
- onClick: () => setPage((p) => Math.min(pages - 1, p + 1)),
1325
- style: chrome(current < pages - 1),
1326
- children: /* @__PURE__ */ jsx(ArrowRight, { size: 24, strokeWidth: 1.5, "aria-hidden": true })
1327
- }
1328
- );
1329
- const dots = /* @__PURE__ */ jsx("div", { style: { display: "flex", gap: 9, justifyContent: "center" }, children: pageGroups.map((_, p) => /* @__PURE__ */ jsx("button", { type: "button", "aria-label": `Page ${p + 1}`, onClick: () => setPage(p), style: dot(p === current) }, p)) });
1330
- if (isMobile) {
1331
- return /* @__PURE__ */ jsxs("div", { "data-ai-carousel": "", style: { display: "flex", flexDirection: "column", gap: AI_TREE_TOKENS.spacing6, alignItems: "center" }, children: [
1332
- viewport,
1333
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: AI_TREE_TOKENS.spacing6, justifyContent: "center" }, children: [
1334
- prevBtn,
1335
- nextBtn
1336
- ] }),
1337
- dots
1338
- ] });
1339
- }
1340
- return /* @__PURE__ */ jsxs("div", { "data-ai-carousel": "", style: { display: "flex", flexDirection: "column", gap: AI_TREE_TOKENS.spacing8, alignItems: "center" }, children: [
1341
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: AI_TREE_TOKENS.spacing6, width: "100%" }, children: [
1342
- prevBtn,
1343
- viewport,
1344
- nextBtn
903
+ children: pageGroups.map((group, p) => /* @__PURE__ */ jsx(
904
+ "div",
905
+ {
906
+ "data-ai-grid": String(itemsPerRow),
907
+ style: {
908
+ flex: "0 0 100%",
909
+ display: "grid",
910
+ gridTemplateColumns: `repeat(${itemsPerRow}, 1fr)`,
911
+ gap: AI_TREE_TOKENS.spacing8,
912
+ alignItems: "start"
913
+ },
914
+ children: group
915
+ },
916
+ p
917
+ ))
918
+ }
919
+ ) }),
920
+ /* @__PURE__ */ jsx(
921
+ "button",
922
+ {
923
+ type: "button",
924
+ "aria-label": "Next",
925
+ onClick: () => setPage((p) => Math.min(pages - 1, p + 1)),
926
+ style: chrome(current < pages - 1),
927
+ children: /* @__PURE__ */ jsx(ArrowRight, { size: 24, strokeWidth: 1.5, "aria-hidden": true })
928
+ }
929
+ )
1345
930
  ] }),
1346
- dots
931
+ pages > 1 && /* @__PURE__ */ jsx("div", { style: { display: "flex", gap: 9, justifyContent: "center" }, children: pageGroups.map((_, p) => /* @__PURE__ */ jsx("button", { type: "button", "aria-label": `Page ${p + 1}`, onClick: () => setPage(p), style: dot(p === current) }, p)) })
1347
932
  ] });
1348
933
  }
1349
934
  function CollectionBlock({ node, ctx, path }) {
@@ -1437,49 +1022,6 @@ function renderNode(node, ctx, path) {
1437
1022
  switch (node.type) {
1438
1023
  case "text":
1439
1024
  return /* @__PURE__ */ jsx(TextBlock, { slots, ctx, path });
1440
- // Layout container: arranges child blocks, contributes no content of its own. `grid` is a
1441
- // nested 12-column grid the children span (a collage is 3–5 media on it, bottom-aligned so
1442
- // mixed aspects read as a composition); `split` is exactly two children at a ratio; `stack`
1443
- // is a column. Children render through this same dispatcher, so edit markers, media
1444
- // resolution, and copy paths all work unchanged inside a group.
1445
- case "group": {
1446
- const layout = str(slots.layout);
1447
- const gap = slots.spacing === "tight" ? AI_TREE_TOKENS.spacing3 : slots.spacing === "airy" ? AI_TREE_TOKENS.spacing8 : AI_TREE_TOKENS.spacing6;
1448
- const kids = (node.children ?? []).map((child, i) => /* @__PURE__ */ jsx(
1449
- "div",
1450
- {
1451
- style: layout === "grid" ? {
1452
- gridColumn: `span ${typeof child.span === "number" ? Math.min(12, Math.max(1, child.span)) : 12}`,
1453
- minWidth: 0
1454
- } : { minWidth: 0 },
1455
- children: renderNode(child, ctx, `${path}.c${i}`)
1456
- },
1457
- i
1458
- ));
1459
- if (layout === "grid") {
1460
- return /* @__PURE__ */ jsx(
1461
- "div",
1462
- {
1463
- "data-ai-group": "grid",
1464
- style: { display: "grid", gridTemplateColumns: "repeat(12, 1fr)", gap, alignItems: "end" },
1465
- children: kids
1466
- }
1467
- );
1468
- }
1469
- if (layout === "split") {
1470
- const ratio = str(slots.ratio);
1471
- const cols = ratio === "3:5" ? "3fr 5fr" : ratio === "5:3" ? "5fr 3fr" : "1fr 1fr";
1472
- return /* @__PURE__ */ jsx(
1473
- "div",
1474
- {
1475
- "data-ai-group": "split",
1476
- style: { display: "grid", gridTemplateColumns: cols, gap, alignItems: "center" },
1477
- children: kids
1478
- }
1479
- );
1480
- }
1481
- return /* @__PURE__ */ jsx("div", { "data-ai-group": "stack", style: { display: "flex", flexDirection: "column", gap }, children: kids });
1482
- }
1483
1025
  case "button":
1484
1026
  return /* @__PURE__ */ jsx(ButtonEl, { slots, ctx, path });
1485
1027
  case "button-row":
@@ -1560,81 +1102,33 @@ function renderNode(node, ctx, path) {
1560
1102
  }
1561
1103
  );
1562
1104
  }
1563
- case "form": {
1564
- const formAttrs = ctx.keyFor ? {
1565
- "data-ohw-editable": "form",
1566
- "data-ohw-key": ctx.keyFor(`${path}.form`),
1567
- "data-ohw-success-text": "Thanks \u2014 we'll be in touch shortly."
1568
- } : {};
1569
- const fieldStyle = {
1570
- width: "100%",
1571
- boxSizing: "border-box",
1572
- border: `1px solid ${ctx.brand.palette.accent}`,
1573
- borderRadius: AI_TREE_TOKENS.radiusButton,
1574
- padding: "12px 14px",
1575
- background: "#fff",
1576
- color: ctx.brand.palette.dark,
1577
- outline: "none",
1578
- ...typeStyle(AI_TREE_TOKENS.type.bodyM, ctx.brand.fonts.body)
1579
- };
1580
- return /* @__PURE__ */ jsx("form", { ...formAttrs, style: { display: "flex", flexDirection: "column", gap: AI_TREE_TOKENS.spacing4 }, children: (node.children ?? []).map((child, i) => {
1105
+ case "form":
1106
+ return /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", gap: AI_TREE_TOKENS.spacing4 }, children: (node.children ?? []).map((child, i) => {
1581
1107
  if (child.type === "input") {
1582
- const cs2 = child.slots ?? {};
1583
- const kind = str(cs2.kind);
1584
- const label = str(cs2.label);
1585
- const placeholder = str(cs2.placeholder);
1586
- const name = label.toLowerCase().replace(/[^a-z0-9]+/gu, "-").replace(/^-+|-+$/gu, "") || `field-${i}`;
1587
- return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
1108
+ const cs = child.slots ?? {};
1109
+ return /* @__PURE__ */ jsxs("div", { children: [
1588
1110
  /* @__PURE__ */ jsx(
1589
- "label",
1111
+ "div",
1590
1112
  {
1591
1113
  ...textAttrs(ctx, `${path}.c${i}.label`),
1592
- style: {
1593
- ...typeStyle(AI_TREE_TOKENS.type.bodyMBold, ctx.brand.fonts.body),
1594
- color: ctx.brand.palette.dark
1595
- },
1596
- children: label
1114
+ style: { ...typeStyle(AI_TREE_TOKENS.type.bodyMBold, ctx.brand.fonts.body), color: ctx.brand.palette.dark, marginBottom: 6 },
1115
+ children: str(cs.label)
1597
1116
  }
1598
1117
  ),
1599
- kind === "textarea" ? /* @__PURE__ */ jsx(
1600
- "textarea",
1601
- {
1602
- name,
1603
- placeholder,
1604
- style: { ...fieldStyle, height: 140, resize: "vertical" }
1605
- }
1606
- ) : /* @__PURE__ */ jsx(
1607
- "input",
1118
+ /* @__PURE__ */ jsx(
1119
+ "div",
1608
1120
  {
1609
- name,
1610
- type: kind === "email" ? "email" : "text",
1611
- placeholder,
1612
- style: { ...fieldStyle, height: 48 }
1121
+ style: {
1122
+ border: `1px solid ${ctx.brand.palette.accent}`,
1123
+ borderRadius: AI_TREE_TOKENS.radiusButton,
1124
+ height: cs.kind === "textarea" ? 96 : 42
1125
+ }
1613
1126
  }
1614
1127
  )
1615
1128
  ] }, i);
1616
1129
  }
1617
- const cs = child.slots ?? {};
1618
- return /* @__PURE__ */ jsx(
1619
- "button",
1620
- {
1621
- type: "submit",
1622
- style: {
1623
- alignSelf: "flex-start",
1624
- border: "none",
1625
- cursor: "pointer",
1626
- padding: `${AI_TREE_TOKENS.spacing3}px ${AI_TREE_TOKENS.spacing6}px`,
1627
- borderRadius: AI_TREE_TOKENS.radiusButton,
1628
- background: ctx.brand.palette.primary,
1629
- color: AI_TREE_TOKENS.textPrimaryForeground,
1630
- ...typeStyle(AI_TREE_TOKENS.type.button, ctx.brand.fonts.body)
1631
- },
1632
- children: /* @__PURE__ */ jsx("span", { ...textAttrs(ctx, `${path}.c${i}.label`), children: str(cs.label) })
1633
- },
1634
- i
1635
- );
1130
+ return /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(ButtonEl, { slots: child.slots ?? {}, ctx, path: `${path}.c${i}` }) }, i);
1636
1131
  }) });
1637
- }
1638
1132
  case "schedule-widget":
1639
1133
  return /* @__PURE__ */ jsx(
1640
1134
  "div",
@@ -1660,14 +1154,11 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1660
1154
  return null;
1661
1155
  }
1662
1156
  const resolvedBrand = brand ?? AI_DEFAULT_BRAND;
1663
- const band = (tree.settings ?? {}).sectionBackground === "accent" ? accentBandContext(resolvedBrand) : null;
1664
- const blockBrand = band?.brand ?? resolvedBrand;
1665
1157
  const ctx = {
1666
- brand: blockBrand,
1158
+ brand: resolvedBrand,
1667
1159
  resolveMedia: resolveMedia ?? (() => null),
1668
- cardSurface: blockBrand.palette.light.toUpperCase() === AI_DEFAULT_BRAND.palette.light.toUpperCase() ? "#ECECEB" : `color-mix(in srgb, ${blockBrand.palette.light} 90%, ${blockBrand.palette.dark})`,
1669
- keyFor: editKeyPrefix ? (path) => `${editKeyPrefix}.${path}` : null,
1670
- ...band ? { buttonLabel: band.buttonLabel } : {}
1160
+ cardSurface: resolvedBrand.palette.light.toUpperCase() === AI_DEFAULT_BRAND.palette.light.toUpperCase() ? "#ECECEB" : `color-mix(in srgb, ${resolvedBrand.palette.light} 90%, ${resolvedBrand.palette.dark})`,
1161
+ keyFor: editKeyPrefix ? (path) => `${editKeyPrefix}.${path}` : null
1671
1162
  };
1672
1163
  const settings = tree.settings ?? {};
1673
1164
  const pad = AI_TREE_TOKENS.sectionPadding[settings.spacing ?? "balanced"];
@@ -1675,20 +1166,6 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1675
1166
  const overlayAlpha = { low: 0.25, medium: 0.45, high: 0.65 }[settings.overlayIntensity ?? "medium"];
1676
1167
  const backgroundUrl = isOverlay && settings.backgroundMedia ? ctx.resolveMedia(settings.backgroundMedia) : null;
1677
1168
  const bgAttrs = ctx.keyFor && isOverlay && settings.backgroundMedia ? { "data-ohw-key": ctx.keyFor("settings.backgroundMedia"), "data-ohw-editable": "bg-image" } : {};
1678
- const toneBackground = (() => {
1679
- const { dark, primary, light } = resolvedBrand.palette;
1680
- switch (settings.sectionBackground) {
1681
- case "surface":
1682
- return `color-mix(in srgb, ${light} 94%, ${dark})`;
1683
- case "accent":
1684
- return primary;
1685
- case "accent-soft":
1686
- return `color-mix(in srgb, ${primary} 12%, ${light})`;
1687
- default:
1688
- return void 0;
1689
- }
1690
- })();
1691
- const distributed = !isOverlay && settings.textDistribution;
1692
1169
  return /* @__PURE__ */ jsxs(
1693
1170
  "section",
1694
1171
  {
@@ -1698,11 +1175,10 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1698
1175
  style: {
1699
1176
  position: "relative",
1700
1177
  padding: `${pad}px 0`,
1701
- background: toneBackground ?? (isOverlay && !backgroundUrl ? "#DBEAFE" : void 0),
1178
+ background: isOverlay && !backgroundUrl ? "#DBEAFE" : void 0,
1702
1179
  backgroundImage: backgroundUrl ? `url(${backgroundUrl})` : void 0,
1703
1180
  backgroundSize: "cover",
1704
- backgroundPosition: "center",
1705
- color: settings.sectionBackground === "accent" ? blockBrand.palette.dark : void 0
1181
+ backgroundPosition: "center"
1706
1182
  },
1707
1183
  children: [
1708
1184
  /* @__PURE__ */ jsx("style", { children: AI_RESPONSIVE_CSS }),
@@ -1726,24 +1202,10 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1726
1202
  display: "grid",
1727
1203
  gridTemplateColumns: "repeat(12, 1fr)",
1728
1204
  gap: AI_TREE_TOKENS.spacing6,
1729
- alignItems: distributed === "space-between" ? "stretch" : (distributed ?? settings.verticalPosition) === "top" ? "start" : "center",
1205
+ alignItems: settings.verticalPosition === "top" ? "start" : "center",
1730
1206
  marginTop: r2 > 0 ? AI_TREE_TOKENS.spacing8 : 0
1731
1207
  },
1732
- children: (row.blocks ?? []).map((block, b) => /* @__PURE__ */ jsx(
1733
- "div",
1734
- {
1735
- "data-ai-cell": "",
1736
- style: {
1737
- gridColumn: `span ${Math.min(12, Math.max(1, block.span))}`,
1738
- minWidth: 0,
1739
- // space-between: each column becomes a flex column whose content spreads over
1740
- // the full row height instead of clumping at the top.
1741
- ...distributed === "space-between" ? { display: "flex", flexDirection: "column", justifyContent: "space-between" } : {}
1742
- },
1743
- children: renderNode(block, ctx, `r${r2}.b${b}`)
1744
- },
1745
- b
1746
- ))
1208
+ children: (row.blocks ?? []).map((block, b) => /* @__PURE__ */ jsx("div", { style: { gridColumn: `span ${Math.min(12, Math.max(1, block.span))}`, minWidth: 0 }, children: renderNode(block, ctx, `r${r2}.b${b}`) }, b))
1747
1209
  },
1748
1210
  r2
1749
1211
  ))
@@ -1759,36 +1221,17 @@ import { jsx as jsx2 } from "react/jsx-runtime";
1759
1221
  var CONTAINER_ATTR = "data-ohw-ai-generated";
1760
1222
  var REPLACED_ATTR = "data-ohw-ai-replaced-by";
1761
1223
  var REMOVED_ATTR = "data-ohw-ai-removed";
1762
- var TEMPLATE_HIDDEN_ATTR = "data-ohw-ai-template-hidden";
1763
- var CHROME_SECTION_IDS = /* @__PURE__ */ new Set(["navbar", "footer"]);
1764
- function readRootVar(name) {
1765
- if (typeof document === "undefined") return "";
1766
- return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
1767
- }
1768
- function deriveBrandOverride() {
1769
- const dark = readRootVar("--ohw-brand-dark");
1770
- const primary = readRootVar("--ohw-brand-primary");
1771
- const light = readRootVar("--ohw-brand-light");
1772
- if (!dark || !primary || !light) return null;
1773
- const accent = readRootVar("--ohw-brand-accent");
1774
- const heading = readRootVar("--font-heading") || readRootVar("--font-display");
1775
- const body = readRootVar("--font-body");
1776
- return {
1777
- palette: { dark, primary, accent: accent || dark, light },
1778
- fonts: {
1779
- heading: heading || AI_DEFAULT_BRAND.fonts.heading,
1780
- body: body || AI_DEFAULT_BRAND.fonts.body
1781
- }
1782
- };
1783
- }
1784
1224
  function deriveTemplateBrand() {
1785
- const dark = readRootVar("--color-dark");
1786
- const primary = readRootVar("--color-primary");
1787
- const light = readRootVar("--color-light");
1225
+ if (typeof document === "undefined") return null;
1226
+ const cs = getComputedStyle(document.documentElement);
1227
+ const read = (name) => cs.getPropertyValue(name).trim();
1228
+ const dark = read("--color-dark");
1229
+ const primary = read("--color-primary");
1230
+ const light = read("--color-light");
1788
1231
  if (!dark || !primary || !light) return null;
1789
- const accent = readRootVar("--color-accent");
1790
- const heading = readRootVar("--font-heading") || readRootVar("--font-display");
1791
- const body = readRootVar("--font-body");
1232
+ const accent = read("--color-accent");
1233
+ const heading = read("--font-heading") || read("--font-display");
1234
+ const body = read("--font-body");
1792
1235
  return {
1793
1236
  palette: { dark, primary, accent: accent || dark, light },
1794
1237
  fonts: {
@@ -1860,24 +1303,6 @@ function syncRemovedSections(state) {
1860
1303
  }
1861
1304
  }
1862
1305
  }
1863
- function syncTemplateHidden(state, pageHasSections) {
1864
- const hide = state.hideTemplate === true && pageHasSections;
1865
- for (const el of Array.from(document.querySelectorAll(`[${TEMPLATE_HIDDEN_ATTR}]`))) {
1866
- if (!hide) {
1867
- el.style.removeProperty("display");
1868
- el.removeAttribute(TEMPLATE_HIDDEN_ATTR);
1869
- }
1870
- }
1871
- if (!hide) return;
1872
- for (const el of Array.from(document.querySelectorAll("[data-ohw-section]"))) {
1873
- if (el.hasAttribute(CONTAINER_ATTR)) continue;
1874
- if (CHROME_SECTION_IDS.has(el.getAttribute("data-ohw-section") ?? "")) continue;
1875
- if (el.parentElement?.closest("[data-ohw-section]")) continue;
1876
- if (el.hasAttribute(REPLACED_ATTR) || el.hasAttribute(REMOVED_ATTR)) continue;
1877
- el.style.display = "none";
1878
- el.setAttribute(TEMPLATE_HIDDEN_ATTR, "");
1879
- }
1880
- }
1881
1306
  function syncReplacedOriginals(state) {
1882
1307
  for (const el of document.querySelectorAll(`[${REPLACED_ATTR}]`)) {
1883
1308
  const byId = el.getAttribute(REPLACED_ATTR) ?? "";
@@ -1896,40 +1321,10 @@ function syncReplacedOriginals(state) {
1896
1321
  }
1897
1322
  }
1898
1323
  }
1899
- function orderByChain(sections) {
1900
- const ids = new Set(sections.map((entry) => entry.id));
1901
- const after = /* @__PURE__ */ new Map();
1902
- const roots = [];
1903
- for (const entry of sections) {
1904
- const anchor = entry.replaces ?? entry.beforeSection ?? entry.afterSection ?? null;
1905
- if (anchor && ids.has(anchor)) {
1906
- const bucket = after.get(anchor);
1907
- if (bucket) bucket.push(entry);
1908
- else after.set(anchor, [entry]);
1909
- } else {
1910
- roots.push(entry);
1911
- }
1912
- }
1913
- const out = [];
1914
- const seen = /* @__PURE__ */ new Set();
1915
- const visit = (entry) => {
1916
- if (seen.has(entry.id)) return;
1917
- seen.add(entry.id);
1918
- out.push(entry);
1919
- for (const child of after.get(entry.id) ?? []) visit(child);
1920
- };
1921
- for (const root of roots) visit(root);
1922
- return out.length === sections.length ? out : sections;
1923
- }
1924
1324
  function applyAiSectionsToDom(state, options) {
1925
1325
  if (typeof document === "undefined") return;
1926
- const brandOverride = deriveBrandOverride();
1927
1326
  const templateBrand = deriveTemplateBrand();
1928
- const brandKey = brandOverride ? JSON.stringify(brandOverride) : "";
1929
- const pagePath = window.location.pathname;
1930
- const pageSections = state.sections.filter((entry) => (entry.path ?? "/") === pagePath);
1931
- const activeIds = new Set(pageSections.map((entry) => entry.id));
1932
- const ordered = state.hideTemplate === true ? orderByChain(pageSections) : pageSections;
1327
+ const activeIds = new Set(state.sections.map((entry) => entry.id));
1933
1328
  for (const [id, section] of mounted) {
1934
1329
  if (!activeIds.has(id)) {
1935
1330
  section.root.unmount();
@@ -1937,8 +1332,8 @@ function applyAiSectionsToDom(state, options) {
1937
1332
  mounted.delete(id);
1938
1333
  }
1939
1334
  }
1940
- for (const entry of ordered) {
1941
- const serialized = JSON.stringify(entry) + brandKey;
1335
+ for (const entry of state.sections) {
1336
+ const serialized = JSON.stringify(entry);
1942
1337
  const existing = mounted.get(entry.id);
1943
1338
  if (existing && existing.serialized === serialized && existing.container.isConnected) {
1944
1339
  continue;
@@ -1952,7 +1347,6 @@ function applyAiSectionsToDom(state, options) {
1952
1347
  mounted.delete(entry.id);
1953
1348
  }
1954
1349
  container.setAttribute("data-ohw-section", entry.id);
1955
- container.setAttribute("data-ohw-instance", entry.id);
1956
1350
  container.setAttribute("data-ohw-section-label", entry.label);
1957
1351
  placeContainer(container, entry);
1958
1352
  const root = mounted.get(entry.id)?.root ?? createRoot(container);
@@ -1963,7 +1357,7 @@ function applyAiSectionsToDom(state, options) {
1963
1357
  AiTreeRenderer,
1964
1358
  {
1965
1359
  tree: entry.tree,
1966
- brand: brandOverride ?? entry.brand ?? options?.brand ?? templateBrand ?? void 0,
1360
+ brand: entry.brand ?? options?.brand ?? templateBrand ?? void 0,
1967
1361
  resolveMedia,
1968
1362
  editKeyPrefix: `ai.${entry.id}`
1969
1363
  }
@@ -1972,20 +1366,8 @@ function applyAiSectionsToDom(state, options) {
1972
1366
  });
1973
1367
  mounted.set(entry.id, { root, container, serialized });
1974
1368
  }
1975
- if (state.hideTemplate === true) {
1976
- let prev = null;
1977
- for (const entry of ordered) {
1978
- const el = mounted.get(entry.id)?.container;
1979
- if (!el) continue;
1980
- if (prev && !(prev.compareDocumentPosition(el) & Node.DOCUMENT_POSITION_FOLLOWING)) {
1981
- prev.insertAdjacentElement("afterend", el);
1982
- }
1983
- prev = el;
1984
- }
1985
- }
1986
1369
  syncReplacedOriginals(state);
1987
1370
  syncRemovedSections(state);
1988
- syncTemplateHidden(state, pageSections.length > 0);
1989
1371
  }
1990
1372
 
1991
1373
  // src/useLinkHrefGuardian.ts
@@ -2592,7 +1974,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
2592
1974
  const autoId = useId();
2593
1975
  const insertAfter = insertAfterProp ?? autoId;
2594
1976
  const [schedule, setSchedule] = useState2(null);
2595
- const [loading, setLoading] = useState2(initialScheduleId !== null);
1977
+ const [loading, setLoading] = useState2(true);
2596
1978
  const [inEditor, setInEditor] = useState2(false);
2597
1979
  const [isHovered, setIsHovered] = useState2(false);
2598
1980
  const [modalState, setModalState] = useState2(null);
@@ -2766,10 +2148,8 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
2766
2148
  "*"
2767
2149
  );
2768
2150
  };
2151
+ if (!inEditor && !loading && !schedule) return null;
2769
2152
  const sectionId = `scheduling-${insertAfter}`;
2770
- if (!inEditor && !loading && !schedule) {
2771
- return /* @__PURE__ */ jsx4("section", { "data-ohw-section": sectionId, "data-ohw-scheduling-anchor": insertAfter, style: { display: "none" } });
2772
- }
2773
2153
  return /* @__PURE__ */ jsxs3(
2774
2154
  "section",
2775
2155
  {
@@ -7686,17 +7066,13 @@ function MediaOverlay({
7686
7066
  hover,
7687
7067
  isUploading,
7688
7068
  fadingOut = false,
7689
- selected = false,
7690
- hovered = false,
7691
7069
  onFadeOutComplete,
7692
7070
  onReplace,
7693
- onSelect,
7694
7071
  onVideoSettingsChange
7695
7072
  }) {
7696
7073
  const { rect } = hover;
7697
7074
  const skeletonRef = React8.useRef(null);
7698
7075
  const isVideo = hover.elementType === "video";
7699
- const showChrome = !selected || hovered;
7700
7076
  const autoplay = hover.videoAutoplay ?? true;
7701
7077
  const muted = hover.videoMuted ?? true;
7702
7078
  const probeRef = React8.useRef(null);
@@ -7710,7 +7086,6 @@ function MediaOverlay({
7710
7086
  (prev) => prev && prev.fullWidth === width && prev.height === height ? prev : { fullWidth: width, height }
7711
7087
  );
7712
7088
  }, [isVideo]);
7713
- const replaceLabel = isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image";
7714
7089
  const replaceMode = buttonSize ? replaceButtonMode({ width: rect.width, height: rect.height }, buttonSize) : "none";
7715
7090
  const box = {
7716
7091
  position: "fixed",
@@ -7744,7 +7119,7 @@ function MediaOverlay({
7744
7119
  }
7745
7120
  );
7746
7121
  }
7747
- const settingsBar = isVideo && !hover.isDragOver && showChrome ? /* @__PURE__ */ jsxs7(
7122
+ const settingsBar = isVideo && !hover.isDragOver ? /* @__PURE__ */ jsxs7(
7748
7123
  "div",
7749
7124
  {
7750
7125
  "data-ohw-bridge": "",
@@ -7814,12 +7189,10 @@ function MediaOverlay({
7814
7189
  // in-document, pointer-events does it natively. The button below opts back in, so
7815
7190
  // Replace still works.
7816
7191
  pointerEvents: hover.hasTextOverlap ? "none" : "auto",
7817
- // Selected: a firm component ring with no wash, so the image reads as chosen rather
7818
- // than hovered. Hover keeps the existing tinted preview.
7819
- boxShadow: selected ? "inset 0 0 0 2px var(--color-primary)" : "inset 0 0 0 1.5px var(--color-primary)",
7820
- background: selected ? "transparent" : "color-mix(in srgb, var(--color-primary) 20%, transparent)"
7192
+ boxShadow: "inset 0 0 0 1.5px var(--color-primary)",
7193
+ background: "color-mix(in srgb, var(--color-primary) 20%, transparent)"
7821
7194
  },
7822
- onClick: () => onSelect && !selected ? onSelect(hover.key) : onReplace(hover.key),
7195
+ onClick: () => onReplace(hover.key),
7823
7196
  children: [
7824
7197
  /* @__PURE__ */ jsxs7(
7825
7198
  Button,
@@ -7840,17 +7213,17 @@ function MediaOverlay({
7840
7213
  },
7841
7214
  children: [
7842
7215
  isVideo ? /* @__PURE__ */ jsx15(Film, { size: 14 }) : /* @__PURE__ */ jsx15(ImageIcon, { size: 14 }),
7843
- replaceLabel
7216
+ isVideo ? "Replace video" : "Replace image"
7844
7217
  ]
7845
7218
  }
7846
7219
  ),
7847
- showChrome && replaceMode !== "none" && /* @__PURE__ */ jsxs7(
7220
+ replaceMode === "none" ? null : /* @__PURE__ */ jsxs7(
7848
7221
  Button,
7849
7222
  {
7850
7223
  "data-ohw-media-overlay": "",
7851
7224
  variant: "outline",
7852
7225
  size: "sm",
7853
- "aria-label": replaceLabel,
7226
+ "aria-label": isVideo ? "Replace video" : "Replace image",
7854
7227
  className: "gap-1.5 cursor-pointer hover:bg-background",
7855
7228
  style: {
7856
7229
  ...OVERLAY_BUTTON_STYLE,
@@ -7873,7 +7246,7 @@ function MediaOverlay({
7873
7246
  },
7874
7247
  children: [
7875
7248
  isVideo ? /* @__PURE__ */ jsx15(Film, { size: 14 }) : /* @__PURE__ */ jsx15(ImageIcon, { size: 14 }),
7876
- replaceMode === "full" ? replaceLabel : null
7249
+ replaceMode === "full" ? isVideo ? "Replace video" : "Replace image" : null
7877
7250
  ]
7878
7251
  }
7879
7252
  )
@@ -7944,9 +7317,6 @@ import { Check, X } from "lucide-react";
7944
7317
 
7945
7318
  // src/lib/sections.ts
7946
7319
  var LINK_PICKER_EXCLUDED_IDS = /* @__PURE__ */ new Set(["navbar", "footer"]);
7947
- function isChromeSection(el) {
7948
- return el.matches("header, nav, footer, aside");
7949
- }
7950
7320
  function titleCaseSectionId(id) {
7951
7321
  return id.split("-").filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
7952
7322
  }
@@ -7957,8 +7327,6 @@ function parseSectionsFromRoot(root) {
7957
7327
  const id = el.getAttribute("data-ohw-section") ?? "";
7958
7328
  if (!id || seen.has(id) || LINK_PICKER_EXCLUDED_IDS.has(id)) continue;
7959
7329
  if (el.parentElement?.closest("[data-ohw-section]")) continue;
7960
- if (el.hasAttribute("data-ohw-ai-template-hidden") || el.hasAttribute("data-ohw-ai-removed") || el.hasAttribute("data-ohw-ai-replaced-by"))
7961
- continue;
7962
7330
  seen.add(id);
7963
7331
  const label = el.getAttribute("data-ohw-section-label") ?? titleCaseSectionId(id);
7964
7332
  sections.push({ id, label });
@@ -7974,169 +7342,21 @@ function parseSectionsFromHtml(html) {
7974
7342
  return parseSectionsFromRoot(doc);
7975
7343
  }
7976
7344
 
7977
- // src/lib/section-instances.ts
7978
- var SECTION_ORDER_KEY = "__ohw_section_order";
7979
- var REMOVED_ATTR2 = "data-ohw-section-removed";
7980
- function isRemovedSection(el) {
7981
- return el.hasAttribute(REMOVED_ATTR2);
7982
- }
7983
- function topLevelSections() {
7984
- return Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
7985
- (el) => !el.parentElement?.closest("[data-ohw-section]") && !isChromeSection(el) && !isRemovedSection(el)
7986
- );
7987
- }
7988
- function instanceIdOf(el) {
7989
- return el.dataset.ohwInstance ?? el.dataset.ohwSection ?? "";
7990
- }
7991
- function planSectionMove(instanceId, targetIndex, currentPath) {
7992
- const sections = topLevelSections();
7993
- const index = sections.findIndex((el) => instanceIdOf(el) === instanceId);
7994
- if (index === -1) return null;
7995
- const dragged = sections[index];
7996
- const others = sections.filter((_, i) => i !== index);
7997
- const clamped = Math.max(0, Math.min(targetIndex, others.length));
7998
- const reordered = [...others.slice(0, clamped), dragged, ...others.slice(clamped)];
7999
- return reordered.map((el, order) => ({
8000
- instanceId: instanceIdOf(el),
8001
- type: el.getAttribute("data-ohw-section") ?? "",
8002
- order,
8003
- pagePath: currentPath
8004
- }));
8005
- }
8006
- function moveSectionInstance(instanceId, direction, currentPath) {
8007
- const sections = topLevelSections();
8008
- const index = sections.findIndex((el) => instanceIdOf(el) === instanceId);
8009
- if (index === -1) return null;
8010
- const siblingIndex = direction === "up" ? index - 1 : index + 1;
8011
- if (siblingIndex < 0 || siblingIndex >= sections.length) return null;
8012
- const entries = planSectionMove(instanceId, siblingIndex, currentPath);
8013
- if (!entries) return null;
8014
- applyPersistedOrder(entries);
8015
- return entries;
8016
- }
8017
- function syncRemovedFlags(entries) {
8018
- const removedIds = new Set(entries.filter((e) => e.removed).map((e) => e.instanceId));
8019
- document.querySelectorAll(`[${REMOVED_ATTR2}]`).forEach((el) => {
8020
- if (!removedIds.has(instanceIdOf(el))) {
8021
- el.style.removeProperty("display");
8022
- el.removeAttribute(REMOVED_ATTR2);
8023
- }
8024
- });
8025
- for (const id of removedIds) {
8026
- const el = document.querySelector(`[data-ohw-instance="${CSS.escape(id)}"]`);
8027
- if (el) {
8028
- el.style.display = "none";
8029
- el.setAttribute(REMOVED_ATTR2, "");
8030
- }
8031
- }
8032
- }
8033
- function applyPersistedOrder(entries) {
8034
- syncRemovedFlags(entries);
8035
- if (entries.length === 0) return;
8036
- const sections = topLevelSections();
8037
- if (sections.length === 0) return;
8038
- const orderIndex = new Map(entries.map((e) => [e.instanceId, e.order]));
8039
- const ordered = [...sections].sort((a, b) => {
8040
- const aOrder = orderIndex.get(instanceIdOf(a));
8041
- const bOrder = orderIndex.get(instanceIdOf(b));
8042
- if (aOrder === void 0 && bOrder === void 0) return 0;
8043
- if (aOrder === void 0) return 1;
8044
- if (bOrder === void 0) return -1;
8045
- return aOrder - bOrder;
8046
- });
8047
- let prev = null;
8048
- for (const el of ordered) {
8049
- if (prev) prev.after(el);
8050
- prev = el;
8051
- }
7345
+ // src/ui/ai-section/AiSectionOverlay.tsx
7346
+ import { Fragment as Fragment5, jsx as jsx17, jsxs as jsxs9 } from "react/jsx-runtime";
7347
+ function readRect(sectionId) {
7348
+ const el = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
7349
+ if (!el) return null;
7350
+ const r2 = el.getBoundingClientRect();
7351
+ if (r2.width <= 0 || r2.height <= 0) return null;
7352
+ return { top: r2.top, left: r2.left, width: r2.width, height: r2.height };
8052
7353
  }
8053
- function setSectionRemoved(instanceId, currentPath, existingEntries, removed) {
8054
- if (!document.querySelector(`[data-ohw-instance="${CSS.escape(instanceId)}"]`)) return null;
8055
- const byId = new Map(existingEntries.map((e) => [e.instanceId, e]));
8056
- const allSections = Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
8057
- (el) => !el.parentElement?.closest("[data-ohw-section]") && !isChromeSection(el)
8058
- );
8059
- allSections.forEach((el, order) => {
8060
- const id = instanceIdOf(el);
8061
- if (!byId.has(id)) {
8062
- byId.set(id, { instanceId: id, type: el.getAttribute("data-ohw-section") ?? "", order, pagePath: currentPath });
8063
- }
8064
- });
8065
- const target = byId.get(instanceId);
8066
- if (!target) return null;
8067
- byId.set(instanceId, { ...target, removed });
8068
- const entries = Array.from(byId.values());
8069
- applyPersistedOrder(entries);
8070
- return entries;
8071
- }
8072
- function deleteSectionInstance(instanceId, currentPath, existingEntries) {
8073
- return setSectionRemoved(instanceId, currentPath, existingEntries, true);
8074
- }
8075
- function restoreSectionInstance(instanceId, currentPath, existingEntries) {
8076
- return setSectionRemoved(instanceId, currentPath, existingEntries, false);
8077
- }
8078
- function newInstanceId() {
8079
- return typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `instance-${Date.now()}-${Math.random().toString(36).slice(2)}`;
8080
- }
8081
- function getPageSectionOrderEntries(raw, currentPath) {
8082
- if (!raw) return [];
8083
- try {
8084
- const entries = JSON.parse(raw);
8085
- return entries.filter((e) => !e.pagePath || e.pagePath === currentPath);
8086
- } catch {
8087
- return [];
8088
- }
8089
- }
8090
- function rekeySectionSubtree(root, instanceId) {
8091
- const suffix = `::${instanceId}`;
8092
- const rekey = (el, attr) => {
8093
- const current = el.getAttribute(attr);
8094
- if (current) el.setAttribute(attr, `${current}${suffix}`);
8095
- };
8096
- if (root.hasAttribute("data-ohw-key")) rekey(root, "data-ohw-key");
8097
- if (root.hasAttribute("data-ohw-href-key")) rekey(root, "data-ohw-href-key");
8098
- root.querySelectorAll("[data-ohw-key]").forEach((el) => rekey(el, "data-ohw-key"));
8099
- root.querySelectorAll("[data-ohw-href-key]").forEach((el) => rekey(el, "data-ohw-href-key"));
8100
- }
8101
- function initSectionInstancesFromContent(content, currentPath) {
8102
- document.querySelectorAll("[data-ohw-section]:not([data-ohw-instance])").forEach((el) => {
8103
- el.setAttribute("data-ohw-instance", el.getAttribute("data-ohw-section") ?? "");
8104
- });
8105
- const entries = getPageSectionOrderEntries(content[SECTION_ORDER_KEY], currentPath);
8106
- for (const entry of entries) {
8107
- if (entry.instanceId === entry.type) continue;
8108
- if (document.querySelector(`[data-ohw-instance="${CSS.escape(entry.instanceId)}"]`)) continue;
8109
- const original = document.querySelector(
8110
- `[data-ohw-section="${CSS.escape(entry.type)}"][data-ohw-instance="${CSS.escape(entry.type)}"]`
8111
- );
8112
- if (!original) continue;
8113
- const clone = original.cloneNode(true);
8114
- clone.setAttribute("data-ohw-instance", entry.instanceId);
8115
- rekeySectionSubtree(clone, entry.instanceId);
8116
- original.insertAdjacentElement("afterend", clone);
8117
- }
8118
- applyPersistedOrder(entries);
8119
- }
8120
-
8121
- // src/ui/ai-section/AiSectionOverlay.tsx
8122
- import { Fragment as Fragment5, jsx as jsx17, jsxs as jsxs9 } from "react/jsx-runtime";
8123
- function findSectionElement(instanceId) {
8124
- const escaped = CSS.escape(instanceId);
8125
- return document.querySelector(`[data-ohw-instance="${escaped}"]`) ?? document.querySelector(`[data-ohw-section="${escaped}"]:not([data-ohw-instance])`);
8126
- }
8127
- function readRect(instanceId) {
8128
- const el = findSectionElement(instanceId);
8129
- if (!el) return null;
8130
- const r2 = el.getBoundingClientRect();
8131
- if (r2.width <= 0 || r2.height <= 0) return null;
8132
- return { top: r2.top, left: r2.left, width: r2.width, height: r2.height };
8133
- }
8134
- function useLiveSectionRect(sectionId) {
8135
- const [rect, setRect] = useState5(null);
8136
- useEffect4(() => {
8137
- if (!sectionId) {
8138
- setRect(null);
8139
- return;
7354
+ function useLiveSectionRect(sectionId) {
7355
+ const [rect, setRect] = useState5(null);
7356
+ useEffect4(() => {
7357
+ if (!sectionId) {
7358
+ setRect(null);
7359
+ return;
8140
7360
  }
8141
7361
  const update = () => {
8142
7362
  const next = readRect(sectionId);
@@ -8148,7 +7368,7 @@ function useLiveSectionRect(sectionId) {
8148
7368
  const opts = { capture: true, passive: true };
8149
7369
  window.addEventListener("scroll", update, opts);
8150
7370
  window.addEventListener("resize", update);
8151
- const el = findSectionElement(sectionId);
7371
+ const el = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
8152
7372
  const ro = el ? new ResizeObserver(update) : null;
8153
7373
  if (el && ro) ro.observe(el);
8154
7374
  const interval = setInterval(update, 500);
@@ -8161,12 +7381,6 @@ function useLiveSectionRect(sectionId) {
8161
7381
  }, [sectionId]);
8162
7382
  return rect;
8163
7383
  }
8164
- function computeSectionBoundaryFlags(instanceId) {
8165
- const topLevel = topLevelSections();
8166
- const index = topLevel.findIndex((el) => (el.dataset.ohwInstance ?? el.dataset.ohwSection) === instanceId);
8167
- if (index === -1) return { isFirst: true, isLast: true };
8168
- return { isFirst: index === 0, isLast: index === topLevel.length - 1 };
8169
- }
8170
7384
  var PRIMARY2 = "#0885FE";
8171
7385
  function edgeAwareRadius(rect) {
8172
7386
  const container = window.innerWidth <= 480 ? 16 : 24;
@@ -8240,7 +7454,6 @@ function AiSectionOverlay({
8240
7454
  }) {
8241
7455
  const [selectedId, setSelectedId] = useState5(null);
8242
7456
  const [reviewId, setReviewId] = useState5(null);
8243
- const [reviewButtonsHidden, setReviewButtonsHidden] = useState5(false);
8244
7457
  const reviewIdRef = useRef4(null);
8245
7458
  reviewIdRef.current = reviewId;
8246
7459
  const selectedIdRef = useRef4(null);
@@ -8249,7 +7462,7 @@ function AiSectionOverlay({
8249
7462
  (el) => {
8250
7463
  postToParent2({
8251
7464
  type: "ow:section-selected",
8252
- sectionId: el ? el.dataset.ohwInstance ?? el.dataset.ohwSection ?? null : null,
7465
+ sectionId: el?.dataset.ohwSection ?? null,
8253
7466
  sectionLabel: el ? el.dataset.ohwSectionLabel ?? titleCaseSectionId(el.dataset.ohwSection ?? "") : null
8254
7467
  });
8255
7468
  },
@@ -8258,7 +7471,7 @@ function AiSectionOverlay({
8258
7471
  const selectFromElement = useCallback2(
8259
7472
  (el, options) => {
8260
7473
  const sectionEl = el?.closest("[data-ohw-section]") ?? null;
8261
- const id = sectionEl ? sectionEl.dataset.ohwInstance ?? sectionEl.dataset.ohwSection ?? null : null;
7474
+ const id = sectionEl?.dataset.ohwSection ?? null;
8262
7475
  if (id === selectedIdRef.current) return;
8263
7476
  setSelectedId(id);
8264
7477
  if (options?.report !== false) report(sectionEl);
@@ -8279,15 +7492,12 @@ function AiSectionOverlay({
8279
7492
  selectFromElement(sectionEl);
8280
7493
  return sectionEl != null;
8281
7494
  },
8282
- clear: () => {
8283
- setSelectedId(null);
8284
- report(null);
8285
- }
7495
+ clear: () => setSelectedId(null)
8286
7496
  };
8287
7497
  return () => {
8288
7498
  apiRef.current = null;
8289
7499
  };
8290
- }, [apiRef, selectFromElement, report]);
7500
+ }, [apiRef, selectFromElement]);
8291
7501
  useEffect4(() => {
8292
7502
  const onMessage = (e) => {
8293
7503
  if (e.data?.type === "ow:ai-select" && e.data.sectionId === null) {
@@ -8302,10 +7512,9 @@ function AiSectionOverlay({
8302
7512
  }
8303
7513
  const found = readRect(sectionId) != null;
8304
7514
  setReviewId(found ? sectionId : null);
8305
- setReviewButtonsHidden(e.data.hideButtons === true);
8306
7515
  postToParent2({ type: "ow:ai-review-started", sectionId, found });
8307
7516
  if (found) {
8308
- document.querySelector(`[data-ohw-instance="${CSS.escape(sectionId)}"]`)?.scrollIntoView({ behavior: "smooth", block: "nearest" });
7517
+ document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`)?.scrollIntoView({ behavior: "smooth", block: "nearest" });
8309
7518
  }
8310
7519
  }
8311
7520
  };
@@ -8324,7 +7533,7 @@ function AiSectionOverlay({
8324
7533
  return;
8325
7534
  }
8326
7535
  const sec = t.closest("[data-ohw-section]");
8327
- setHoveredId(sec ? sec.dataset.ohwInstance ?? sec.dataset.ohwSection ?? null : null);
7536
+ setHoveredId(sec?.dataset.ohwSection ?? null);
8328
7537
  };
8329
7538
  const onLeave = () => setHoveredId(null);
8330
7539
  document.addEventListener("mousemove", onMove, { passive: true });
@@ -8356,30 +7565,9 @@ function AiSectionOverlay({
8356
7565
  },
8357
7566
  [postToParent2]
8358
7567
  );
8359
- const activeSelectionId = reviewId ? null : selectedId;
8360
- const selectionRect = useLiveSectionRect(activeSelectionId);
7568
+ const selectionRect = useLiveSectionRect(reviewId ? null : selectedId);
8361
7569
  const reviewRect = useLiveSectionRect(reviewId);
8362
7570
  const hoverRect = useLiveSectionRect(reviewId || hoveredId === selectedId ? null : hoveredId);
8363
- useEffect4(() => {
8364
- const selectedEl = activeSelectionId ? findSectionElement(activeSelectionId) : null;
8365
- if (!activeSelectionId || !selectionRect || selectedEl && isChromeSection(selectedEl)) {
8366
- postToParent2({ type: "ow:section-rect", instanceId: null, rect: null });
8367
- return;
8368
- }
8369
- const { isFirst, isLast } = computeSectionBoundaryFlags(activeSelectionId);
8370
- postToParent2({
8371
- type: "ow:section-rect",
8372
- instanceId: activeSelectionId,
8373
- rect: {
8374
- top: selectionRect.top + window.scrollY,
8375
- left: selectionRect.left + window.scrollX,
8376
- width: selectionRect.width,
8377
- height: selectionRect.height
8378
- },
8379
- isFirst,
8380
- isLast
8381
- });
8382
- }, [activeSelectionId, selectionRect, postToParent2]);
8383
7571
  return /* @__PURE__ */ jsxs9(Fragment5, { children: [
8384
7572
  hoverRect && /* @__PURE__ */ jsx17(
8385
7573
  "div",
@@ -8430,16 +7618,13 @@ function AiSectionOverlay({
8430
7618
  border: `2px solid ${PRIMARY2}`,
8431
7619
  borderRadius: edgeAwareRadius(reviewRect),
8432
7620
  zIndex: 2147483200,
8433
- // The veil itself: swallows clicks so the section stays locked until decided. This
8434
- // stopPropagation only guards the bubble phase; the bridge's capture-phase click
8435
- // handlers additionally bail on `[data-ohw-ai-review]`, without which a click on
8436
- // Accept/Discard resolves to the media beneath and opens the file picker.
7621
+ // The veil itself: swallows clicks so the section stays locked until decided.
8437
7622
  background: "rgba(8, 133, 254, 0.04)",
8438
7623
  pointerEvents: "auto",
8439
7624
  cursor: "default"
8440
7625
  },
8441
7626
  onClick: (e) => e.stopPropagation(),
8442
- children: !reviewButtonsHidden && /* @__PURE__ */ jsxs9(
7627
+ children: /* @__PURE__ */ jsxs9(
8443
7628
  "div",
8444
7629
  {
8445
7630
  style: {
@@ -8462,6 +7647,47 @@ function AiSectionOverlay({
8462
7647
  ] });
8463
7648
  }
8464
7649
 
7650
+ // src/lib/section-instances.ts
7651
+ var SECTION_ORDER_KEY = "__ohw_section_order";
7652
+ function getPageSectionOrderEntries(raw, currentPath) {
7653
+ if (!raw) return [];
7654
+ try {
7655
+ const entries = JSON.parse(raw);
7656
+ return entries.filter((e) => !e.pagePath || e.pagePath === currentPath);
7657
+ } catch {
7658
+ return [];
7659
+ }
7660
+ }
7661
+ function rekeySectionSubtree(root, instanceId) {
7662
+ const suffix = `::${instanceId}`;
7663
+ const rekey = (el, attr) => {
7664
+ const current = el.getAttribute(attr);
7665
+ if (current) el.setAttribute(attr, `${current}${suffix}`);
7666
+ };
7667
+ if (root.hasAttribute("data-ohw-key")) rekey(root, "data-ohw-key");
7668
+ if (root.hasAttribute("data-ohw-href-key")) rekey(root, "data-ohw-href-key");
7669
+ root.querySelectorAll("[data-ohw-key]").forEach((el) => rekey(el, "data-ohw-key"));
7670
+ root.querySelectorAll("[data-ohw-href-key]").forEach((el) => rekey(el, "data-ohw-href-key"));
7671
+ }
7672
+ function initSectionInstancesFromContent(content, currentPath) {
7673
+ document.querySelectorAll("[data-ohw-section]:not([data-ohw-instance])").forEach((el) => {
7674
+ el.setAttribute("data-ohw-instance", el.getAttribute("data-ohw-section") ?? "");
7675
+ });
7676
+ const entries = getPageSectionOrderEntries(content[SECTION_ORDER_KEY], currentPath);
7677
+ for (const entry of entries) {
7678
+ if (entry.instanceId === entry.type) continue;
7679
+ if (document.querySelector(`[data-ohw-instance="${CSS.escape(entry.instanceId)}"]`)) continue;
7680
+ const original = document.querySelector(
7681
+ `[data-ohw-section="${CSS.escape(entry.type)}"][data-ohw-instance="${CSS.escape(entry.type)}"]`
7682
+ );
7683
+ if (!original) continue;
7684
+ const clone = original.cloneNode(true);
7685
+ clone.setAttribute("data-ohw-instance", entry.instanceId);
7686
+ rekeySectionSubtree(clone, entry.instanceId);
7687
+ original.insertAdjacentElement("afterend", clone);
7688
+ }
7689
+ }
7690
+
8465
7691
  // src/OhhwellsBridge.tsx
8466
7692
  import { createPortal as createPortal2 } from "react-dom";
8467
7693
  import { usePathname as usePathname2, useRouter as useRouter3, useSearchParams } from "next/navigation";
@@ -11006,13 +10232,8 @@ function referenceBox(slot) {
11006
10232
  const neighbour = row ? Array.from(row.querySelectorAll('[data-ohw-editable="icon"]')).find(
11007
10233
  (el) => el !== slot && !el.hasAttribute("data-ohw-social-icon-placeholder")
11008
10234
  ) : null;
11009
- if (neighbour) {
11010
- const box2 = neighbour.querySelector(GLYPH_SELECTOR)?.getBoundingClientRect();
11011
- if (box2?.width && box2.height) return box2;
11012
- }
11013
- const own = slot.getBoundingClientRect();
11014
- if (own.width && own.height) return own;
11015
- const box = slot.querySelector(GLYPH_SELECTOR)?.getBoundingClientRect() ?? null;
10235
+ const source = (neighbour ?? slot).querySelector(GLYPH_SELECTOR);
10236
+ const box = source?.getBoundingClientRect() ?? null;
11016
10237
  return box?.width && box.height ? box : null;
11017
10238
  }
11018
10239
  function iconMarkupSizedFor(slot, markup) {
@@ -12813,7 +12034,6 @@ function readLogoSizeState(content, placement) {
12813
12034
  function getLogoElement(el) {
12814
12035
  const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
12815
12036
  if (marked) return marked;
12816
- if (el.closest('[data-ohw-editable="icon"]')) return null;
12817
12037
  const root = el.closest("nav, [data-ohw-nav-root], footer");
12818
12038
  if (!root) return null;
12819
12039
  const anchor = el.closest("a");
@@ -13693,333 +12913,6 @@ function useNavItemDrag({
13693
12913
  };
13694
12914
  }
13695
12915
 
13696
- // src/useSectionDrag.ts
13697
- import { useCallback as useCallback7, useEffect as useEffect11, useRef as useRef9, useState as useState11 } from "react";
13698
-
13699
- // src/lib/section-dnd.ts
13700
- function isFooterSection(el) {
13701
- return el.dataset.ohwSection === "footer";
13702
- }
13703
- function buildSectionDropSlots(draggedInstanceId) {
13704
- const sections = topLevelSections().filter(
13705
- (el) => instanceIdOf(el) !== draggedInstanceId && !isFooterSection(el)
13706
- );
13707
- const slots = [];
13708
- if (sections.length === 0) return slots;
13709
- const left = 0;
13710
- const width = document.documentElement.clientWidth;
13711
- for (let i = 0; i <= sections.length; i++) {
13712
- let y;
13713
- if (i === 0) {
13714
- y = sections[0].getBoundingClientRect().top;
13715
- } else if (i === sections.length) {
13716
- y = sections[sections.length - 1].getBoundingClientRect().bottom;
13717
- } else {
13718
- const prev = sections[i - 1].getBoundingClientRect();
13719
- const next = sections[i].getBoundingClientRect();
13720
- y = (prev.bottom + next.top) / 2;
13721
- }
13722
- slots.push({ insertIndex: i, y, left, width });
13723
- }
13724
- return slots;
13725
- }
13726
- function hitTestSectionDropSlot(y, slots) {
13727
- let best = null;
13728
- for (const slot of slots) {
13729
- const dist = Math.abs(y - slot.y);
13730
- if (!best || dist < best.dist) best = { slot, dist };
13731
- }
13732
- return best?.slot ?? null;
13733
- }
13734
-
13735
- // src/useSectionDrag.ts
13736
- var PRESS_THRESHOLD = 10;
13737
- var EDGE_ZONE = 60;
13738
- var MAX_AUTO_SCROLL_SPEED = 18;
13739
- var SECTION_DRAG_EXCLUDED_SELECTOR = [
13740
- "[data-ohw-toolbar]",
13741
- "[data-ohw-edit-chrome]",
13742
- "[data-ohw-item-interaction]",
13743
- "[data-ohw-drag-handle-container]",
13744
- '[data-slot="drag-handle"]',
13745
- "[data-ohw-item-toolbar-anchor]",
13746
- "[data-ohw-item-drag-surface]",
13747
- "[data-ohw-more-menu]",
13748
- '[data-slot="dropdown-menu-content"]',
13749
- '[data-slot="dropdown-menu-item"]',
13750
- "[data-ohw-state-toggle]",
13751
- "[data-ohw-max-badge]",
13752
- "[data-ohw-floating-panel]",
13753
- "[data-ohw-section-picker]",
13754
- "[data-ohw-link-popover-root]",
13755
- "[data-ohw-link-modal-root]",
13756
- "[data-ohw-link-page-dropdown]",
13757
- '[data-slot="popover-content"]',
13758
- '[data-slot="dialog-content"]',
13759
- '[data-slot="dialog-overlay"]',
13760
- "[data-ohw-ai-review]",
13761
- "[data-ohw-editable]",
13762
- "[data-ohw-editable-state]",
13763
- "[contenteditable]",
13764
- "[data-ohw-href-key]",
13765
- "[data-ohw-footer-col]",
13766
- "[data-ohw-social-label]",
13767
- "a",
13768
- "button",
13769
- '[role="button"]',
13770
- '[data-ohw-role="navbar-button"]',
13771
- '[data-ohw-role="button"]',
13772
- "[data-ohw-carousel]",
13773
- "[data-ohw-carousel-value]",
13774
- "[data-ohw-carousel-slide]",
13775
- "[data-ohw-carousel-overlay]",
13776
- "[data-ohw-media-chrome]",
13777
- "[data-ohw-media-overlay]",
13778
- "[data-ohw-media-skeleton]"
13779
- ].join(", ");
13780
- function visibleClip(ps) {
13781
- if (!ps) return null;
13782
- const top = Math.max(0, ps.headerH - ps.iframeOffsetTop);
13783
- const bottom = Math.min(window.innerHeight, ps.headerH + ps.canvasH - ps.iframeOffsetTop);
13784
- return { top, bottom: Math.max(top, bottom) };
13785
- }
13786
- function useSectionDrag({
13787
- isEditMode,
13788
- editContentRef,
13789
- postToParentRef,
13790
- parentScrollRef,
13791
- navDragRef,
13792
- footerDragRef,
13793
- suppressNextClickRef,
13794
- suppressClickUntilRef
13795
- }) {
13796
- const sectionDragRef = useRef9(null);
13797
- const [sectionDropSlots, setSectionDropSlots] = useState11([]);
13798
- const [activeSectionDropIndex, setActiveSectionDropIndex] = useState11(null);
13799
- const [isSectionDragging, setIsSectionDragging] = useState11(false);
13800
- const sectionPointerDragRef = useRef9(null);
13801
- const autoScrollRafRef = useRef9(null);
13802
- const autoScrollDeltaRef = useRef9(0);
13803
- const stopAutoScroll = useCallback7(() => {
13804
- if (autoScrollRafRef.current != null) {
13805
- cancelAnimationFrame(autoScrollRafRef.current);
13806
- autoScrollRafRef.current = null;
13807
- }
13808
- autoScrollDeltaRef.current = 0;
13809
- }, []);
13810
- const tickAutoScroll = useCallback7(() => {
13811
- if (!sectionDragRef.current) {
13812
- stopAutoScroll();
13813
- return;
13814
- }
13815
- if (autoScrollDeltaRef.current !== 0) {
13816
- postToParentRef.current({ type: "ow:request-scroll", deltaY: autoScrollDeltaRef.current });
13817
- }
13818
- autoScrollRafRef.current = requestAnimationFrame(tickAutoScroll);
13819
- }, [postToParentRef, stopAutoScroll]);
13820
- const updateAutoScroll = useCallback7(
13821
- (clientY) => {
13822
- const clip = visibleClip(parentScrollRef.current);
13823
- let delta = 0;
13824
- if (clip) {
13825
- const distTop = clientY - clip.top;
13826
- const distBottom = clip.bottom - clientY;
13827
- if (distTop >= 0 && distTop < EDGE_ZONE) {
13828
- delta = -MAX_AUTO_SCROLL_SPEED * (1 - distTop / EDGE_ZONE);
13829
- } else if (distBottom >= 0 && distBottom < EDGE_ZONE) {
13830
- delta = MAX_AUTO_SCROLL_SPEED * (1 - distBottom / EDGE_ZONE);
13831
- }
13832
- }
13833
- autoScrollDeltaRef.current = delta;
13834
- if (delta !== 0 && autoScrollRafRef.current == null) {
13835
- autoScrollRafRef.current = requestAnimationFrame(tickAutoScroll);
13836
- } else if (delta === 0) {
13837
- stopAutoScroll();
13838
- }
13839
- },
13840
- [parentScrollRef, stopAutoScroll, tickAutoScroll]
13841
- );
13842
- const clearSectionDragVisuals = useCallback7(() => {
13843
- sectionDragRef.current?.draggedEl.removeAttribute("data-ohw-section-dragging");
13844
- sectionDragRef.current = null;
13845
- setSectionDropSlots([]);
13846
- setActiveSectionDropIndex(null);
13847
- setIsSectionDragging(false);
13848
- stopAutoScroll();
13849
- document.documentElement.removeAttribute("data-ohw-section-dragging-root");
13850
- unlockItemDragInteraction();
13851
- }, [stopAutoScroll]);
13852
- const refreshSectionDragVisuals = useCallback7(
13853
- (session, clientX, clientY) => {
13854
- session.lastClientX = clientX;
13855
- session.lastClientY = clientY;
13856
- const slots = buildSectionDropSlots(session.instanceId);
13857
- const activeSlot = hitTestSectionDropSlot(clientY, slots);
13858
- session.activeSlot = activeSlot;
13859
- setSectionDropSlots(slots);
13860
- const activeIdx = activeSlot ? slots.findIndex((s) => s.insertIndex === activeSlot.insertIndex) : -1;
13861
- setActiveSectionDropIndex(activeIdx >= 0 ? activeIdx : null);
13862
- updateAutoScroll(clientY);
13863
- },
13864
- [updateAutoScroll]
13865
- );
13866
- const beginSectionDrag = useCallback7(
13867
- (session) => {
13868
- sectionDragRef.current = session;
13869
- setIsSectionDragging(true);
13870
- lockItemDuringDrag();
13871
- document.documentElement.setAttribute("data-ohw-section-dragging-root", "");
13872
- session.draggedEl.setAttribute("data-ohw-section-dragging", "");
13873
- refreshSectionDragVisuals(session, session.lastClientX, session.lastClientY);
13874
- },
13875
- [refreshSectionDragVisuals]
13876
- );
13877
- const commitSectionDrag = useCallback7(() => {
13878
- const session = sectionDragRef.current;
13879
- if (!session) {
13880
- clearSectionDragVisuals();
13881
- return;
13882
- }
13883
- const slot = session.activeSlot ?? hitTestSectionDropSlot(session.lastClientY, buildSectionDropSlots(session.instanceId));
13884
- const entries = slot ? planSectionMove(session.instanceId, slot.insertIndex, window.location.pathname) : null;
13885
- if (!entries) {
13886
- clearSectionDragVisuals();
13887
- return;
13888
- }
13889
- const orderJson = JSON.stringify(entries);
13890
- editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
13891
- postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
13892
- applyPersistedOrder(entries);
13893
- clearSectionDragVisuals();
13894
- requestAnimationFrame(() => {
13895
- if (editContentRef.current[SECTION_ORDER_KEY] === orderJson) {
13896
- applyPersistedOrder(entries);
13897
- }
13898
- requestAnimationFrame(() => {
13899
- window.dispatchEvent(new Event("resize"));
13900
- });
13901
- });
13902
- }, [clearSectionDragVisuals, editContentRef, postToParentRef]);
13903
- const startSectionPressDrag = useCallback7(
13904
- (el, clientX, clientY, pointerId) => {
13905
- if (navDragRef.current || footerDragRef.current || sectionDragRef.current) return false;
13906
- const instanceId = instanceIdOf(el);
13907
- if (!instanceId) return false;
13908
- sectionPointerDragRef.current = {
13909
- el,
13910
- instanceId,
13911
- startX: clientX,
13912
- startY: clientY,
13913
- pointerId,
13914
- started: false
13915
- };
13916
- return true;
13917
- },
13918
- [footerDragRef, navDragRef]
13919
- );
13920
- useEffect11(() => {
13921
- if (!isEditMode) return;
13922
- const onPointerDown = (e) => {
13923
- if (e.button !== 0) return;
13924
- if (navDragRef.current || footerDragRef.current || sectionDragRef.current) return;
13925
- if (sectionPointerDragRef.current) return;
13926
- const target = e.target;
13927
- if (!(target instanceof HTMLElement)) return;
13928
- if (target.closest(SECTION_DRAG_EXCLUDED_SELECTOR)) return;
13929
- const sectionEl = target.closest("[data-ohw-section]");
13930
- if (!sectionEl || isChromeSection(sectionEl) || sectionEl.dataset.ohwSection === "footer") return;
13931
- if (!topLevelSections().includes(sectionEl)) return;
13932
- startSectionPressDrag(sectionEl, e.clientX, e.clientY, e.pointerId);
13933
- };
13934
- const onPointerMove = (e) => {
13935
- const pending = sectionPointerDragRef.current;
13936
- if (!pending) return;
13937
- if (pending.started) {
13938
- e.preventDefault();
13939
- clearTextSelection();
13940
- const session = sectionDragRef.current;
13941
- if (!session) return;
13942
- refreshSectionDragVisuals(session, e.clientX, e.clientY);
13943
- return;
13944
- }
13945
- const dx = e.clientX - pending.startX;
13946
- const dy = e.clientY - pending.startY;
13947
- if (dx * dx + dy * dy < PRESS_THRESHOLD * PRESS_THRESHOLD) return;
13948
- e.preventDefault();
13949
- pending.started = true;
13950
- armItemPressDrag();
13951
- clearTextSelection();
13952
- try {
13953
- document.body.setPointerCapture(pending.pointerId);
13954
- } catch {
13955
- }
13956
- beginSectionDrag({
13957
- instanceId: pending.instanceId,
13958
- draggedEl: pending.el,
13959
- lastClientX: e.clientX,
13960
- lastClientY: e.clientY,
13961
- activeSlot: null
13962
- });
13963
- };
13964
- const endPointerDrag = (e) => {
13965
- const pending = sectionPointerDragRef.current;
13966
- sectionPointerDragRef.current = null;
13967
- try {
13968
- if (document.body.hasPointerCapture(e.pointerId)) {
13969
- document.body.releasePointerCapture(e.pointerId);
13970
- }
13971
- } catch {
13972
- }
13973
- if (!pending) return;
13974
- if (!pending.started) {
13975
- unlockItemDragInteraction();
13976
- return;
13977
- }
13978
- suppressNextClickRef.current = true;
13979
- suppressClickUntilRef.current = Date.now() + 500;
13980
- commitSectionDrag();
13981
- };
13982
- const onKeyDown = (e) => {
13983
- if (e.key !== "Escape") return;
13984
- if (!sectionDragRef.current && !sectionPointerDragRef.current) return;
13985
- sectionPointerDragRef.current = null;
13986
- clearSectionDragVisuals();
13987
- };
13988
- document.addEventListener("pointerdown", onPointerDown, true);
13989
- document.addEventListener("pointermove", onPointerMove, true);
13990
- document.addEventListener("pointerup", endPointerDrag, true);
13991
- document.addEventListener("pointercancel", endPointerDrag, true);
13992
- document.addEventListener("keydown", onKeyDown, true);
13993
- return () => {
13994
- document.removeEventListener("pointerdown", onPointerDown, true);
13995
- document.removeEventListener("pointermove", onPointerMove, true);
13996
- document.removeEventListener("pointerup", endPointerDrag, true);
13997
- document.removeEventListener("pointercancel", endPointerDrag, true);
13998
- document.removeEventListener("keydown", onKeyDown, true);
13999
- unlockItemDragInteraction();
14000
- stopAutoScroll();
14001
- };
14002
- }, [
14003
- beginSectionDrag,
14004
- clearSectionDragVisuals,
14005
- commitSectionDrag,
14006
- footerDragRef,
14007
- isEditMode,
14008
- navDragRef,
14009
- refreshSectionDragVisuals,
14010
- startSectionPressDrag,
14011
- stopAutoScroll,
14012
- suppressClickUntilRef,
14013
- suppressNextClickRef
14014
- ]);
14015
- return {
14016
- sectionDragRef,
14017
- sectionDropSlots,
14018
- activeSectionDropIndex,
14019
- isSectionDragging
14020
- };
14021
- }
14022
-
14023
12916
  // src/ui/footer-container-chrome.tsx
14024
12917
  import { Plus as Plus2 } from "lucide-react";
14025
12918
  import { jsx as jsx29, jsxs as jsxs19 } from "react/jsx-runtime";
@@ -14072,7 +12965,7 @@ function FooterContainerChrome({
14072
12965
  }
14073
12966
 
14074
12967
  // src/lib/carousel.ts
14075
- import { useEffect as useEffect12, useState as useState12 } from "react";
12968
+ import { useEffect as useEffect11, useState as useState11 } from "react";
14076
12969
  var CAROUSEL_ATTR = "data-ohw-carousel";
14077
12970
  var CAROUSEL_VALUE_ATTR = "data-ohw-carousel-value";
14078
12971
  var CAROUSEL_SLIDE_ATTR = "data-ohw-carousel-slide";
@@ -14134,8 +13027,8 @@ function applyCarouselNode(key, val) {
14134
13027
  return true;
14135
13028
  }
14136
13029
  function useOhwCarousel(key, initial) {
14137
- const [images, setImages] = useState12(initial);
14138
- useEffect12(() => {
13030
+ const [images, setImages] = useState11(initial);
13031
+ useEffect11(() => {
14139
13032
  const el = document.querySelector(
14140
13033
  `[${CAROUSEL_ATTR}][data-ohw-key="${CSS.escape(key)}"]`
14141
13034
  );
@@ -14217,7 +13110,6 @@ function collectEditableNodes(extraContent, root = document) {
14217
13110
  NAV_ORDER_KEY,
14218
13111
  FOOTER_ORDER_KEY,
14219
13112
  NAV_COUNT_KEY,
14220
- SECTION_ORDER_KEY,
14221
13113
  // A socials row's order and its icons-vs-words setting live under keys no element carries,
14222
13114
  // so collecting the DOM alone left them behind: the draft knew the row was showing icons and
14223
13115
  // had gained an item, and the published page went back to the template's own (OHH-736).
@@ -14681,7 +13573,6 @@ function fadeInImageElement(img, onReady) {
14681
13573
  function applyEditableImageSrc(img, url) {
14682
13574
  img.removeAttribute("srcset");
14683
13575
  img.removeAttribute("sizes");
14684
- if (img.loading === "lazy") img.loading = "eager";
14685
13576
  img.src = url;
14686
13577
  }
14687
13578
  function fadeInBgImage(el, url, onReady) {
@@ -14746,10 +13637,21 @@ function parseSchedulingInsertAfter(insertAfter) {
14746
13637
  insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
14747
13638
  };
14748
13639
  }
14749
- function resolveEntryAnchor(entry) {
14750
- if (entry.anchorId !== void 0) return { anchorId: entry.anchorId, beforeId: entry.beforeId ?? null };
14751
- const parsed = parseSchedulingInsertAfter(entry.insertAfter);
14752
- return { anchorId: parsed.anchor, beforeId: parsed.insertBefore };
13640
+ function resolveSchedulingInsert(insertAfter, explicitInsertBefore) {
13641
+ const parsed = parseSchedulingInsertAfter(insertAfter);
13642
+ const insertBefore = explicitInsertBefore ?? parsed.insertBefore;
13643
+ const effectiveInsertAfter = insertBefore && parsed.insertBefore === null ? `${insertAfter}${INSERT_BEFORE_MARKER}${insertBefore}` : insertAfter;
13644
+ return { effectiveInsertAfter, insertBefore };
13645
+ }
13646
+ function getSchedulingMountPoint(insertAfter) {
13647
+ const { anchor } = parseSchedulingInsertAfter(insertAfter);
13648
+ let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
13649
+ if (!anchorEl && anchor === "scheduling") {
13650
+ const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
13651
+ anchorEl = widgets.at(-1) ?? null;
13652
+ }
13653
+ if (!anchorEl) return null;
13654
+ return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
14753
13655
  }
14754
13656
  function schedulingMountDepth(insertAfter) {
14755
13657
  if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
@@ -14766,7 +13668,8 @@ function getPageSchedulingEntries(raw) {
14766
13668
  }
14767
13669
  }
14768
13670
  function isSchedulingWidgetMissing(entry) {
14769
- return !document.querySelector(`[data-ohw-section="${schedulingSectionId(entry.insertAfter)}"]`);
13671
+ const { effectiveInsertAfter } = resolveSchedulingInsert(entry.insertAfter);
13672
+ return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
14770
13673
  }
14771
13674
  function hasMissingSchedulingWidgets(entries) {
14772
13675
  return entries.some(isSchedulingWidgetMissing);
@@ -14796,17 +13699,16 @@ function initSectionsFromContent(content, removeExisting = false) {
14796
13699
  } catch {
14797
13700
  }
14798
13701
  }
14799
- function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, beforeId, existingWidgetId) {
14800
- const widgetId = existingWidgetId ?? (beforeId ? `${anchorId}${INSERT_BEFORE_MARKER}${beforeId}` : anchorId);
14801
- const sectionId = schedulingSectionId(widgetId);
13702
+ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId, explicitInsertBefore) {
13703
+ const { effectiveInsertAfter, insertBefore } = resolveSchedulingInsert(insertAfter, explicitInsertBefore);
13704
+ const sectionId = schedulingSectionId(effectiveInsertAfter);
14802
13705
  if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
14803
- const anchorEl = document.querySelector(`[data-ohw-section="${anchorId}"]`);
14804
- if (!anchorEl) return false;
14805
- const mountPoint = anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
13706
+ const mountPoint = getSchedulingMountPoint(effectiveInsertAfter);
13707
+ if (!mountPoint) return false;
14806
13708
  const container = document.createElement("div");
14807
13709
  container.dataset.ohwSectionContainer = "scheduling";
14808
- if (beforeId) {
14809
- const beforeAnchor = document.querySelector(`[data-ohw-section="${beforeId}"]`);
13710
+ if (insertBefore) {
13711
+ const beforeAnchor = document.querySelector(`[data-ohw-section="${insertBefore}"]`);
14810
13712
  const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
14811
13713
  if (!beforePoint) return false;
14812
13714
  beforePoint.insertAdjacentElement("beforebegin", container);
@@ -14817,25 +13719,19 @@ function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, be
14817
13719
  }
14818
13720
  tail.insertAdjacentElement("afterend", container);
14819
13721
  }
14820
- try {
14821
- const root = createRoot2(container);
14822
- flushSync2(() => {
14823
- root.render(
14824
- /* @__PURE__ */ jsx33(
14825
- SchedulingWidget,
14826
- {
14827
- notifyOnConnect,
14828
- initialScheduleId: scheduleId,
14829
- insertAfter: widgetId
14830
- }
14831
- )
14832
- );
14833
- });
14834
- } catch (err) {
14835
- console.error("[ow:scheduling] render threw", err);
14836
- container.remove();
14837
- return false;
14838
- }
13722
+ const root = createRoot2(container);
13723
+ flushSync2(() => {
13724
+ root.render(
13725
+ /* @__PURE__ */ jsx33(
13726
+ SchedulingWidget,
13727
+ {
13728
+ notifyOnConnect,
13729
+ initialScheduleId: scheduleId,
13730
+ insertAfter: effectiveInsertAfter
13731
+ }
13732
+ )
13733
+ );
13734
+ });
14839
13735
  const tracker = getSectionsTracker();
14840
13736
  let sections = [];
14841
13737
  try {
@@ -14843,12 +13739,10 @@ function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, be
14843
13739
  } catch {
14844
13740
  }
14845
13741
  const inEditor = typeof window !== "undefined" && window.self !== window.top;
14846
- if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === widgetId)) {
13742
+ if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === effectiveInsertAfter)) {
14847
13743
  sections.push({
14848
13744
  type: "scheduling",
14849
- insertAfter: widgetId,
14850
- anchorId,
14851
- beforeId: beforeId ?? null,
13745
+ insertAfter: effectiveInsertAfter,
14852
13746
  pagePath: window.location.pathname,
14853
13747
  ...scheduleId ? { scheduleId } : {}
14854
13748
  });
@@ -14862,8 +13756,7 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
14862
13756
  for (let i = pending.length - 1; i >= 0; i--) {
14863
13757
  const entry = pending[i];
14864
13758
  const shouldNotify = typeof notifyOnConnect === "function" ? notifyOnConnect(entry) : notifyOnConnect;
14865
- const { anchorId, beforeId } = resolveEntryAnchor(entry);
14866
- if (mountSchedulingWidget(anchorId, shouldNotify, entry.scheduleId ?? null, beforeId, entry.insertAfter)) {
13759
+ if (mountSchedulingWidget(entry.insertAfter, shouldNotify, entry.scheduleId)) {
14867
13760
  pending.splice(i, 1);
14868
13761
  }
14869
13762
  }
@@ -15021,11 +13914,6 @@ function applyLinkByKey(key, val) {
15021
13914
  hrefAnchors.forEach((el) => applyLinkHref(el, val));
15022
13915
  }
15023
13916
  }
15024
- function isInsideLinkEditor(target) {
15025
- return Boolean(
15026
- 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"]')
15027
- );
15028
- }
15029
13917
  function isInsideFloatingPanel(target) {
15030
13918
  return Boolean(target.closest("[data-ohw-floating-panel]"));
15031
13919
  }
@@ -15033,6 +13921,11 @@ function isPointOverFloatingPanel(clientX, clientY) {
15033
13921
  const el = document.elementFromPoint(clientX, clientY);
15034
13922
  return Boolean(el instanceof Element && el.closest("[data-ohw-floating-panel]"));
15035
13923
  }
13924
+ function isInsideLinkEditor(target) {
13925
+ return Boolean(
13926
+ 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"]')
13927
+ );
13928
+ }
15036
13929
  function getHrefKeyFromElement(el) {
15037
13930
  if (!el) return null;
15038
13931
  const anchor = el.closest("[data-ohw-href-key]");
@@ -15291,7 +14184,7 @@ function getNavigationSelectionParent(el) {
15291
14184
  if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
15292
14185
  return getFooterLinksContainer();
15293
14186
  }
15294
- 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)) {
14187
+ if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
15295
14188
  return getNavigationRoot(el);
15296
14189
  }
15297
14190
  return null;
@@ -15506,6 +14399,7 @@ var ICONS = {
15506
14399
  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"/>',
15507
14400
  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"/>'
15508
14401
  };
14402
+ var HEIGHT_SETTLE_DELAYS = [150, 400, 800];
15509
14403
  var SELECTION_CHROME_GAP2 = 4;
15510
14404
  var TOOLBAR_STROKE_GAP2 = 4;
15511
14405
  var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
@@ -15885,45 +14779,6 @@ function StateToggle({
15885
14779
  );
15886
14780
  }
15887
14781
  var contentCache = /* @__PURE__ */ new Map();
15888
- var fetchedContentPaths = /* @__PURE__ */ new Set();
15889
- var OHW_LOADER_STYLE = {
15890
- position: "fixed",
15891
- inset: 0,
15892
- background: "#fff",
15893
- zIndex: 2147483646,
15894
- display: "flex",
15895
- alignItems: "center",
15896
- justifyContent: "center"
15897
- };
15898
- function OhwLoaderSpinner() {
15899
- return /* @__PURE__ */ jsxs20("svg", { width: "28", height: "28", viewBox: "0 0 28 28", fill: "none", "aria-hidden": true, children: [
15900
- /* @__PURE__ */ jsx33("circle", { cx: "14", cy: "14", r: "11", stroke: "#E7E5E4", strokeWidth: "3" }),
15901
- /* @__PURE__ */ jsx33(
15902
- "circle",
15903
- {
15904
- cx: "14",
15905
- cy: "14",
15906
- r: "11",
15907
- stroke: "#1C1917",
15908
- strokeWidth: "3",
15909
- strokeDasharray: "17 52",
15910
- strokeLinecap: "round",
15911
- children: /* @__PURE__ */ jsx33(
15912
- "animateTransform",
15913
- {
15914
- attributeName: "transform",
15915
- type: "rotate",
15916
- from: "0 14 14",
15917
- to: "360 14 14",
15918
- dur: "0.7s",
15919
- repeatCount: "indefinite"
15920
- }
15921
- )
15922
- }
15923
- )
15924
- ] });
15925
- }
15926
- 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){}})();`;
15927
14782
  function resolveSubdomain(subdomainFromQuery) {
15928
14783
  if (subdomainFromQuery) return subdomainFromQuery;
15929
14784
  if (typeof window !== "undefined") {
@@ -15946,8 +14801,8 @@ function OhhwellsBridge() {
15946
14801
  const router = useRouter3();
15947
14802
  const searchParams = useSearchParams();
15948
14803
  const isEditMode = isEditSessionActive();
15949
- const [bridgeRoot, setBridgeRoot] = useState13(null);
15950
- useEffect13(() => {
14804
+ const [bridgeRoot, setBridgeRoot] = useState12(null);
14805
+ useEffect12(() => {
15951
14806
  const figtreeFontId = "ohw-figtree-font";
15952
14807
  if (!document.getElementById(figtreeFontId)) {
15953
14808
  const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
@@ -15976,146 +14831,82 @@ function OhhwellsBridge() {
15976
14831
  const subdomain = resolveSubdomain(subdomainFromQuery);
15977
14832
  useLinkHrefGuardian(pathname, subdomain, isEditMode);
15978
14833
  useSavedLinkNavigation(isEditMode);
15979
- const postToParent2 = useCallback8((data) => {
14834
+ const postToParent2 = useCallback7((data) => {
15980
14835
  if (typeof window !== "undefined" && window.parent !== window) {
15981
14836
  window.parent.postMessage(data, "*");
15982
14837
  }
15983
14838
  }, []);
15984
- const [fetchState, setFetchState] = useState13("idle");
15985
- const autoSaveTimers = useRef10(/* @__PURE__ */ new Map());
15986
- const activeElRef = useRef10(null);
15987
- const pointerHeldRef = useRef10(false);
15988
- const selectedElRef = useRef10(null);
15989
- const selectedHrefKeyRef = useRef10(null);
15990
- const selectedFooterColAttrRef = useRef10(null);
15991
- const originalContentRef = useRef10(null);
15992
- const activeStateElRef = useRef10(null);
15993
- const parentScrollRef = useRef10(null);
15994
- const visibleViewportRef = useRef10(null);
15995
- const [dialogPortalContainer, setDialogPortalContainer] = useState13(null);
15996
- const attachVisibleViewport = useCallback8((node) => {
14839
+ const [fetchState, setFetchState] = useState12("idle");
14840
+ const autoSaveTimers = useRef9(/* @__PURE__ */ new Map());
14841
+ const activeElRef = useRef9(null);
14842
+ const pointerHeldRef = useRef9(false);
14843
+ const selectedElRef = useRef9(null);
14844
+ const selectedHrefKeyRef = useRef9(null);
14845
+ const selectedFooterColAttrRef = useRef9(null);
14846
+ const originalContentRef = useRef9(null);
14847
+ const activeStateElRef = useRef9(null);
14848
+ const parentScrollRef = useRef9(null);
14849
+ const visibleViewportRef = useRef9(null);
14850
+ const [dialogPortalContainer, setDialogPortalContainer] = useState12(null);
14851
+ const attachVisibleViewport = useCallback7((node) => {
15997
14852
  visibleViewportRef.current = node;
15998
14853
  setDialogPortalContainer(node);
15999
14854
  if (node) applyVisibleViewport(node, parentScrollRef.current);
16000
14855
  }, []);
16001
- const toolbarElRef = useRef10(null);
16002
- const glowElRef = useRef10(null);
16003
- const hoveredImageRef = useRef10(null);
16004
- const hoveredImageHasTextOverlapRef = useRef10(false);
16005
- const dragOverElRef = useRef10(null);
16006
- const [mediaHover, setMediaHover] = useState13(null);
16007
- const [selectedMedia, setSelectedMedia] = useState13(null);
16008
- const selectedMediaElRef = useRef10(null);
16009
- const clearMediaSelection = useCallback8(() => {
16010
- const prev = selectedMediaElRef.current;
16011
- selectedMediaElRef.current = null;
16012
- setSelectedMedia(null);
16013
- const sectionEl = prev?.closest("[data-ohw-section]") ?? null;
16014
- if (sectionEl) {
16015
- postToParentRef.current({
16016
- type: "ow:section-selected",
16017
- sectionId: sectionEl.dataset.ohwSection ?? null,
16018
- sectionLabel: sectionEl.dataset.ohwSectionLabel ?? titleCaseSectionId(sectionEl.dataset.ohwSection ?? ""),
16019
- key: null
16020
- });
16021
- }
16022
- }, []);
16023
- const clearMediaSelectionRef = useRef10(clearMediaSelection);
16024
- clearMediaSelectionRef.current = clearMediaSelection;
16025
- const selectMediaElement = useCallback8((el) => {
16026
- const r2 = el.getBoundingClientRect();
16027
- const video = el.dataset.ohwEditable === "video" ? el.querySelector("video") : null;
16028
- selectedMediaElRef.current = el;
16029
- setSelectedMedia({
16030
- key: el.dataset.ohwKey ?? "",
16031
- rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height },
16032
- elementType: el.dataset.ohwEditable ?? "image",
16033
- hasTextOverlap: false,
16034
- isDragOver: false,
16035
- ...video ? { videoAutoplay: video.autoplay, videoMuted: video.muted } : {}
16036
- });
16037
- const sectionEl = el.closest("[data-ohw-section]");
16038
- aiSectionApiRef.current?.selectFromElement(el, { report: false });
16039
- postToParentRef.current({
16040
- type: "ow:section-selected",
16041
- sectionId: sectionEl?.dataset.ohwSection ?? null,
16042
- sectionLabel: sectionEl ? sectionEl.dataset.ohwSectionLabel ?? titleCaseSectionId(sectionEl.dataset.ohwSection ?? "") : null,
16043
- key: el.dataset.ohwKey ?? null,
16044
- // Display name for the pill — the raw key prettifies into fragments ("Img"); the
16045
- // bridge knows what the node IS, so it names it.
16046
- keyLabel: el.dataset.ohwEditable === "video" ? "Video" : el.dataset.ohwEditable === "bg-image" ? "Background" : "Image"
16047
- });
16048
- }, []);
16049
- const selectMediaElementRef = useRef10(selectMediaElement);
16050
- selectMediaElementRef.current = selectMediaElement;
16051
- useEffect13(() => {
16052
- if (!selectedMedia) return;
16053
- const update = () => {
16054
- const el = selectedMediaElRef.current;
16055
- if (!el || !el.isConnected) {
16056
- clearMediaSelection();
16057
- return;
16058
- }
16059
- const r2 = el.getBoundingClientRect();
16060
- setSelectedMedia(
16061
- (prev) => prev ? { ...prev, rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height } } : prev
16062
- );
16063
- };
16064
- window.addEventListener("scroll", update, true);
16065
- window.addEventListener("resize", update);
16066
- return () => {
16067
- window.removeEventListener("scroll", update, true);
16068
- window.removeEventListener("resize", update);
16069
- };
16070
- }, [selectedMedia !== null]);
16071
- const [carouselHover, setCarouselHover] = useState13(null);
16072
- const [uploadingRects, setUploadingRects] = useState13({});
16073
- const hoveredGapRef = useRef10(null);
16074
- const imageUnhoverTimerRef = useRef10(null);
16075
- const imageShowTimerRef = useRef10(null);
16076
- const editStylesRef = useRef10(null);
16077
- const activateRef = useRef10(() => {
14856
+ const toolbarElRef = useRef9(null);
14857
+ const glowElRef = useRef9(null);
14858
+ const hoveredImageRef = useRef9(null);
14859
+ const hoveredImageHasTextOverlapRef = useRef9(false);
14860
+ const dragOverElRef = useRef9(null);
14861
+ const [mediaHover, setMediaHover] = useState12(null);
14862
+ const [carouselHover, setCarouselHover] = useState12(null);
14863
+ const [uploadingRects, setUploadingRects] = useState12({});
14864
+ const hoveredGapRef = useRef9(null);
14865
+ const imageUnhoverTimerRef = useRef9(null);
14866
+ const imageShowTimerRef = useRef9(null);
14867
+ const editStylesRef = useRef9(null);
14868
+ const activateRef = useRef9(() => {
16078
14869
  });
16079
- const deactivateRef = useRef10(() => {
14870
+ const deactivateRef = useRef9(() => {
16080
14871
  });
16081
- const selectRef = useRef10(() => {
14872
+ const selectRef = useRef9(() => {
16082
14873
  });
16083
- const selectFrameRef = useRef10(() => {
14874
+ const selectFrameRef = useRef9(() => {
16084
14875
  });
16085
- const selectLogoRef = useRef10(() => {
14876
+ const selectLogoRef = useRef9(() => {
16086
14877
  });
16087
- const openLogoSizePanelRef = useRef10(() => {
14878
+ const openLogoSizePanelRef = useRef9(() => {
16088
14879
  });
16089
- const deselectRef = useRef10(() => {
14880
+ const deselectRef = useRef9(() => {
16090
14881
  });
16091
- const closeFloatingPanelOnlyRef = useRef10(() => {
14882
+ const closeFloatingPanelOnlyRef = useRef9(() => {
16092
14883
  });
16093
- const reselectNavigationItemRef = useRef10(() => {
14884
+ const reselectNavigationItemRef = useRef9(() => {
16094
14885
  });
16095
- const commitNavigationTextEditRef = useRef10(() => {
14886
+ const commitNavigationTextEditRef = useRef9(() => {
16096
14887
  });
16097
- const handleDeleteSelectedRef = useRef10(() => false);
16098
- const runPendingDeleteUndoRef = useRef10(() => false);
16099
- const isFooterFrameSelectionRef = useRef10(false);
16100
- const refreshActiveCommandsRef = useRef10(() => {
14888
+ const handleDeleteSelectedRef = useRef9(() => false);
14889
+ const runPendingDeleteUndoRef = useRef9(() => false);
14890
+ const isFooterFrameSelectionRef = useRef9(false);
14891
+ const refreshActiveCommandsRef = useRef9(() => {
16101
14892
  });
16102
- const postToParentRef = useRef10(postToParent2);
14893
+ const postToParentRef = useRef9(postToParent2);
16103
14894
  postToParentRef.current = postToParent2;
16104
- const aiSectionApiRef = useRef10(null);
16105
- const sectionsLoadedRef = useRef10(false);
16106
- const pendingScheduleConfigRequests = useRef10([]);
16107
- const [toolbarRect, setToolbarRect] = useState13(null);
16108
- const [formPickRect, setFormPickRect] = useState13(null);
16109
- const formPickElRef = useRef10(null);
16110
- const [formViewState, setFormViewStateUi] = useState13("default");
16111
- const [formPickCount, setFormPickCount] = useState13(null);
16112
- const [formHoverRect, setFormHoverRect] = useState13(null);
16113
- const formHoverElRef = useRef10(null);
16114
- const [fieldPickRect, setFieldPickRect] = useState13(null);
16115
- const fieldPickElRef = useRef10(null);
16116
- const [fieldPickState, setFieldPickState] = useState13(null);
16117
- const [fieldTypePickerOpen, setFieldTypePickerOpen] = useState13(false);
16118
- const clearFormPick = useCallback8(() => {
14895
+ const aiSectionApiRef = useRef9(null);
14896
+ const sectionsLoadedRef = useRef9(false);
14897
+ const pendingScheduleConfigRequests = useRef9([]);
14898
+ const [toolbarRect, setToolbarRect] = useState12(null);
14899
+ const [formPickRect, setFormPickRect] = useState12(null);
14900
+ const formPickElRef = useRef9(null);
14901
+ const [formViewState, setFormViewStateUi] = useState12("default");
14902
+ const [formPickCount, setFormPickCount] = useState12(null);
14903
+ const [formHoverRect, setFormHoverRect] = useState12(null);
14904
+ const formHoverElRef = useRef9(null);
14905
+ const [fieldPickRect, setFieldPickRect] = useState12(null);
14906
+ const fieldPickElRef = useRef9(null);
14907
+ const [fieldPickState, setFieldPickState] = useState12(null);
14908
+ const [fieldTypePickerOpen, setFieldTypePickerOpen] = useState12(false);
14909
+ const clearFormPick = useCallback7(() => {
16119
14910
  const form = formPickElRef.current;
16120
14911
  const editing = fieldPickElRef.current;
16121
14912
  if (commitPlaceholderEdit(editing) && editing) {
@@ -16135,7 +14926,7 @@ function OhhwellsBridge() {
16135
14926
  formPickElRef.current = null;
16136
14927
  setFormPickRect(null);
16137
14928
  }, []);
16138
- const clearFieldPick = useCallback8(() => {
14929
+ const clearFieldPick = useCallback7(() => {
16139
14930
  const wrapper = fieldPickElRef.current;
16140
14931
  if (commitPlaceholderEdit(wrapper) && wrapper) {
16141
14932
  const form = wrapper.closest('[data-ohw-editable="form"]');
@@ -16145,9 +14936,9 @@ function OhhwellsBridge() {
16145
14936
  setFieldPickRect(null);
16146
14937
  setFieldPickState(null);
16147
14938
  }, []);
16148
- const persistFieldsRef = useRef10(() => {
14939
+ const persistFieldsRef = useRef9(() => {
16149
14940
  });
16150
- const persistFields = useCallback8(
14941
+ const persistFields = useCallback7(
16151
14942
  (form) => {
16152
14943
  const key = formKeyOf(form);
16153
14944
  if (!key) return;
@@ -16158,7 +14949,7 @@ function OhhwellsBridge() {
16158
14949
  []
16159
14950
  );
16160
14951
  persistFieldsRef.current = persistFields;
16161
- const selectField = useCallback8((wrapper) => {
14952
+ const selectField = useCallback7((wrapper) => {
16162
14953
  if (fieldPickElRef.current && fieldPickElRef.current !== wrapper) {
16163
14954
  commitPlaceholderEdit(fieldPickElRef.current);
16164
14955
  }
@@ -16171,7 +14962,7 @@ function OhhwellsBridge() {
16171
14962
  setFieldPickState({ type: fieldTypeOf(wrapper), required: isFieldRequired(wrapper) });
16172
14963
  setFieldTypePickerOpen(false);
16173
14964
  }, []);
16174
- const withSelectedField = useCallback8(
14965
+ const withSelectedField = useCallback7(
16175
14966
  (run) => {
16176
14967
  const wrapper = fieldPickElRef.current;
16177
14968
  const form = formPickElRef.current;
@@ -16184,28 +14975,28 @@ function OhhwellsBridge() {
16184
14975
  },
16185
14976
  [persistFields]
16186
14977
  );
16187
- const handleFieldTypeChange = useCallback8(
14978
+ const handleFieldTypeChange = useCallback7(
16188
14979
  (type) => withSelectedField((_form, wrapper) => {
16189
14980
  applyFieldType(wrapper, type);
16190
14981
  selectField(wrapper);
16191
14982
  }),
16192
14983
  [selectField, withSelectedField]
16193
14984
  );
16194
- const handleFieldRequiredToggle = useCallback8(
14985
+ const handleFieldRequiredToggle = useCallback7(
16195
14986
  () => withSelectedField((_form, wrapper) => {
16196
14987
  setFieldRequired(wrapper, !isFieldRequired(wrapper));
16197
14988
  selectField(wrapper);
16198
14989
  }),
16199
14990
  [selectField, withSelectedField]
16200
14991
  );
16201
- const handleFieldDuplicate = useCallback8(
14992
+ const handleFieldDuplicate = useCallback7(
16202
14993
  () => withSelectedField((form, wrapper) => {
16203
14994
  const copy = duplicateField(form, wrapper);
16204
14995
  selectField(copy);
16205
14996
  }),
16206
14997
  [selectField, withSelectedField]
16207
14998
  );
16208
- const handleFieldDelete = useCallback8(
14999
+ const handleFieldDelete = useCallback7(
16209
15000
  () => withSelectedField((_form, wrapper) => {
16210
15001
  removeField(wrapper);
16211
15002
  clearFieldPick();
@@ -16213,7 +15004,7 @@ function OhhwellsBridge() {
16213
15004
  }),
16214
15005
  [clearFieldPick, withSelectedField]
16215
15006
  );
16216
- const handleAddField = useCallback8(
15007
+ const handleAddField = useCallback7(
16217
15008
  (type) => {
16218
15009
  const form = formPickElRef.current;
16219
15010
  if (!form) return;
@@ -16229,8 +15020,8 @@ function OhhwellsBridge() {
16229
15020
  },
16230
15021
  [persistFields, selectField]
16231
15022
  );
16232
- const fieldDragRef = useRef10(null);
16233
- const buildFieldDropSlots = useCallback8((form, draggedKey) => {
15023
+ const fieldDragRef = useRef9(null);
15024
+ const buildFieldDropSlots = useCallback7((form, draggedKey) => {
16234
15025
  const others = listFieldWrappers(form).filter((el) => fieldKeyOf(el) !== draggedKey);
16235
15026
  const slots = others.map((el) => {
16236
15027
  const rect = el.getBoundingClientRect();
@@ -16243,7 +15034,7 @@ function OhhwellsBridge() {
16243
15034
  }
16244
15035
  return slots;
16245
15036
  }, []);
16246
- const handleFieldDragStart = useCallback8(() => {
15037
+ const handleFieldDragStart = useCallback7(() => {
16247
15038
  const wrapper = fieldPickElRef.current;
16248
15039
  const form = formPickElRef.current;
16249
15040
  if (!wrapper || !form) return;
@@ -16252,18 +15043,18 @@ function OhhwellsBridge() {
16252
15043
  setFieldDragging(true);
16253
15044
  setFieldDropSlots(buildFieldDropSlots(form, key));
16254
15045
  }, [buildFieldDropSlots]);
16255
- const handleFieldDragEnd = useCallback8(() => {
15046
+ const handleFieldDragEnd = useCallback7(() => {
16256
15047
  fieldDragRef.current = null;
16257
15048
  setFieldDropIndex(null);
16258
15049
  setFieldDropSlots([]);
16259
15050
  setFieldDragging(false);
16260
15051
  }, []);
16261
- const [fieldDropIndex, setFieldDropIndex] = useState13(null);
16262
- const [fieldDropSlots, setFieldDropSlots] = useState13([]);
16263
- const [fieldDragging, setFieldDragging] = useState13(false);
16264
- const clearFormPickRef = useRef10(clearFormPick);
15052
+ const [fieldDropIndex, setFieldDropIndex] = useState12(null);
15053
+ const [fieldDropSlots, setFieldDropSlots] = useState12([]);
15054
+ const [fieldDragging, setFieldDragging] = useState12(false);
15055
+ const clearFormPickRef = useRef9(clearFormPick);
16265
15056
  clearFormPickRef.current = clearFormPick;
16266
- useEffect13(() => {
15057
+ useEffect12(() => {
16267
15058
  const el = fieldPickElRef.current;
16268
15059
  if (!el || fieldPickRect === null) return;
16269
15060
  const observer = new ResizeObserver(() => {
@@ -16272,7 +15063,7 @@ function OhhwellsBridge() {
16272
15063
  observer.observe(el);
16273
15064
  return () => observer.disconnect();
16274
15065
  }, [fieldPickRect !== null, fieldPickState]);
16275
- useEffect13(() => {
15066
+ useEffect12(() => {
16276
15067
  const el = formPickElRef.current;
16277
15068
  if (!el || formPickRect === null) return;
16278
15069
  const observer = new ResizeObserver(() => {
@@ -16281,25 +15072,25 @@ function OhhwellsBridge() {
16281
15072
  observer.observe(el);
16282
15073
  return () => observer.disconnect();
16283
15074
  }, [formPickRect !== null, formViewState]);
16284
- const [toolbarVariant, setToolbarVariant] = useState13("none");
16285
- const toolbarVariantRef = useRef10("none");
15075
+ const [toolbarVariant, setToolbarVariant] = useState12("none");
15076
+ const toolbarVariantRef = useRef9("none");
16286
15077
  toolbarVariantRef.current = toolbarVariant;
16287
- const [selectedIsCta, setSelectedIsCta] = useState13(false);
16288
- const [selectedIsSocial, setSelectedIsSocial] = useState13(false);
16289
- const [selectedIsSocialsRow, setSelectedIsSocialsRow] = useState13(false);
16290
- const [reorderHrefKey, setReorderHrefKey] = useState13(null);
16291
- const [reorderDragDisabled, setReorderDragDisabled] = useState13(false);
16292
- const [toggleState, setToggleState] = useState13(null);
16293
- const [maxBadge, setMaxBadge] = useState13(null);
16294
- const [activeCommands, setActiveCommands] = useState13(/* @__PURE__ */ new Set());
16295
- const [sectionGap, setSectionGap] = useState13(null);
16296
- const [toolbarShowEditLink, setToolbarShowEditLink] = useState13(false);
16297
- const hoveredNavContainerRef = useRef10(null);
16298
- const [hoveredNavContainerRect, setHoveredNavContainerRect] = useState13(null);
16299
- const hoveredItemElRef = useRef10(null);
16300
- const [hoveredItemRect, setHoveredItemRect] = useState13(null);
16301
- const [hoveredTextRect, setHoveredTextRect] = useState13(null);
16302
- useEffect13(() => {
15078
+ const [selectedIsCta, setSelectedIsCta] = useState12(false);
15079
+ const [selectedIsSocial, setSelectedIsSocial] = useState12(false);
15080
+ const [selectedIsSocialsRow, setSelectedIsSocialsRow] = useState12(false);
15081
+ const [reorderHrefKey, setReorderHrefKey] = useState12(null);
15082
+ const [reorderDragDisabled, setReorderDragDisabled] = useState12(false);
15083
+ const [toggleState, setToggleState] = useState12(null);
15084
+ const [maxBadge, setMaxBadge] = useState12(null);
15085
+ const [activeCommands, setActiveCommands] = useState12(/* @__PURE__ */ new Set());
15086
+ const [sectionGap, setSectionGap] = useState12(null);
15087
+ const [toolbarShowEditLink, setToolbarShowEditLink] = useState12(false);
15088
+ const hoveredNavContainerRef = useRef9(null);
15089
+ const [hoveredNavContainerRect, setHoveredNavContainerRect] = useState12(null);
15090
+ const hoveredItemElRef = useRef9(null);
15091
+ const [hoveredItemRect, setHoveredItemRect] = useState12(null);
15092
+ const [hoveredTextRect, setHoveredTextRect] = useState12(null);
15093
+ useEffect12(() => {
16303
15094
  const sync = () => {
16304
15095
  const el = document.querySelector(
16305
15096
  '[data-ohw-hovered]:not([contenteditable]):not([data-ohw-href-key]):not([data-ohw-editable="form"] *)'
@@ -16324,56 +15115,43 @@ function OhhwellsBridge() {
16324
15115
  });
16325
15116
  return () => observer.disconnect();
16326
15117
  }, []);
16327
- const siblingHintElRef = useRef10(null);
16328
- const [siblingHintRect, setSiblingHintRect] = useState13(null);
16329
- const [siblingHintRects, setSiblingHintRects] = useState13([]);
16330
- const [isItemDragging, setIsItemDragging] = useState13(false);
16331
- const [isFooterFrameSelection, setIsFooterFrameSelection] = useState13(false);
15118
+ const siblingHintElRef = useRef9(null);
15119
+ const [siblingHintRect, setSiblingHintRect] = useState12(null);
15120
+ const [siblingHintRects, setSiblingHintRects] = useState12([]);
15121
+ const [isItemDragging, setIsItemDragging] = useState12(false);
15122
+ const [isFooterFrameSelection, setIsFooterFrameSelection] = useState12(false);
16332
15123
  isFooterFrameSelectionRef.current = isFooterFrameSelection;
16333
- const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = useState13(null);
16334
- const [footerHeadingVisible, setFooterHeadingVisible] = useState13(null);
16335
- const footerDragRef = useRef10(null);
16336
- const [footerDropSlots, setFooterDropSlots] = useState13([]);
16337
- const [activeFooterDropIndex, setActiveFooterDropIndex] = useState13(null);
16338
- const [draggedItemRect, setDraggedItemRect] = useState13(null);
16339
- const footerPointerDragRef = useRef10(null);
16340
- const suppressNextClickRef = useRef10(false);
16341
- const suppressClickUntilRef = useRef10(0);
16342
- const [linkPopover, setLinkPopover] = useState13(null);
16343
- const linkPopoverSessionRef = useRef10(null);
16344
- const addNavAfterAnchorRef = useRef10(null);
16345
- const editContentRef = useRef10({});
16346
- const aiSectionsRef = useRef10("");
16347
- const brandKitRef = useRef10("");
16348
- const stylesRef = useRef10("");
16349
- const pendingDeleteUndoRef = useRef10(null);
16350
- const [floatingPanel, setFloatingPanel] = useState13(null);
16351
- const floatingPanelOpenRef = useRef10(false);
16352
- const setFloatingPanelRef = useRef10(setFloatingPanel);
16353
- const [floatingPanelPos, setFloatingPanelPos] = useState13(null);
16354
- const [logoSizeDraft, setLogoSizeDraft] = useState13(null);
16355
- const [editorViewport, setEditorViewport] = useState13("desktop");
16356
- const [parentScrollSnap, setParentScrollSnap] = useState13(null);
16357
- const [sitePages, setSitePages] = useState13([]);
16358
- const [sectionsByPath, setSectionsByPath] = useState13({});
16359
- const sectionsPrefetchGenRef = useRef10(0);
16360
- const setLinkPopoverRef = useRef10(setLinkPopover);
16361
- const linkPopoverPanelRef = useRef10(null);
16362
- const linkPopoverOpenRef = useRef10(false);
16363
- const linkPopoverGraceUntilRef = useRef10(0);
15124
+ const [floatingPanel, setFloatingPanel] = useState12(null);
15125
+ const floatingPanelOpenRef = useRef9(false);
15126
+ floatingPanelOpenRef.current = floatingPanel !== null;
15127
+ const [floatingPanelPos, setFloatingPanelPos] = useState12(null);
15128
+ const [logoSizeDraft, setLogoSizeDraft] = useState12(null);
15129
+ const [editorViewport, setEditorViewport] = useState12("desktop");
15130
+ const [parentScrollSnap, setParentScrollSnap] = useState12(null);
15131
+ const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = useState12(null);
15132
+ const [footerHeadingVisible, setFooterHeadingVisible] = useState12(null);
15133
+ const footerDragRef = useRef9(null);
15134
+ const [footerDropSlots, setFooterDropSlots] = useState12([]);
15135
+ const [activeFooterDropIndex, setActiveFooterDropIndex] = useState12(null);
15136
+ const [draggedItemRect, setDraggedItemRect] = useState12(null);
15137
+ const footerPointerDragRef = useRef9(null);
15138
+ const suppressNextClickRef = useRef9(false);
15139
+ const suppressClickUntilRef = useRef9(0);
15140
+ const [linkPopover, setLinkPopover] = useState12(null);
15141
+ const linkPopoverSessionRef = useRef9(null);
15142
+ const addNavAfterAnchorRef = useRef9(null);
15143
+ const editContentRef = useRef9({});
15144
+ const aiSectionsRef = useRef9("");
15145
+ const pendingDeleteUndoRef = useRef9(null);
15146
+ const [sitePages, setSitePages] = useState12([]);
15147
+ const [sectionsByPath, setSectionsByPath] = useState12({});
15148
+ const sectionsPrefetchGenRef = useRef9(0);
15149
+ const setLinkPopoverRef = useRef9(setLinkPopover);
15150
+ const linkPopoverPanelRef = useRef9(null);
15151
+ const linkPopoverOpenRef = useRef9(false);
15152
+ const linkPopoverGraceUntilRef = useRef9(0);
16364
15153
  setLinkPopoverRef.current = setLinkPopover;
16365
- setFloatingPanelRef.current = setFloatingPanel;
16366
15154
  linkPopoverSessionRef.current = linkPopover;
16367
- floatingPanelOpenRef.current = Boolean(floatingPanel);
16368
- useEffect13(() => {
16369
- const syncViewport = () => {
16370
- const next = window.innerWidth <= 480 ? "mobile" : "desktop";
16371
- setEditorViewport((prev) => prev === next ? prev : next);
16372
- };
16373
- syncViewport();
16374
- window.addEventListener("resize", syncViewport);
16375
- return () => window.removeEventListener("resize", syncViewport);
16376
- }, []);
16377
15155
  const {
16378
15156
  navDragRef,
16379
15157
  navDropSlots,
@@ -16406,20 +15184,10 @@ function OhhwellsBridge() {
16406
15184
  getNavigationItemAnchor,
16407
15185
  isDragHandleDisabled
16408
15186
  });
16409
- const { sectionDropSlots, activeSectionDropIndex, isSectionDragging } = useSectionDrag({
16410
- isEditMode,
16411
- editContentRef,
16412
- postToParentRef,
16413
- parentScrollRef,
16414
- navDragRef,
16415
- footerDragRef,
16416
- suppressNextClickRef,
16417
- suppressClickUntilRef
16418
- });
16419
15187
  const bumpLinkPopoverGrace = () => {
16420
15188
  linkPopoverGraceUntilRef.current = Date.now() + 350;
16421
15189
  };
16422
- const runSectionsPrefetch = useCallback8((pages) => {
15190
+ const runSectionsPrefetch = useCallback7((pages) => {
16423
15191
  if (!isEditMode || shouldUseDevFixtures() || pages.length === 0) return;
16424
15192
  const gen = ++sectionsPrefetchGenRef.current;
16425
15193
  const paths = pages.map((p) => p.path);
@@ -16438,9 +15206,9 @@ function OhhwellsBridge() {
16438
15206
  );
16439
15207
  });
16440
15208
  }, [isEditMode, pathname]);
16441
- const runSectionsPrefetchRef = useRef10(runSectionsPrefetch);
15209
+ const runSectionsPrefetchRef = useRef9(runSectionsPrefetch);
16442
15210
  runSectionsPrefetchRef.current = runSectionsPrefetch;
16443
- useEffect13(() => {
15211
+ useEffect12(() => {
16444
15212
  if (!linkPopover) {
16445
15213
  document.documentElement.removeAttribute("data-ohw-link-popover-open");
16446
15214
  return;
@@ -16468,7 +15236,7 @@ function OhhwellsBridge() {
16468
15236
  document.documentElement.removeAttribute("data-ohw-link-popover-open");
16469
15237
  };
16470
15238
  }, [linkPopover, postToParent2]);
16471
- useEffect13(() => {
15239
+ useEffect12(() => {
16472
15240
  if (!isEditMode) return;
16473
15241
  const useFixtures = shouldUseDevFixtures();
16474
15242
  if (useFixtures) {
@@ -16492,14 +15260,14 @@ function OhhwellsBridge() {
16492
15260
  if (!useFixtures) postToParent2({ type: "ow:request-site-pages" });
16493
15261
  return () => window.removeEventListener("message", onSitePages);
16494
15262
  }, [isEditMode, postToParent2]);
16495
- useEffect13(() => {
15263
+ useEffect12(() => {
16496
15264
  if (!isEditMode || shouldUseDevFixtures()) return;
16497
15265
  void loadAllSectionsManifest().then((manifest) => {
16498
15266
  if (Object.keys(manifest).length === 0) return;
16499
15267
  setSectionsByPath((prev) => ({ ...manifest, ...prev }));
16500
15268
  });
16501
15269
  }, [isEditMode]);
16502
- useEffect13(() => {
15270
+ useEffect12(() => {
16503
15271
  const update = () => {
16504
15272
  const el = activeElRef.current ?? selectedElRef.current;
16505
15273
  if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
@@ -16523,10 +15291,10 @@ function OhhwellsBridge() {
16523
15291
  vvp.removeEventListener("resize", update);
16524
15292
  };
16525
15293
  }, []);
16526
- const refreshStateRules = useCallback8(() => {
15294
+ const refreshStateRules = useCallback7(() => {
16527
15295
  editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
16528
15296
  }, []);
16529
- const processConfigRequest = useCallback8((insertAfterVal) => {
15297
+ const processConfigRequest = useCallback7((insertAfterVal) => {
16530
15298
  const tracker = getSectionsTracker();
16531
15299
  let entries = [];
16532
15300
  try {
@@ -16549,7 +15317,7 @@ function OhhwellsBridge() {
16549
15317
  }
16550
15318
  window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: null }, "*");
16551
15319
  }, [isEditMode]);
16552
- const deactivate = useCallback8(() => {
15320
+ const deactivate = useCallback7(() => {
16553
15321
  const el = activeElRef.current;
16554
15322
  if (!el) return;
16555
15323
  const isFormBlock = el.dataset.ohwEditable === "form";
@@ -16565,7 +15333,7 @@ function OhhwellsBridge() {
16565
15333
  const original = originalContentRef.current ?? "";
16566
15334
  if (html !== sanitizeHtml(original)) {
16567
15335
  postToParentRef.current({ type: "ow:change", nodes: [{ key, text: html }] });
16568
- const h = document.body.scrollHeight;
15336
+ const h = document.documentElement.scrollHeight;
16569
15337
  if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
16570
15338
  }
16571
15339
  }
@@ -16590,12 +15358,12 @@ function OhhwellsBridge() {
16590
15358
  setToolbarShowEditLink(false);
16591
15359
  postToParent2({ type: "ow:exit-edit" });
16592
15360
  }, [postToParent2]);
16593
- const clearSelectedAttr = useCallback8(() => {
15361
+ const clearSelectedAttr = useCallback7(() => {
16594
15362
  document.querySelectorAll("[data-ohw-selected]").forEach((el) => {
16595
15363
  el.removeAttribute("data-ohw-selected");
16596
15364
  });
16597
15365
  }, []);
16598
- const deselect = useCallback8(() => {
15366
+ const deselect = useCallback7(() => {
16599
15367
  clearSelectedAttr();
16600
15368
  selectedElRef.current = null;
16601
15369
  selectedHrefKeyRef.current = null;
@@ -16624,20 +15392,20 @@ function OhhwellsBridge() {
16624
15392
  setToolbarVariant("none");
16625
15393
  }
16626
15394
  }, [clearSelectedAttr]);
16627
- const markSelected = useCallback8((el) => {
15395
+ const markSelected = useCallback7((el) => {
16628
15396
  clearSelectedAttr();
16629
15397
  el.removeAttribute("data-ohw-hovered");
16630
15398
  el.setAttribute("data-ohw-selected", "");
16631
15399
  }, [clearSelectedAttr]);
16632
- const isSelectedForHover = useCallback8((el) => {
15400
+ const isSelectedForHover = useCallback7((el) => {
16633
15401
  if (!el) return false;
16634
15402
  return [selectedElRef.current, activeElRef.current].some(
16635
15403
  (busy) => busy && (el === busy || busy.contains(el) || el.contains(busy))
16636
15404
  );
16637
15405
  }, []);
16638
- const isSelectedForHoverRef = useRef10(isSelectedForHover);
15406
+ const isSelectedForHoverRef = useRef9(isSelectedForHover);
16639
15407
  isSelectedForHoverRef.current = isSelectedForHover;
16640
- const resolveHrefKeyElement = useCallback8((hrefKey) => {
15408
+ const resolveHrefKeyElement = useCallback7((hrefKey) => {
16641
15409
  if (isFooterHrefKey(hrefKey)) {
16642
15410
  return document.querySelector(
16643
15411
  `footer [data-ohw-href-key="${CSS.escape(hrefKey)}"]`
@@ -16652,7 +15420,7 @@ function OhhwellsBridge() {
16652
15420
  `[data-ohw-href-key="${CSS.escape(hrefKey)}"]`
16653
15421
  );
16654
15422
  }, []);
16655
- const resyncSelectedNavigationItem = useCallback8(() => {
15423
+ const resyncSelectedNavigationItem = useCallback7(() => {
16656
15424
  const hrefKey = selectedHrefKeyRef.current;
16657
15425
  if (hrefKey) {
16658
15426
  const link = resolveHrefKeyElement(hrefKey);
@@ -16690,7 +15458,7 @@ function OhhwellsBridge() {
16690
15458
  );
16691
15459
  }
16692
15460
  }, [resolveHrefKeyElement]);
16693
- const reselectNavigationItem = useCallback8((navAnchor) => {
15461
+ const reselectNavigationItem = useCallback7((navAnchor) => {
16694
15462
  selectedElRef.current = navAnchor;
16695
15463
  selectedHrefKeyRef.current = navAnchor.getAttribute("data-ohw-href-key");
16696
15464
  selectedFooterColAttrRef.current = null;
@@ -16721,7 +15489,7 @@ function OhhwellsBridge() {
16721
15489
  setToolbarShowEditLink(false);
16722
15490
  setActiveCommands(/* @__PURE__ */ new Set());
16723
15491
  }, [markSelected]);
16724
- const commitNavigationTextEdit = useCallback8((navAnchor) => {
15492
+ const commitNavigationTextEdit = useCallback7((navAnchor) => {
16725
15493
  const el = activeElRef.current;
16726
15494
  if (!el) return;
16727
15495
  const key = el.dataset.ohwKey;
@@ -16735,7 +15503,7 @@ function OhhwellsBridge() {
16735
15503
  const original = originalContentRef.current ?? "";
16736
15504
  if (html !== sanitizeHtml(original)) {
16737
15505
  postToParent2({ type: "ow:change", nodes: [{ key, text: html }] });
16738
- const h = document.body.scrollHeight;
15506
+ const h = document.documentElement.scrollHeight;
16739
15507
  if (h > 50) postToParent2({ type: "ow:height", height: h });
16740
15508
  }
16741
15509
  }
@@ -16754,7 +15522,7 @@ function OhhwellsBridge() {
16754
15522
  postToParent2({ type: "ow:exit-edit" });
16755
15523
  reselectNavigationItem(navAnchor);
16756
15524
  }, [postToParent2, reselectNavigationItem]);
16757
- const handleAddTopLevelNavItem = useCallback8(() => {
15525
+ const handleAddTopLevelNavItem = useCallback7(() => {
16758
15526
  const items = listNavbarRootItems();
16759
15527
  addNavAfterAnchorRef.current = items[items.length - 1] ?? null;
16760
15528
  deselectRef.current();
@@ -16766,7 +15534,7 @@ function OhhwellsBridge() {
16766
15534
  intent: "add-nav"
16767
15535
  });
16768
15536
  }, []);
16769
- const maybeWarnNavLinkDropdownConflict = useCallback8(
15537
+ const maybeWarnNavLinkDropdownConflict = useCallback7(
16770
15538
  (anchor) => {
16771
15539
  if (!isNavbarHrefKey(anchor.getAttribute("data-ohw-href-key"))) return;
16772
15540
  if (!navDropdownsOpenOnClick()) return;
@@ -16779,7 +15547,7 @@ function OhhwellsBridge() {
16779
15547
  },
16780
15548
  [postToParent2]
16781
15549
  );
16782
- const handleNavDropdownOpenChange = useCallback8((open) => {
15550
+ const handleNavDropdownOpenChange = useCallback7((open) => {
16783
15551
  const selected = selectedElRef.current;
16784
15552
  if (!selected || !isNavigationItem2(selected)) return;
16785
15553
  setNavGroupForceOpen(selected, open);
@@ -16791,7 +15559,7 @@ function OhhwellsBridge() {
16791
15559
  }
16792
15560
  });
16793
15561
  }, []);
16794
- const handleFooterHeadingVisibleChange = useCallback8(
15562
+ const handleFooterHeadingVisibleChange = useCallback7(
16795
15563
  (visible) => {
16796
15564
  const selected = selectedElRef.current;
16797
15565
  if (!selected || !isFooterFrameSelectionRef.current) return;
@@ -16815,7 +15583,7 @@ function OhhwellsBridge() {
16815
15583
  },
16816
15584
  [postToParent2]
16817
15585
  );
16818
- const enterEditOnNewItem = useCallback8((anchor) => {
15586
+ const enterEditOnNewItem = useCallback7((anchor) => {
16819
15587
  const label = anchor.querySelector('[data-ohw-editable="text"]');
16820
15588
  if (!label) {
16821
15589
  selectRef.current(anchor);
@@ -16824,8 +15592,8 @@ function OhhwellsBridge() {
16824
15592
  setNavGroupForceOpen(anchor, true);
16825
15593
  activateRef.current(label);
16826
15594
  }, []);
16827
- const pendingSocialAddRef = useRef10(null);
16828
- const handleAddChildItem = useCallback8(() => {
15595
+ const pendingSocialAddRef = useRef9(null);
15596
+ const handleAddChildItem = useCallback7(() => {
16829
15597
  const selected = selectedElRef.current;
16830
15598
  if (!selected) return;
16831
15599
  const socialsRow = isSocialsRow(selected) ? selected : findSocialsRow(selected);
@@ -16934,7 +15702,7 @@ function OhhwellsBridge() {
16934
15702
  enterEditOnNewItem(result.anchor);
16935
15703
  });
16936
15704
  }, [enterEditOnNewItem, isFooterFrameSelection, maybeWarnNavLinkDropdownConflict, postToParent2]);
16937
- const handleAddFooterColumn = useCallback8(() => {
15705
+ const handleAddFooterColumn = useCallback7(() => {
16938
15706
  if (!canAddFooterColumn()) {
16939
15707
  postToParent2({
16940
15708
  type: "ow:toast",
@@ -16955,7 +15723,7 @@ function OhhwellsBridge() {
16955
15723
  selectRef.current(result.firstLink);
16956
15724
  });
16957
15725
  }, [postToParent2]);
16958
- const clearFooterDragVisuals = useCallback8(() => {
15726
+ const clearFooterDragVisuals = useCallback7(() => {
16959
15727
  footerDragRef.current = null;
16960
15728
  setSiblingHintRects([]);
16961
15729
  setFooterDropSlots([]);
@@ -16964,7 +15732,7 @@ function OhhwellsBridge() {
16964
15732
  setIsItemDragging(false);
16965
15733
  unlockFooterDragInteraction();
16966
15734
  }, []);
16967
- const refreshFooterDragVisuals = useCallback8((session, activeSlot, clientX, clientY) => {
15735
+ const refreshFooterDragVisuals = useCallback7((session, activeSlot, clientX, clientY) => {
16968
15736
  const dragged = session.draggedEl;
16969
15737
  setDraggedItemRect(dragged.getBoundingClientRect());
16970
15738
  if (typeof clientX === "number" && typeof clientY === "number") {
@@ -16996,13 +15764,13 @@ function OhhwellsBridge() {
16996
15764
  const activeIdx = activeSlot ? slots.findIndex((s) => s.insertIndex === activeSlot.insertIndex) : -1;
16997
15765
  setActiveFooterDropIndex(activeIdx >= 0 ? activeIdx : null);
16998
15766
  }, []);
16999
- const refreshFooterDragVisualsRef = useRef10(refreshFooterDragVisuals);
15767
+ const refreshFooterDragVisualsRef = useRef9(refreshFooterDragVisuals);
17000
15768
  refreshFooterDragVisualsRef.current = refreshFooterDragVisuals;
17001
- const commitFooterDragRef = useRef10(() => {
15769
+ const commitFooterDragRef = useRef9(() => {
17002
15770
  });
17003
- const beginFooterDragRef = useRef10(() => {
15771
+ const beginFooterDragRef = useRef9(() => {
17004
15772
  });
17005
- const beginFooterDrag = useCallback8(
15773
+ const beginFooterDrag = useCallback7(
17006
15774
  (session) => {
17007
15775
  const rect = session.draggedEl.getBoundingClientRect();
17008
15776
  session.lastClientX = session.lastClientX || rect.left + rect.width / 2;
@@ -17022,7 +15790,7 @@ function OhhwellsBridge() {
17022
15790
  [refreshFooterDragVisuals]
17023
15791
  );
17024
15792
  beginFooterDragRef.current = beginFooterDrag;
17025
- const commitFooterDrag = useCallback8(
15793
+ const commitFooterDrag = useCallback7(
17026
15794
  (clientX, clientY) => {
17027
15795
  const session = footerDragRef.current;
17028
15796
  if (!session) {
@@ -17151,7 +15919,7 @@ function OhhwellsBridge() {
17151
15919
  [clearFooterDragVisuals, resolveHrefKeyElement, resyncSelectedNavigationItem]
17152
15920
  );
17153
15921
  commitFooterDragRef.current = commitFooterDrag;
17154
- const startFooterLinkDrag = useCallback8(
15922
+ const startFooterLinkDrag = useCallback7(
17155
15923
  (anchor, clientX, clientY, wasSelected) => {
17156
15924
  const hrefKey = anchor.getAttribute("data-ohw-href-key");
17157
15925
  if (!hrefKey) return false;
@@ -17187,7 +15955,7 @@ function OhhwellsBridge() {
17187
15955
  },
17188
15956
  [beginFooterDrag]
17189
15957
  );
17190
- const startFooterColumnDrag = useCallback8(
15958
+ const startFooterColumnDrag = useCallback7(
17191
15959
  (columnEl, clientX, clientY, wasSelected) => {
17192
15960
  const columns = listFooterColumns();
17193
15961
  const idx = columns.indexOf(columnEl);
@@ -17207,7 +15975,7 @@ function OhhwellsBridge() {
17207
15975
  },
17208
15976
  [beginFooterDrag]
17209
15977
  );
17210
- const handleItemDragStart = useCallback8(
15978
+ const handleItemDragStart = useCallback7(
17211
15979
  (e) => {
17212
15980
  const selected = selectedElRef.current;
17213
15981
  if (!selected) {
@@ -17227,7 +15995,7 @@ function OhhwellsBridge() {
17227
15995
  },
17228
15996
  [startFooterColumnDrag, startFooterLinkDrag, startNavLinkDrag]
17229
15997
  );
17230
- const handleItemDragEnd = useCallback8(
15998
+ const handleItemDragEnd = useCallback7(
17231
15999
  (e) => {
17232
16000
  if (footerDragRef.current) {
17233
16001
  const x = e?.clientX;
@@ -17253,7 +16021,7 @@ function OhhwellsBridge() {
17253
16021
  },
17254
16022
  [commitFooterDrag, commitNavDrag, navDragRef]
17255
16023
  );
17256
- const handleItemChromePointerDown = useCallback8((e) => {
16024
+ const handleItemChromePointerDown = useCallback7((e) => {
17257
16025
  if (e.button !== 0) return;
17258
16026
  const selected = selectedElRef.current;
17259
16027
  if (!selected) return;
@@ -17284,7 +16052,7 @@ function OhhwellsBridge() {
17284
16052
  }
17285
16053
  if (armNavPressFromChrome(selected, e.clientX, e.clientY, e.pointerId)) return;
17286
16054
  }, [armNavPressFromChrome]);
17287
- const handleItemChromeClick = useCallback8((clientX, clientY) => {
16055
+ const handleItemChromeClick = useCallback7((clientX, clientY) => {
17288
16056
  if (suppressNextClickRef.current || Date.now() < suppressClickUntilRef.current) {
17289
16057
  suppressNextClickRef.current = false;
17290
16058
  return;
@@ -17297,7 +16065,7 @@ function OhhwellsBridge() {
17297
16065
  }, []);
17298
16066
  reselectNavigationItemRef.current = reselectNavigationItem;
17299
16067
  commitNavigationTextEditRef.current = commitNavigationTextEdit;
17300
- const select = useCallback8((anchor) => {
16068
+ const select = useCallback7((anchor) => {
17301
16069
  if (!isNavigationItem2(anchor)) return;
17302
16070
  if (activeElRef.current) deactivate();
17303
16071
  aiSectionApiRef.current?.selectFromElement(anchor);
@@ -17340,7 +16108,7 @@ function OhhwellsBridge() {
17340
16108
  setFloatingPanel(null);
17341
16109
  setLogoSizeDraft(null);
17342
16110
  }, [deactivate, markSelected]);
17343
- const selectFrame = useCallback8((el) => {
16111
+ const selectFrame = useCallback7((el) => {
17344
16112
  if (!isNavigationContainer(el)) return;
17345
16113
  if (activeElRef.current) deactivate();
17346
16114
  aiSectionApiRef.current?.selectFromElement(el);
@@ -17391,7 +16159,7 @@ function OhhwellsBridge() {
17391
16159
  setFloatingPanel(null);
17392
16160
  setLogoSizeDraft(null);
17393
16161
  }, [deactivate, markSelected, postToParent2]);
17394
- const selectLogo = useCallback8(
16162
+ const selectLogo = useCallback7(
17395
16163
  (logoEl) => {
17396
16164
  if (activeElRef.current) deactivate();
17397
16165
  selectedElRef.current = logoEl;
@@ -17420,7 +16188,7 @@ function OhhwellsBridge() {
17420
16188
  },
17421
16189
  [deactivate, markSelected]
17422
16190
  );
17423
- const openLogoSizePanel = useCallback8((logoEl) => {
16191
+ const openLogoSizePanel = useCallback7((logoEl) => {
17424
16192
  const placement = getLogoPlacement(logoEl);
17425
16193
  const draft = readLogoSizeState(editContentRef.current, placement);
17426
16194
  setLogoSizeDraft(draft);
@@ -17433,7 +16201,7 @@ function OhhwellsBridge() {
17433
16201
  placement
17434
16202
  });
17435
16203
  }, []);
17436
- const openSocialsDisplayPanel = useCallback8((row) => {
16204
+ const openSocialsDisplayPanel = useCallback7((row) => {
17437
16205
  setParentScrollSnap(parentScrollRef.current);
17438
16206
  setFloatingPanel({
17439
16207
  key: "socials-display",
@@ -17443,11 +16211,11 @@ function OhhwellsBridge() {
17443
16211
  row
17444
16212
  });
17445
16213
  }, []);
17446
- const isEditModeRef = useRef10(false);
17447
- const requestMissingSocialIconsRef = useRef10(() => {
16214
+ const isEditModeRef = useRef9(false);
16215
+ const requestMissingSocialIconsRef = useRef9(() => {
17448
16216
  });
17449
- const askedSocialIconsRef = useRef10(/* @__PURE__ */ new Set());
17450
- const requestMissingSocialIcons = useCallback8(() => {
16217
+ const askedSocialIconsRef = useRef9(/* @__PURE__ */ new Set());
16218
+ const requestMissingSocialIcons = useCallback7(() => {
17451
16219
  const items = Array.from(document.querySelectorAll(`[${SOCIALS_ROW_ATTR}]`)).filter((row) => socialsDisplayFor(row, editContentRef.current).icon).flatMap((row) => {
17452
16220
  const missing = socialsMissingIcons(row);
17453
16221
  listSocialItems(row).forEach((item) => ensureIconSlot(item));
@@ -17459,7 +16227,7 @@ function OhhwellsBridge() {
17459
16227
  }, []);
17460
16228
  requestMissingSocialIconsRef.current = requestMissingSocialIcons;
17461
16229
  isEditModeRef.current = isEditMode;
17462
- const changeSocialsDisplay = useCallback8(
16230
+ const changeSocialsDisplay = useCallback7(
17463
16231
  (row, next) => {
17464
16232
  if (next.icon) {
17465
16233
  const missing = socialsMissingIcons(row);
@@ -17483,17 +16251,17 @@ function OhhwellsBridge() {
17483
16251
  },
17484
16252
  []
17485
16253
  );
17486
- const closeFloatingPanelOnly = useCallback8(() => {
16254
+ const closeFloatingPanelOnly = useCallback7(() => {
17487
16255
  setFloatingPanel(null);
17488
16256
  setLogoSizeDraft(null);
17489
16257
  }, []);
17490
16258
  closeFloatingPanelOnlyRef.current = closeFloatingPanelOnly;
17491
- const closeFloatingPanelAndDeselect = useCallback8(() => {
16259
+ const closeFloatingPanelAndDeselect = useCallback7(() => {
17492
16260
  setFloatingPanel(null);
17493
16261
  setLogoSizeDraft(null);
17494
16262
  deselectRef.current();
17495
16263
  }, []);
17496
- useEffect13(() => {
16264
+ useEffect12(() => {
17497
16265
  const session = floatingPanel && floatingPanel.kind === "logo-size" ? floatingPanel : null;
17498
16266
  if (!session || !logoSizeDraft) {
17499
16267
  postToParentRef.current({ type: "ow:logo-size-panel", open: false });
@@ -17512,7 +16280,7 @@ function OhhwellsBridge() {
17512
16280
  max: LOGO_SIZE_MAX
17513
16281
  });
17514
16282
  }, [floatingPanel, logoSizeDraft, editorViewport]);
17515
- const persistLogoSizeDraft = useCallback8(
16283
+ const persistLogoSizeDraft = useCallback7(
17516
16284
  (placement, draft) => {
17517
16285
  const desktopKey = LOGO_SIZE_DESKTOP_KEYS[placement];
17518
16286
  const mobileKey = LOGO_SIZE_MOBILE_KEYS[placement];
@@ -17552,7 +16320,7 @@ function OhhwellsBridge() {
17552
16320
  },
17553
16321
  [postToParent2]
17554
16322
  );
17555
- const activate = useCallback8((el, options) => {
16323
+ const activate = useCallback7((el, options) => {
17556
16324
  if (activeElRef.current === el) return;
17557
16325
  document.querySelectorAll("[data-ohw-hovered]").forEach((hovered) => {
17558
16326
  hovered.removeAttribute("data-ohw-hovered");
@@ -17650,8 +16418,8 @@ function OhhwellsBridge() {
17650
16418
  openLogoSizePanelRef.current = openLogoSizePanel;
17651
16419
  deselectRef.current = deselect;
17652
16420
  closeFloatingPanelOnlyRef.current = closeFloatingPanelOnly;
17653
- const lastSiteWideScopeRef = useRef10(null);
17654
- useEffect13(() => {
16421
+ const lastSiteWideScopeRef = useRef9(null);
16422
+ useEffect12(() => {
17655
16423
  if (!isEditMode) {
17656
16424
  if (lastSiteWideScopeRef.current !== false) {
17657
16425
  lastSiteWideScopeRef.current = false;
@@ -17684,27 +16452,15 @@ function OhhwellsBridge() {
17684
16452
  }
17685
16453
  const applyContent = (content) => {
17686
16454
  const imageLoads = [];
17687
- if (typeof content[BRAND_KIT_KEY] === "string") {
17688
- brandKitRef.current = content[BRAND_KIT_KEY];
17689
- applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
17690
- }
17691
16455
  if (typeof content[AI_SECTIONS_KEY] === "string") {
17692
16456
  aiSectionsRef.current = content[AI_SECTIONS_KEY];
17693
16457
  applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
17694
16458
  }
17695
- if (typeof content[STYLE_STORE_KEY] === "string") {
17696
- stylesRef.current = content[STYLE_STORE_KEY];
17697
- applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
17698
- }
17699
- applyBrandChrome(content);
17700
16459
  for (const [key, val] of Object.entries(content)) {
17701
16460
  if (key === "__ohw_sections") continue;
17702
16461
  if (key === AI_SECTIONS_KEY) continue;
17703
16462
  if (key === LOGO_PLACEHOLDER_KEY) continue;
17704
16463
  if (LOGO_IMAGE_KEYS.includes(key)) continue;
17705
- if (key === BRAND_KIT_KEY) continue;
17706
- if (key === STYLE_STORE_KEY) continue;
17707
- if (BRAND_CHROME_KEYS.has(key)) continue;
17708
16464
  if (applyVideoSettingNode(key, val)) continue;
17709
16465
  if (applyCarouselNode(key, val)) continue;
17710
16466
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -17765,9 +16521,7 @@ function OhhwellsBridge() {
17765
16521
  let cancelled = false;
17766
16522
  setFetchState("loading");
17767
16523
  const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
17768
- const initialPath = pathname;
17769
- fetchedContentPaths.add(`${subdomain}::${initialPath}`);
17770
- fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
16524
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
17771
16525
  if (cancelled) return;
17772
16526
  const content = data?.content ?? {};
17773
16527
  contentCache.set(subdomain, content);
@@ -17780,7 +16534,7 @@ function OhhwellsBridge() {
17780
16534
  cancelled = true;
17781
16535
  };
17782
16536
  }, [subdomain, isEditMode]);
17783
- useEffect13(() => {
16537
+ useEffect12(() => {
17784
16538
  if (!isEditMode) return;
17785
16539
  const resolveIndex = (form, clientY) => {
17786
16540
  const wrappers = listFieldWrappers(form).filter((el) => fieldKeyOf(el) !== fieldDragRef.current?.key);
@@ -17821,7 +16575,7 @@ function OhhwellsBridge() {
17821
16575
  window.removeEventListener("drop", onDrop, true);
17822
16576
  };
17823
16577
  }, [buildFieldDropSlots, isEditMode, persistFields, selectField]);
17824
- useEffect13(() => {
16578
+ useEffect12(() => {
17825
16579
  if (!isEditMode) return;
17826
16580
  const mark = () => document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
17827
16581
  markFormFields(form);
@@ -17833,7 +16587,7 @@ function OhhwellsBridge() {
17833
16587
  });
17834
16588
  return () => observer.disconnect();
17835
16589
  }, [isEditMode, fetchState, pathname]);
17836
- useEffect13(() => {
16590
+ useEffect12(() => {
17837
16591
  if (!isEditMode) return;
17838
16592
  let saveTimer = null;
17839
16593
  const onInput = (e) => {
@@ -17855,14 +16609,14 @@ function OhhwellsBridge() {
17855
16609
  document.addEventListener("input", onInput, true);
17856
16610
  return () => document.removeEventListener("input", onInput, true);
17857
16611
  }, [isEditMode, persistFields]);
17858
- useEffect13(() => {
16612
+ useEffect12(() => {
17859
16613
  if (isEditMode || fetchState !== "done") return;
17860
16614
  const content = contentCache.get(subdomain) ?? {};
17861
16615
  document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
17862
16616
  reconcileFieldsFromContent(form, content);
17863
16617
  });
17864
16618
  }, [isEditMode, fetchState, subdomain]);
17865
- useEffect13(() => {
16619
+ useEffect12(() => {
17866
16620
  if (!isEditMode) return;
17867
16621
  const swallow = (e) => {
17868
16622
  const target = e.target;
@@ -17871,12 +16625,12 @@ function OhhwellsBridge() {
17871
16625
  document.addEventListener("submit", swallow, true);
17872
16626
  return () => document.removeEventListener("submit", swallow, true);
17873
16627
  }, [isEditMode]);
17874
- useEffect13(() => {
16628
+ useEffect12(() => {
17875
16629
  if (isEditMode || fetchState !== "done") return;
17876
16630
  const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
17877
16631
  bindPublishedForms(apiUrl, subdomain, contentCache.get(subdomain) ?? {});
17878
16632
  }, [isEditMode, fetchState, subdomain]);
17879
- useEffect13(() => {
16633
+ useEffect12(() => {
17880
16634
  if (!subdomain || isEditMode) return;
17881
16635
  let debounceTimer = null;
17882
16636
  let observer = null;
@@ -17887,25 +16641,10 @@ function OhhwellsBridge() {
17887
16641
  initSectionInstancesFromContent(content, window.location.pathname);
17888
16642
  observer?.disconnect();
17889
16643
  try {
17890
- applyBrandChrome(content);
17891
- if (typeof content[BRAND_KIT_KEY] === "string") {
17892
- applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
17893
- }
17894
- if (typeof content[AI_SECTIONS_KEY] === "string") {
17895
- applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
17896
- }
17897
- if (typeof content[STYLE_STORE_KEY] === "string") {
17898
- applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
17899
- }
17900
16644
  for (const [key, val] of Object.entries(content)) {
17901
16645
  if (key === "__ohw_sections") continue;
17902
- if (key === AI_SECTIONS_KEY) continue;
17903
16646
  if (key === LOGO_PLACEHOLDER_KEY) continue;
17904
16647
  if (LOGO_IMAGE_KEYS.includes(key)) continue;
17905
- if (key === BRAND_KIT_KEY) continue;
17906
- if (key === STYLE_STORE_KEY) continue;
17907
- if (key === STYLE_STORE_KEY) continue;
17908
- if (BRAND_CHROME_KEYS.has(key)) continue;
17909
16648
  if (applyVideoSettingNode(key, val)) continue;
17910
16649
  if (applyCarouselNode(key, val)) continue;
17911
16650
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -17951,17 +16690,6 @@ function OhhwellsBridge() {
17951
16690
  debounceTimer = setTimeout(applyFromCache, 150);
17952
16691
  };
17953
16692
  applyFromCache();
17954
- const pathCacheKey = `${subdomain}::${pathname}`;
17955
- if (!fetchedContentPaths.has(pathCacheKey)) {
17956
- fetchedContentPaths.add(pathCacheKey);
17957
- const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
17958
- fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(pathname)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
17959
- if (!data?.content) return;
17960
- contentCache.set(subdomain, data.content);
17961
- applyFromCache();
17962
- }).catch(() => {
17963
- });
17964
- }
17965
16693
  observer = new MutationObserver(scheduleApply);
17966
16694
  observer.observe(document.body, { childList: true, subtree: true });
17967
16695
  return () => {
@@ -17975,10 +16703,10 @@ function OhhwellsBridge() {
17975
16703
  const visible = Boolean(subdomain) && fetchState !== "done";
17976
16704
  el.style.display = visible ? "flex" : "none";
17977
16705
  }, [subdomain, fetchState]);
17978
- useEffect13(() => {
16706
+ useEffect12(() => {
17979
16707
  postToParent2({ type: "ow:navigation", path: pathname });
17980
16708
  }, [pathname, postToParent2]);
17981
- useEffect13(() => {
16709
+ useEffect12(() => {
17982
16710
  if (!isEditMode) return;
17983
16711
  if (linkPopoverSessionRef.current?.intent === "add-nav") return;
17984
16712
  if (document.querySelector("[data-ohw-section-picker]")) return;
@@ -17986,7 +16714,7 @@ function OhhwellsBridge() {
17986
16714
  deselectRef.current();
17987
16715
  deactivateRef.current();
17988
16716
  }, [pathname, isEditMode]);
17989
- useEffect13(() => {
16717
+ useEffect12(() => {
17990
16718
  const contentForNav = () => {
17991
16719
  if (isEditMode) return editContentRef.current;
17992
16720
  if (!subdomain) return {};
@@ -18055,36 +16783,35 @@ function OhhwellsBridge() {
18055
16783
  observer?.disconnect();
18056
16784
  };
18057
16785
  }, [isEditMode, pathname, subdomain, fetchState, resyncSelectedNavigationItem]);
18058
- useEffect13(() => {
16786
+ useEffect12(() => {
18059
16787
  if (!isEditMode) return;
18060
- let lastPosted = 0;
18061
16788
  const measure = () => {
18062
16789
  const h = document.body.scrollHeight;
18063
- if (h > 50 && Math.abs(h - lastPosted) > 1) {
18064
- lastPosted = h;
18065
- postToParent2({ type: "ow:height", height: h });
18066
- }
18067
- };
18068
- let raf = null;
18069
- const schedule = () => {
18070
- if (raf != null) return;
18071
- raf = requestAnimationFrame(() => {
18072
- raf = null;
18073
- measure();
18074
- });
16790
+ if (h > 50) postToParent2({ type: "ow:height", height: h });
18075
16791
  };
18076
16792
  const t1 = setTimeout(measure, 50);
18077
16793
  const t2 = setTimeout(measure, 500);
18078
- const ro = new ResizeObserver(schedule);
18079
- ro.observe(document.body);
16794
+ let lastWidth = window.innerWidth;
16795
+ let resizeTimers = [];
16796
+ const clearResizeTimers = () => {
16797
+ resizeTimers.forEach(clearTimeout);
16798
+ resizeTimers = [];
16799
+ };
16800
+ const handleResize = () => {
16801
+ if (window.innerWidth === lastWidth) return;
16802
+ lastWidth = window.innerWidth;
16803
+ clearResizeTimers();
16804
+ resizeTimers = HEIGHT_SETTLE_DELAYS.map((delay) => setTimeout(measure, delay));
16805
+ };
16806
+ window.addEventListener("resize", handleResize);
18080
16807
  return () => {
18081
16808
  clearTimeout(t1);
18082
16809
  clearTimeout(t2);
18083
- if (raf != null) cancelAnimationFrame(raf);
18084
- ro.disconnect();
16810
+ clearResizeTimers();
16811
+ window.removeEventListener("resize", handleResize);
18085
16812
  };
18086
16813
  }, [pathname, isEditMode, postToParent2]);
18087
- useEffect13(() => {
16814
+ useEffect12(() => {
18088
16815
  if (!subdomainFromQuery || isEditMode) return;
18089
16816
  const handleClick = (e) => {
18090
16817
  const anchor = e.target.closest("a");
@@ -18100,7 +16827,7 @@ function OhhwellsBridge() {
18100
16827
  document.addEventListener("click", handleClick, true);
18101
16828
  return () => document.removeEventListener("click", handleClick, true);
18102
16829
  }, [subdomainFromQuery, isEditMode, router]);
18103
- useEffect13(() => {
16830
+ useEffect12(() => {
18104
16831
  if (!isEditMode) {
18105
16832
  editStylesRef.current?.base.remove();
18106
16833
  editStylesRef.current?.forceHover.remove();
@@ -18322,7 +17049,6 @@ function OhhwellsBridge() {
18322
17049
  return;
18323
17050
  }
18324
17051
  const target = e.target;
18325
- if (target.closest("[data-ohw-ai-review]")) return;
18326
17052
  if (target.closest("[data-ohw-toolbar]")) return;
18327
17053
  if (target.closest("[data-ohw-state-toggle]")) return;
18328
17054
  if (target.closest("[data-ohw-max-badge]")) return;
@@ -18334,9 +17060,6 @@ function OhhwellsBridge() {
18334
17060
  )) {
18335
17061
  return;
18336
17062
  }
18337
- if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
18338
- clearMediaSelectionRef.current();
18339
- }
18340
17063
  {
18341
17064
  const formEl = getFormElement(target);
18342
17065
  const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
@@ -18491,11 +17214,8 @@ function OhhwellsBridge() {
18491
17214
  if (isMediaEditable(editable) && !buttonOnMedia) {
18492
17215
  e.preventDefault();
18493
17216
  e.stopPropagation();
18494
- if (selectedMediaElRef.current === editable) {
18495
- postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "", elementType: editable.dataset.ohwEditable ?? "image" });
18496
- } else {
18497
- selectMediaElementRef.current(editable);
18498
- }
17217
+ aiSectionApiRef.current?.selectFromElement(editable);
17218
+ postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "", elementType: editable.dataset.ohwEditable ?? "image" });
18499
17219
  return;
18500
17220
  }
18501
17221
  const socialItem = getSocialItem(editable);
@@ -18631,7 +17351,6 @@ function OhhwellsBridge() {
18631
17351
  };
18632
17352
  const handleDblClick = (e) => {
18633
17353
  const target = e.target;
18634
- if (target.closest("[data-ohw-ai-review]")) return;
18635
17354
  if (target.closest("[data-ohw-toolbar]")) return;
18636
17355
  if (target.closest("[data-ohw-state-toggle]")) return;
18637
17356
  if (target.closest("[data-ohw-max-badge]")) return;
@@ -18683,9 +17402,6 @@ function OhhwellsBridge() {
18683
17402
  setHoveredItemRect(null);
18684
17403
  hoveredNavContainerRef.current = null;
18685
17404
  setHoveredNavContainerRect(null);
18686
- siblingHintElRef.current = null;
18687
- setSiblingHintRect(null);
18688
- setSiblingHintRects([]);
18689
17405
  return;
18690
17406
  }
18691
17407
  {
@@ -18804,6 +17520,7 @@ function OhhwellsBridge() {
18804
17520
  hoveredNavContainerRef.current = null;
18805
17521
  setHoveredNavContainerRect(null);
18806
17522
  hoveredItemElRef.current = editable;
17523
+ setHoveredItemRect(isSelectedForHoverRef.current(editable) ? null : editable.getBoundingClientRect());
18807
17524
  }
18808
17525
  }
18809
17526
  }
@@ -19100,7 +17817,7 @@ function OhhwellsBridge() {
19100
17817
  }
19101
17818
  };
19102
17819
  const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
19103
- if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
17820
+ if (linkPopoverOpenRef.current) {
19104
17821
  if (hoveredImageRef.current) {
19105
17822
  hoveredImageRef.current = null;
19106
17823
  hoveredImageHasTextOverlapRef.current = false;
@@ -19434,9 +18151,7 @@ function OhhwellsBridge() {
19434
18151
  return;
19435
18152
  }
19436
18153
  const { y } = toProbeCoords(clientX, clientY, fromParentViewport);
19437
- const sections = Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
19438
- (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
19439
- ).sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);
18154
+ const sections = Array.from(document.querySelectorAll("[data-ohw-section]")).sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);
19440
18155
  const ZONE = 20;
19441
18156
  for (let i = 0; i < sections.length; i++) {
19442
18157
  const a = sections[i];
@@ -19465,7 +18180,8 @@ function OhhwellsBridge() {
19465
18180
  };
19466
18181
  const handleMouseMove = (e) => {
19467
18182
  const { clientX, clientY } = e;
19468
- if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY) || isOverEditorChrome(clientX, clientY)) {
18183
+ if (pointOwnedByFloatingPanel(clientX, clientY)) return;
18184
+ if (isOverEditorChrome(clientX, clientY)) {
19469
18185
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
19470
18186
  formHoverElRef.current = null;
19471
18187
  setFormHoverRect(null);
@@ -19473,12 +18189,6 @@ function OhhwellsBridge() {
19473
18189
  setHoveredItemRect(null);
19474
18190
  hoveredNavContainerRef.current = null;
19475
18191
  setHoveredNavContainerRect(null);
19476
- siblingHintElRef.current = null;
19477
- setSiblingHintRect(null);
19478
- setSiblingHintRects([]);
19479
- dismissImageHover();
19480
- clearImageHover();
19481
- setSectionGap(null);
19482
18192
  return;
19483
18193
  }
19484
18194
  if (probeSocialsRowAt(clientX, clientY)) return;
@@ -19490,11 +18200,7 @@ function OhhwellsBridge() {
19490
18200
  if (e.data?.type !== "ow:pointer-sync") return;
19491
18201
  const { clientX, clientY } = e.data;
19492
18202
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
19493
- if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
19494
- dismissImageHover();
19495
- clearImageHover();
19496
- return;
19497
- }
18203
+ if (pointOwnedByFloatingPanel(clientX, clientY)) return;
19498
18204
  if (probeSocialsRowAt(clientX, clientY)) return;
19499
18205
  probeSectionGapAt(clientX, clientY);
19500
18206
  probeImageAt(clientX, clientY);
@@ -19769,7 +18475,7 @@ function OhhwellsBridge() {
19769
18475
  timers.set(key, setTimeout(() => {
19770
18476
  timers.delete(key);
19771
18477
  postToParentRef.current({ type: "ow:change", nodes: [{ key, text: html }] });
19772
- const h = document.body.scrollHeight;
18478
+ const h = document.documentElement.scrollHeight;
19773
18479
  if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
19774
18480
  }, 400));
19775
18481
  };
@@ -19777,19 +18483,10 @@ function OhhwellsBridge() {
19777
18483
  if (e.data?.type !== "ow:hydrate") return;
19778
18484
  const content = e.data.content;
19779
18485
  if (!content) return;
19780
- if (typeof content[BRAND_KIT_KEY] === "string") {
19781
- brandKitRef.current = content[BRAND_KIT_KEY];
19782
- applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
19783
- }
19784
18486
  if (typeof content[AI_SECTIONS_KEY] === "string") {
19785
18487
  aiSectionsRef.current = content[AI_SECTIONS_KEY];
19786
18488
  applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
19787
18489
  }
19788
- if (typeof content[STYLE_STORE_KEY] === "string") {
19789
- stylesRef.current = content[STYLE_STORE_KEY];
19790
- applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
19791
- }
19792
- applyBrandChrome(content);
19793
18490
  let sectionsJson = null;
19794
18491
  for (const [key, val] of Object.entries(content)) {
19795
18492
  if (key === "__ohw_sections") {
@@ -19799,9 +18496,6 @@ function OhhwellsBridge() {
19799
18496
  if (key === AI_SECTIONS_KEY) continue;
19800
18497
  if (key === LOGO_PLACEHOLDER_KEY) continue;
19801
18498
  if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
19802
- if (key === BRAND_KIT_KEY) continue;
19803
- if (key === STYLE_STORE_KEY) continue;
19804
- if (BRAND_CHROME_KEYS.has(key)) continue;
19805
18499
  if (applyVideoSettingNode(key, val)) continue;
19806
18500
  if (applyCarouselNode(key, val)) continue;
19807
18501
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -19815,8 +18509,6 @@ function OhhwellsBridge() {
19815
18509
  if (video && video.src !== val) applyVideoSrc(video, val);
19816
18510
  } else if (el.dataset.ohwEditable === "link") {
19817
18511
  applyLinkHref(el, val);
19818
- } else if (el.dataset.ohwEditable === "icon") {
19819
- applyIconMarkup(el, val);
19820
18512
  } else if (isIconMarkupValue(val)) {
19821
18513
  } else {
19822
18514
  el.innerHTML = val;
@@ -19837,7 +18529,7 @@ function OhhwellsBridge() {
19837
18529
  reconcileFooterOrderFromContent(editContentRef.current);
19838
18530
  syncNavigationDragCursorAttrs();
19839
18531
  enforceLinkHrefs();
19840
- const hydratedHeight = document.body.scrollHeight;
18532
+ const hydratedHeight = document.documentElement.scrollHeight;
19841
18533
  if (hydratedHeight > 50) postToParentRef.current({ type: "ow:height", height: hydratedHeight });
19842
18534
  postToParentRef.current({ type: "ow:hydrate-done" });
19843
18535
  };
@@ -19901,25 +18593,16 @@ function OhhwellsBridge() {
19901
18593
  nodes: collectEditableNodes(editContentRef.current)
19902
18594
  });
19903
18595
  };
19904
- const clearInteractionChrome = () => {
19905
- deactivateRef.current();
19906
- deselectRef.current();
19907
- clearMediaSelectionRef.current();
19908
- };
19909
18596
  const handleAiApplyTree = (e) => {
19910
18597
  if (e.data?.type !== "ow:ai-apply-tree") return;
19911
18598
  const payload = e.data.payload;
19912
18599
  if (!payload || typeof payload.id !== "string" || !isRenderableTree(payload.tree)) return;
19913
- clearInteractionChrome();
19914
18600
  const previous = aiSectionsRef.current;
19915
- const nextState = applyTreeToState(parseAiSectionsState(previous), {
19916
- ...payload,
19917
- path: payload.path ?? window.location.pathname
19918
- });
18601
+ const nextState = applyTreeToState(parseAiSectionsState(previous), payload);
19919
18602
  const nextValue = serializeAiSectionsState(nextState);
19920
18603
  aiSectionsRef.current = nextValue;
19921
18604
  applyAiSectionsToDom(nextState);
19922
- const newHeight = document.body.scrollHeight;
18605
+ const newHeight = document.documentElement.scrollHeight;
19923
18606
  if (newHeight > 50) postToParentRef.current({ type: "ow:height", height: newHeight });
19924
18607
  postToParentRef.current({ type: "ow:change", nodes: [{ key: AI_SECTIONS_KEY, text: nextValue }] });
19925
18608
  const appliedEl = document.querySelector(`[data-ohw-section="${CSS.escape(payload.id)}"]`);
@@ -19936,13 +18619,12 @@ function OhhwellsBridge() {
19936
18619
  const exists = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
19937
18620
  if (!exists) return;
19938
18621
  if (isPageFrameSection(exists)) return;
19939
- clearInteractionChrome();
19940
18622
  const previous = aiSectionsRef.current;
19941
18623
  const nextState = deleteSectionFromState(parseAiSectionsState(previous), sectionId);
19942
18624
  const nextValue = serializeAiSectionsState(nextState);
19943
18625
  aiSectionsRef.current = nextValue;
19944
18626
  applyAiSectionsToDom(nextState);
19945
- const newHeight = document.body.scrollHeight;
18627
+ const newHeight = document.documentElement.scrollHeight;
19946
18628
  if (newHeight > 50) postToParentRef.current({ type: "ow:height", height: newHeight });
19947
18629
  postToParentRef.current({ type: "ow:change", nodes: [{ key: AI_SECTIONS_KEY, text: nextValue }] });
19948
18630
  postToParentRef.current({ type: "ow:ai-section-deleted", sectionId, previous, value: nextValue });
@@ -19952,106 +18634,20 @@ function OhhwellsBridge() {
19952
18634
  const handleAiSetSections = (e) => {
19953
18635
  if (e.data?.type !== "ow:ai-set-sections") return;
19954
18636
  const value = typeof e.data.value === "string" ? e.data.value : "";
19955
- clearInteractionChrome();
19956
18637
  aiSectionsRef.current = value;
19957
18638
  applyAiSectionsToDom(parseAiSectionsState(value));
19958
- applyStylesToDom(parseStyleStore(stylesRef.current));
19959
- const restoredHeight = document.body.scrollHeight;
18639
+ const restoredHeight = document.documentElement.scrollHeight;
19960
18640
  if (restoredHeight > 50) postToParentRef.current({ type: "ow:height", height: restoredHeight });
19961
18641
  postToParentRef.current({ type: "ow:change", nodes: [{ key: AI_SECTIONS_KEY, text: value }] });
19962
18642
  postAiSectionsChanged();
19963
18643
  };
19964
18644
  window.addEventListener("message", handleAiSetSections);
19965
- const handleMoveSection = (e) => {
19966
- if (e.data?.type !== "ow:move-section") return;
19967
- const instanceId = typeof e.data.instanceId === "string" ? e.data.instanceId : "";
19968
- const direction = e.data.direction === "up" || e.data.direction === "down" ? e.data.direction : null;
19969
- if (!instanceId || !direction) return;
19970
- const entries = moveSectionInstance(instanceId, direction, window.location.pathname);
19971
- if (!entries) return;
19972
- const orderJson = JSON.stringify(entries);
19973
- editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
19974
- postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
19975
- window.dispatchEvent(new Event("resize"));
19976
- };
19977
- window.addEventListener("message", handleMoveSection);
19978
- const handleAiSetBrand = (e) => {
19979
- if (e.data?.type !== "ow:ai-set-brand") return;
19980
- const value = typeof e.data.value === "string" ? e.data.value : "";
19981
- const previous = brandKitRef.current;
19982
- brandKitRef.current = value;
19983
- applyBrandToDom(parseBrandKit(value));
19984
- if (aiSectionsRef.current) applyAiSectionsToDom(parseAiSectionsState(aiSectionsRef.current));
19985
- applyStylesToDom(parseStyleStore(stylesRef.current));
19986
- postToParentRef.current({ type: "ow:change", nodes: [{ key: BRAND_KIT_KEY, text: value }] });
19987
- postToParentRef.current({ type: "ow:ai-brand-applied", previous, value });
19988
- };
19989
- window.addEventListener("message", handleAiSetBrand);
19990
- const handleAiSetStyles = (e) => {
19991
- if (e.data?.type !== "ow:ai-set-styles") return;
19992
- const value = typeof e.data.value === "string" ? e.data.value : "";
19993
- const previous = stylesRef.current;
19994
- stylesRef.current = value;
19995
- applyStylesToDom(parseStyleStore(value));
19996
- postToParentRef.current({ type: "ow:change", nodes: [{ key: STYLE_STORE_KEY, text: value }] });
19997
- postToParentRef.current({ type: "ow:ai-styles-applied", previous, value });
19998
- };
19999
- window.addEventListener("message", handleAiSetStyles);
20000
- const handleGetBrand = (e) => {
20001
- if (e.data?.type !== "ow:get-brand") return;
20002
- const template = deriveTemplateBrand();
20003
- const value = brandKitRef.current || (template ? JSON.stringify(template) : "");
20004
- postToParentRef.current({ type: "ow:brand-value", value });
20005
- };
20006
- window.addEventListener("message", handleGetBrand);
20007
18645
  const handlePanelDragging = (e) => {
20008
18646
  if (e.data?.type !== "ow:panel-dragging") return;
20009
18647
  if (e.data.dragging) document.documentElement.setAttribute("data-ohw-panel-dragging", "");
20010
18648
  else document.documentElement.removeAttribute("data-ohw-panel-dragging");
20011
18649
  };
20012
18650
  window.addEventListener("message", handlePanelDragging);
20013
- const handleDeleteSection = (e) => {
20014
- if (e.data?.type !== "ow:delete-section") return;
20015
- const instanceId = typeof e.data.instanceId === "string" ? e.data.instanceId : "";
20016
- if (!instanceId) return;
20017
- const currentEntries = getPageSectionOrderEntries(editContentRef.current[SECTION_ORDER_KEY], window.location.pathname);
20018
- const entries = deleteSectionInstance(instanceId, window.location.pathname, currentEntries);
20019
- if (!entries) return;
20020
- const orderJson = JSON.stringify(entries);
20021
- editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
20022
- postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
20023
- aiSectionApiRef.current?.clear();
20024
- window.dispatchEvent(new Event("resize"));
20025
- const deleteHeight = document.body.scrollHeight;
20026
- if (deleteHeight > 50) postToParentRef.current({ type: "ow:height", height: deleteHeight });
20027
- const actionId = newInstanceId();
20028
- pendingDeleteUndoRef.current = {
20029
- actionId,
20030
- restore: () => {
20031
- const restoredEntries = getPageSectionOrderEntries(
20032
- editContentRef.current[SECTION_ORDER_KEY],
20033
- window.location.pathname
20034
- );
20035
- const restored = restoreSectionInstance(instanceId, window.location.pathname, restoredEntries);
20036
- if (!restored) return;
20037
- const restoredJson = JSON.stringify(restored);
20038
- editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: restoredJson };
20039
- postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: restoredJson }] });
20040
- window.dispatchEvent(new Event("resize"));
20041
- const restoreHeight = document.body.scrollHeight;
20042
- if (restoreHeight > 50) postToParentRef.current({ type: "ow:height", height: restoreHeight });
20043
- }
20044
- };
20045
- postToParentRef.current({
20046
- type: "ow:toast",
20047
- title: "Section deleted",
20048
- toastType: "success",
20049
- actionLabel: "Undo",
20050
- actionId,
20051
- duration: 6e3
20052
- });
20053
- };
20054
- window.addEventListener("message", handleDeleteSection);
20055
18651
  const handleDeactivate = (e) => {
20056
18652
  if (e.data?.type !== "ow:deactivate") return;
20057
18653
  if (document.documentElement.hasAttribute("data-ohw-panel-dragging")) return;
@@ -20061,15 +18657,8 @@ function OhhwellsBridge() {
20061
18657
  closeLinkPopoverRef.current();
20062
18658
  return;
20063
18659
  }
20064
- if (floatingPanelOpenRef.current) {
20065
- setFloatingPanelRef.current(null);
20066
- deselectRef.current();
20067
- deactivateRef.current();
20068
- return;
20069
- }
20070
18660
  deselectRef.current();
20071
18661
  deactivateRef.current();
20072
- clearMediaSelectionRef.current();
20073
18662
  };
20074
18663
  window.addEventListener("message", handleDeactivate);
20075
18664
  const handleToastAction = (e) => {
@@ -20155,10 +18744,6 @@ function OhhwellsBridge() {
20155
18744
  const handleKeyDown = (e) => {
20156
18745
  if (e.key === "Escape" && document.querySelector("[data-ohw-section-picker]")) return;
20157
18746
  if (e.key === "Escape" && document.querySelector("[data-ohw-more-menu]")) return;
20158
- if (e.key === "Escape" && selectedMediaElRef.current) {
20159
- clearMediaSelectionRef.current();
20160
- return;
20161
- }
20162
18747
  if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "a" && activeElRef.current) {
20163
18748
  e.preventDefault();
20164
18749
  selectAllTextInEditable(activeElRef.current);
@@ -20318,12 +18903,6 @@ function OhhwellsBridge() {
20318
18903
  if (aiSectionsRef.current) {
20319
18904
  nodes.push({ key: AI_SECTIONS_KEY, type: "sections", text: aiSectionsRef.current });
20320
18905
  }
20321
- if (stylesRef.current) {
20322
- nodes.push({ key: STYLE_STORE_KEY, type: "styles", text: stylesRef.current });
20323
- }
20324
- if (brandKitRef.current) {
20325
- nodes.push({ key: BRAND_KIT_KEY, type: "brand", text: brandKitRef.current });
20326
- }
20327
18906
  document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
20328
18907
  const formKey = formKeyOf(form);
20329
18908
  if (!formKey) return;
@@ -20341,12 +18920,8 @@ function OhhwellsBridge() {
20341
18920
  if (inserted) {
20342
18921
  const tracker = getSectionsTracker();
20343
18922
  postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
20344
- const reportHeight = () => {
20345
- const h = document.body.scrollHeight;
20346
- if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
20347
- };
20348
- reportHeight();
20349
- setTimeout(reportHeight, 500);
18923
+ const h = document.documentElement.scrollHeight;
18924
+ if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
20350
18925
  }
20351
18926
  };
20352
18927
  const handleSwitchSchedule = (e) => {
@@ -20387,7 +18962,7 @@ function OhhwellsBridge() {
20387
18962
  const updated = sections.filter((s) => !(s.type === "scheduling" && s.pagePath === currentPath));
20388
18963
  tracker.textContent = JSON.stringify(updated);
20389
18964
  postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent }] });
20390
- const h = document.body.scrollHeight;
18965
+ const h = document.documentElement.scrollHeight;
20391
18966
  if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
20392
18967
  };
20393
18968
  const handleCollectSection = (e) => {
@@ -20742,24 +19317,19 @@ function OhhwellsBridge() {
20742
19317
  window.removeEventListener("message", handleAiApplyTree);
20743
19318
  window.removeEventListener("message", handleAiDeleteSection);
20744
19319
  window.removeEventListener("message", handleAiSetSections);
20745
- window.removeEventListener("message", handleMoveSection);
20746
- window.removeEventListener("message", handleAiSetBrand);
20747
- window.removeEventListener("message", handleAiSetStyles);
20748
- window.removeEventListener("message", handleGetBrand);
20749
19320
  window.removeEventListener("message", handlePanelDragging);
20750
- window.removeEventListener("message", handleDeleteSection);
20751
19321
  window.removeEventListener("message", handleDeactivate);
19322
+ document.documentElement.removeAttribute("data-ohw-panel-dragging");
20752
19323
  window.removeEventListener("message", handleToastAction);
20753
19324
  window.removeEventListener("message", handleFormCount);
20754
19325
  window.removeEventListener("message", handleUiEscape);
20755
- document.documentElement.removeAttribute("data-ohw-panel-dragging");
20756
19326
  autoSaveTimers.current.forEach(clearTimeout);
20757
19327
  autoSaveTimers.current.clear();
20758
19328
  if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
20759
19329
  if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
20760
19330
  };
20761
19331
  }, [isEditMode, refreshStateRules]);
20762
- useEffect13(() => {
19332
+ useEffect12(() => {
20763
19333
  if (!isEditMode) return;
20764
19334
  const THRESHOLD = 10;
20765
19335
  const resolveWasSelected = (el) => {
@@ -20915,7 +19485,7 @@ function OhhwellsBridge() {
20915
19485
  unlockFooterDragInteraction();
20916
19486
  };
20917
19487
  }, [isEditMode]);
20918
- useEffect13(() => {
19488
+ useEffect12(() => {
20919
19489
  const handler = (e) => {
20920
19490
  if (e.data?.type !== "ow:request-schedule-config") return;
20921
19491
  const insertAfterVal = e.data.insertAfter;
@@ -20931,7 +19501,7 @@ function OhhwellsBridge() {
20931
19501
  window.addEventListener("message", handler);
20932
19502
  return () => window.removeEventListener("message", handler);
20933
19503
  }, [processConfigRequest]);
20934
- useEffect13(() => {
19504
+ useEffect12(() => {
20935
19505
  if (!isEditMode) return;
20936
19506
  document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
20937
19507
  el.removeAttribute("data-ohw-active-state");
@@ -20955,7 +19525,7 @@ function OhhwellsBridge() {
20955
19525
  postToParent2({
20956
19526
  type: "ow:ready",
20957
19527
  version: "1",
20958
- bridgeVersion: "0.1.71-next.218",
19528
+ bridgeVersion: "0.1.72",
20959
19529
  path: pathname,
20960
19530
  nodes: collectEditableNodes(editContentRef.current),
20961
19531
  sections
@@ -20967,13 +19537,13 @@ function OhhwellsBridge() {
20967
19537
  clearTimeout(timer);
20968
19538
  };
20969
19539
  }, [pathname, isEditMode, refreshStateRules, postToParent2]);
20970
- useEffect13(() => {
19540
+ useEffect12(() => {
20971
19541
  scrollToHashSectionWhenReady();
20972
19542
  const onHashChange = () => scrollToHashSectionWhenReady();
20973
19543
  window.addEventListener("hashchange", onHashChange);
20974
19544
  return () => window.removeEventListener("hashchange", onHashChange);
20975
19545
  }, [pathname]);
20976
- const handleCommand = useCallback8((cmd) => {
19546
+ const handleCommand = useCallback7((cmd) => {
20977
19547
  const el = activeElRef.current;
20978
19548
  const selBefore = window.getSelection();
20979
19549
  let savedOffsets = null;
@@ -21009,7 +19579,7 @@ function OhhwellsBridge() {
21009
19579
  if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
21010
19580
  refreshActiveCommandsRef.current();
21011
19581
  }, []);
21012
- useEffect13(() => {
19582
+ useEffect12(() => {
21013
19583
  const session = floatingPanel && floatingPanel.kind === "logo-size" ? floatingPanel : null;
21014
19584
  if (!session || !logoSizeDraft) return;
21015
19585
  const onPanelAction = (e) => {
@@ -21047,7 +19617,7 @@ function OhhwellsBridge() {
21047
19617
  window.addEventListener("message", onPanelAction);
21048
19618
  return () => window.removeEventListener("message", onPanelAction);
21049
19619
  }, [floatingPanel, logoSizeDraft, editorViewport, persistLogoSizeDraft, closeFloatingPanelAndDeselect]);
21050
- const handleStateChange = useCallback8((state) => {
19620
+ const handleStateChange = useCallback7((state) => {
21051
19621
  if (!activeStateElRef.current) return;
21052
19622
  const el = activeStateElRef.current;
21053
19623
  if (state === "Default") {
@@ -21060,7 +19630,7 @@ function OhhwellsBridge() {
21060
19630
  }
21061
19631
  setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
21062
19632
  }, [deactivate]);
21063
- const reselectAfterLinkPopover = useCallback8(
19633
+ const reselectAfterLinkPopover = useCallback7(
21064
19634
  (hrefKey) => {
21065
19635
  requestAnimationFrame(() => {
21066
19636
  const el = resolveHrefKeyElement(hrefKey);
@@ -21069,7 +19639,7 @@ function OhhwellsBridge() {
21069
19639
  },
21070
19640
  [resolveHrefKeyElement]
21071
19641
  );
21072
- const closeLinkPopover = useCallback8(() => {
19642
+ const closeLinkPopover = useCallback7(() => {
21073
19643
  const session = linkPopoverSessionRef.current;
21074
19644
  addNavAfterAnchorRef.current = null;
21075
19645
  setLinkPopover(null);
@@ -21077,9 +19647,9 @@ function OhhwellsBridge() {
21077
19647
  reselectAfterLinkPopover(session.key);
21078
19648
  }
21079
19649
  }, [reselectAfterLinkPopover]);
21080
- const closeLinkPopoverRef = useRef10(closeLinkPopover);
19650
+ const closeLinkPopoverRef = useRef9(closeLinkPopover);
21081
19651
  closeLinkPopoverRef.current = closeLinkPopover;
21082
- const openLinkPopoverForActive = useCallback8(() => {
19652
+ const openLinkPopoverForActive = useCallback7(() => {
21083
19653
  const hrefCtx = getHrefKeyFromElement(activeElRef.current);
21084
19654
  if (!hrefCtx) return;
21085
19655
  bumpLinkPopoverGrace();
@@ -21090,7 +19660,7 @@ function OhhwellsBridge() {
21090
19660
  });
21091
19661
  deactivate();
21092
19662
  }, [deactivate]);
21093
- const openLinkPopoverForSelected = useCallback8(() => {
19663
+ const openLinkPopoverForSelected = useCallback7(() => {
21094
19664
  const anchor = selectedElRef.current;
21095
19665
  if (!anchor) return;
21096
19666
  const key = anchor.getAttribute("data-ohw-href-key");
@@ -21107,7 +19677,7 @@ function OhhwellsBridge() {
21107
19677
  });
21108
19678
  deselect();
21109
19679
  }, [deselect]);
21110
- const handleSelectParent = useCallback8(() => {
19680
+ const handleSelectParent = useCallback7(() => {
21111
19681
  const selected = selectedElRef.current;
21112
19682
  if (!selected) return;
21113
19683
  if (toolbarVariantRef.current === "select-frame") {
@@ -21134,7 +19704,7 @@ function OhhwellsBridge() {
21134
19704
  }
21135
19705
  deselectRef.current();
21136
19706
  }, []);
21137
- const handleDuplicateSelected = useCallback8(() => {
19707
+ const handleDuplicateSelected = useCallback7(() => {
21138
19708
  const selected = selectedElRef.current;
21139
19709
  if (!selected || !isNavigationItem2(selected)) return;
21140
19710
  const hrefKey = selected.getAttribute("data-ohw-href-key");
@@ -21267,7 +19837,7 @@ function OhhwellsBridge() {
21267
19837
  });
21268
19838
  }
21269
19839
  }, [postToParent2]);
21270
- const runPendingDeleteUndo = useCallback8(() => {
19840
+ const runPendingDeleteUndo = useCallback7(() => {
21271
19841
  const pending = pendingDeleteUndoRef.current;
21272
19842
  if (!pending) return false;
21273
19843
  pendingDeleteUndoRef.current = null;
@@ -21275,7 +19845,7 @@ function OhhwellsBridge() {
21275
19845
  enforceLinkHrefs();
21276
19846
  return true;
21277
19847
  }, []);
21278
- const handleDeleteSelected = useCallback8(() => {
19848
+ const handleDeleteSelected = useCallback7(() => {
21279
19849
  const selected = selectedElRef.current;
21280
19850
  if (!selected) return false;
21281
19851
  return deleteSelectedNavFooterItem({
@@ -21296,7 +19866,7 @@ function OhhwellsBridge() {
21296
19866
  }, [postToParent2]);
21297
19867
  handleDeleteSelectedRef.current = handleDeleteSelected;
21298
19868
  runPendingDeleteUndoRef.current = runPendingDeleteUndo;
21299
- const handleLinkPopoverSubmit = useCallback8(
19869
+ const handleLinkPopoverSubmit = useCallback7(
21300
19870
  (target) => {
21301
19871
  const session = linkPopoverSessionRef.current;
21302
19872
  if (!session) return;
@@ -21362,30 +19932,19 @@ function OhhwellsBridge() {
21362
19932
  const showEditLink = toolbarShowEditLink;
21363
19933
  const currentSections = sectionsByPath[pathname] ?? [];
21364
19934
  linkPopoverOpenRef.current = linkPopover !== null;
21365
- const handleMediaSelect = useCallback8((key) => {
21366
- const el = hoveredImageRef.current?.dataset.ohwKey === key ? hoveredImageRef.current : Array.from(document.querySelectorAll(MEDIA_SELECTOR)).find(
21367
- (m) => (m.dataset.ohwKey ?? "") === key
21368
- ) ?? null;
21369
- if (!el) return;
21370
- selectMediaElementRef.current(el);
21371
- }, []);
21372
- const handleMediaReplace = useCallback8(
19935
+ const handleMediaReplace = useCallback7(
21373
19936
  (key) => {
21374
- postToParent2({
21375
- type: "ow:image-pick",
21376
- key,
21377
- elementType: mediaHover?.elementType ?? selectedMedia?.elementType ?? "image"
21378
- });
19937
+ postToParent2({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
21379
19938
  },
21380
- [postToParent2, mediaHover?.elementType, selectedMedia?.elementType]
19939
+ [postToParent2, mediaHover?.elementType]
21381
19940
  );
21382
- const handleEditCarousel = useCallback8(
19941
+ const handleEditCarousel = useCallback7(
21383
19942
  (key) => {
21384
19943
  postToParent2({ type: "ow:carousel-open", key, images: readCarouselValue(key) });
21385
19944
  },
21386
19945
  [postToParent2]
21387
19946
  );
21388
- const handleMediaFadeOutComplete = useCallback8((key) => {
19947
+ const handleMediaFadeOutComplete = useCallback7((key) => {
21389
19948
  setUploadingRects((prev) => {
21390
19949
  if (!(key in prev)) return prev;
21391
19950
  const next = { ...prev };
@@ -21393,7 +19952,7 @@ function OhhwellsBridge() {
21393
19952
  return next;
21394
19953
  });
21395
19954
  }, []);
21396
- const handleVideoSettingsChange = useCallback8(
19955
+ const handleVideoSettingsChange = useCallback7(
21397
19956
  (key, settings) => {
21398
19957
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
21399
19958
  const video = getVideoEl2(el);
@@ -21415,516 +19974,430 @@ function OhhwellsBridge() {
21415
19974
  },
21416
19975
  [postToParent2]
21417
19976
  );
21418
- return /* @__PURE__ */ jsxs20(Fragment8, { children: [
21419
- /* @__PURE__ */ jsx33("div", { id: "ohw-loader", suppressHydrationWarning: true, style: { ...OHW_LOADER_STYLE, display: "none" }, children: /* @__PURE__ */ jsx33(OhwLoaderSpinner, {}) }),
21420
- /* @__PURE__ */ jsx33("script", { suppressHydrationWarning: true, dangerouslySetInnerHTML: { __html: OHW_LOADER_PREHYDRATE_SCRIPT } }),
21421
- bridgeRoot ? createPortal2(
21422
- /* @__PURE__ */ jsxs20(Fragment8, { children: [
21423
- /* @__PURE__ */ jsx33("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
21424
- isEditMode && /* @__PURE__ */ jsx33(AiSectionOverlay, { postToParent: postToParent2, apiRef: aiSectionApiRef }),
21425
- isSectionDragging && sectionDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
21426
- "div",
21427
- {
21428
- className: "pointer-events-none fixed z-2147483646",
21429
- style: { left: slot.left, top: slot.y, width: slot.width, height: 3, transform: "translateY(-50%)" },
21430
- children: /* @__PURE__ */ jsx33(
21431
- DropIndicator,
21432
- {
21433
- direction: "horizontal",
21434
- state: activeSectionDropIndex === i ? "dragActive" : "dragIdle",
21435
- className: "!h-full !w-full"
21436
- }
21437
- )
21438
- },
21439
- `section-drop-${slot.insertIndex}-${i}`
21440
- )),
21441
- Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ jsx33(
21442
- MediaOverlay,
21443
- {
21444
- hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
21445
- isUploading: true,
21446
- fadingOut,
21447
- onFadeOutComplete: handleMediaFadeOutComplete,
21448
- onReplace: handleMediaReplace
21449
- },
21450
- `uploading-${key}`
21451
- )),
21452
- mediaHover && !(mediaHover.key in uploadingRects) && mediaHover.key !== selectedMedia?.key && /* @__PURE__ */ jsx33(
21453
- MediaOverlay,
21454
- {
21455
- hover: mediaHover,
21456
- isUploading: false,
21457
- onReplace: handleMediaReplace,
21458
- onSelect: handleMediaSelect,
21459
- onVideoSettingsChange: handleVideoSettingsChange
21460
- }
21461
- ),
21462
- selectedMedia && !(selectedMedia.key in uploadingRects) && !linkPopover && /* @__PURE__ */ jsx33(
21463
- MediaOverlay,
21464
- {
21465
- hover: selectedMedia,
21466
- selected: true,
21467
- hovered: mediaHover?.key === selectedMedia.key,
21468
- isUploading: false,
21469
- onReplace: handleMediaReplace,
21470
- onSelect: handleMediaSelect,
21471
- onVideoSettingsChange: handleVideoSettingsChange
21472
- }
21473
- ),
21474
- carouselHover && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx33(CarouselOverlay, { hover: carouselHover, onEdit: handleEditCarousel }),
21475
- siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
21476
- siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
21477
- isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
21478
- isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
21479
- "div",
21480
- {
21481
- className: "pointer-events-none fixed z-2147483646",
21482
- style: {
21483
- left: slot.left,
21484
- top: slot.top,
21485
- width: slot.width,
21486
- height: slot.height
21487
- },
21488
- children: /* @__PURE__ */ jsx33(
21489
- DropIndicator,
21490
- {
21491
- direction: slot.direction,
21492
- state: activeFooterDropIndex === i ? "dragActive" : "dragIdle",
21493
- className: "!h-full !w-full"
21494
- }
21495
- )
21496
- },
21497
- `footer-drop-${slot.direction}-${slot.columnIndex}-${slot.insertIndex}-${i}`
21498
- )),
21499
- isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
21500
- "div",
21501
- {
21502
- className: "pointer-events-none fixed z-2147483646",
21503
- style: {
21504
- left: slot.left,
21505
- top: slot.top,
21506
- width: slot.width,
21507
- height: slot.height
21508
- },
21509
- children: /* @__PURE__ */ jsx33(
21510
- DropIndicator,
21511
- {
21512
- direction: slot.direction,
21513
- state: activeNavDropIndex === i ? "dragActive" : "dragIdle",
21514
- className: "!h-full !w-full"
21515
- }
21516
- )
19977
+ return bridgeRoot ? createPortal2(
19978
+ /* @__PURE__ */ jsxs20(Fragment8, { children: [
19979
+ /* @__PURE__ */ jsx33("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
19980
+ isEditMode && /* @__PURE__ */ jsx33(AiSectionOverlay, { postToParent: postToParent2, apiRef: aiSectionApiRef }),
19981
+ Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ jsx33(
19982
+ MediaOverlay,
19983
+ {
19984
+ hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
19985
+ isUploading: true,
19986
+ fadingOut,
19987
+ onFadeOutComplete: handleMediaFadeOutComplete,
19988
+ onReplace: handleMediaReplace
19989
+ },
19990
+ `uploading-${key}`
19991
+ )),
19992
+ mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ jsx33(
19993
+ MediaOverlay,
19994
+ {
19995
+ hover: mediaHover,
19996
+ isUploading: false,
19997
+ onReplace: handleMediaReplace,
19998
+ onVideoSettingsChange: handleVideoSettingsChange
19999
+ }
20000
+ ),
20001
+ carouselHover && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx33(CarouselOverlay, { hover: carouselHover, onEdit: handleEditCarousel }),
20002
+ siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
20003
+ siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
20004
+ isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
20005
+ isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
20006
+ "div",
20007
+ {
20008
+ className: "pointer-events-none fixed z-2147483646",
20009
+ style: {
20010
+ left: slot.left,
20011
+ top: slot.top,
20012
+ width: slot.width,
20013
+ height: slot.height
21517
20014
  },
21518
- `nav-drop-${slot.direction}-${slot.parentId ?? "root"}-${slot.insertIndex}-${i}`
21519
- )),
21520
- hoveredNavContainerRect && (toolbarVariant !== "select-frame" || isFooterFrameSelection) && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
21521
- hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
21522
- hoveredTextRect && !hoveredNavContainerRect && !hoveredItemRect && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredTextRect, state: "hover" }),
21523
- formPickRect && !isItemDragging && /* @__PURE__ */ jsx33(
21524
- ItemInteractionLayer,
21525
- {
21526
- rect: formPickRect,
21527
- state: "active-top",
21528
- itemDragSurface: false,
21529
- toolbarAlign: "left",
21530
- chromeGap: 24,
21531
- toolbar: fieldPickRect ? void 0 : /* @__PURE__ */ jsxs20(
21532
- "div",
21533
- {
21534
- "data-ohw-form-toolbar": "",
21535
- className: "pointer-events-auto flex items-center gap-0.5 whitespace-nowrap rounded-lg border border-border bg-background p-1 shadow-md",
21536
- children: [
21537
- /* @__PURE__ */ jsx33(
21538
- "button",
21539
- {
21540
- type: "button",
21541
- "aria-label": "Add field",
21542
- className: "flex h-7 w-7 items-center justify-center rounded-md text-foreground transition-colors hover:bg-muted/80",
21543
- onClick: () => setFieldTypePickerOpen((open) => !open),
21544
- "data-ohw-add-field": "",
21545
- children: /* @__PURE__ */ jsx33(Plus4, { size: 15, "aria-hidden": true })
21546
- }
21547
- ),
21548
- /* @__PURE__ */ jsx33("div", { className: "mx-0.5 h-5 w-px bg-border" }),
21549
- /* @__PURE__ */ jsxs20(
21550
- "button",
21551
- {
21552
- type: "button",
21553
- 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",
21554
- onClick: () => {
21555
- setFieldTypePickerOpen(false);
21556
- const form = formPickElRef.current;
21557
- if (!form) return;
21558
- postToParent2({
21559
- type: "ow:form-pick",
21560
- formKey: formKeyOf(form),
21561
- hasLongText: formHasLongText(form)
21562
- });
21563
- },
21564
- children: [
21565
- /* @__PURE__ */ jsx33(Settings, { size: 14, "aria-hidden": true }),
21566
- "Form settings",
21567
- formPickCount ? (
21568
- // Counter pill, per the design — not a text suffix.
21569
- /* @__PURE__ */ jsx33(
21570
- "span",
21571
- {
21572
- "data-ohw-form-count": "",
21573
- 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",
21574
- children: formPickCount
21575
- }
21576
- )
21577
- ) : null
21578
- ]
21579
- }
21580
- ),
21581
- /* @__PURE__ */ jsx33("div", { className: "mx-0.5 h-5 w-px bg-border" }),
21582
- /* @__PURE__ */ jsx33("div", { className: "flex items-center rounded-md bg-muted/70 p-0.5", children: ["default", "success"].map((state) => /* @__PURE__ */ jsx33(
21583
- "button",
21584
- {
21585
- type: "button",
21586
- "aria-pressed": formViewState === state,
21587
- 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"),
21588
- onClick: () => {
21589
- setFieldTypePickerOpen(false);
21590
- const form = formPickElRef.current;
21591
- const key = form ? formKeyOf(form) : null;
21592
- if (!form || !key) return;
21593
- const initial = successInitialFor(form, key, editContentRef.current);
21594
- setFormViewState(form, key, state, initial);
21595
- setFormViewStateUi(state);
21596
- setFormPickRect(form.getBoundingClientRect());
21597
- if (state === "success") {
21598
- const successEl = form.querySelector(`[${SUCCESS_TEXT_ATTR}]`);
21599
- if (successEl) requestAnimationFrame(() => activateRef.current(successEl));
21600
- } else {
21601
- deactivateRef.current();
21602
- }
21603
- },
21604
- children: state
21605
- },
21606
- state
21607
- )) })
21608
- ]
21609
- }
21610
- )
21611
- }
21612
- ),
21613
- formHoverRect && !isItemDragging && /* @__PURE__ */ jsx33(
21614
- ItemInteractionLayer,
21615
- {
21616
- rect: formHoverRect,
21617
- state: "hover",
21618
- chromeGap: formHoverElRef.current && getFieldWrapper(formHoverElRef.current) ? 8 : 24
21619
- }
21620
- ),
21621
- fieldPickRect && fieldPickState && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ jsx33(
21622
- ItemInteractionLayer,
21623
- {
21624
- rect: fieldPickRect,
21625
- state: fieldDragging ? "dragging" : "active-top",
21626
- itemDragSurface: false,
21627
- toolbarAlign: "left",
21628
- chromeGap: 10,
21629
- showHandle: true,
21630
- dragHandleLabel: "Reorder field",
21631
- onDragHandleDragStart: handleFieldDragStart,
21632
- onDragHandleDragEnd: handleFieldDragEnd,
21633
- toolbar: /* @__PURE__ */ jsx33(
21634
- FormFieldToolbar,
21635
- {
21636
- type: fieldPickState.type,
21637
- required: fieldPickState.required,
21638
- onTypeChange: handleFieldTypeChange,
21639
- onRequiredToggle: handleFieldRequiredToggle,
21640
- onDuplicate: handleFieldDuplicate,
21641
- onDelete: handleFieldDelete
21642
- }
21643
- )
21644
- }
21645
- ),
21646
- fieldDragging ? fieldDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
21647
- "div",
21648
- {
21649
- className: "pointer-events-none fixed z-[2147483644]",
21650
- style: { top: slot.top, left: slot.left, width: slot.width },
21651
- children: /* @__PURE__ */ jsx33(
21652
- DropIndicator,
21653
- {
21654
- direction: "horizontal",
21655
- state: fieldDropIndex === i ? "dragActive" : "dragIdle",
21656
- className: "!w-full"
21657
- }
21658
- )
20015
+ children: /* @__PURE__ */ jsx33(
20016
+ DropIndicator,
20017
+ {
20018
+ direction: slot.direction,
20019
+ state: activeFooterDropIndex === i ? "dragActive" : "dragIdle",
20020
+ className: "!h-full !w-full"
20021
+ }
20022
+ )
20023
+ },
20024
+ `footer-drop-${slot.direction}-${slot.columnIndex}-${slot.insertIndex}-${i}`
20025
+ )),
20026
+ isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
20027
+ "div",
20028
+ {
20029
+ className: "pointer-events-none fixed z-2147483646",
20030
+ style: {
20031
+ left: slot.left,
20032
+ top: slot.top,
20033
+ width: slot.width,
20034
+ height: slot.height
21659
20035
  },
21660
- `field-drop-${i}`
21661
- )) : null,
21662
- fieldTypePickerOpen && formPickRect ? (() => {
21663
- const toolbar = document.querySelector("[data-ohw-form-toolbar]")?.getBoundingClientRect();
21664
- return /* @__PURE__ */ jsx33(
21665
- "div",
20036
+ children: /* @__PURE__ */ jsx33(
20037
+ DropIndicator,
21666
20038
  {
21667
- className: "pointer-events-none fixed z-[2147483645]",
21668
- style: {
21669
- top: toolbar ? toolbar.bottom + 6 : formPickRect.top + 16,
21670
- left: toolbar ? toolbar.left : formPickRect.left + 24
21671
- },
21672
- children: /* @__PURE__ */ jsx33(FieldTypePicker, { onPick: handleAddField })
20039
+ direction: slot.direction,
20040
+ state: activeNavDropIndex === i ? "dragActive" : "dragIdle",
20041
+ className: "!h-full !w-full"
21673
20042
  }
21674
- );
21675
- })() : null,
21676
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer2(selectedElRef.current) && /* @__PURE__ */ jsx33(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
21677
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isFooterLinksContainer(selectedElRef.current) && /* @__PURE__ */ jsx33(
21678
- FooterContainerChrome,
21679
- {
21680
- rect: toolbarRect,
21681
- onAdd: handleAddFooterColumn,
21682
- addDisabled: !canAddFooterColumn()
21683
- }
21684
- ),
21685
- toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame" || toolbarVariant === "logo") && /* @__PURE__ */ jsx33(
21686
- ItemInteractionLayer,
21687
- {
21688
- rect: isItemDragging && draggedItemRect && (footerDragRef.current?.wasSelected || navDragRef.current?.wasSelected) ? draggedItemRect : toolbarRect,
21689
- toolbarBelowRect: selectedElRef.current && isNavigationItem2(selectedElRef.current) ? getOpenNavDropdownPanelRect(selectedElRef.current) : null,
21690
- elRef: glowElRef,
21691
- state: isItemDragging ? "dragging" : selectedElRef.current && isNavigationItem2(selectedElRef.current) && getOpenNavDropdownPanelRect(selectedElRef.current) ? "active-bottom" : resolveItemInteractionState(toolbarRect, parentScrollRef.current),
21692
- showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey) || toolbarVariant === "select-frame" && isFooterFrameSelection,
21693
- dragDisabled: reorderDragDisabled,
21694
- dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
21695
- onDragHandleDragStart: handleItemDragStart,
21696
- onDragHandleDragEnd: handleItemDragEnd,
21697
- onItemPointerDown: toolbarVariant === "logo" ? void 0 : handleItemChromePointerDown,
21698
- onItemClick: toolbarVariant === "logo" ? void 0 : handleItemChromeClick,
21699
- itemDragSurface: toolbarVariant !== "logo" && !isFooterFrameSelection && !selectedIsSocialsRow,
21700
- toolbar: toolbarVariant === "link-action" && !isItemDragging || toolbarVariant === "select-frame" && (isFooterFrameSelection || selectedIsSocialsRow) && !isItemDragging ? /* @__PURE__ */ jsx33(
21701
- ItemActionToolbar,
21702
- {
21703
- onEditLink: openLinkPopoverForSelected,
21704
- onStyle: () => {
21705
- const row = selectedElRef.current;
21706
- if (!row) return;
21707
- if (floatingPanel?.kind === "socials-display") closeFloatingPanelOnly();
21708
- else openSocialsDisplayPanel(row);
21709
- },
21710
- showStyle: selectedIsSocialsRow,
21711
- styleActive: floatingPanel?.kind === "socials-display",
21712
- onAddItem: handleAddChildItem,
21713
- onSelectParent: handleSelectParent,
21714
- onDuplicate: handleDuplicateSelected,
21715
- onDelete: handleDeleteSelected,
21716
- addItemDisabled: isFooterFrameSelection && isFooterAddItemDisabled(selectedElRef.current) || selectedElRef.current !== null && (() => {
21717
- const row = isSocialsRow(selectedElRef.current) ? selectedElRef.current : findSocialsRow(selectedElRef.current);
21718
- return row ? !canAddSocialItem(row) : false;
21719
- })(),
21720
- editLinkDisabled: false,
21721
- moreDisabled: false,
21722
- deleteDisabled: selectedElRef.current !== null && (() => {
21723
- const social = getSocialItem(selectedElRef.current);
21724
- return social ? !canRemoveSocialItem(social) : false;
21725
- })(),
21726
- duplicateDisabled: isFooterFrameSelection || selectedIsSocialsRow || selectedElRef.current !== null && (() => {
21727
- const social = getSocialItem(selectedElRef.current);
21728
- const row = social ? findSocialsRow(social) : null;
21729
- return row ? !canAddSocialItem(row) : false;
21730
- })(),
21731
- showEditLink: !isFooterFrameSelection && !selectedIsSocialsRow && navDropdownPreviewOpen === null,
21732
- showAddItem: isFooterFrameSelection || selectedIsSocialsRow || !selectedIsCta && Boolean(
21733
- selectedElRef.current && isNavbarHrefKey(selectedElRef.current.getAttribute("data-ohw-href-key")) && !isNestedNavChild(selectedElRef.current)
20043
+ )
20044
+ },
20045
+ `nav-drop-${slot.direction}-${slot.parentId ?? "root"}-${slot.insertIndex}-${i}`
20046
+ )),
20047
+ hoveredNavContainerRect && (toolbarVariant !== "select-frame" || isFooterFrameSelection) && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
20048
+ hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
20049
+ hoveredTextRect && !hoveredNavContainerRect && !hoveredItemRect && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredTextRect, state: "hover" }),
20050
+ formPickRect && !isItemDragging && /* @__PURE__ */ jsx33(
20051
+ ItemInteractionLayer,
20052
+ {
20053
+ rect: formPickRect,
20054
+ state: "active-top",
20055
+ itemDragSurface: false,
20056
+ toolbarAlign: "left",
20057
+ chromeGap: 24,
20058
+ toolbar: fieldPickRect ? void 0 : /* @__PURE__ */ jsxs20(
20059
+ "div",
20060
+ {
20061
+ "data-ohw-form-toolbar": "",
20062
+ className: "pointer-events-auto flex items-center gap-0.5 whitespace-nowrap rounded-lg border border-border bg-background p-1 shadow-md",
20063
+ children: [
20064
+ /* @__PURE__ */ jsx33(
20065
+ "button",
20066
+ {
20067
+ type: "button",
20068
+ "aria-label": "Add field",
20069
+ className: "flex h-7 w-7 items-center justify-center rounded-md text-foreground transition-colors hover:bg-muted/80",
20070
+ onClick: () => setFieldTypePickerOpen((open) => !open),
20071
+ "data-ohw-add-field": "",
20072
+ children: /* @__PURE__ */ jsx33(Plus4, { size: 15, "aria-hidden": true })
20073
+ }
21734
20074
  ),
21735
- showMore: (!selectedIsCta || isFooterFrameSelection) && !selectedIsSocialsRow,
21736
- dropdownOpen: navDropdownPreviewOpen,
21737
- onDropdownOpenChange: handleNavDropdownOpenChange,
21738
- headingVisible: footerHeadingVisible,
21739
- onHeadingVisibleChange: handleFooterHeadingVisibleChange
21740
- }
21741
- ) : void 0
21742
- }
21743
- ),
21744
- toolbarRect && toolbarVariant === "rich-text" && !linkPopover && /* @__PURE__ */ jsxs20(Fragment8, { children: [
21745
- /* @__PURE__ */ jsx33(
21746
- EditGlowChrome,
20075
+ /* @__PURE__ */ jsx33("div", { className: "mx-0.5 h-5 w-px bg-border" }),
20076
+ /* @__PURE__ */ jsxs20(
20077
+ "button",
20078
+ {
20079
+ type: "button",
20080
+ 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",
20081
+ onClick: () => {
20082
+ setFieldTypePickerOpen(false);
20083
+ const form = formPickElRef.current;
20084
+ if (!form) return;
20085
+ postToParent2({
20086
+ type: "ow:form-pick",
20087
+ formKey: formKeyOf(form),
20088
+ hasLongText: formHasLongText(form)
20089
+ });
20090
+ },
20091
+ children: [
20092
+ /* @__PURE__ */ jsx33(Settings, { size: 14, "aria-hidden": true }),
20093
+ "Form settings",
20094
+ formPickCount ? (
20095
+ // Counter pill, per the design — not a text suffix.
20096
+ /* @__PURE__ */ jsx33(
20097
+ "span",
20098
+ {
20099
+ "data-ohw-form-count": "",
20100
+ 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",
20101
+ children: formPickCount
20102
+ }
20103
+ )
20104
+ ) : null
20105
+ ]
20106
+ }
20107
+ ),
20108
+ /* @__PURE__ */ jsx33("div", { className: "mx-0.5 h-5 w-px bg-border" }),
20109
+ /* @__PURE__ */ jsx33("div", { className: "flex items-center rounded-md bg-muted/70 p-0.5", children: ["default", "success"].map((state) => /* @__PURE__ */ jsx33(
20110
+ "button",
20111
+ {
20112
+ type: "button",
20113
+ "aria-pressed": formViewState === state,
20114
+ 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"),
20115
+ onClick: () => {
20116
+ setFieldTypePickerOpen(false);
20117
+ const form = formPickElRef.current;
20118
+ const key = form ? formKeyOf(form) : null;
20119
+ if (!form || !key) return;
20120
+ const initial = successInitialFor(form, key, editContentRef.current);
20121
+ setFormViewState(form, key, state, initial);
20122
+ setFormViewStateUi(state);
20123
+ setFormPickRect(form.getBoundingClientRect());
20124
+ if (state === "success") {
20125
+ const successEl = form.querySelector(`[${SUCCESS_TEXT_ATTR}]`);
20126
+ if (successEl) requestAnimationFrame(() => activateRef.current(successEl));
20127
+ } else {
20128
+ deactivateRef.current();
20129
+ }
20130
+ },
20131
+ children: state
20132
+ },
20133
+ state
20134
+ )) })
20135
+ ]
20136
+ }
20137
+ )
20138
+ }
20139
+ ),
20140
+ formHoverRect && !isItemDragging && /* @__PURE__ */ jsx33(
20141
+ ItemInteractionLayer,
20142
+ {
20143
+ rect: formHoverRect,
20144
+ state: "hover",
20145
+ chromeGap: formHoverElRef.current && getFieldWrapper(formHoverElRef.current) ? 8 : 24
20146
+ }
20147
+ ),
20148
+ fieldPickRect && fieldPickState && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ jsx33(
20149
+ ItemInteractionLayer,
20150
+ {
20151
+ rect: fieldPickRect,
20152
+ state: fieldDragging ? "dragging" : "active-top",
20153
+ itemDragSurface: false,
20154
+ toolbarAlign: "left",
20155
+ chromeGap: 10,
20156
+ showHandle: true,
20157
+ dragHandleLabel: "Reorder field",
20158
+ onDragHandleDragStart: handleFieldDragStart,
20159
+ onDragHandleDragEnd: handleFieldDragEnd,
20160
+ toolbar: /* @__PURE__ */ jsx33(
20161
+ FormFieldToolbar,
21747
20162
  {
21748
- rect: toolbarRect,
21749
- elRef: glowElRef,
21750
- reorderHrefKey,
21751
- dragDisabled: reorderDragDisabled,
21752
- hideHandle: isItemDragging
20163
+ type: fieldPickState.type,
20164
+ required: fieldPickState.required,
20165
+ onTypeChange: handleFieldTypeChange,
20166
+ onRequiredToggle: handleFieldRequiredToggle,
20167
+ onDuplicate: handleFieldDuplicate,
20168
+ onDelete: handleFieldDelete
21753
20169
  }
21754
- ),
21755
- /* @__PURE__ */ jsx33(
21756
- FloatingToolbar,
20170
+ )
20171
+ }
20172
+ ),
20173
+ fieldDragging ? fieldDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
20174
+ "div",
20175
+ {
20176
+ className: "pointer-events-none fixed z-[2147483644]",
20177
+ style: { top: slot.top, left: slot.left, width: slot.width },
20178
+ children: /* @__PURE__ */ jsx33(
20179
+ DropIndicator,
21757
20180
  {
21758
- rect: toolbarRect,
21759
- parentScroll: parentScrollRef.current,
21760
- elRef: toolbarElRef,
21761
- onCommand: handleCommand,
21762
- activeCommands,
21763
- showEditLink,
21764
- onEditLink: openLinkPopoverForActive
20181
+ direction: "horizontal",
20182
+ state: fieldDropIndex === i ? "dragActive" : "dragIdle",
20183
+ className: "!w-full"
21765
20184
  }
21766
20185
  )
21767
- ] }),
21768
- maxBadge && /* @__PURE__ */ jsxs20(
20186
+ },
20187
+ `field-drop-${i}`
20188
+ )) : null,
20189
+ fieldTypePickerOpen && formPickRect ? (() => {
20190
+ const toolbar = document.querySelector("[data-ohw-form-toolbar]")?.getBoundingClientRect();
20191
+ return /* @__PURE__ */ jsx33(
21769
20192
  "div",
21770
20193
  {
21771
- "data-ohw-max-badge": "",
20194
+ className: "pointer-events-none fixed z-[2147483645]",
21772
20195
  style: {
21773
- position: "fixed",
21774
- top: maxBadge.rect.bottom + 4,
21775
- left: maxBadge.rect.right,
21776
- transform: "translateX(-100%)",
21777
- zIndex: 2147483647,
21778
- background: maxBadge.current > maxBadge.max ? "#FEF2F2" : "#F5F5F4",
21779
- color: maxBadge.current > maxBadge.max ? "#DC2626" : "#78716C",
21780
- border: `1px solid ${maxBadge.current > maxBadge.max ? "#FECACA" : "#E7E5E4"}`,
21781
- borderRadius: 4,
21782
- padding: "2px 6px",
21783
- fontSize: 11,
21784
- fontWeight: 500,
21785
- pointerEvents: "none"
20196
+ top: toolbar ? toolbar.bottom + 6 : formPickRect.top + 16,
20197
+ left: toolbar ? toolbar.left : formPickRect.left + 24
21786
20198
  },
21787
- children: [
21788
- maxBadge.current,
21789
- "/",
21790
- maxBadge.max
21791
- ]
20199
+ children: /* @__PURE__ */ jsx33(FieldTypePicker, { onPick: handleAddField })
21792
20200
  }
21793
- ),
21794
- toggleState && !linkPopover && /* @__PURE__ */ jsx33(
21795
- StateToggle,
20201
+ );
20202
+ })() : null,
20203
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer2(selectedElRef.current) && /* @__PURE__ */ jsx33(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
20204
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isFooterLinksContainer(selectedElRef.current) && /* @__PURE__ */ jsx33(
20205
+ FooterContainerChrome,
20206
+ {
20207
+ rect: toolbarRect,
20208
+ onAdd: handleAddFooterColumn,
20209
+ addDisabled: !canAddFooterColumn()
20210
+ }
20211
+ ),
20212
+ toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame" || toolbarVariant === "logo") && /* @__PURE__ */ jsx33(
20213
+ ItemInteractionLayer,
20214
+ {
20215
+ rect: isItemDragging && draggedItemRect && (footerDragRef.current?.wasSelected || navDragRef.current?.wasSelected) ? draggedItemRect : toolbarRect,
20216
+ toolbarBelowRect: selectedElRef.current && isNavigationItem2(selectedElRef.current) ? getOpenNavDropdownPanelRect(selectedElRef.current) : null,
20217
+ elRef: glowElRef,
20218
+ state: isItemDragging ? "dragging" : selectedElRef.current && isNavigationItem2(selectedElRef.current) && getOpenNavDropdownPanelRect(selectedElRef.current) ? "active-bottom" : resolveItemInteractionState(toolbarRect, parentScrollRef.current),
20219
+ showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey) || toolbarVariant === "select-frame" && isFooterFrameSelection,
20220
+ dragDisabled: reorderDragDisabled,
20221
+ dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
20222
+ onDragHandleDragStart: handleItemDragStart,
20223
+ onDragHandleDragEnd: handleItemDragEnd,
20224
+ onItemPointerDown: toolbarVariant === "logo" ? void 0 : handleItemChromePointerDown,
20225
+ onItemClick: toolbarVariant === "logo" ? void 0 : handleItemChromeClick,
20226
+ itemDragSurface: toolbarVariant !== "logo" && !isFooterFrameSelection && !selectedIsSocialsRow,
20227
+ toolbar: toolbarVariant === "link-action" && !isItemDragging || toolbarVariant === "select-frame" && (isFooterFrameSelection || selectedIsSocialsRow) && !isItemDragging ? /* @__PURE__ */ jsx33(
20228
+ ItemActionToolbar,
20229
+ {
20230
+ onEditLink: openLinkPopoverForSelected,
20231
+ onStyle: () => {
20232
+ const row = selectedElRef.current;
20233
+ if (!row) return;
20234
+ if (floatingPanel?.kind === "socials-display") closeFloatingPanelOnly();
20235
+ else openSocialsDisplayPanel(row);
20236
+ },
20237
+ showStyle: selectedIsSocialsRow,
20238
+ styleActive: floatingPanel?.kind === "socials-display",
20239
+ onAddItem: handleAddChildItem,
20240
+ onSelectParent: handleSelectParent,
20241
+ onDuplicate: handleDuplicateSelected,
20242
+ onDelete: handleDeleteSelected,
20243
+ addItemDisabled: isFooterFrameSelection && isFooterAddItemDisabled(selectedElRef.current) || selectedElRef.current !== null && (() => {
20244
+ const row = isSocialsRow(selectedElRef.current) ? selectedElRef.current : findSocialsRow(selectedElRef.current);
20245
+ return row ? !canAddSocialItem(row) : false;
20246
+ })(),
20247
+ editLinkDisabled: false,
20248
+ moreDisabled: false,
20249
+ deleteDisabled: selectedElRef.current !== null && (() => {
20250
+ const social = getSocialItem(selectedElRef.current);
20251
+ return social ? !canRemoveSocialItem(social) : false;
20252
+ })(),
20253
+ duplicateDisabled: isFooterFrameSelection || selectedIsSocialsRow || selectedElRef.current !== null && (() => {
20254
+ const social = getSocialItem(selectedElRef.current);
20255
+ const row = social ? findSocialsRow(social) : null;
20256
+ return row ? !canAddSocialItem(row) : false;
20257
+ })(),
20258
+ showEditLink: !isFooterFrameSelection && !selectedIsSocialsRow && navDropdownPreviewOpen === null,
20259
+ showAddItem: isFooterFrameSelection || selectedIsSocialsRow || !selectedIsCta && Boolean(
20260
+ selectedElRef.current && isNavbarHrefKey(selectedElRef.current.getAttribute("data-ohw-href-key")) && !isNestedNavChild(selectedElRef.current)
20261
+ ),
20262
+ showMore: (!selectedIsCta || isFooterFrameSelection) && !selectedIsSocialsRow,
20263
+ dropdownOpen: navDropdownPreviewOpen,
20264
+ onDropdownOpenChange: handleNavDropdownOpenChange,
20265
+ headingVisible: footerHeadingVisible,
20266
+ onHeadingVisibleChange: handleFooterHeadingVisibleChange
20267
+ }
20268
+ ) : void 0
20269
+ }
20270
+ ),
20271
+ toolbarRect && toolbarVariant === "rich-text" && !linkPopover && /* @__PURE__ */ jsxs20(Fragment8, { children: [
20272
+ /* @__PURE__ */ jsx33(
20273
+ EditGlowChrome,
21796
20274
  {
21797
- rect: toggleState.rect,
21798
- activeState: toggleState.activeState,
21799
- states: toggleState.states,
21800
- onStateChange: handleStateChange
20275
+ rect: toolbarRect,
20276
+ elRef: glowElRef,
20277
+ reorderHrefKey,
20278
+ dragDisabled: reorderDragDisabled,
20279
+ hideHandle: isItemDragging
21801
20280
  }
21802
20281
  ),
21803
- sectionGap && !linkPopover && /* @__PURE__ */ jsxs20(
21804
- "div",
20282
+ /* @__PURE__ */ jsx33(
20283
+ FloatingToolbar,
21805
20284
  {
21806
- "data-ohw-section-insert-line": "",
21807
- className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
21808
- style: { top: sectionGap.y, transform: "translateY(-50%)" },
21809
- children: [
21810
- /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
21811
- /* @__PURE__ */ jsx33(
21812
- Badge,
21813
- {
21814
- 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",
21815
- onClick: () => {
21816
- window.parent.postMessage(
21817
- {
21818
- type: "ow:add-section",
21819
- insertAfter: sectionGap.insertAfter,
21820
- insertBefore: sectionGap.insertBefore
21821
- },
21822
- "*"
21823
- );
21824
- },
21825
- children: "Add Section"
21826
- }
21827
- ),
21828
- /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-primary", style: { height: 3 } })
21829
- ]
20285
+ rect: toolbarRect,
20286
+ parentScroll: parentScrollRef.current,
20287
+ elRef: toolbarElRef,
20288
+ onCommand: handleCommand,
20289
+ activeCommands,
20290
+ showEditLink,
20291
+ onEditLink: openLinkPopoverForActive
21830
20292
  }
21831
- ),
21832
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx33(
21833
- LinkPopover,
21834
- {
21835
- panelRef: linkPopoverPanelRef,
21836
- portalContainer: dialogPortalContainer,
21837
- open: true,
21838
- mode: linkPopover.mode ?? "edit",
21839
- pages: sitePages,
21840
- sections: currentSections,
21841
- sectionsByPath,
21842
- initialTarget: linkPopover.target,
21843
- existingTargets: linkPopover.intent === "add-nav" ? getNavbarExistingTargets() : [],
21844
- onClose: closeLinkPopover,
21845
- onSubmit: handleLinkPopoverSubmit
20293
+ )
20294
+ ] }),
20295
+ maxBadge && /* @__PURE__ */ jsxs20(
20296
+ "div",
20297
+ {
20298
+ "data-ohw-max-badge": "",
20299
+ style: {
20300
+ position: "fixed",
20301
+ top: maxBadge.rect.bottom + 4,
20302
+ left: maxBadge.rect.right,
20303
+ transform: "translateX(-100%)",
20304
+ zIndex: 2147483647,
20305
+ background: maxBadge.current > maxBadge.max ? "#FEF2F2" : "#F5F5F4",
20306
+ color: maxBadge.current > maxBadge.max ? "#DC2626" : "#78716C",
20307
+ border: `1px solid ${maxBadge.current > maxBadge.max ? "#FECACA" : "#E7E5E4"}`,
20308
+ borderRadius: 4,
20309
+ padding: "2px 6px",
20310
+ fontSize: 11,
20311
+ fontWeight: 500,
20312
+ pointerEvents: "none"
21846
20313
  },
21847
- linkPopover.key
21848
- ) : null,
21849
- floatingPanel && floatingPanel.kind === "socials-display" ? /* @__PURE__ */ jsx33(
21850
- FloatingPanel,
21851
- {
21852
- open: true,
21853
- title: floatingPanel.title,
21854
- context: floatingPanel.context,
21855
- position: floatingPanelPos,
21856
- onPositionChange: setFloatingPanelPos,
21857
- parentScroll: parentScrollSnap ?? parentScrollRef.current,
21858
- onClose: closeFloatingPanelOnly,
21859
- children: /* @__PURE__ */ jsx33(
21860
- SocialsDisplayPanel,
20314
+ children: [
20315
+ maxBadge.current,
20316
+ "/",
20317
+ maxBadge.max
20318
+ ]
20319
+ }
20320
+ ),
20321
+ toggleState && !linkPopover && /* @__PURE__ */ jsx33(
20322
+ StateToggle,
20323
+ {
20324
+ rect: toggleState.rect,
20325
+ activeState: toggleState.activeState,
20326
+ states: toggleState.states,
20327
+ onStateChange: handleStateChange
20328
+ }
20329
+ ),
20330
+ sectionGap && !linkPopover && /* @__PURE__ */ jsxs20(
20331
+ "div",
20332
+ {
20333
+ "data-ohw-section-insert-line": "",
20334
+ className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
20335
+ style: { top: sectionGap.y, transform: "translateY(-50%)" },
20336
+ children: [
20337
+ /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
20338
+ /* @__PURE__ */ jsx33(
20339
+ Badge,
21861
20340
  {
21862
- display: socialsDisplayFor(floatingPanel.row, editContentRef.current),
21863
- onChange: (next) => {
21864
- changeSocialsDisplay(floatingPanel.row, next);
21865
- setFloatingPanel({ ...floatingPanel });
21866
- }
20341
+ 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",
20342
+ onClick: () => {
20343
+ window.parent.postMessage(
20344
+ {
20345
+ type: "ow:add-section",
20346
+ insertAfter: sectionGap.insertAfter,
20347
+ insertBefore: sectionGap.insertBefore
20348
+ },
20349
+ "*"
20350
+ );
20351
+ },
20352
+ children: "Add Section"
21867
20353
  }
21868
- )
21869
- }
21870
- ) : null
21871
- ] }),
21872
- bridgeRoot
21873
- ) : null
21874
- ] });
21875
- }
21876
-
21877
- // src/ui/EmptySection.tsx
21878
- import Link3 from "next/link";
21879
- import { Fragment as Fragment9, jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
21880
- function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey }) {
21881
- return /* @__PURE__ */ jsxs21(Fragment9, { children: [
21882
- /* @__PURE__ */ jsx34(
21883
- "p",
21884
- {
21885
- style: {
21886
- fontFamily: "var(--brand-font-body)",
21887
- fontSize: "0.75rem",
21888
- fontWeight: 500,
21889
- letterSpacing: "0.15em",
21890
- textTransform: "uppercase",
21891
- color: "var(--brand-accent)",
21892
- marginBottom: "1.5rem"
21893
- },
21894
- children: /* @__PURE__ */ jsx34(Link3, { href: homeHref, style: { color: "inherit" }, children: /* @__PURE__ */ jsx34("span", { ...eyebrowKey ? { "data-ohw-editable": "text", "data-ohw-key": eyebrowKey } : {}, children: "Home" }) })
21895
- }
21896
- ),
21897
- /* @__PURE__ */ jsx34(
21898
- "h1",
21899
- {
21900
- style: {
21901
- fontFamily: "var(--brand-font-heading)",
21902
- fontSize: "clamp(2rem, 3.5vw, 2.75rem)",
21903
- lineHeight: 1.1,
21904
- letterSpacing: "-0.025em",
21905
- color: "var(--brand-text)",
21906
- marginBottom: "1rem"
21907
- },
21908
- ...titleKey ? { "data-ohw-editable": "text", "data-ohw-key": titleKey, "data-ohw-max-length": 80 } : {},
21909
- children: title
21910
- }
21911
- ),
21912
- /* @__PURE__ */ jsx34(
21913
- "p",
21914
- {
21915
- style: {
21916
- fontFamily: "var(--brand-font-body)",
21917
- fontSize: "1rem",
21918
- lineHeight: 1.7,
21919
- fontWeight: 300,
21920
- color: "var(--brand-text-muted)",
21921
- maxWidth: "340px"
20354
+ ),
20355
+ /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-primary", style: { height: 3 } })
20356
+ ]
20357
+ }
20358
+ ),
20359
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx33(
20360
+ LinkPopover,
20361
+ {
20362
+ panelRef: linkPopoverPanelRef,
20363
+ portalContainer: dialogPortalContainer,
20364
+ open: true,
20365
+ mode: linkPopover.mode ?? "edit",
20366
+ pages: sitePages,
20367
+ sections: currentSections,
20368
+ sectionsByPath,
20369
+ initialTarget: linkPopover.target,
20370
+ existingTargets: linkPopover.intent === "add-nav" ? getNavbarExistingTargets() : [],
20371
+ onClose: closeLinkPopover,
20372
+ onSubmit: handleLinkPopoverSubmit
21922
20373
  },
21923
- ...subtitleKey ? { "data-ohw-editable": "text", "data-ohw-key": subtitleKey, "data-ohw-max-length": 160 } : {},
21924
- children: "This page doesn't have any content yet."
21925
- }
21926
- )
21927
- ] });
20374
+ linkPopover.key
20375
+ ) : null,
20376
+ floatingPanel && floatingPanel.kind === "socials-display" ? /* @__PURE__ */ jsx33(
20377
+ FloatingPanel,
20378
+ {
20379
+ open: true,
20380
+ title: floatingPanel.title,
20381
+ context: floatingPanel.context,
20382
+ position: floatingPanelPos,
20383
+ onPositionChange: setFloatingPanelPos,
20384
+ parentScroll: parentScrollSnap ?? parentScrollRef.current,
20385
+ onClose: closeFloatingPanelOnly,
20386
+ children: /* @__PURE__ */ jsx33(
20387
+ SocialsDisplayPanel,
20388
+ {
20389
+ display: socialsDisplayFor(floatingPanel.row, editContentRef.current),
20390
+ onChange: (next) => {
20391
+ changeSocialsDisplay(floatingPanel.row, next);
20392
+ setFloatingPanel({ ...floatingPanel });
20393
+ }
20394
+ }
20395
+ )
20396
+ }
20397
+ ) : null
20398
+ ] }),
20399
+ bridgeRoot
20400
+ ) : null;
21928
20401
  }
21929
20402
  export {
21930
20403
  AI_DEFAULT_BRAND,
@@ -21942,7 +20415,6 @@ export {
21942
20415
  DropdownMenuItem,
21943
20416
  DropdownMenuSeparator,
21944
20417
  DropdownMenuTrigger,
21945
- EmptySection,
21946
20418
  ItemActionToolbar,
21947
20419
  ItemInteractionLayer,
21948
20420
  LinkEditorPanel,