@ohhwells/bridge 0.1.71-next.217 → 0.1.71

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.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).
@@ -14265,18 +13157,6 @@ function collectEditableNodes(extraContent, root = document) {
14265
13157
  }
14266
13158
  if (extraContent && !isScoped) {
14267
13159
  applyNavFooterDeleteOverrides(byKey, extraContent);
14268
- for (const key of LOGO_IMAGE_KEYS) {
14269
- if (!(key in extraContent)) continue;
14270
- byKey.set(key, { key, type: "image", text: extraContent[key] ?? "" });
14271
- }
14272
- for (const key of [LOGO_PLACEHOLDER_KEY, LOGO_ALT_KEY]) {
14273
- if (!(key in extraContent)) continue;
14274
- byKey.set(key, { key, type: "meta", text: extraContent[key] ?? "" });
14275
- }
14276
- for (const key of LOGO_SIZE_KEYS) {
14277
- if (!(key in extraContent)) continue;
14278
- byKey.set(key, { key, type: "meta", text: extraContent[key] ?? "" });
14279
- }
14280
13160
  }
14281
13161
  return Array.from(byKey.values());
14282
13162
  }
@@ -14681,7 +13561,6 @@ function fadeInImageElement(img, onReady) {
14681
13561
  function applyEditableImageSrc(img, url) {
14682
13562
  img.removeAttribute("srcset");
14683
13563
  img.removeAttribute("sizes");
14684
- if (img.loading === "lazy") img.loading = "eager";
14685
13564
  img.src = url;
14686
13565
  }
14687
13566
  function fadeInBgImage(el, url, onReady) {
@@ -14746,10 +13625,21 @@ function parseSchedulingInsertAfter(insertAfter) {
14746
13625
  insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
14747
13626
  };
14748
13627
  }
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 };
13628
+ function resolveSchedulingInsert(insertAfter, explicitInsertBefore) {
13629
+ const parsed = parseSchedulingInsertAfter(insertAfter);
13630
+ const insertBefore = explicitInsertBefore ?? parsed.insertBefore;
13631
+ const effectiveInsertAfter = insertBefore && parsed.insertBefore === null ? `${insertAfter}${INSERT_BEFORE_MARKER}${insertBefore}` : insertAfter;
13632
+ return { effectiveInsertAfter, insertBefore };
13633
+ }
13634
+ function getSchedulingMountPoint(insertAfter) {
13635
+ const { anchor } = parseSchedulingInsertAfter(insertAfter);
13636
+ let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
13637
+ if (!anchorEl && anchor === "scheduling") {
13638
+ const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
13639
+ anchorEl = widgets.at(-1) ?? null;
13640
+ }
13641
+ if (!anchorEl) return null;
13642
+ return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
14753
13643
  }
14754
13644
  function schedulingMountDepth(insertAfter) {
14755
13645
  if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
@@ -14766,7 +13656,8 @@ function getPageSchedulingEntries(raw) {
14766
13656
  }
14767
13657
  }
14768
13658
  function isSchedulingWidgetMissing(entry) {
14769
- return !document.querySelector(`[data-ohw-section="${schedulingSectionId(entry.insertAfter)}"]`);
13659
+ const { effectiveInsertAfter } = resolveSchedulingInsert(entry.insertAfter);
13660
+ return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
14770
13661
  }
14771
13662
  function hasMissingSchedulingWidgets(entries) {
14772
13663
  return entries.some(isSchedulingWidgetMissing);
@@ -14796,17 +13687,16 @@ function initSectionsFromContent(content, removeExisting = false) {
14796
13687
  } catch {
14797
13688
  }
14798
13689
  }
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);
13690
+ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId, explicitInsertBefore) {
13691
+ const { effectiveInsertAfter, insertBefore } = resolveSchedulingInsert(insertAfter, explicitInsertBefore);
13692
+ const sectionId = schedulingSectionId(effectiveInsertAfter);
14802
13693
  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;
13694
+ const mountPoint = getSchedulingMountPoint(effectiveInsertAfter);
13695
+ if (!mountPoint) return false;
14806
13696
  const container = document.createElement("div");
14807
13697
  container.dataset.ohwSectionContainer = "scheduling";
14808
- if (beforeId) {
14809
- const beforeAnchor = document.querySelector(`[data-ohw-section="${beforeId}"]`);
13698
+ if (insertBefore) {
13699
+ const beforeAnchor = document.querySelector(`[data-ohw-section="${insertBefore}"]`);
14810
13700
  const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
14811
13701
  if (!beforePoint) return false;
14812
13702
  beforePoint.insertAdjacentElement("beforebegin", container);
@@ -14817,38 +13707,30 @@ function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, be
14817
13707
  }
14818
13708
  tail.insertAdjacentElement("afterend", container);
14819
13709
  }
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
- }
14839
- const tracker = getSectionsTracker();
14840
- let sections = [];
13710
+ const root = createRoot2(container);
13711
+ flushSync2(() => {
13712
+ root.render(
13713
+ /* @__PURE__ */ jsx33(
13714
+ SchedulingWidget,
13715
+ {
13716
+ notifyOnConnect,
13717
+ initialScheduleId: scheduleId,
13718
+ insertAfter: effectiveInsertAfter
13719
+ }
13720
+ )
13721
+ );
13722
+ });
13723
+ const tracker = getSectionsTracker();
13724
+ let sections = [];
14841
13725
  try {
14842
13726
  sections = JSON.parse(tracker.textContent || "[]");
14843
13727
  } catch {
14844
13728
  }
14845
13729
  const inEditor = typeof window !== "undefined" && window.self !== window.top;
14846
- if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === widgetId)) {
13730
+ if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === effectiveInsertAfter)) {
14847
13731
  sections.push({
14848
13732
  type: "scheduling",
14849
- insertAfter: widgetId,
14850
- anchorId,
14851
- beforeId: beforeId ?? null,
13733
+ insertAfter: effectiveInsertAfter,
14852
13734
  pagePath: window.location.pathname,
14853
13735
  ...scheduleId ? { scheduleId } : {}
14854
13736
  });
@@ -14862,8 +13744,7 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
14862
13744
  for (let i = pending.length - 1; i >= 0; i--) {
14863
13745
  const entry = pending[i];
14864
13746
  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)) {
13747
+ if (mountSchedulingWidget(entry.insertAfter, shouldNotify, entry.scheduleId)) {
14867
13748
  pending.splice(i, 1);
14868
13749
  }
14869
13750
  }
@@ -15009,11 +13890,6 @@ function applyLinkByKey(key, val) {
15009
13890
  hrefAnchors.forEach((el) => applyLinkHref(el, val));
15010
13891
  }
15011
13892
  }
15012
- function isInsideLinkEditor(target) {
15013
- return Boolean(
15014
- 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"]')
15015
- );
15016
- }
15017
13893
  function isInsideFloatingPanel(target) {
15018
13894
  return Boolean(target.closest("[data-ohw-floating-panel]"));
15019
13895
  }
@@ -15021,6 +13897,11 @@ function isPointOverFloatingPanel(clientX, clientY) {
15021
13897
  const el = document.elementFromPoint(clientX, clientY);
15022
13898
  return Boolean(el instanceof Element && el.closest("[data-ohw-floating-panel]"));
15023
13899
  }
13900
+ function isInsideLinkEditor(target) {
13901
+ return Boolean(
13902
+ 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"]')
13903
+ );
13904
+ }
15024
13905
  function getHrefKeyFromElement(el) {
15025
13906
  if (!el) return null;
15026
13907
  const anchor = el.closest("[data-ohw-href-key]");
@@ -15279,7 +14160,7 @@ function getNavigationSelectionParent(el) {
15279
14160
  if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
15280
14161
  return getFooterLinksContainer();
15281
14162
  }
15282
- 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)) {
14163
+ if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
15283
14164
  return getNavigationRoot(el);
15284
14165
  }
15285
14166
  return null;
@@ -15494,6 +14375,7 @@ var ICONS = {
15494
14375
  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"/>',
15495
14376
  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"/>'
15496
14377
  };
14378
+ var HEIGHT_SETTLE_DELAYS = [150, 400, 800];
15497
14379
  var SELECTION_CHROME_GAP2 = 4;
15498
14380
  var TOOLBAR_STROKE_GAP2 = 4;
15499
14381
  var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
@@ -15873,45 +14755,6 @@ function StateToggle({
15873
14755
  );
15874
14756
  }
15875
14757
  var contentCache = /* @__PURE__ */ new Map();
15876
- var fetchedContentPaths = /* @__PURE__ */ new Set();
15877
- var OHW_LOADER_STYLE = {
15878
- position: "fixed",
15879
- inset: 0,
15880
- background: "#fff",
15881
- zIndex: 2147483646,
15882
- display: "flex",
15883
- alignItems: "center",
15884
- justifyContent: "center"
15885
- };
15886
- function OhwLoaderSpinner() {
15887
- return /* @__PURE__ */ jsxs20("svg", { width: "28", height: "28", viewBox: "0 0 28 28", fill: "none", "aria-hidden": true, children: [
15888
- /* @__PURE__ */ jsx33("circle", { cx: "14", cy: "14", r: "11", stroke: "#E7E5E4", strokeWidth: "3" }),
15889
- /* @__PURE__ */ jsx33(
15890
- "circle",
15891
- {
15892
- cx: "14",
15893
- cy: "14",
15894
- r: "11",
15895
- stroke: "#1C1917",
15896
- strokeWidth: "3",
15897
- strokeDasharray: "17 52",
15898
- strokeLinecap: "round",
15899
- children: /* @__PURE__ */ jsx33(
15900
- "animateTransform",
15901
- {
15902
- attributeName: "transform",
15903
- type: "rotate",
15904
- from: "0 14 14",
15905
- to: "360 14 14",
15906
- dur: "0.7s",
15907
- repeatCount: "indefinite"
15908
- }
15909
- )
15910
- }
15911
- )
15912
- ] });
15913
- }
15914
- 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){}})();`;
15915
14758
  function resolveSubdomain(subdomainFromQuery) {
15916
14759
  if (subdomainFromQuery) return subdomainFromQuery;
15917
14760
  if (typeof window !== "undefined") {
@@ -15934,8 +14777,8 @@ function OhhwellsBridge() {
15934
14777
  const router = useRouter3();
15935
14778
  const searchParams = useSearchParams();
15936
14779
  const isEditMode = isEditSessionActive();
15937
- const [bridgeRoot, setBridgeRoot] = useState13(null);
15938
- useEffect13(() => {
14780
+ const [bridgeRoot, setBridgeRoot] = useState12(null);
14781
+ useEffect12(() => {
15939
14782
  const figtreeFontId = "ohw-figtree-font";
15940
14783
  if (!document.getElementById(figtreeFontId)) {
15941
14784
  const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
@@ -15964,146 +14807,82 @@ function OhhwellsBridge() {
15964
14807
  const subdomain = resolveSubdomain(subdomainFromQuery);
15965
14808
  useLinkHrefGuardian(pathname, subdomain, isEditMode);
15966
14809
  useSavedLinkNavigation(isEditMode);
15967
- const postToParent2 = useCallback8((data) => {
14810
+ const postToParent2 = useCallback7((data) => {
15968
14811
  if (typeof window !== "undefined" && window.parent !== window) {
15969
14812
  window.parent.postMessage(data, "*");
15970
14813
  }
15971
14814
  }, []);
15972
- const [fetchState, setFetchState] = useState13("idle");
15973
- const autoSaveTimers = useRef10(/* @__PURE__ */ new Map());
15974
- const activeElRef = useRef10(null);
15975
- const pointerHeldRef = useRef10(false);
15976
- const selectedElRef = useRef10(null);
15977
- const selectedHrefKeyRef = useRef10(null);
15978
- const selectedFooterColAttrRef = useRef10(null);
15979
- const originalContentRef = useRef10(null);
15980
- const activeStateElRef = useRef10(null);
15981
- const parentScrollRef = useRef10(null);
15982
- const visibleViewportRef = useRef10(null);
15983
- const [dialogPortalContainer, setDialogPortalContainer] = useState13(null);
15984
- const attachVisibleViewport = useCallback8((node) => {
14815
+ const [fetchState, setFetchState] = useState12("idle");
14816
+ const autoSaveTimers = useRef9(/* @__PURE__ */ new Map());
14817
+ const activeElRef = useRef9(null);
14818
+ const pointerHeldRef = useRef9(false);
14819
+ const selectedElRef = useRef9(null);
14820
+ const selectedHrefKeyRef = useRef9(null);
14821
+ const selectedFooterColAttrRef = useRef9(null);
14822
+ const originalContentRef = useRef9(null);
14823
+ const activeStateElRef = useRef9(null);
14824
+ const parentScrollRef = useRef9(null);
14825
+ const visibleViewportRef = useRef9(null);
14826
+ const [dialogPortalContainer, setDialogPortalContainer] = useState12(null);
14827
+ const attachVisibleViewport = useCallback7((node) => {
15985
14828
  visibleViewportRef.current = node;
15986
14829
  setDialogPortalContainer(node);
15987
14830
  if (node) applyVisibleViewport(node, parentScrollRef.current);
15988
14831
  }, []);
15989
- const toolbarElRef = useRef10(null);
15990
- const glowElRef = useRef10(null);
15991
- const hoveredImageRef = useRef10(null);
15992
- const hoveredImageHasTextOverlapRef = useRef10(false);
15993
- const dragOverElRef = useRef10(null);
15994
- const [mediaHover, setMediaHover] = useState13(null);
15995
- const [selectedMedia, setSelectedMedia] = useState13(null);
15996
- const selectedMediaElRef = useRef10(null);
15997
- const clearMediaSelection = useCallback8(() => {
15998
- const prev = selectedMediaElRef.current;
15999
- selectedMediaElRef.current = null;
16000
- setSelectedMedia(null);
16001
- const sectionEl = prev?.closest("[data-ohw-section]") ?? null;
16002
- if (sectionEl) {
16003
- postToParentRef.current({
16004
- type: "ow:section-selected",
16005
- sectionId: sectionEl.dataset.ohwSection ?? null,
16006
- sectionLabel: sectionEl.dataset.ohwSectionLabel ?? titleCaseSectionId(sectionEl.dataset.ohwSection ?? ""),
16007
- key: null
16008
- });
16009
- }
16010
- }, []);
16011
- const clearMediaSelectionRef = useRef10(clearMediaSelection);
16012
- clearMediaSelectionRef.current = clearMediaSelection;
16013
- const selectMediaElement = useCallback8((el) => {
16014
- const r2 = el.getBoundingClientRect();
16015
- const video = el.dataset.ohwEditable === "video" ? el.querySelector("video") : null;
16016
- selectedMediaElRef.current = el;
16017
- setSelectedMedia({
16018
- key: el.dataset.ohwKey ?? "",
16019
- rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height },
16020
- elementType: el.dataset.ohwEditable ?? "image",
16021
- hasTextOverlap: false,
16022
- isDragOver: false,
16023
- ...video ? { videoAutoplay: video.autoplay, videoMuted: video.muted } : {}
16024
- });
16025
- const sectionEl = el.closest("[data-ohw-section]");
16026
- aiSectionApiRef.current?.selectFromElement(el, { report: false });
16027
- postToParentRef.current({
16028
- type: "ow:section-selected",
16029
- sectionId: sectionEl?.dataset.ohwSection ?? null,
16030
- sectionLabel: sectionEl ? sectionEl.dataset.ohwSectionLabel ?? titleCaseSectionId(sectionEl.dataset.ohwSection ?? "") : null,
16031
- key: el.dataset.ohwKey ?? null,
16032
- // Display name for the pill — the raw key prettifies into fragments ("Img"); the
16033
- // bridge knows what the node IS, so it names it.
16034
- keyLabel: el.dataset.ohwEditable === "video" ? "Video" : el.dataset.ohwEditable === "bg-image" ? "Background" : "Image"
16035
- });
16036
- }, []);
16037
- const selectMediaElementRef = useRef10(selectMediaElement);
16038
- selectMediaElementRef.current = selectMediaElement;
16039
- useEffect13(() => {
16040
- if (!selectedMedia) return;
16041
- const update = () => {
16042
- const el = selectedMediaElRef.current;
16043
- if (!el || !el.isConnected) {
16044
- clearMediaSelection();
16045
- return;
16046
- }
16047
- const r2 = el.getBoundingClientRect();
16048
- setSelectedMedia(
16049
- (prev) => prev ? { ...prev, rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height } } : prev
16050
- );
16051
- };
16052
- window.addEventListener("scroll", update, true);
16053
- window.addEventListener("resize", update);
16054
- return () => {
16055
- window.removeEventListener("scroll", update, true);
16056
- window.removeEventListener("resize", update);
16057
- };
16058
- }, [selectedMedia !== null]);
16059
- const [carouselHover, setCarouselHover] = useState13(null);
16060
- const [uploadingRects, setUploadingRects] = useState13({});
16061
- const hoveredGapRef = useRef10(null);
16062
- const imageUnhoverTimerRef = useRef10(null);
16063
- const imageShowTimerRef = useRef10(null);
16064
- const editStylesRef = useRef10(null);
16065
- const activateRef = useRef10(() => {
14832
+ const toolbarElRef = useRef9(null);
14833
+ const glowElRef = useRef9(null);
14834
+ const hoveredImageRef = useRef9(null);
14835
+ const hoveredImageHasTextOverlapRef = useRef9(false);
14836
+ const dragOverElRef = useRef9(null);
14837
+ const [mediaHover, setMediaHover] = useState12(null);
14838
+ const [carouselHover, setCarouselHover] = useState12(null);
14839
+ const [uploadingRects, setUploadingRects] = useState12({});
14840
+ const hoveredGapRef = useRef9(null);
14841
+ const imageUnhoverTimerRef = useRef9(null);
14842
+ const imageShowTimerRef = useRef9(null);
14843
+ const editStylesRef = useRef9(null);
14844
+ const activateRef = useRef9(() => {
16066
14845
  });
16067
- const deactivateRef = useRef10(() => {
14846
+ const deactivateRef = useRef9(() => {
16068
14847
  });
16069
- const selectRef = useRef10(() => {
14848
+ const selectRef = useRef9(() => {
16070
14849
  });
16071
- const selectFrameRef = useRef10(() => {
14850
+ const selectFrameRef = useRef9(() => {
16072
14851
  });
16073
- const selectLogoRef = useRef10(() => {
14852
+ const selectLogoRef = useRef9(() => {
16074
14853
  });
16075
- const openLogoSizePanelRef = useRef10(() => {
14854
+ const openLogoSizePanelRef = useRef9(() => {
16076
14855
  });
16077
- const deselectRef = useRef10(() => {
14856
+ const deselectRef = useRef9(() => {
16078
14857
  });
16079
- const closeFloatingPanelOnlyRef = useRef10(() => {
14858
+ const closeFloatingPanelOnlyRef = useRef9(() => {
16080
14859
  });
16081
- const reselectNavigationItemRef = useRef10(() => {
14860
+ const reselectNavigationItemRef = useRef9(() => {
16082
14861
  });
16083
- const commitNavigationTextEditRef = useRef10(() => {
14862
+ const commitNavigationTextEditRef = useRef9(() => {
16084
14863
  });
16085
- const handleDeleteSelectedRef = useRef10(() => false);
16086
- const runPendingDeleteUndoRef = useRef10(() => false);
16087
- const isFooterFrameSelectionRef = useRef10(false);
16088
- const refreshActiveCommandsRef = useRef10(() => {
14864
+ const handleDeleteSelectedRef = useRef9(() => false);
14865
+ const runPendingDeleteUndoRef = useRef9(() => false);
14866
+ const isFooterFrameSelectionRef = useRef9(false);
14867
+ const refreshActiveCommandsRef = useRef9(() => {
16089
14868
  });
16090
- const postToParentRef = useRef10(postToParent2);
14869
+ const postToParentRef = useRef9(postToParent2);
16091
14870
  postToParentRef.current = postToParent2;
16092
- const aiSectionApiRef = useRef10(null);
16093
- const sectionsLoadedRef = useRef10(false);
16094
- const pendingScheduleConfigRequests = useRef10([]);
16095
- const [toolbarRect, setToolbarRect] = useState13(null);
16096
- const [formPickRect, setFormPickRect] = useState13(null);
16097
- const formPickElRef = useRef10(null);
16098
- const [formViewState, setFormViewStateUi] = useState13("default");
16099
- const [formPickCount, setFormPickCount] = useState13(null);
16100
- const [formHoverRect, setFormHoverRect] = useState13(null);
16101
- const formHoverElRef = useRef10(null);
16102
- const [fieldPickRect, setFieldPickRect] = useState13(null);
16103
- const fieldPickElRef = useRef10(null);
16104
- const [fieldPickState, setFieldPickState] = useState13(null);
16105
- const [fieldTypePickerOpen, setFieldTypePickerOpen] = useState13(false);
16106
- const clearFormPick = useCallback8(() => {
14871
+ const aiSectionApiRef = useRef9(null);
14872
+ const sectionsLoadedRef = useRef9(false);
14873
+ const pendingScheduleConfigRequests = useRef9([]);
14874
+ const [toolbarRect, setToolbarRect] = useState12(null);
14875
+ const [formPickRect, setFormPickRect] = useState12(null);
14876
+ const formPickElRef = useRef9(null);
14877
+ const [formViewState, setFormViewStateUi] = useState12("default");
14878
+ const [formPickCount, setFormPickCount] = useState12(null);
14879
+ const [formHoverRect, setFormHoverRect] = useState12(null);
14880
+ const formHoverElRef = useRef9(null);
14881
+ const [fieldPickRect, setFieldPickRect] = useState12(null);
14882
+ const fieldPickElRef = useRef9(null);
14883
+ const [fieldPickState, setFieldPickState] = useState12(null);
14884
+ const [fieldTypePickerOpen, setFieldTypePickerOpen] = useState12(false);
14885
+ const clearFormPick = useCallback7(() => {
16107
14886
  const form = formPickElRef.current;
16108
14887
  const editing = fieldPickElRef.current;
16109
14888
  if (commitPlaceholderEdit(editing) && editing) {
@@ -16123,7 +14902,7 @@ function OhhwellsBridge() {
16123
14902
  formPickElRef.current = null;
16124
14903
  setFormPickRect(null);
16125
14904
  }, []);
16126
- const clearFieldPick = useCallback8(() => {
14905
+ const clearFieldPick = useCallback7(() => {
16127
14906
  const wrapper = fieldPickElRef.current;
16128
14907
  if (commitPlaceholderEdit(wrapper) && wrapper) {
16129
14908
  const form = wrapper.closest('[data-ohw-editable="form"]');
@@ -16133,9 +14912,9 @@ function OhhwellsBridge() {
16133
14912
  setFieldPickRect(null);
16134
14913
  setFieldPickState(null);
16135
14914
  }, []);
16136
- const persistFieldsRef = useRef10(() => {
14915
+ const persistFieldsRef = useRef9(() => {
16137
14916
  });
16138
- const persistFields = useCallback8(
14917
+ const persistFields = useCallback7(
16139
14918
  (form) => {
16140
14919
  const key = formKeyOf(form);
16141
14920
  if (!key) return;
@@ -16146,7 +14925,7 @@ function OhhwellsBridge() {
16146
14925
  []
16147
14926
  );
16148
14927
  persistFieldsRef.current = persistFields;
16149
- const selectField = useCallback8((wrapper) => {
14928
+ const selectField = useCallback7((wrapper) => {
16150
14929
  if (fieldPickElRef.current && fieldPickElRef.current !== wrapper) {
16151
14930
  commitPlaceholderEdit(fieldPickElRef.current);
16152
14931
  }
@@ -16159,7 +14938,7 @@ function OhhwellsBridge() {
16159
14938
  setFieldPickState({ type: fieldTypeOf(wrapper), required: isFieldRequired(wrapper) });
16160
14939
  setFieldTypePickerOpen(false);
16161
14940
  }, []);
16162
- const withSelectedField = useCallback8(
14941
+ const withSelectedField = useCallback7(
16163
14942
  (run) => {
16164
14943
  const wrapper = fieldPickElRef.current;
16165
14944
  const form = formPickElRef.current;
@@ -16172,28 +14951,28 @@ function OhhwellsBridge() {
16172
14951
  },
16173
14952
  [persistFields]
16174
14953
  );
16175
- const handleFieldTypeChange = useCallback8(
14954
+ const handleFieldTypeChange = useCallback7(
16176
14955
  (type) => withSelectedField((_form, wrapper) => {
16177
14956
  applyFieldType(wrapper, type);
16178
14957
  selectField(wrapper);
16179
14958
  }),
16180
14959
  [selectField, withSelectedField]
16181
14960
  );
16182
- const handleFieldRequiredToggle = useCallback8(
14961
+ const handleFieldRequiredToggle = useCallback7(
16183
14962
  () => withSelectedField((_form, wrapper) => {
16184
14963
  setFieldRequired(wrapper, !isFieldRequired(wrapper));
16185
14964
  selectField(wrapper);
16186
14965
  }),
16187
14966
  [selectField, withSelectedField]
16188
14967
  );
16189
- const handleFieldDuplicate = useCallback8(
14968
+ const handleFieldDuplicate = useCallback7(
16190
14969
  () => withSelectedField((form, wrapper) => {
16191
14970
  const copy = duplicateField(form, wrapper);
16192
14971
  selectField(copy);
16193
14972
  }),
16194
14973
  [selectField, withSelectedField]
16195
14974
  );
16196
- const handleFieldDelete = useCallback8(
14975
+ const handleFieldDelete = useCallback7(
16197
14976
  () => withSelectedField((_form, wrapper) => {
16198
14977
  removeField(wrapper);
16199
14978
  clearFieldPick();
@@ -16201,7 +14980,7 @@ function OhhwellsBridge() {
16201
14980
  }),
16202
14981
  [clearFieldPick, withSelectedField]
16203
14982
  );
16204
- const handleAddField = useCallback8(
14983
+ const handleAddField = useCallback7(
16205
14984
  (type) => {
16206
14985
  const form = formPickElRef.current;
16207
14986
  if (!form) return;
@@ -16217,8 +14996,8 @@ function OhhwellsBridge() {
16217
14996
  },
16218
14997
  [persistFields, selectField]
16219
14998
  );
16220
- const fieldDragRef = useRef10(null);
16221
- const buildFieldDropSlots = useCallback8((form, draggedKey) => {
14999
+ const fieldDragRef = useRef9(null);
15000
+ const buildFieldDropSlots = useCallback7((form, draggedKey) => {
16222
15001
  const others = listFieldWrappers(form).filter((el) => fieldKeyOf(el) !== draggedKey);
16223
15002
  const slots = others.map((el) => {
16224
15003
  const rect = el.getBoundingClientRect();
@@ -16231,7 +15010,7 @@ function OhhwellsBridge() {
16231
15010
  }
16232
15011
  return slots;
16233
15012
  }, []);
16234
- const handleFieldDragStart = useCallback8(() => {
15013
+ const handleFieldDragStart = useCallback7(() => {
16235
15014
  const wrapper = fieldPickElRef.current;
16236
15015
  const form = formPickElRef.current;
16237
15016
  if (!wrapper || !form) return;
@@ -16240,18 +15019,18 @@ function OhhwellsBridge() {
16240
15019
  setFieldDragging(true);
16241
15020
  setFieldDropSlots(buildFieldDropSlots(form, key));
16242
15021
  }, [buildFieldDropSlots]);
16243
- const handleFieldDragEnd = useCallback8(() => {
15022
+ const handleFieldDragEnd = useCallback7(() => {
16244
15023
  fieldDragRef.current = null;
16245
15024
  setFieldDropIndex(null);
16246
15025
  setFieldDropSlots([]);
16247
15026
  setFieldDragging(false);
16248
15027
  }, []);
16249
- const [fieldDropIndex, setFieldDropIndex] = useState13(null);
16250
- const [fieldDropSlots, setFieldDropSlots] = useState13([]);
16251
- const [fieldDragging, setFieldDragging] = useState13(false);
16252
- const clearFormPickRef = useRef10(clearFormPick);
15028
+ const [fieldDropIndex, setFieldDropIndex] = useState12(null);
15029
+ const [fieldDropSlots, setFieldDropSlots] = useState12([]);
15030
+ const [fieldDragging, setFieldDragging] = useState12(false);
15031
+ const clearFormPickRef = useRef9(clearFormPick);
16253
15032
  clearFormPickRef.current = clearFormPick;
16254
- useEffect13(() => {
15033
+ useEffect12(() => {
16255
15034
  const el = fieldPickElRef.current;
16256
15035
  if (!el || fieldPickRect === null) return;
16257
15036
  const observer = new ResizeObserver(() => {
@@ -16260,7 +15039,7 @@ function OhhwellsBridge() {
16260
15039
  observer.observe(el);
16261
15040
  return () => observer.disconnect();
16262
15041
  }, [fieldPickRect !== null, fieldPickState]);
16263
- useEffect13(() => {
15042
+ useEffect12(() => {
16264
15043
  const el = formPickElRef.current;
16265
15044
  if (!el || formPickRect === null) return;
16266
15045
  const observer = new ResizeObserver(() => {
@@ -16269,25 +15048,25 @@ function OhhwellsBridge() {
16269
15048
  observer.observe(el);
16270
15049
  return () => observer.disconnect();
16271
15050
  }, [formPickRect !== null, formViewState]);
16272
- const [toolbarVariant, setToolbarVariant] = useState13("none");
16273
- const toolbarVariantRef = useRef10("none");
15051
+ const [toolbarVariant, setToolbarVariant] = useState12("none");
15052
+ const toolbarVariantRef = useRef9("none");
16274
15053
  toolbarVariantRef.current = toolbarVariant;
16275
- const [selectedIsCta, setSelectedIsCta] = useState13(false);
16276
- const [selectedIsSocial, setSelectedIsSocial] = useState13(false);
16277
- const [selectedIsSocialsRow, setSelectedIsSocialsRow] = useState13(false);
16278
- const [reorderHrefKey, setReorderHrefKey] = useState13(null);
16279
- const [reorderDragDisabled, setReorderDragDisabled] = useState13(false);
16280
- const [toggleState, setToggleState] = useState13(null);
16281
- const [maxBadge, setMaxBadge] = useState13(null);
16282
- const [activeCommands, setActiveCommands] = useState13(/* @__PURE__ */ new Set());
16283
- const [sectionGap, setSectionGap] = useState13(null);
16284
- const [toolbarShowEditLink, setToolbarShowEditLink] = useState13(false);
16285
- const hoveredNavContainerRef = useRef10(null);
16286
- const [hoveredNavContainerRect, setHoveredNavContainerRect] = useState13(null);
16287
- const hoveredItemElRef = useRef10(null);
16288
- const [hoveredItemRect, setHoveredItemRect] = useState13(null);
16289
- const [hoveredTextRect, setHoveredTextRect] = useState13(null);
16290
- useEffect13(() => {
15054
+ const [selectedIsCta, setSelectedIsCta] = useState12(false);
15055
+ const [selectedIsSocial, setSelectedIsSocial] = useState12(false);
15056
+ const [selectedIsSocialsRow, setSelectedIsSocialsRow] = useState12(false);
15057
+ const [reorderHrefKey, setReorderHrefKey] = useState12(null);
15058
+ const [reorderDragDisabled, setReorderDragDisabled] = useState12(false);
15059
+ const [toggleState, setToggleState] = useState12(null);
15060
+ const [maxBadge, setMaxBadge] = useState12(null);
15061
+ const [activeCommands, setActiveCommands] = useState12(/* @__PURE__ */ new Set());
15062
+ const [sectionGap, setSectionGap] = useState12(null);
15063
+ const [toolbarShowEditLink, setToolbarShowEditLink] = useState12(false);
15064
+ const hoveredNavContainerRef = useRef9(null);
15065
+ const [hoveredNavContainerRect, setHoveredNavContainerRect] = useState12(null);
15066
+ const hoveredItemElRef = useRef9(null);
15067
+ const [hoveredItemRect, setHoveredItemRect] = useState12(null);
15068
+ const [hoveredTextRect, setHoveredTextRect] = useState12(null);
15069
+ useEffect12(() => {
16291
15070
  const sync = () => {
16292
15071
  const el = document.querySelector(
16293
15072
  '[data-ohw-hovered]:not([contenteditable]):not([data-ohw-href-key]):not([data-ohw-editable="form"] *)'
@@ -16308,56 +15087,43 @@ function OhhwellsBridge() {
16308
15087
  });
16309
15088
  return () => observer.disconnect();
16310
15089
  }, []);
16311
- const siblingHintElRef = useRef10(null);
16312
- const [siblingHintRect, setSiblingHintRect] = useState13(null);
16313
- const [siblingHintRects, setSiblingHintRects] = useState13([]);
16314
- const [isItemDragging, setIsItemDragging] = useState13(false);
16315
- const [isFooterFrameSelection, setIsFooterFrameSelection] = useState13(false);
15090
+ const siblingHintElRef = useRef9(null);
15091
+ const [siblingHintRect, setSiblingHintRect] = useState12(null);
15092
+ const [siblingHintRects, setSiblingHintRects] = useState12([]);
15093
+ const [isItemDragging, setIsItemDragging] = useState12(false);
15094
+ const [isFooterFrameSelection, setIsFooterFrameSelection] = useState12(false);
16316
15095
  isFooterFrameSelectionRef.current = isFooterFrameSelection;
16317
- const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = useState13(null);
16318
- const [footerHeadingVisible, setFooterHeadingVisible] = useState13(null);
16319
- const footerDragRef = useRef10(null);
16320
- const [footerDropSlots, setFooterDropSlots] = useState13([]);
16321
- const [activeFooterDropIndex, setActiveFooterDropIndex] = useState13(null);
16322
- const [draggedItemRect, setDraggedItemRect] = useState13(null);
16323
- const footerPointerDragRef = useRef10(null);
16324
- const suppressNextClickRef = useRef10(false);
16325
- const suppressClickUntilRef = useRef10(0);
16326
- const [linkPopover, setLinkPopover] = useState13(null);
16327
- const linkPopoverSessionRef = useRef10(null);
16328
- const addNavAfterAnchorRef = useRef10(null);
16329
- const editContentRef = useRef10({});
16330
- const aiSectionsRef = useRef10("");
16331
- const brandKitRef = useRef10("");
16332
- const stylesRef = useRef10("");
16333
- const pendingDeleteUndoRef = useRef10(null);
16334
- const [floatingPanel, setFloatingPanel] = useState13(null);
16335
- const floatingPanelOpenRef = useRef10(false);
16336
- const setFloatingPanelRef = useRef10(setFloatingPanel);
16337
- const [floatingPanelPos, setFloatingPanelPos] = useState13(null);
16338
- const [logoSizeDraft, setLogoSizeDraft] = useState13(null);
16339
- const [editorViewport, setEditorViewport] = useState13("desktop");
16340
- const [parentScrollSnap, setParentScrollSnap] = useState13(null);
16341
- const [sitePages, setSitePages] = useState13([]);
16342
- const [sectionsByPath, setSectionsByPath] = useState13({});
16343
- const sectionsPrefetchGenRef = useRef10(0);
16344
- const setLinkPopoverRef = useRef10(setLinkPopover);
16345
- const linkPopoverPanelRef = useRef10(null);
16346
- const linkPopoverOpenRef = useRef10(false);
16347
- const linkPopoverGraceUntilRef = useRef10(0);
15096
+ const [floatingPanel, setFloatingPanel] = useState12(null);
15097
+ const floatingPanelOpenRef = useRef9(false);
15098
+ floatingPanelOpenRef.current = floatingPanel !== null;
15099
+ const [floatingPanelPos, setFloatingPanelPos] = useState12(null);
15100
+ const [logoSizeDraft, setLogoSizeDraft] = useState12(null);
15101
+ const [editorViewport, setEditorViewport] = useState12("desktop");
15102
+ const [parentScrollSnap, setParentScrollSnap] = useState12(null);
15103
+ const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = useState12(null);
15104
+ const [footerHeadingVisible, setFooterHeadingVisible] = useState12(null);
15105
+ const footerDragRef = useRef9(null);
15106
+ const [footerDropSlots, setFooterDropSlots] = useState12([]);
15107
+ const [activeFooterDropIndex, setActiveFooterDropIndex] = useState12(null);
15108
+ const [draggedItemRect, setDraggedItemRect] = useState12(null);
15109
+ const footerPointerDragRef = useRef9(null);
15110
+ const suppressNextClickRef = useRef9(false);
15111
+ const suppressClickUntilRef = useRef9(0);
15112
+ const [linkPopover, setLinkPopover] = useState12(null);
15113
+ const linkPopoverSessionRef = useRef9(null);
15114
+ const addNavAfterAnchorRef = useRef9(null);
15115
+ const editContentRef = useRef9({});
15116
+ const aiSectionsRef = useRef9("");
15117
+ const pendingDeleteUndoRef = useRef9(null);
15118
+ const [sitePages, setSitePages] = useState12([]);
15119
+ const [sectionsByPath, setSectionsByPath] = useState12({});
15120
+ const sectionsPrefetchGenRef = useRef9(0);
15121
+ const setLinkPopoverRef = useRef9(setLinkPopover);
15122
+ const linkPopoverPanelRef = useRef9(null);
15123
+ const linkPopoverOpenRef = useRef9(false);
15124
+ const linkPopoverGraceUntilRef = useRef9(0);
16348
15125
  setLinkPopoverRef.current = setLinkPopover;
16349
- setFloatingPanelRef.current = setFloatingPanel;
16350
15126
  linkPopoverSessionRef.current = linkPopover;
16351
- floatingPanelOpenRef.current = Boolean(floatingPanel);
16352
- useEffect13(() => {
16353
- const syncViewport = () => {
16354
- const next = window.innerWidth <= 480 ? "mobile" : "desktop";
16355
- setEditorViewport((prev) => prev === next ? prev : next);
16356
- };
16357
- syncViewport();
16358
- window.addEventListener("resize", syncViewport);
16359
- return () => window.removeEventListener("resize", syncViewport);
16360
- }, []);
16361
15127
  const {
16362
15128
  navDragRef,
16363
15129
  navDropSlots,
@@ -16390,20 +15156,10 @@ function OhhwellsBridge() {
16390
15156
  getNavigationItemAnchor,
16391
15157
  isDragHandleDisabled
16392
15158
  });
16393
- const { sectionDropSlots, activeSectionDropIndex, isSectionDragging } = useSectionDrag({
16394
- isEditMode,
16395
- editContentRef,
16396
- postToParentRef,
16397
- parentScrollRef,
16398
- navDragRef,
16399
- footerDragRef,
16400
- suppressNextClickRef,
16401
- suppressClickUntilRef
16402
- });
16403
15159
  const bumpLinkPopoverGrace = () => {
16404
15160
  linkPopoverGraceUntilRef.current = Date.now() + 350;
16405
15161
  };
16406
- const runSectionsPrefetch = useCallback8((pages) => {
15162
+ const runSectionsPrefetch = useCallback7((pages) => {
16407
15163
  if (!isEditMode || shouldUseDevFixtures() || pages.length === 0) return;
16408
15164
  const gen = ++sectionsPrefetchGenRef.current;
16409
15165
  const paths = pages.map((p) => p.path);
@@ -16422,9 +15178,9 @@ function OhhwellsBridge() {
16422
15178
  );
16423
15179
  });
16424
15180
  }, [isEditMode, pathname]);
16425
- const runSectionsPrefetchRef = useRef10(runSectionsPrefetch);
15181
+ const runSectionsPrefetchRef = useRef9(runSectionsPrefetch);
16426
15182
  runSectionsPrefetchRef.current = runSectionsPrefetch;
16427
- useEffect13(() => {
15183
+ useEffect12(() => {
16428
15184
  if (!linkPopover) {
16429
15185
  document.documentElement.removeAttribute("data-ohw-link-popover-open");
16430
15186
  return;
@@ -16452,7 +15208,7 @@ function OhhwellsBridge() {
16452
15208
  document.documentElement.removeAttribute("data-ohw-link-popover-open");
16453
15209
  };
16454
15210
  }, [linkPopover, postToParent2]);
16455
- useEffect13(() => {
15211
+ useEffect12(() => {
16456
15212
  if (!isEditMode) return;
16457
15213
  const useFixtures = shouldUseDevFixtures();
16458
15214
  if (useFixtures) {
@@ -16476,14 +15232,14 @@ function OhhwellsBridge() {
16476
15232
  if (!useFixtures) postToParent2({ type: "ow:request-site-pages" });
16477
15233
  return () => window.removeEventListener("message", onSitePages);
16478
15234
  }, [isEditMode, postToParent2]);
16479
- useEffect13(() => {
15235
+ useEffect12(() => {
16480
15236
  if (!isEditMode || shouldUseDevFixtures()) return;
16481
15237
  void loadAllSectionsManifest().then((manifest) => {
16482
15238
  if (Object.keys(manifest).length === 0) return;
16483
15239
  setSectionsByPath((prev) => ({ ...manifest, ...prev }));
16484
15240
  });
16485
15241
  }, [isEditMode]);
16486
- useEffect13(() => {
15242
+ useEffect12(() => {
16487
15243
  const update = () => {
16488
15244
  const el = activeElRef.current ?? selectedElRef.current;
16489
15245
  if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
@@ -16507,10 +15263,10 @@ function OhhwellsBridge() {
16507
15263
  vvp.removeEventListener("resize", update);
16508
15264
  };
16509
15265
  }, []);
16510
- const refreshStateRules = useCallback8(() => {
15266
+ const refreshStateRules = useCallback7(() => {
16511
15267
  editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
16512
15268
  }, []);
16513
- const processConfigRequest = useCallback8((insertAfterVal) => {
15269
+ const processConfigRequest = useCallback7((insertAfterVal) => {
16514
15270
  const tracker = getSectionsTracker();
16515
15271
  let entries = [];
16516
15272
  try {
@@ -16533,7 +15289,7 @@ function OhhwellsBridge() {
16533
15289
  }
16534
15290
  window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: null }, "*");
16535
15291
  }, [isEditMode]);
16536
- const deactivate = useCallback8(() => {
15292
+ const deactivate = useCallback7(() => {
16537
15293
  const el = activeElRef.current;
16538
15294
  if (!el) return;
16539
15295
  const isFormBlock = el.dataset.ohwEditable === "form";
@@ -16549,7 +15305,7 @@ function OhhwellsBridge() {
16549
15305
  const original = originalContentRef.current ?? "";
16550
15306
  if (html !== sanitizeHtml(original)) {
16551
15307
  postToParentRef.current({ type: "ow:change", nodes: [{ key, text: html }] });
16552
- const h = document.body.scrollHeight;
15308
+ const h = document.documentElement.scrollHeight;
16553
15309
  if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
16554
15310
  }
16555
15311
  }
@@ -16574,12 +15330,12 @@ function OhhwellsBridge() {
16574
15330
  setToolbarShowEditLink(false);
16575
15331
  postToParent2({ type: "ow:exit-edit" });
16576
15332
  }, [postToParent2]);
16577
- const clearSelectedAttr = useCallback8(() => {
15333
+ const clearSelectedAttr = useCallback7(() => {
16578
15334
  document.querySelectorAll("[data-ohw-selected]").forEach((el) => {
16579
15335
  el.removeAttribute("data-ohw-selected");
16580
15336
  });
16581
15337
  }, []);
16582
- const deselect = useCallback8(() => {
15338
+ const deselect = useCallback7(() => {
16583
15339
  clearSelectedAttr();
16584
15340
  selectedElRef.current = null;
16585
15341
  selectedHrefKeyRef.current = null;
@@ -16608,12 +15364,12 @@ function OhhwellsBridge() {
16608
15364
  setToolbarVariant("none");
16609
15365
  }
16610
15366
  }, [clearSelectedAttr]);
16611
- const markSelected = useCallback8((el) => {
15367
+ const markSelected = useCallback7((el) => {
16612
15368
  clearSelectedAttr();
16613
15369
  el.removeAttribute("data-ohw-hovered");
16614
15370
  el.setAttribute("data-ohw-selected", "");
16615
15371
  }, [clearSelectedAttr]);
16616
- const resolveHrefKeyElement = useCallback8((hrefKey) => {
15372
+ const resolveHrefKeyElement = useCallback7((hrefKey) => {
16617
15373
  if (isFooterHrefKey(hrefKey)) {
16618
15374
  return document.querySelector(
16619
15375
  `footer [data-ohw-href-key="${CSS.escape(hrefKey)}"]`
@@ -16628,7 +15384,7 @@ function OhhwellsBridge() {
16628
15384
  `[data-ohw-href-key="${CSS.escape(hrefKey)}"]`
16629
15385
  );
16630
15386
  }, []);
16631
- const resyncSelectedNavigationItem = useCallback8(() => {
15387
+ const resyncSelectedNavigationItem = useCallback7(() => {
16632
15388
  const hrefKey = selectedHrefKeyRef.current;
16633
15389
  if (hrefKey) {
16634
15390
  const link = resolveHrefKeyElement(hrefKey);
@@ -16666,7 +15422,7 @@ function OhhwellsBridge() {
16666
15422
  );
16667
15423
  }
16668
15424
  }, [resolveHrefKeyElement]);
16669
- const reselectNavigationItem = useCallback8((navAnchor) => {
15425
+ const reselectNavigationItem = useCallback7((navAnchor) => {
16670
15426
  selectedElRef.current = navAnchor;
16671
15427
  selectedHrefKeyRef.current = navAnchor.getAttribute("data-ohw-href-key");
16672
15428
  selectedFooterColAttrRef.current = null;
@@ -16697,7 +15453,7 @@ function OhhwellsBridge() {
16697
15453
  setToolbarShowEditLink(false);
16698
15454
  setActiveCommands(/* @__PURE__ */ new Set());
16699
15455
  }, [markSelected]);
16700
- const commitNavigationTextEdit = useCallback8((navAnchor) => {
15456
+ const commitNavigationTextEdit = useCallback7((navAnchor) => {
16701
15457
  const el = activeElRef.current;
16702
15458
  if (!el) return;
16703
15459
  const key = el.dataset.ohwKey;
@@ -16711,7 +15467,7 @@ function OhhwellsBridge() {
16711
15467
  const original = originalContentRef.current ?? "";
16712
15468
  if (html !== sanitizeHtml(original)) {
16713
15469
  postToParent2({ type: "ow:change", nodes: [{ key, text: html }] });
16714
- const h = document.body.scrollHeight;
15470
+ const h = document.documentElement.scrollHeight;
16715
15471
  if (h > 50) postToParent2({ type: "ow:height", height: h });
16716
15472
  }
16717
15473
  }
@@ -16730,7 +15486,7 @@ function OhhwellsBridge() {
16730
15486
  postToParent2({ type: "ow:exit-edit" });
16731
15487
  reselectNavigationItem(navAnchor);
16732
15488
  }, [postToParent2, reselectNavigationItem]);
16733
- const handleAddTopLevelNavItem = useCallback8(() => {
15489
+ const handleAddTopLevelNavItem = useCallback7(() => {
16734
15490
  const items = listNavbarRootItems();
16735
15491
  addNavAfterAnchorRef.current = items[items.length - 1] ?? null;
16736
15492
  deselectRef.current();
@@ -16742,7 +15498,7 @@ function OhhwellsBridge() {
16742
15498
  intent: "add-nav"
16743
15499
  });
16744
15500
  }, []);
16745
- const maybeWarnNavLinkDropdownConflict = useCallback8(
15501
+ const maybeWarnNavLinkDropdownConflict = useCallback7(
16746
15502
  (anchor) => {
16747
15503
  if (!isNavbarHrefKey(anchor.getAttribute("data-ohw-href-key"))) return;
16748
15504
  if (!navDropdownsOpenOnClick()) return;
@@ -16755,7 +15511,7 @@ function OhhwellsBridge() {
16755
15511
  },
16756
15512
  [postToParent2]
16757
15513
  );
16758
- const handleNavDropdownOpenChange = useCallback8((open) => {
15514
+ const handleNavDropdownOpenChange = useCallback7((open) => {
16759
15515
  const selected = selectedElRef.current;
16760
15516
  if (!selected || !isNavigationItem2(selected)) return;
16761
15517
  setNavGroupForceOpen(selected, open);
@@ -16767,7 +15523,7 @@ function OhhwellsBridge() {
16767
15523
  }
16768
15524
  });
16769
15525
  }, []);
16770
- const handleFooterHeadingVisibleChange = useCallback8(
15526
+ const handleFooterHeadingVisibleChange = useCallback7(
16771
15527
  (visible) => {
16772
15528
  const selected = selectedElRef.current;
16773
15529
  if (!selected || !isFooterFrameSelectionRef.current) return;
@@ -16791,7 +15547,7 @@ function OhhwellsBridge() {
16791
15547
  },
16792
15548
  [postToParent2]
16793
15549
  );
16794
- const enterEditOnNewItem = useCallback8((anchor) => {
15550
+ const enterEditOnNewItem = useCallback7((anchor) => {
16795
15551
  const label = anchor.querySelector('[data-ohw-editable="text"]');
16796
15552
  if (!label) {
16797
15553
  selectRef.current(anchor);
@@ -16800,8 +15556,8 @@ function OhhwellsBridge() {
16800
15556
  setNavGroupForceOpen(anchor, true);
16801
15557
  activateRef.current(label);
16802
15558
  }, []);
16803
- const pendingSocialAddRef = useRef10(null);
16804
- const handleAddChildItem = useCallback8(() => {
15559
+ const pendingSocialAddRef = useRef9(null);
15560
+ const handleAddChildItem = useCallback7(() => {
16805
15561
  const selected = selectedElRef.current;
16806
15562
  if (!selected) return;
16807
15563
  const socialsRow = isSocialsRow(selected) ? selected : findSocialsRow(selected);
@@ -16910,7 +15666,7 @@ function OhhwellsBridge() {
16910
15666
  enterEditOnNewItem(result.anchor);
16911
15667
  });
16912
15668
  }, [enterEditOnNewItem, isFooterFrameSelection, maybeWarnNavLinkDropdownConflict, postToParent2]);
16913
- const handleAddFooterColumn = useCallback8(() => {
15669
+ const handleAddFooterColumn = useCallback7(() => {
16914
15670
  if (!canAddFooterColumn()) {
16915
15671
  postToParent2({
16916
15672
  type: "ow:toast",
@@ -16931,7 +15687,7 @@ function OhhwellsBridge() {
16931
15687
  selectRef.current(result.firstLink);
16932
15688
  });
16933
15689
  }, [postToParent2]);
16934
- const clearFooterDragVisuals = useCallback8(() => {
15690
+ const clearFooterDragVisuals = useCallback7(() => {
16935
15691
  footerDragRef.current = null;
16936
15692
  setSiblingHintRects([]);
16937
15693
  setFooterDropSlots([]);
@@ -16940,7 +15696,7 @@ function OhhwellsBridge() {
16940
15696
  setIsItemDragging(false);
16941
15697
  unlockFooterDragInteraction();
16942
15698
  }, []);
16943
- const refreshFooterDragVisuals = useCallback8((session, activeSlot, clientX, clientY) => {
15699
+ const refreshFooterDragVisuals = useCallback7((session, activeSlot, clientX, clientY) => {
16944
15700
  const dragged = session.draggedEl;
16945
15701
  setDraggedItemRect(dragged.getBoundingClientRect());
16946
15702
  if (typeof clientX === "number" && typeof clientY === "number") {
@@ -16972,13 +15728,13 @@ function OhhwellsBridge() {
16972
15728
  const activeIdx = activeSlot ? slots.findIndex((s) => s.insertIndex === activeSlot.insertIndex) : -1;
16973
15729
  setActiveFooterDropIndex(activeIdx >= 0 ? activeIdx : null);
16974
15730
  }, []);
16975
- const refreshFooterDragVisualsRef = useRef10(refreshFooterDragVisuals);
15731
+ const refreshFooterDragVisualsRef = useRef9(refreshFooterDragVisuals);
16976
15732
  refreshFooterDragVisualsRef.current = refreshFooterDragVisuals;
16977
- const commitFooterDragRef = useRef10(() => {
15733
+ const commitFooterDragRef = useRef9(() => {
16978
15734
  });
16979
- const beginFooterDragRef = useRef10(() => {
15735
+ const beginFooterDragRef = useRef9(() => {
16980
15736
  });
16981
- const beginFooterDrag = useCallback8(
15737
+ const beginFooterDrag = useCallback7(
16982
15738
  (session) => {
16983
15739
  const rect = session.draggedEl.getBoundingClientRect();
16984
15740
  session.lastClientX = session.lastClientX || rect.left + rect.width / 2;
@@ -16998,7 +15754,7 @@ function OhhwellsBridge() {
16998
15754
  [refreshFooterDragVisuals]
16999
15755
  );
17000
15756
  beginFooterDragRef.current = beginFooterDrag;
17001
- const commitFooterDrag = useCallback8(
15757
+ const commitFooterDrag = useCallback7(
17002
15758
  (clientX, clientY) => {
17003
15759
  const session = footerDragRef.current;
17004
15760
  if (!session) {
@@ -17127,7 +15883,7 @@ function OhhwellsBridge() {
17127
15883
  [clearFooterDragVisuals, resolveHrefKeyElement, resyncSelectedNavigationItem]
17128
15884
  );
17129
15885
  commitFooterDragRef.current = commitFooterDrag;
17130
- const startFooterLinkDrag = useCallback8(
15886
+ const startFooterLinkDrag = useCallback7(
17131
15887
  (anchor, clientX, clientY, wasSelected) => {
17132
15888
  const hrefKey = anchor.getAttribute("data-ohw-href-key");
17133
15889
  if (!hrefKey) return false;
@@ -17163,7 +15919,7 @@ function OhhwellsBridge() {
17163
15919
  },
17164
15920
  [beginFooterDrag]
17165
15921
  );
17166
- const startFooterColumnDrag = useCallback8(
15922
+ const startFooterColumnDrag = useCallback7(
17167
15923
  (columnEl, clientX, clientY, wasSelected) => {
17168
15924
  const columns = listFooterColumns();
17169
15925
  const idx = columns.indexOf(columnEl);
@@ -17183,7 +15939,7 @@ function OhhwellsBridge() {
17183
15939
  },
17184
15940
  [beginFooterDrag]
17185
15941
  );
17186
- const handleItemDragStart = useCallback8(
15942
+ const handleItemDragStart = useCallback7(
17187
15943
  (e) => {
17188
15944
  const selected = selectedElRef.current;
17189
15945
  if (!selected) {
@@ -17203,7 +15959,7 @@ function OhhwellsBridge() {
17203
15959
  },
17204
15960
  [startFooterColumnDrag, startFooterLinkDrag, startNavLinkDrag]
17205
15961
  );
17206
- const handleItemDragEnd = useCallback8(
15962
+ const handleItemDragEnd = useCallback7(
17207
15963
  (e) => {
17208
15964
  if (footerDragRef.current) {
17209
15965
  const x = e?.clientX;
@@ -17229,7 +15985,7 @@ function OhhwellsBridge() {
17229
15985
  },
17230
15986
  [commitFooterDrag, commitNavDrag, navDragRef]
17231
15987
  );
17232
- const handleItemChromePointerDown = useCallback8((e) => {
15988
+ const handleItemChromePointerDown = useCallback7((e) => {
17233
15989
  if (e.button !== 0) return;
17234
15990
  const selected = selectedElRef.current;
17235
15991
  if (!selected) return;
@@ -17260,7 +16016,7 @@ function OhhwellsBridge() {
17260
16016
  }
17261
16017
  if (armNavPressFromChrome(selected, e.clientX, e.clientY, e.pointerId)) return;
17262
16018
  }, [armNavPressFromChrome]);
17263
- const handleItemChromeClick = useCallback8((clientX, clientY) => {
16019
+ const handleItemChromeClick = useCallback7((clientX, clientY) => {
17264
16020
  if (suppressNextClickRef.current || Date.now() < suppressClickUntilRef.current) {
17265
16021
  suppressNextClickRef.current = false;
17266
16022
  return;
@@ -17273,7 +16029,7 @@ function OhhwellsBridge() {
17273
16029
  }, []);
17274
16030
  reselectNavigationItemRef.current = reselectNavigationItem;
17275
16031
  commitNavigationTextEditRef.current = commitNavigationTextEdit;
17276
- const select = useCallback8((anchor) => {
16032
+ const select = useCallback7((anchor) => {
17277
16033
  if (!isNavigationItem2(anchor)) return;
17278
16034
  if (activeElRef.current) deactivate();
17279
16035
  aiSectionApiRef.current?.selectFromElement(anchor);
@@ -17316,7 +16072,7 @@ function OhhwellsBridge() {
17316
16072
  setFloatingPanel(null);
17317
16073
  setLogoSizeDraft(null);
17318
16074
  }, [deactivate, markSelected]);
17319
- const selectFrame = useCallback8((el) => {
16075
+ const selectFrame = useCallback7((el) => {
17320
16076
  if (!isNavigationContainer(el)) return;
17321
16077
  if (activeElRef.current) deactivate();
17322
16078
  aiSectionApiRef.current?.selectFromElement(el);
@@ -17367,7 +16123,7 @@ function OhhwellsBridge() {
17367
16123
  setFloatingPanel(null);
17368
16124
  setLogoSizeDraft(null);
17369
16125
  }, [deactivate, markSelected, postToParent2]);
17370
- const selectLogo = useCallback8(
16126
+ const selectLogo = useCallback7(
17371
16127
  (logoEl) => {
17372
16128
  if (activeElRef.current) deactivate();
17373
16129
  selectedElRef.current = logoEl;
@@ -17396,7 +16152,7 @@ function OhhwellsBridge() {
17396
16152
  },
17397
16153
  [deactivate, markSelected]
17398
16154
  );
17399
- const openLogoSizePanel = useCallback8((logoEl) => {
16155
+ const openLogoSizePanel = useCallback7((logoEl) => {
17400
16156
  const placement = getLogoPlacement(logoEl);
17401
16157
  const draft = readLogoSizeState(editContentRef.current, placement);
17402
16158
  setLogoSizeDraft(draft);
@@ -17409,7 +16165,7 @@ function OhhwellsBridge() {
17409
16165
  placement
17410
16166
  });
17411
16167
  }, []);
17412
- const openSocialsDisplayPanel = useCallback8((row) => {
16168
+ const openSocialsDisplayPanel = useCallback7((row) => {
17413
16169
  setParentScrollSnap(parentScrollRef.current);
17414
16170
  setFloatingPanel({
17415
16171
  key: "socials-display",
@@ -17419,11 +16175,11 @@ function OhhwellsBridge() {
17419
16175
  row
17420
16176
  });
17421
16177
  }, []);
17422
- const isEditModeRef = useRef10(false);
17423
- const requestMissingSocialIconsRef = useRef10(() => {
16178
+ const isEditModeRef = useRef9(false);
16179
+ const requestMissingSocialIconsRef = useRef9(() => {
17424
16180
  });
17425
- const askedSocialIconsRef = useRef10(/* @__PURE__ */ new Set());
17426
- const requestMissingSocialIcons = useCallback8(() => {
16181
+ const askedSocialIconsRef = useRef9(/* @__PURE__ */ new Set());
16182
+ const requestMissingSocialIcons = useCallback7(() => {
17427
16183
  const items = Array.from(document.querySelectorAll(`[${SOCIALS_ROW_ATTR}]`)).filter((row) => socialsDisplayFor(row, editContentRef.current).icon).flatMap((row) => {
17428
16184
  const missing = socialsMissingIcons(row);
17429
16185
  listSocialItems(row).forEach((item) => ensureIconSlot(item));
@@ -17435,7 +16191,7 @@ function OhhwellsBridge() {
17435
16191
  }, []);
17436
16192
  requestMissingSocialIconsRef.current = requestMissingSocialIcons;
17437
16193
  isEditModeRef.current = isEditMode;
17438
- const changeSocialsDisplay = useCallback8(
16194
+ const changeSocialsDisplay = useCallback7(
17439
16195
  (row, next) => {
17440
16196
  if (next.icon) {
17441
16197
  const missing = socialsMissingIcons(row);
@@ -17459,17 +16215,17 @@ function OhhwellsBridge() {
17459
16215
  },
17460
16216
  []
17461
16217
  );
17462
- const closeFloatingPanelOnly = useCallback8(() => {
16218
+ const closeFloatingPanelOnly = useCallback7(() => {
17463
16219
  setFloatingPanel(null);
17464
16220
  setLogoSizeDraft(null);
17465
16221
  }, []);
17466
16222
  closeFloatingPanelOnlyRef.current = closeFloatingPanelOnly;
17467
- const closeFloatingPanelAndDeselect = useCallback8(() => {
16223
+ const closeFloatingPanelAndDeselect = useCallback7(() => {
17468
16224
  setFloatingPanel(null);
17469
16225
  setLogoSizeDraft(null);
17470
16226
  deselectRef.current();
17471
16227
  }, []);
17472
- useEffect13(() => {
16228
+ useEffect12(() => {
17473
16229
  const session = floatingPanel && floatingPanel.kind === "logo-size" ? floatingPanel : null;
17474
16230
  if (!session || !logoSizeDraft) {
17475
16231
  postToParentRef.current({ type: "ow:logo-size-panel", open: false });
@@ -17488,7 +16244,7 @@ function OhhwellsBridge() {
17488
16244
  max: LOGO_SIZE_MAX
17489
16245
  });
17490
16246
  }, [floatingPanel, logoSizeDraft, editorViewport]);
17491
- const persistLogoSizeDraft = useCallback8(
16247
+ const persistLogoSizeDraft = useCallback7(
17492
16248
  (placement, draft) => {
17493
16249
  const desktopKey = LOGO_SIZE_DESKTOP_KEYS[placement];
17494
16250
  const mobileKey = LOGO_SIZE_MOBILE_KEYS[placement];
@@ -17528,7 +16284,7 @@ function OhhwellsBridge() {
17528
16284
  },
17529
16285
  [postToParent2]
17530
16286
  );
17531
- const activate = useCallback8((el, options) => {
16287
+ const activate = useCallback7((el, options) => {
17532
16288
  if (activeElRef.current === el) return;
17533
16289
  if (isIconEditable(el)) {
17534
16290
  const social = getSocialItem(el);
@@ -17620,8 +16376,8 @@ function OhhwellsBridge() {
17620
16376
  openLogoSizePanelRef.current = openLogoSizePanel;
17621
16377
  deselectRef.current = deselect;
17622
16378
  closeFloatingPanelOnlyRef.current = closeFloatingPanelOnly;
17623
- const lastSiteWideScopeRef = useRef10(null);
17624
- useEffect13(() => {
16379
+ const lastSiteWideScopeRef = useRef9(null);
16380
+ useEffect12(() => {
17625
16381
  if (!isEditMode) {
17626
16382
  if (lastSiteWideScopeRef.current !== false) {
17627
16383
  lastSiteWideScopeRef.current = false;
@@ -17654,27 +16410,15 @@ function OhhwellsBridge() {
17654
16410
  }
17655
16411
  const applyContent = (content) => {
17656
16412
  const imageLoads = [];
17657
- if (typeof content[BRAND_KIT_KEY] === "string") {
17658
- brandKitRef.current = content[BRAND_KIT_KEY];
17659
- applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
17660
- }
17661
16413
  if (typeof content[AI_SECTIONS_KEY] === "string") {
17662
16414
  aiSectionsRef.current = content[AI_SECTIONS_KEY];
17663
16415
  applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
17664
16416
  }
17665
- if (typeof content[STYLE_STORE_KEY] === "string") {
17666
- stylesRef.current = content[STYLE_STORE_KEY];
17667
- applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
17668
- }
17669
- applyBrandChrome(content);
17670
16417
  for (const [key, val] of Object.entries(content)) {
17671
16418
  if (key === "__ohw_sections") continue;
17672
16419
  if (key === AI_SECTIONS_KEY) continue;
17673
16420
  if (key === LOGO_PLACEHOLDER_KEY) continue;
17674
16421
  if (LOGO_IMAGE_KEYS.includes(key)) continue;
17675
- if (key === BRAND_KIT_KEY) continue;
17676
- if (key === STYLE_STORE_KEY) continue;
17677
- if (BRAND_CHROME_KEYS.has(key)) continue;
17678
16422
  if (applyVideoSettingNode(key, val)) continue;
17679
16423
  if (applyCarouselNode(key, val)) continue;
17680
16424
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -17735,9 +16479,7 @@ function OhhwellsBridge() {
17735
16479
  let cancelled = false;
17736
16480
  setFetchState("loading");
17737
16481
  const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
17738
- const initialPath = pathname;
17739
- fetchedContentPaths.add(`${subdomain}::${initialPath}`);
17740
- fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
16482
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
17741
16483
  if (cancelled) return;
17742
16484
  const content = data?.content ?? {};
17743
16485
  contentCache.set(subdomain, content);
@@ -17750,7 +16492,7 @@ function OhhwellsBridge() {
17750
16492
  cancelled = true;
17751
16493
  };
17752
16494
  }, [subdomain, isEditMode]);
17753
- useEffect13(() => {
16495
+ useEffect12(() => {
17754
16496
  if (!isEditMode) return;
17755
16497
  const resolveIndex = (form, clientY) => {
17756
16498
  const wrappers = listFieldWrappers(form).filter((el) => fieldKeyOf(el) !== fieldDragRef.current?.key);
@@ -17791,7 +16533,7 @@ function OhhwellsBridge() {
17791
16533
  window.removeEventListener("drop", onDrop, true);
17792
16534
  };
17793
16535
  }, [buildFieldDropSlots, isEditMode, persistFields, selectField]);
17794
- useEffect13(() => {
16536
+ useEffect12(() => {
17795
16537
  if (!isEditMode) return;
17796
16538
  const mark = () => document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
17797
16539
  markFormFields(form);
@@ -17803,7 +16545,7 @@ function OhhwellsBridge() {
17803
16545
  });
17804
16546
  return () => observer.disconnect();
17805
16547
  }, [isEditMode, fetchState, pathname]);
17806
- useEffect13(() => {
16548
+ useEffect12(() => {
17807
16549
  if (!isEditMode) return;
17808
16550
  let saveTimer = null;
17809
16551
  const onInput = (e) => {
@@ -17825,14 +16567,14 @@ function OhhwellsBridge() {
17825
16567
  document.addEventListener("input", onInput, true);
17826
16568
  return () => document.removeEventListener("input", onInput, true);
17827
16569
  }, [isEditMode, persistFields]);
17828
- useEffect13(() => {
16570
+ useEffect12(() => {
17829
16571
  if (isEditMode || fetchState !== "done") return;
17830
16572
  const content = contentCache.get(subdomain) ?? {};
17831
16573
  document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
17832
16574
  reconcileFieldsFromContent(form, content);
17833
16575
  });
17834
16576
  }, [isEditMode, fetchState, subdomain]);
17835
- useEffect13(() => {
16577
+ useEffect12(() => {
17836
16578
  if (!isEditMode) return;
17837
16579
  const swallow = (e) => {
17838
16580
  const target = e.target;
@@ -17841,12 +16583,12 @@ function OhhwellsBridge() {
17841
16583
  document.addEventListener("submit", swallow, true);
17842
16584
  return () => document.removeEventListener("submit", swallow, true);
17843
16585
  }, [isEditMode]);
17844
- useEffect13(() => {
16586
+ useEffect12(() => {
17845
16587
  if (isEditMode || fetchState !== "done") return;
17846
16588
  const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
17847
16589
  bindPublishedForms(apiUrl, subdomain, contentCache.get(subdomain) ?? {});
17848
16590
  }, [isEditMode, fetchState, subdomain]);
17849
- useEffect13(() => {
16591
+ useEffect12(() => {
17850
16592
  if (!subdomain || isEditMode) return;
17851
16593
  let debounceTimer = null;
17852
16594
  let observer = null;
@@ -17857,25 +16599,10 @@ function OhhwellsBridge() {
17857
16599
  initSectionInstancesFromContent(content, window.location.pathname);
17858
16600
  observer?.disconnect();
17859
16601
  try {
17860
- applyBrandChrome(content);
17861
- if (typeof content[BRAND_KIT_KEY] === "string") {
17862
- applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
17863
- }
17864
- if (typeof content[AI_SECTIONS_KEY] === "string") {
17865
- applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
17866
- }
17867
- if (typeof content[STYLE_STORE_KEY] === "string") {
17868
- applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
17869
- }
17870
16602
  for (const [key, val] of Object.entries(content)) {
17871
16603
  if (key === "__ohw_sections") continue;
17872
- if (key === AI_SECTIONS_KEY) continue;
17873
16604
  if (key === LOGO_PLACEHOLDER_KEY) continue;
17874
16605
  if (LOGO_IMAGE_KEYS.includes(key)) continue;
17875
- if (key === BRAND_KIT_KEY) continue;
17876
- if (key === STYLE_STORE_KEY) continue;
17877
- if (key === STYLE_STORE_KEY) continue;
17878
- if (BRAND_CHROME_KEYS.has(key)) continue;
17879
16606
  if (applyVideoSettingNode(key, val)) continue;
17880
16607
  if (applyCarouselNode(key, val)) continue;
17881
16608
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -17921,17 +16648,6 @@ function OhhwellsBridge() {
17921
16648
  debounceTimer = setTimeout(applyFromCache, 150);
17922
16649
  };
17923
16650
  applyFromCache();
17924
- const pathCacheKey = `${subdomain}::${pathname}`;
17925
- if (!fetchedContentPaths.has(pathCacheKey)) {
17926
- fetchedContentPaths.add(pathCacheKey);
17927
- const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
17928
- fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(pathname)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
17929
- if (!data?.content) return;
17930
- contentCache.set(subdomain, data.content);
17931
- applyFromCache();
17932
- }).catch(() => {
17933
- });
17934
- }
17935
16651
  observer = new MutationObserver(scheduleApply);
17936
16652
  observer.observe(document.body, { childList: true, subtree: true });
17937
16653
  return () => {
@@ -17945,10 +16661,10 @@ function OhhwellsBridge() {
17945
16661
  const visible = Boolean(subdomain) && fetchState !== "done";
17946
16662
  el.style.display = visible ? "flex" : "none";
17947
16663
  }, [subdomain, fetchState]);
17948
- useEffect13(() => {
16664
+ useEffect12(() => {
17949
16665
  postToParent2({ type: "ow:navigation", path: pathname });
17950
16666
  }, [pathname, postToParent2]);
17951
- useEffect13(() => {
16667
+ useEffect12(() => {
17952
16668
  if (!isEditMode) return;
17953
16669
  if (linkPopoverSessionRef.current?.intent === "add-nav") return;
17954
16670
  if (document.querySelector("[data-ohw-section-picker]")) return;
@@ -17956,7 +16672,7 @@ function OhhwellsBridge() {
17956
16672
  deselectRef.current();
17957
16673
  deactivateRef.current();
17958
16674
  }, [pathname, isEditMode]);
17959
- useEffect13(() => {
16675
+ useEffect12(() => {
17960
16676
  const contentForNav = () => {
17961
16677
  if (isEditMode) return editContentRef.current;
17962
16678
  if (!subdomain) return {};
@@ -18025,36 +16741,35 @@ function OhhwellsBridge() {
18025
16741
  observer?.disconnect();
18026
16742
  };
18027
16743
  }, [isEditMode, pathname, subdomain, fetchState, resyncSelectedNavigationItem]);
18028
- useEffect13(() => {
16744
+ useEffect12(() => {
18029
16745
  if (!isEditMode) return;
18030
- let lastPosted = 0;
18031
16746
  const measure = () => {
18032
16747
  const h = document.body.scrollHeight;
18033
- if (h > 50 && Math.abs(h - lastPosted) > 1) {
18034
- lastPosted = h;
18035
- postToParent2({ type: "ow:height", height: h });
18036
- }
18037
- };
18038
- let raf = null;
18039
- const schedule = () => {
18040
- if (raf != null) return;
18041
- raf = requestAnimationFrame(() => {
18042
- raf = null;
18043
- measure();
18044
- });
16748
+ if (h > 50) postToParent2({ type: "ow:height", height: h });
18045
16749
  };
18046
16750
  const t1 = setTimeout(measure, 50);
18047
16751
  const t2 = setTimeout(measure, 500);
18048
- const ro = new ResizeObserver(schedule);
18049
- ro.observe(document.body);
16752
+ let lastWidth = window.innerWidth;
16753
+ let resizeTimers = [];
16754
+ const clearResizeTimers = () => {
16755
+ resizeTimers.forEach(clearTimeout);
16756
+ resizeTimers = [];
16757
+ };
16758
+ const handleResize = () => {
16759
+ if (window.innerWidth === lastWidth) return;
16760
+ lastWidth = window.innerWidth;
16761
+ clearResizeTimers();
16762
+ resizeTimers = HEIGHT_SETTLE_DELAYS.map((delay) => setTimeout(measure, delay));
16763
+ };
16764
+ window.addEventListener("resize", handleResize);
18050
16765
  return () => {
18051
16766
  clearTimeout(t1);
18052
16767
  clearTimeout(t2);
18053
- if (raf != null) cancelAnimationFrame(raf);
18054
- ro.disconnect();
16768
+ clearResizeTimers();
16769
+ window.removeEventListener("resize", handleResize);
18055
16770
  };
18056
16771
  }, [pathname, isEditMode, postToParent2]);
18057
- useEffect13(() => {
16772
+ useEffect12(() => {
18058
16773
  if (!subdomainFromQuery || isEditMode) return;
18059
16774
  const handleClick = (e) => {
18060
16775
  const anchor = e.target.closest("a");
@@ -18070,7 +16785,7 @@ function OhhwellsBridge() {
18070
16785
  document.addEventListener("click", handleClick, true);
18071
16786
  return () => document.removeEventListener("click", handleClick, true);
18072
16787
  }, [subdomainFromQuery, isEditMode, router]);
18073
- useEffect13(() => {
16788
+ useEffect12(() => {
18074
16789
  if (!isEditMode) {
18075
16790
  editStylesRef.current?.base.remove();
18076
16791
  editStylesRef.current?.forceHover.remove();
@@ -18292,7 +17007,6 @@ function OhhwellsBridge() {
18292
17007
  return;
18293
17008
  }
18294
17009
  const target = e.target;
18295
- if (target.closest("[data-ohw-ai-review]")) return;
18296
17010
  if (target.closest("[data-ohw-toolbar]")) return;
18297
17011
  if (target.closest("[data-ohw-state-toggle]")) return;
18298
17012
  if (target.closest("[data-ohw-max-badge]")) return;
@@ -18304,9 +17018,6 @@ function OhhwellsBridge() {
18304
17018
  )) {
18305
17019
  return;
18306
17020
  }
18307
- if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
18308
- clearMediaSelectionRef.current();
18309
- }
18310
17021
  {
18311
17022
  const formEl = getFormElement(target);
18312
17023
  const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
@@ -18461,11 +17172,8 @@ function OhhwellsBridge() {
18461
17172
  if (isMediaEditable(editable) && !buttonOnMedia) {
18462
17173
  e.preventDefault();
18463
17174
  e.stopPropagation();
18464
- if (selectedMediaElRef.current === editable) {
18465
- postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "", elementType: editable.dataset.ohwEditable ?? "image" });
18466
- } else {
18467
- selectMediaElementRef.current(editable);
18468
- }
17175
+ aiSectionApiRef.current?.selectFromElement(editable);
17176
+ postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "", elementType: editable.dataset.ohwEditable ?? "image" });
18469
17177
  return;
18470
17178
  }
18471
17179
  const socialItem = getSocialItem(editable);
@@ -18601,7 +17309,6 @@ function OhhwellsBridge() {
18601
17309
  };
18602
17310
  const handleDblClick = (e) => {
18603
17311
  const target = e.target;
18604
- if (target.closest("[data-ohw-ai-review]")) return;
18605
17312
  if (target.closest("[data-ohw-toolbar]")) return;
18606
17313
  if (target.closest("[data-ohw-state-toggle]")) return;
18607
17314
  if (target.closest("[data-ohw-max-badge]")) return;
@@ -18653,9 +17360,6 @@ function OhhwellsBridge() {
18653
17360
  setHoveredItemRect(null);
18654
17361
  hoveredNavContainerRef.current = null;
18655
17362
  setHoveredNavContainerRect(null);
18656
- siblingHintElRef.current = null;
18657
- setSiblingHintRect(null);
18658
- setSiblingHintRects([]);
18659
17363
  return;
18660
17364
  }
18661
17365
  {
@@ -18774,6 +17478,7 @@ function OhhwellsBridge() {
18774
17478
  hoveredNavContainerRef.current = null;
18775
17479
  setHoveredNavContainerRect(null);
18776
17480
  hoveredItemElRef.current = editable;
17481
+ setHoveredItemRect(editable.getBoundingClientRect());
18777
17482
  }
18778
17483
  }
18779
17484
  }
@@ -19070,7 +17775,7 @@ function OhhwellsBridge() {
19070
17775
  }
19071
17776
  };
19072
17777
  const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
19073
- if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
17778
+ if (linkPopoverOpenRef.current) {
19074
17779
  if (hoveredImageRef.current) {
19075
17780
  hoveredImageRef.current = null;
19076
17781
  hoveredImageHasTextOverlapRef.current = false;
@@ -19404,9 +18109,7 @@ function OhhwellsBridge() {
19404
18109
  return;
19405
18110
  }
19406
18111
  const { y } = toProbeCoords(clientX, clientY, fromParentViewport);
19407
- const sections = Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
19408
- (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
19409
- ).sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);
18112
+ const sections = Array.from(document.querySelectorAll("[data-ohw-section]")).sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);
19410
18113
  const ZONE = 20;
19411
18114
  for (let i = 0; i < sections.length; i++) {
19412
18115
  const a = sections[i];
@@ -19435,7 +18138,8 @@ function OhhwellsBridge() {
19435
18138
  };
19436
18139
  const handleMouseMove = (e) => {
19437
18140
  const { clientX, clientY } = e;
19438
- if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY) || isOverEditorChrome(clientX, clientY)) {
18141
+ if (pointOwnedByFloatingPanel(clientX, clientY)) return;
18142
+ if (isOverEditorChrome(clientX, clientY)) {
19439
18143
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
19440
18144
  formHoverElRef.current = null;
19441
18145
  setFormHoverRect(null);
@@ -19443,12 +18147,6 @@ function OhhwellsBridge() {
19443
18147
  setHoveredItemRect(null);
19444
18148
  hoveredNavContainerRef.current = null;
19445
18149
  setHoveredNavContainerRect(null);
19446
- siblingHintElRef.current = null;
19447
- setSiblingHintRect(null);
19448
- setSiblingHintRects([]);
19449
- dismissImageHover();
19450
- clearImageHover();
19451
- setSectionGap(null);
19452
18150
  return;
19453
18151
  }
19454
18152
  if (probeSocialsRowAt(clientX, clientY)) return;
@@ -19460,11 +18158,7 @@ function OhhwellsBridge() {
19460
18158
  if (e.data?.type !== "ow:pointer-sync") return;
19461
18159
  const { clientX, clientY } = e.data;
19462
18160
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
19463
- if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
19464
- dismissImageHover();
19465
- clearImageHover();
19466
- return;
19467
- }
18161
+ if (pointOwnedByFloatingPanel(clientX, clientY)) return;
19468
18162
  if (probeSocialsRowAt(clientX, clientY)) return;
19469
18163
  probeSectionGapAt(clientX, clientY);
19470
18164
  probeImageAt(clientX, clientY);
@@ -19739,7 +18433,7 @@ function OhhwellsBridge() {
19739
18433
  timers.set(key, setTimeout(() => {
19740
18434
  timers.delete(key);
19741
18435
  postToParentRef.current({ type: "ow:change", nodes: [{ key, text: html }] });
19742
- const h = document.body.scrollHeight;
18436
+ const h = document.documentElement.scrollHeight;
19743
18437
  if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
19744
18438
  }, 400));
19745
18439
  };
@@ -19747,19 +18441,10 @@ function OhhwellsBridge() {
19747
18441
  if (e.data?.type !== "ow:hydrate") return;
19748
18442
  const content = e.data.content;
19749
18443
  if (!content) return;
19750
- if (typeof content[BRAND_KIT_KEY] === "string") {
19751
- brandKitRef.current = content[BRAND_KIT_KEY];
19752
- applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
19753
- }
19754
18444
  if (typeof content[AI_SECTIONS_KEY] === "string") {
19755
18445
  aiSectionsRef.current = content[AI_SECTIONS_KEY];
19756
18446
  applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
19757
18447
  }
19758
- if (typeof content[STYLE_STORE_KEY] === "string") {
19759
- stylesRef.current = content[STYLE_STORE_KEY];
19760
- applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
19761
- }
19762
- applyBrandChrome(content);
19763
18448
  let sectionsJson = null;
19764
18449
  for (const [key, val] of Object.entries(content)) {
19765
18450
  if (key === "__ohw_sections") {
@@ -19769,9 +18454,6 @@ function OhhwellsBridge() {
19769
18454
  if (key === AI_SECTIONS_KEY) continue;
19770
18455
  if (key === LOGO_PLACEHOLDER_KEY) continue;
19771
18456
  if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
19772
- if (key === BRAND_KIT_KEY) continue;
19773
- if (key === STYLE_STORE_KEY) continue;
19774
- if (BRAND_CHROME_KEYS.has(key)) continue;
19775
18457
  if (applyVideoSettingNode(key, val)) continue;
19776
18458
  if (applyCarouselNode(key, val)) continue;
19777
18459
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -19785,8 +18467,6 @@ function OhhwellsBridge() {
19785
18467
  if (video && video.src !== val) applyVideoSrc(video, val);
19786
18468
  } else if (el.dataset.ohwEditable === "link") {
19787
18469
  applyLinkHref(el, val);
19788
- } else if (el.dataset.ohwEditable === "icon") {
19789
- applyIconMarkup(el, val);
19790
18470
  } else if (isIconMarkupValue(val)) {
19791
18471
  } else {
19792
18472
  el.innerHTML = val;
@@ -19807,7 +18487,7 @@ function OhhwellsBridge() {
19807
18487
  reconcileFooterOrderFromContent(editContentRef.current);
19808
18488
  syncNavigationDragCursorAttrs();
19809
18489
  enforceLinkHrefs();
19810
- const hydratedHeight = document.body.scrollHeight;
18490
+ const hydratedHeight = document.documentElement.scrollHeight;
19811
18491
  if (hydratedHeight > 50) postToParentRef.current({ type: "ow:height", height: hydratedHeight });
19812
18492
  postToParentRef.current({ type: "ow:hydrate-done" });
19813
18493
  };
@@ -19871,25 +18551,16 @@ function OhhwellsBridge() {
19871
18551
  nodes: collectEditableNodes(editContentRef.current)
19872
18552
  });
19873
18553
  };
19874
- const clearInteractionChrome = () => {
19875
- deactivateRef.current();
19876
- deselectRef.current();
19877
- clearMediaSelectionRef.current();
19878
- };
19879
18554
  const handleAiApplyTree = (e) => {
19880
18555
  if (e.data?.type !== "ow:ai-apply-tree") return;
19881
18556
  const payload = e.data.payload;
19882
18557
  if (!payload || typeof payload.id !== "string" || !isRenderableTree(payload.tree)) return;
19883
- clearInteractionChrome();
19884
18558
  const previous = aiSectionsRef.current;
19885
- const nextState = applyTreeToState(parseAiSectionsState(previous), {
19886
- ...payload,
19887
- path: payload.path ?? window.location.pathname
19888
- });
18559
+ const nextState = applyTreeToState(parseAiSectionsState(previous), payload);
19889
18560
  const nextValue = serializeAiSectionsState(nextState);
19890
18561
  aiSectionsRef.current = nextValue;
19891
18562
  applyAiSectionsToDom(nextState);
19892
- const newHeight = document.body.scrollHeight;
18563
+ const newHeight = document.documentElement.scrollHeight;
19893
18564
  if (newHeight > 50) postToParentRef.current({ type: "ow:height", height: newHeight });
19894
18565
  postToParentRef.current({ type: "ow:change", nodes: [{ key: AI_SECTIONS_KEY, text: nextValue }] });
19895
18566
  const appliedEl = document.querySelector(`[data-ohw-section="${CSS.escape(payload.id)}"]`);
@@ -19905,13 +18576,12 @@ function OhhwellsBridge() {
19905
18576
  if (!sectionId || sectionId === "navbar" || sectionId === "footer") return;
19906
18577
  const exists = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
19907
18578
  if (!exists) return;
19908
- clearInteractionChrome();
19909
18579
  const previous = aiSectionsRef.current;
19910
18580
  const nextState = deleteSectionFromState(parseAiSectionsState(previous), sectionId);
19911
18581
  const nextValue = serializeAiSectionsState(nextState);
19912
18582
  aiSectionsRef.current = nextValue;
19913
18583
  applyAiSectionsToDom(nextState);
19914
- const newHeight = document.body.scrollHeight;
18584
+ const newHeight = document.documentElement.scrollHeight;
19915
18585
  if (newHeight > 50) postToParentRef.current({ type: "ow:height", height: newHeight });
19916
18586
  postToParentRef.current({ type: "ow:change", nodes: [{ key: AI_SECTIONS_KEY, text: nextValue }] });
19917
18587
  postToParentRef.current({ type: "ow:ai-section-deleted", sectionId, previous, value: nextValue });
@@ -19921,106 +18591,20 @@ function OhhwellsBridge() {
19921
18591
  const handleAiSetSections = (e) => {
19922
18592
  if (e.data?.type !== "ow:ai-set-sections") return;
19923
18593
  const value = typeof e.data.value === "string" ? e.data.value : "";
19924
- clearInteractionChrome();
19925
18594
  aiSectionsRef.current = value;
19926
18595
  applyAiSectionsToDom(parseAiSectionsState(value));
19927
- applyStylesToDom(parseStyleStore(stylesRef.current));
19928
- const restoredHeight = document.body.scrollHeight;
18596
+ const restoredHeight = document.documentElement.scrollHeight;
19929
18597
  if (restoredHeight > 50) postToParentRef.current({ type: "ow:height", height: restoredHeight });
19930
18598
  postToParentRef.current({ type: "ow:change", nodes: [{ key: AI_SECTIONS_KEY, text: value }] });
19931
18599
  postAiSectionsChanged();
19932
18600
  };
19933
18601
  window.addEventListener("message", handleAiSetSections);
19934
- const handleMoveSection = (e) => {
19935
- if (e.data?.type !== "ow:move-section") return;
19936
- const instanceId = typeof e.data.instanceId === "string" ? e.data.instanceId : "";
19937
- const direction = e.data.direction === "up" || e.data.direction === "down" ? e.data.direction : null;
19938
- if (!instanceId || !direction) return;
19939
- const entries = moveSectionInstance(instanceId, direction, window.location.pathname);
19940
- if (!entries) return;
19941
- const orderJson = JSON.stringify(entries);
19942
- editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
19943
- postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
19944
- window.dispatchEvent(new Event("resize"));
19945
- };
19946
- window.addEventListener("message", handleMoveSection);
19947
- const handleAiSetBrand = (e) => {
19948
- if (e.data?.type !== "ow:ai-set-brand") return;
19949
- const value = typeof e.data.value === "string" ? e.data.value : "";
19950
- const previous = brandKitRef.current;
19951
- brandKitRef.current = value;
19952
- applyBrandToDom(parseBrandKit(value));
19953
- if (aiSectionsRef.current) applyAiSectionsToDom(parseAiSectionsState(aiSectionsRef.current));
19954
- applyStylesToDom(parseStyleStore(stylesRef.current));
19955
- postToParentRef.current({ type: "ow:change", nodes: [{ key: BRAND_KIT_KEY, text: value }] });
19956
- postToParentRef.current({ type: "ow:ai-brand-applied", previous, value });
19957
- };
19958
- window.addEventListener("message", handleAiSetBrand);
19959
- const handleAiSetStyles = (e) => {
19960
- if (e.data?.type !== "ow:ai-set-styles") return;
19961
- const value = typeof e.data.value === "string" ? e.data.value : "";
19962
- const previous = stylesRef.current;
19963
- stylesRef.current = value;
19964
- applyStylesToDom(parseStyleStore(value));
19965
- postToParentRef.current({ type: "ow:change", nodes: [{ key: STYLE_STORE_KEY, text: value }] });
19966
- postToParentRef.current({ type: "ow:ai-styles-applied", previous, value });
19967
- };
19968
- window.addEventListener("message", handleAiSetStyles);
19969
- const handleGetBrand = (e) => {
19970
- if (e.data?.type !== "ow:get-brand") return;
19971
- const template = deriveTemplateBrand();
19972
- const value = brandKitRef.current || (template ? JSON.stringify(template) : "");
19973
- postToParentRef.current({ type: "ow:brand-value", value });
19974
- };
19975
- window.addEventListener("message", handleGetBrand);
19976
18602
  const handlePanelDragging = (e) => {
19977
18603
  if (e.data?.type !== "ow:panel-dragging") return;
19978
18604
  if (e.data.dragging) document.documentElement.setAttribute("data-ohw-panel-dragging", "");
19979
18605
  else document.documentElement.removeAttribute("data-ohw-panel-dragging");
19980
18606
  };
19981
18607
  window.addEventListener("message", handlePanelDragging);
19982
- const handleDeleteSection = (e) => {
19983
- if (e.data?.type !== "ow:delete-section") return;
19984
- const instanceId = typeof e.data.instanceId === "string" ? e.data.instanceId : "";
19985
- if (!instanceId) return;
19986
- const currentEntries = getPageSectionOrderEntries(editContentRef.current[SECTION_ORDER_KEY], window.location.pathname);
19987
- const entries = deleteSectionInstance(instanceId, window.location.pathname, currentEntries);
19988
- if (!entries) return;
19989
- const orderJson = JSON.stringify(entries);
19990
- editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
19991
- postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
19992
- aiSectionApiRef.current?.clear();
19993
- window.dispatchEvent(new Event("resize"));
19994
- const deleteHeight = document.body.scrollHeight;
19995
- if (deleteHeight > 50) postToParentRef.current({ type: "ow:height", height: deleteHeight });
19996
- const actionId = newInstanceId();
19997
- pendingDeleteUndoRef.current = {
19998
- actionId,
19999
- restore: () => {
20000
- const restoredEntries = getPageSectionOrderEntries(
20001
- editContentRef.current[SECTION_ORDER_KEY],
20002
- window.location.pathname
20003
- );
20004
- const restored = restoreSectionInstance(instanceId, window.location.pathname, restoredEntries);
20005
- if (!restored) return;
20006
- const restoredJson = JSON.stringify(restored);
20007
- editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: restoredJson };
20008
- postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: restoredJson }] });
20009
- window.dispatchEvent(new Event("resize"));
20010
- const restoreHeight = document.body.scrollHeight;
20011
- if (restoreHeight > 50) postToParentRef.current({ type: "ow:height", height: restoreHeight });
20012
- }
20013
- };
20014
- postToParentRef.current({
20015
- type: "ow:toast",
20016
- title: "Section deleted",
20017
- toastType: "success",
20018
- actionLabel: "Undo",
20019
- actionId,
20020
- duration: 6e3
20021
- });
20022
- };
20023
- window.addEventListener("message", handleDeleteSection);
20024
18608
  const handleDeactivate = (e) => {
20025
18609
  if (e.data?.type !== "ow:deactivate") return;
20026
18610
  if (document.documentElement.hasAttribute("data-ohw-panel-dragging")) return;
@@ -20030,15 +18614,8 @@ function OhhwellsBridge() {
20030
18614
  closeLinkPopoverRef.current();
20031
18615
  return;
20032
18616
  }
20033
- if (floatingPanelOpenRef.current) {
20034
- setFloatingPanelRef.current(null);
20035
- deselectRef.current();
20036
- deactivateRef.current();
20037
- return;
20038
- }
20039
18617
  deselectRef.current();
20040
18618
  deactivateRef.current();
20041
- clearMediaSelectionRef.current();
20042
18619
  };
20043
18620
  window.addEventListener("message", handleDeactivate);
20044
18621
  const handleToastAction = (e) => {
@@ -20124,10 +18701,6 @@ function OhhwellsBridge() {
20124
18701
  const handleKeyDown = (e) => {
20125
18702
  if (e.key === "Escape" && document.querySelector("[data-ohw-section-picker]")) return;
20126
18703
  if (e.key === "Escape" && document.querySelector("[data-ohw-more-menu]")) return;
20127
- if (e.key === "Escape" && selectedMediaElRef.current) {
20128
- clearMediaSelectionRef.current();
20129
- return;
20130
- }
20131
18704
  if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "a" && activeElRef.current) {
20132
18705
  e.preventDefault();
20133
18706
  selectAllTextInEditable(activeElRef.current);
@@ -20287,12 +18860,6 @@ function OhhwellsBridge() {
20287
18860
  if (aiSectionsRef.current) {
20288
18861
  nodes.push({ key: AI_SECTIONS_KEY, type: "sections", text: aiSectionsRef.current });
20289
18862
  }
20290
- if (stylesRef.current) {
20291
- nodes.push({ key: STYLE_STORE_KEY, type: "styles", text: stylesRef.current });
20292
- }
20293
- if (brandKitRef.current) {
20294
- nodes.push({ key: BRAND_KIT_KEY, type: "brand", text: brandKitRef.current });
20295
- }
20296
18863
  document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
20297
18864
  const formKey = formKeyOf(form);
20298
18865
  if (!formKey) return;
@@ -20310,12 +18877,8 @@ function OhhwellsBridge() {
20310
18877
  if (inserted) {
20311
18878
  const tracker = getSectionsTracker();
20312
18879
  postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
20313
- const reportHeight = () => {
20314
- const h = document.body.scrollHeight;
20315
- if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
20316
- };
20317
- reportHeight();
20318
- setTimeout(reportHeight, 500);
18880
+ const h = document.documentElement.scrollHeight;
18881
+ if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
20319
18882
  }
20320
18883
  };
20321
18884
  const handleSwitchSchedule = (e) => {
@@ -20356,7 +18919,7 @@ function OhhwellsBridge() {
20356
18919
  const updated = sections.filter((s) => !(s.type === "scheduling" && s.pagePath === currentPath));
20357
18920
  tracker.textContent = JSON.stringify(updated);
20358
18921
  postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent }] });
20359
- const h = document.body.scrollHeight;
18922
+ const h = document.documentElement.scrollHeight;
20360
18923
  if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
20361
18924
  };
20362
18925
  const handleCollectSection = (e) => {
@@ -20702,24 +19265,19 @@ function OhhwellsBridge() {
20702
19265
  window.removeEventListener("message", handleAiApplyTree);
20703
19266
  window.removeEventListener("message", handleAiDeleteSection);
20704
19267
  window.removeEventListener("message", handleAiSetSections);
20705
- window.removeEventListener("message", handleMoveSection);
20706
- window.removeEventListener("message", handleAiSetBrand);
20707
- window.removeEventListener("message", handleAiSetStyles);
20708
- window.removeEventListener("message", handleGetBrand);
20709
19268
  window.removeEventListener("message", handlePanelDragging);
20710
- window.removeEventListener("message", handleDeleteSection);
20711
19269
  window.removeEventListener("message", handleDeactivate);
19270
+ document.documentElement.removeAttribute("data-ohw-panel-dragging");
20712
19271
  window.removeEventListener("message", handleToastAction);
20713
19272
  window.removeEventListener("message", handleFormCount);
20714
19273
  window.removeEventListener("message", handleUiEscape);
20715
- document.documentElement.removeAttribute("data-ohw-panel-dragging");
20716
19274
  autoSaveTimers.current.forEach(clearTimeout);
20717
19275
  autoSaveTimers.current.clear();
20718
19276
  if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
20719
19277
  if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
20720
19278
  };
20721
19279
  }, [isEditMode, refreshStateRules]);
20722
- useEffect13(() => {
19280
+ useEffect12(() => {
20723
19281
  if (!isEditMode) return;
20724
19282
  const THRESHOLD = 10;
20725
19283
  const resolveWasSelected = (el) => {
@@ -20875,7 +19433,7 @@ function OhhwellsBridge() {
20875
19433
  unlockFooterDragInteraction();
20876
19434
  };
20877
19435
  }, [isEditMode]);
20878
- useEffect13(() => {
19436
+ useEffect12(() => {
20879
19437
  const handler = (e) => {
20880
19438
  if (e.data?.type !== "ow:request-schedule-config") return;
20881
19439
  const insertAfterVal = e.data.insertAfter;
@@ -20891,7 +19449,7 @@ function OhhwellsBridge() {
20891
19449
  window.addEventListener("message", handler);
20892
19450
  return () => window.removeEventListener("message", handler);
20893
19451
  }, [processConfigRequest]);
20894
- useEffect13(() => {
19452
+ useEffect12(() => {
20895
19453
  if (!isEditMode) return;
20896
19454
  document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
20897
19455
  el.removeAttribute("data-ohw-active-state");
@@ -20915,7 +19473,7 @@ function OhhwellsBridge() {
20915
19473
  postToParent2({
20916
19474
  type: "ow:ready",
20917
19475
  version: "1",
20918
- bridgeVersion: "0.1.71",
19476
+ bridgeVersion: "0.1.70",
20919
19477
  path: pathname,
20920
19478
  nodes: collectEditableNodes(editContentRef.current),
20921
19479
  sections
@@ -20927,13 +19485,13 @@ function OhhwellsBridge() {
20927
19485
  clearTimeout(timer);
20928
19486
  };
20929
19487
  }, [pathname, isEditMode, refreshStateRules, postToParent2]);
20930
- useEffect13(() => {
19488
+ useEffect12(() => {
20931
19489
  scrollToHashSectionWhenReady();
20932
19490
  const onHashChange = () => scrollToHashSectionWhenReady();
20933
19491
  window.addEventListener("hashchange", onHashChange);
20934
19492
  return () => window.removeEventListener("hashchange", onHashChange);
20935
19493
  }, [pathname]);
20936
- const handleCommand = useCallback8((cmd) => {
19494
+ const handleCommand = useCallback7((cmd) => {
20937
19495
  const el = activeElRef.current;
20938
19496
  const selBefore = window.getSelection();
20939
19497
  let savedOffsets = null;
@@ -20969,7 +19527,7 @@ function OhhwellsBridge() {
20969
19527
  if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
20970
19528
  refreshActiveCommandsRef.current();
20971
19529
  }, []);
20972
- useEffect13(() => {
19530
+ useEffect12(() => {
20973
19531
  const session = floatingPanel && floatingPanel.kind === "logo-size" ? floatingPanel : null;
20974
19532
  if (!session || !logoSizeDraft) return;
20975
19533
  const onPanelAction = (e) => {
@@ -21007,7 +19565,7 @@ function OhhwellsBridge() {
21007
19565
  window.addEventListener("message", onPanelAction);
21008
19566
  return () => window.removeEventListener("message", onPanelAction);
21009
19567
  }, [floatingPanel, logoSizeDraft, editorViewport, persistLogoSizeDraft, closeFloatingPanelAndDeselect]);
21010
- const handleStateChange = useCallback8((state) => {
19568
+ const handleStateChange = useCallback7((state) => {
21011
19569
  if (!activeStateElRef.current) return;
21012
19570
  const el = activeStateElRef.current;
21013
19571
  if (state === "Default") {
@@ -21020,7 +19578,7 @@ function OhhwellsBridge() {
21020
19578
  }
21021
19579
  setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
21022
19580
  }, [deactivate]);
21023
- const reselectAfterLinkPopover = useCallback8(
19581
+ const reselectAfterLinkPopover = useCallback7(
21024
19582
  (hrefKey) => {
21025
19583
  requestAnimationFrame(() => {
21026
19584
  const el = resolveHrefKeyElement(hrefKey);
@@ -21029,7 +19587,7 @@ function OhhwellsBridge() {
21029
19587
  },
21030
19588
  [resolveHrefKeyElement]
21031
19589
  );
21032
- const closeLinkPopover = useCallback8(() => {
19590
+ const closeLinkPopover = useCallback7(() => {
21033
19591
  const session = linkPopoverSessionRef.current;
21034
19592
  addNavAfterAnchorRef.current = null;
21035
19593
  setLinkPopover(null);
@@ -21037,9 +19595,9 @@ function OhhwellsBridge() {
21037
19595
  reselectAfterLinkPopover(session.key);
21038
19596
  }
21039
19597
  }, [reselectAfterLinkPopover]);
21040
- const closeLinkPopoverRef = useRef10(closeLinkPopover);
19598
+ const closeLinkPopoverRef = useRef9(closeLinkPopover);
21041
19599
  closeLinkPopoverRef.current = closeLinkPopover;
21042
- const openLinkPopoverForActive = useCallback8(() => {
19600
+ const openLinkPopoverForActive = useCallback7(() => {
21043
19601
  const hrefCtx = getHrefKeyFromElement(activeElRef.current);
21044
19602
  if (!hrefCtx) return;
21045
19603
  bumpLinkPopoverGrace();
@@ -21050,7 +19608,7 @@ function OhhwellsBridge() {
21050
19608
  });
21051
19609
  deactivate();
21052
19610
  }, [deactivate]);
21053
- const openLinkPopoverForSelected = useCallback8(() => {
19611
+ const openLinkPopoverForSelected = useCallback7(() => {
21054
19612
  const anchor = selectedElRef.current;
21055
19613
  if (!anchor) return;
21056
19614
  const key = anchor.getAttribute("data-ohw-href-key");
@@ -21067,7 +19625,7 @@ function OhhwellsBridge() {
21067
19625
  });
21068
19626
  deselect();
21069
19627
  }, [deselect]);
21070
- const handleSelectParent = useCallback8(() => {
19628
+ const handleSelectParent = useCallback7(() => {
21071
19629
  const selected = selectedElRef.current;
21072
19630
  if (!selected) return;
21073
19631
  if (toolbarVariantRef.current === "select-frame") {
@@ -21094,7 +19652,7 @@ function OhhwellsBridge() {
21094
19652
  }
21095
19653
  deselectRef.current();
21096
19654
  }, []);
21097
- const handleDuplicateSelected = useCallback8(() => {
19655
+ const handleDuplicateSelected = useCallback7(() => {
21098
19656
  const selected = selectedElRef.current;
21099
19657
  if (!selected || !isNavigationItem2(selected)) return;
21100
19658
  const hrefKey = selected.getAttribute("data-ohw-href-key");
@@ -21227,7 +19785,7 @@ function OhhwellsBridge() {
21227
19785
  });
21228
19786
  }
21229
19787
  }, [postToParent2]);
21230
- const runPendingDeleteUndo = useCallback8(() => {
19788
+ const runPendingDeleteUndo = useCallback7(() => {
21231
19789
  const pending = pendingDeleteUndoRef.current;
21232
19790
  if (!pending) return false;
21233
19791
  pendingDeleteUndoRef.current = null;
@@ -21235,7 +19793,7 @@ function OhhwellsBridge() {
21235
19793
  enforceLinkHrefs();
21236
19794
  return true;
21237
19795
  }, []);
21238
- const handleDeleteSelected = useCallback8(() => {
19796
+ const handleDeleteSelected = useCallback7(() => {
21239
19797
  const selected = selectedElRef.current;
21240
19798
  if (!selected) return false;
21241
19799
  return deleteSelectedNavFooterItem({
@@ -21256,7 +19814,7 @@ function OhhwellsBridge() {
21256
19814
  }, [postToParent2]);
21257
19815
  handleDeleteSelectedRef.current = handleDeleteSelected;
21258
19816
  runPendingDeleteUndoRef.current = runPendingDeleteUndo;
21259
- const handleLinkPopoverSubmit = useCallback8(
19817
+ const handleLinkPopoverSubmit = useCallback7(
21260
19818
  (target) => {
21261
19819
  const session = linkPopoverSessionRef.current;
21262
19820
  if (!session) return;
@@ -21322,30 +19880,19 @@ function OhhwellsBridge() {
21322
19880
  const showEditLink = toolbarShowEditLink;
21323
19881
  const currentSections = sectionsByPath[pathname] ?? [];
21324
19882
  linkPopoverOpenRef.current = linkPopover !== null;
21325
- const handleMediaSelect = useCallback8((key) => {
21326
- const el = hoveredImageRef.current?.dataset.ohwKey === key ? hoveredImageRef.current : Array.from(document.querySelectorAll(MEDIA_SELECTOR)).find(
21327
- (m) => (m.dataset.ohwKey ?? "") === key
21328
- ) ?? null;
21329
- if (!el) return;
21330
- selectMediaElementRef.current(el);
21331
- }, []);
21332
- const handleMediaReplace = useCallback8(
19883
+ const handleMediaReplace = useCallback7(
21333
19884
  (key) => {
21334
- postToParent2({
21335
- type: "ow:image-pick",
21336
- key,
21337
- elementType: mediaHover?.elementType ?? selectedMedia?.elementType ?? "image"
21338
- });
19885
+ postToParent2({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
21339
19886
  },
21340
- [postToParent2, mediaHover?.elementType, selectedMedia?.elementType]
19887
+ [postToParent2, mediaHover?.elementType]
21341
19888
  );
21342
- const handleEditCarousel = useCallback8(
19889
+ const handleEditCarousel = useCallback7(
21343
19890
  (key) => {
21344
19891
  postToParent2({ type: "ow:carousel-open", key, images: readCarouselValue(key) });
21345
19892
  },
21346
19893
  [postToParent2]
21347
19894
  );
21348
- const handleMediaFadeOutComplete = useCallback8((key) => {
19895
+ const handleMediaFadeOutComplete = useCallback7((key) => {
21349
19896
  setUploadingRects((prev) => {
21350
19897
  if (!(key in prev)) return prev;
21351
19898
  const next = { ...prev };
@@ -21353,7 +19900,7 @@ function OhhwellsBridge() {
21353
19900
  return next;
21354
19901
  });
21355
19902
  }, []);
21356
- const handleVideoSettingsChange = useCallback8(
19903
+ const handleVideoSettingsChange = useCallback7(
21357
19904
  (key, settings) => {
21358
19905
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
21359
19906
  const video = getVideoEl2(el);
@@ -21375,516 +19922,430 @@ function OhhwellsBridge() {
21375
19922
  },
21376
19923
  [postToParent2]
21377
19924
  );
21378
- return /* @__PURE__ */ jsxs20(Fragment8, { children: [
21379
- /* @__PURE__ */ jsx33("div", { id: "ohw-loader", suppressHydrationWarning: true, style: { ...OHW_LOADER_STYLE, display: "none" }, children: /* @__PURE__ */ jsx33(OhwLoaderSpinner, {}) }),
21380
- /* @__PURE__ */ jsx33("script", { suppressHydrationWarning: true, dangerouslySetInnerHTML: { __html: OHW_LOADER_PREHYDRATE_SCRIPT } }),
21381
- bridgeRoot ? createPortal2(
21382
- /* @__PURE__ */ jsxs20(Fragment8, { children: [
21383
- /* @__PURE__ */ jsx33("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
21384
- isEditMode && /* @__PURE__ */ jsx33(AiSectionOverlay, { postToParent: postToParent2, apiRef: aiSectionApiRef }),
21385
- isSectionDragging && sectionDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
21386
- "div",
21387
- {
21388
- className: "pointer-events-none fixed z-2147483646",
21389
- style: { left: slot.left, top: slot.y, width: slot.width, height: 3, transform: "translateY(-50%)" },
21390
- children: /* @__PURE__ */ jsx33(
21391
- DropIndicator,
21392
- {
21393
- direction: "horizontal",
21394
- state: activeSectionDropIndex === i ? "dragActive" : "dragIdle",
21395
- className: "!h-full !w-full"
21396
- }
21397
- )
21398
- },
21399
- `section-drop-${slot.insertIndex}-${i}`
21400
- )),
21401
- Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ jsx33(
21402
- MediaOverlay,
21403
- {
21404
- hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
21405
- isUploading: true,
21406
- fadingOut,
21407
- onFadeOutComplete: handleMediaFadeOutComplete,
21408
- onReplace: handleMediaReplace
21409
- },
21410
- `uploading-${key}`
21411
- )),
21412
- mediaHover && !(mediaHover.key in uploadingRects) && mediaHover.key !== selectedMedia?.key && /* @__PURE__ */ jsx33(
21413
- MediaOverlay,
21414
- {
21415
- hover: mediaHover,
21416
- isUploading: false,
21417
- onReplace: handleMediaReplace,
21418
- onSelect: handleMediaSelect,
21419
- onVideoSettingsChange: handleVideoSettingsChange
21420
- }
21421
- ),
21422
- selectedMedia && !(selectedMedia.key in uploadingRects) && !linkPopover && /* @__PURE__ */ jsx33(
21423
- MediaOverlay,
21424
- {
21425
- hover: selectedMedia,
21426
- selected: true,
21427
- hovered: mediaHover?.key === selectedMedia.key,
21428
- isUploading: false,
21429
- onReplace: handleMediaReplace,
21430
- onSelect: handleMediaSelect,
21431
- onVideoSettingsChange: handleVideoSettingsChange
21432
- }
21433
- ),
21434
- carouselHover && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx33(CarouselOverlay, { hover: carouselHover, onEdit: handleEditCarousel }),
21435
- siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
21436
- siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
21437
- isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
21438
- isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
21439
- "div",
21440
- {
21441
- className: "pointer-events-none fixed z-2147483646",
21442
- style: {
21443
- left: slot.left,
21444
- top: slot.top,
21445
- width: slot.width,
21446
- height: slot.height
21447
- },
21448
- children: /* @__PURE__ */ jsx33(
21449
- DropIndicator,
21450
- {
21451
- direction: slot.direction,
21452
- state: activeFooterDropIndex === i ? "dragActive" : "dragIdle",
21453
- className: "!h-full !w-full"
21454
- }
21455
- )
21456
- },
21457
- `footer-drop-${slot.direction}-${slot.columnIndex}-${slot.insertIndex}-${i}`
21458
- )),
21459
- isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
21460
- "div",
21461
- {
21462
- className: "pointer-events-none fixed z-2147483646",
21463
- style: {
21464
- left: slot.left,
21465
- top: slot.top,
21466
- width: slot.width,
21467
- height: slot.height
21468
- },
21469
- children: /* @__PURE__ */ jsx33(
21470
- DropIndicator,
21471
- {
21472
- direction: slot.direction,
21473
- state: activeNavDropIndex === i ? "dragActive" : "dragIdle",
21474
- className: "!h-full !w-full"
21475
- }
21476
- )
19925
+ return bridgeRoot ? createPortal2(
19926
+ /* @__PURE__ */ jsxs20(Fragment8, { children: [
19927
+ /* @__PURE__ */ jsx33("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
19928
+ isEditMode && /* @__PURE__ */ jsx33(AiSectionOverlay, { postToParent: postToParent2, apiRef: aiSectionApiRef }),
19929
+ Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ jsx33(
19930
+ MediaOverlay,
19931
+ {
19932
+ hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
19933
+ isUploading: true,
19934
+ fadingOut,
19935
+ onFadeOutComplete: handleMediaFadeOutComplete,
19936
+ onReplace: handleMediaReplace
19937
+ },
19938
+ `uploading-${key}`
19939
+ )),
19940
+ mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ jsx33(
19941
+ MediaOverlay,
19942
+ {
19943
+ hover: mediaHover,
19944
+ isUploading: false,
19945
+ onReplace: handleMediaReplace,
19946
+ onVideoSettingsChange: handleVideoSettingsChange
19947
+ }
19948
+ ),
19949
+ carouselHover && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx33(CarouselOverlay, { hover: carouselHover, onEdit: handleEditCarousel }),
19950
+ siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
19951
+ siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
19952
+ isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
19953
+ isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
19954
+ "div",
19955
+ {
19956
+ className: "pointer-events-none fixed z-2147483646",
19957
+ style: {
19958
+ left: slot.left,
19959
+ top: slot.top,
19960
+ width: slot.width,
19961
+ height: slot.height
21477
19962
  },
21478
- `nav-drop-${slot.direction}-${slot.parentId ?? "root"}-${slot.insertIndex}-${i}`
21479
- )),
21480
- hoveredNavContainerRect && (toolbarVariant !== "select-frame" || isFooterFrameSelection) && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
21481
- hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
21482
- hoveredTextRect && !hoveredNavContainerRect && !hoveredItemRect && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredTextRect, state: "hover" }),
21483
- formPickRect && !isItemDragging && /* @__PURE__ */ jsx33(
21484
- ItemInteractionLayer,
21485
- {
21486
- rect: formPickRect,
21487
- state: "active-top",
21488
- itemDragSurface: false,
21489
- toolbarAlign: "left",
21490
- chromeGap: 24,
21491
- toolbar: fieldPickRect ? void 0 : /* @__PURE__ */ jsxs20(
21492
- "div",
21493
- {
21494
- "data-ohw-form-toolbar": "",
21495
- className: "pointer-events-auto flex items-center gap-0.5 whitespace-nowrap rounded-lg border border-border bg-background p-1 shadow-md",
21496
- children: [
21497
- /* @__PURE__ */ jsx33(
21498
- "button",
21499
- {
21500
- type: "button",
21501
- "aria-label": "Add field",
21502
- className: "flex h-7 w-7 items-center justify-center rounded-md text-foreground transition-colors hover:bg-muted/80",
21503
- onClick: () => setFieldTypePickerOpen((open) => !open),
21504
- "data-ohw-add-field": "",
21505
- children: /* @__PURE__ */ jsx33(Plus4, { size: 15, "aria-hidden": true })
21506
- }
21507
- ),
21508
- /* @__PURE__ */ jsx33("div", { className: "mx-0.5 h-5 w-px bg-border" }),
21509
- /* @__PURE__ */ jsxs20(
21510
- "button",
21511
- {
21512
- type: "button",
21513
- 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",
21514
- onClick: () => {
21515
- setFieldTypePickerOpen(false);
21516
- const form = formPickElRef.current;
21517
- if (!form) return;
21518
- postToParent2({
21519
- type: "ow:form-pick",
21520
- formKey: formKeyOf(form),
21521
- hasLongText: formHasLongText(form)
21522
- });
21523
- },
21524
- children: [
21525
- /* @__PURE__ */ jsx33(Settings, { size: 14, "aria-hidden": true }),
21526
- "Form settings",
21527
- formPickCount ? (
21528
- // Counter pill, per the design — not a text suffix.
21529
- /* @__PURE__ */ jsx33(
21530
- "span",
21531
- {
21532
- "data-ohw-form-count": "",
21533
- 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",
21534
- children: formPickCount
21535
- }
21536
- )
21537
- ) : null
21538
- ]
21539
- }
21540
- ),
21541
- /* @__PURE__ */ jsx33("div", { className: "mx-0.5 h-5 w-px bg-border" }),
21542
- /* @__PURE__ */ jsx33("div", { className: "flex items-center rounded-md bg-muted/70 p-0.5", children: ["default", "success"].map((state) => /* @__PURE__ */ jsx33(
21543
- "button",
21544
- {
21545
- type: "button",
21546
- "aria-pressed": formViewState === state,
21547
- 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"),
21548
- onClick: () => {
21549
- setFieldTypePickerOpen(false);
21550
- const form = formPickElRef.current;
21551
- const key = form ? formKeyOf(form) : null;
21552
- if (!form || !key) return;
21553
- const initial = successInitialFor(form, key, editContentRef.current);
21554
- setFormViewState(form, key, state, initial);
21555
- setFormViewStateUi(state);
21556
- setFormPickRect(form.getBoundingClientRect());
21557
- if (state === "success") {
21558
- const successEl = form.querySelector(`[${SUCCESS_TEXT_ATTR}]`);
21559
- if (successEl) requestAnimationFrame(() => activateRef.current(successEl));
21560
- } else {
21561
- deactivateRef.current();
21562
- }
21563
- },
21564
- children: state
21565
- },
21566
- state
21567
- )) })
21568
- ]
21569
- }
21570
- )
21571
- }
21572
- ),
21573
- formHoverRect && !isItemDragging && /* @__PURE__ */ jsx33(
21574
- ItemInteractionLayer,
21575
- {
21576
- rect: formHoverRect,
21577
- state: "hover",
21578
- chromeGap: formHoverElRef.current && getFieldWrapper(formHoverElRef.current) ? 8 : 24
21579
- }
21580
- ),
21581
- fieldPickRect && fieldPickState && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ jsx33(
21582
- ItemInteractionLayer,
21583
- {
21584
- rect: fieldPickRect,
21585
- state: fieldDragging ? "dragging" : "active-top",
21586
- itemDragSurface: false,
21587
- toolbarAlign: "left",
21588
- chromeGap: 10,
21589
- showHandle: true,
21590
- dragHandleLabel: "Reorder field",
21591
- onDragHandleDragStart: handleFieldDragStart,
21592
- onDragHandleDragEnd: handleFieldDragEnd,
21593
- toolbar: /* @__PURE__ */ jsx33(
21594
- FormFieldToolbar,
21595
- {
21596
- type: fieldPickState.type,
21597
- required: fieldPickState.required,
21598
- onTypeChange: handleFieldTypeChange,
21599
- onRequiredToggle: handleFieldRequiredToggle,
21600
- onDuplicate: handleFieldDuplicate,
21601
- onDelete: handleFieldDelete
21602
- }
21603
- )
21604
- }
21605
- ),
21606
- fieldDragging ? fieldDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
21607
- "div",
21608
- {
21609
- className: "pointer-events-none fixed z-[2147483644]",
21610
- style: { top: slot.top, left: slot.left, width: slot.width },
21611
- children: /* @__PURE__ */ jsx33(
21612
- DropIndicator,
21613
- {
21614
- direction: "horizontal",
21615
- state: fieldDropIndex === i ? "dragActive" : "dragIdle",
21616
- className: "!w-full"
21617
- }
21618
- )
19963
+ children: /* @__PURE__ */ jsx33(
19964
+ DropIndicator,
19965
+ {
19966
+ direction: slot.direction,
19967
+ state: activeFooterDropIndex === i ? "dragActive" : "dragIdle",
19968
+ className: "!h-full !w-full"
19969
+ }
19970
+ )
19971
+ },
19972
+ `footer-drop-${slot.direction}-${slot.columnIndex}-${slot.insertIndex}-${i}`
19973
+ )),
19974
+ isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
19975
+ "div",
19976
+ {
19977
+ className: "pointer-events-none fixed z-2147483646",
19978
+ style: {
19979
+ left: slot.left,
19980
+ top: slot.top,
19981
+ width: slot.width,
19982
+ height: slot.height
21619
19983
  },
21620
- `field-drop-${i}`
21621
- )) : null,
21622
- fieldTypePickerOpen && formPickRect ? (() => {
21623
- const toolbar = document.querySelector("[data-ohw-form-toolbar]")?.getBoundingClientRect();
21624
- return /* @__PURE__ */ jsx33(
21625
- "div",
19984
+ children: /* @__PURE__ */ jsx33(
19985
+ DropIndicator,
21626
19986
  {
21627
- className: "pointer-events-none fixed z-[2147483645]",
21628
- style: {
21629
- top: toolbar ? toolbar.bottom + 6 : formPickRect.top + 16,
21630
- left: toolbar ? toolbar.left : formPickRect.left + 24
21631
- },
21632
- children: /* @__PURE__ */ jsx33(FieldTypePicker, { onPick: handleAddField })
19987
+ direction: slot.direction,
19988
+ state: activeNavDropIndex === i ? "dragActive" : "dragIdle",
19989
+ className: "!h-full !w-full"
21633
19990
  }
21634
- );
21635
- })() : null,
21636
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer2(selectedElRef.current) && /* @__PURE__ */ jsx33(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
21637
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isFooterLinksContainer(selectedElRef.current) && /* @__PURE__ */ jsx33(
21638
- FooterContainerChrome,
21639
- {
21640
- rect: toolbarRect,
21641
- onAdd: handleAddFooterColumn,
21642
- addDisabled: !canAddFooterColumn()
21643
- }
21644
- ),
21645
- toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame" || toolbarVariant === "logo") && /* @__PURE__ */ jsx33(
21646
- ItemInteractionLayer,
21647
- {
21648
- rect: isItemDragging && draggedItemRect && (footerDragRef.current?.wasSelected || navDragRef.current?.wasSelected) ? draggedItemRect : toolbarRect,
21649
- toolbarBelowRect: selectedElRef.current && isNavigationItem2(selectedElRef.current) ? getOpenNavDropdownPanelRect(selectedElRef.current) : null,
21650
- elRef: glowElRef,
21651
- state: isItemDragging ? "dragging" : selectedElRef.current && isNavigationItem2(selectedElRef.current) && getOpenNavDropdownPanelRect(selectedElRef.current) ? "active-bottom" : resolveItemInteractionState(toolbarRect, parentScrollRef.current),
21652
- showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey) || toolbarVariant === "select-frame" && isFooterFrameSelection,
21653
- dragDisabled: reorderDragDisabled,
21654
- dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
21655
- onDragHandleDragStart: handleItemDragStart,
21656
- onDragHandleDragEnd: handleItemDragEnd,
21657
- onItemPointerDown: toolbarVariant === "logo" ? void 0 : handleItemChromePointerDown,
21658
- onItemClick: toolbarVariant === "logo" ? void 0 : handleItemChromeClick,
21659
- itemDragSurface: toolbarVariant !== "logo" && !isFooterFrameSelection && !selectedIsSocialsRow,
21660
- toolbar: toolbarVariant === "link-action" && !isItemDragging || toolbarVariant === "select-frame" && (isFooterFrameSelection || selectedIsSocialsRow) && !isItemDragging ? /* @__PURE__ */ jsx33(
21661
- ItemActionToolbar,
21662
- {
21663
- onEditLink: openLinkPopoverForSelected,
21664
- onStyle: () => {
21665
- const row = selectedElRef.current;
21666
- if (!row) return;
21667
- if (floatingPanel?.kind === "socials-display") closeFloatingPanelOnly();
21668
- else openSocialsDisplayPanel(row);
21669
- },
21670
- showStyle: selectedIsSocialsRow,
21671
- styleActive: floatingPanel?.kind === "socials-display",
21672
- onAddItem: handleAddChildItem,
21673
- onSelectParent: handleSelectParent,
21674
- onDuplicate: handleDuplicateSelected,
21675
- onDelete: handleDeleteSelected,
21676
- addItemDisabled: isFooterFrameSelection && isFooterAddItemDisabled(selectedElRef.current) || selectedElRef.current !== null && (() => {
21677
- const row = isSocialsRow(selectedElRef.current) ? selectedElRef.current : findSocialsRow(selectedElRef.current);
21678
- return row ? !canAddSocialItem(row) : false;
21679
- })(),
21680
- editLinkDisabled: false,
21681
- moreDisabled: false,
21682
- deleteDisabled: selectedElRef.current !== null && (() => {
21683
- const social = getSocialItem(selectedElRef.current);
21684
- return social ? !canRemoveSocialItem(social) : false;
21685
- })(),
21686
- duplicateDisabled: isFooterFrameSelection || selectedIsSocialsRow || selectedElRef.current !== null && (() => {
21687
- const social = getSocialItem(selectedElRef.current);
21688
- const row = social ? findSocialsRow(social) : null;
21689
- return row ? !canAddSocialItem(row) : false;
21690
- })(),
21691
- showEditLink: !isFooterFrameSelection && !selectedIsSocialsRow && navDropdownPreviewOpen === null,
21692
- showAddItem: isFooterFrameSelection || selectedIsSocialsRow || !selectedIsCta && Boolean(
21693
- selectedElRef.current && isNavbarHrefKey(selectedElRef.current.getAttribute("data-ohw-href-key")) && !isNestedNavChild(selectedElRef.current)
19991
+ )
19992
+ },
19993
+ `nav-drop-${slot.direction}-${slot.parentId ?? "root"}-${slot.insertIndex}-${i}`
19994
+ )),
19995
+ hoveredNavContainerRect && (toolbarVariant !== "select-frame" || isFooterFrameSelection) && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
19996
+ hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
19997
+ hoveredTextRect && !hoveredNavContainerRect && !hoveredItemRect && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredTextRect, state: "hover" }),
19998
+ formPickRect && !isItemDragging && /* @__PURE__ */ jsx33(
19999
+ ItemInteractionLayer,
20000
+ {
20001
+ rect: formPickRect,
20002
+ state: "active-top",
20003
+ itemDragSurface: false,
20004
+ toolbarAlign: "left",
20005
+ chromeGap: 24,
20006
+ toolbar: fieldPickRect ? void 0 : /* @__PURE__ */ jsxs20(
20007
+ "div",
20008
+ {
20009
+ "data-ohw-form-toolbar": "",
20010
+ className: "pointer-events-auto flex items-center gap-0.5 whitespace-nowrap rounded-lg border border-border bg-background p-1 shadow-md",
20011
+ children: [
20012
+ /* @__PURE__ */ jsx33(
20013
+ "button",
20014
+ {
20015
+ type: "button",
20016
+ "aria-label": "Add field",
20017
+ className: "flex h-7 w-7 items-center justify-center rounded-md text-foreground transition-colors hover:bg-muted/80",
20018
+ onClick: () => setFieldTypePickerOpen((open) => !open),
20019
+ "data-ohw-add-field": "",
20020
+ children: /* @__PURE__ */ jsx33(Plus4, { size: 15, "aria-hidden": true })
20021
+ }
21694
20022
  ),
21695
- showMore: (!selectedIsCta || isFooterFrameSelection) && !selectedIsSocialsRow,
21696
- dropdownOpen: navDropdownPreviewOpen,
21697
- onDropdownOpenChange: handleNavDropdownOpenChange,
21698
- headingVisible: footerHeadingVisible,
21699
- onHeadingVisibleChange: handleFooterHeadingVisibleChange
21700
- }
21701
- ) : void 0
21702
- }
21703
- ),
21704
- toolbarRect && toolbarVariant === "rich-text" && !linkPopover && /* @__PURE__ */ jsxs20(Fragment8, { children: [
21705
- /* @__PURE__ */ jsx33(
21706
- EditGlowChrome,
20023
+ /* @__PURE__ */ jsx33("div", { className: "mx-0.5 h-5 w-px bg-border" }),
20024
+ /* @__PURE__ */ jsxs20(
20025
+ "button",
20026
+ {
20027
+ type: "button",
20028
+ 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",
20029
+ onClick: () => {
20030
+ setFieldTypePickerOpen(false);
20031
+ const form = formPickElRef.current;
20032
+ if (!form) return;
20033
+ postToParent2({
20034
+ type: "ow:form-pick",
20035
+ formKey: formKeyOf(form),
20036
+ hasLongText: formHasLongText(form)
20037
+ });
20038
+ },
20039
+ children: [
20040
+ /* @__PURE__ */ jsx33(Settings, { size: 14, "aria-hidden": true }),
20041
+ "Form settings",
20042
+ formPickCount ? (
20043
+ // Counter pill, per the design — not a text suffix.
20044
+ /* @__PURE__ */ jsx33(
20045
+ "span",
20046
+ {
20047
+ "data-ohw-form-count": "",
20048
+ 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",
20049
+ children: formPickCount
20050
+ }
20051
+ )
20052
+ ) : null
20053
+ ]
20054
+ }
20055
+ ),
20056
+ /* @__PURE__ */ jsx33("div", { className: "mx-0.5 h-5 w-px bg-border" }),
20057
+ /* @__PURE__ */ jsx33("div", { className: "flex items-center rounded-md bg-muted/70 p-0.5", children: ["default", "success"].map((state) => /* @__PURE__ */ jsx33(
20058
+ "button",
20059
+ {
20060
+ type: "button",
20061
+ "aria-pressed": formViewState === state,
20062
+ 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"),
20063
+ onClick: () => {
20064
+ setFieldTypePickerOpen(false);
20065
+ const form = formPickElRef.current;
20066
+ const key = form ? formKeyOf(form) : null;
20067
+ if (!form || !key) return;
20068
+ const initial = successInitialFor(form, key, editContentRef.current);
20069
+ setFormViewState(form, key, state, initial);
20070
+ setFormViewStateUi(state);
20071
+ setFormPickRect(form.getBoundingClientRect());
20072
+ if (state === "success") {
20073
+ const successEl = form.querySelector(`[${SUCCESS_TEXT_ATTR}]`);
20074
+ if (successEl) requestAnimationFrame(() => activateRef.current(successEl));
20075
+ } else {
20076
+ deactivateRef.current();
20077
+ }
20078
+ },
20079
+ children: state
20080
+ },
20081
+ state
20082
+ )) })
20083
+ ]
20084
+ }
20085
+ )
20086
+ }
20087
+ ),
20088
+ formHoverRect && !isItemDragging && /* @__PURE__ */ jsx33(
20089
+ ItemInteractionLayer,
20090
+ {
20091
+ rect: formHoverRect,
20092
+ state: "hover",
20093
+ chromeGap: formHoverElRef.current && getFieldWrapper(formHoverElRef.current) ? 8 : 24
20094
+ }
20095
+ ),
20096
+ fieldPickRect && fieldPickState && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ jsx33(
20097
+ ItemInteractionLayer,
20098
+ {
20099
+ rect: fieldPickRect,
20100
+ state: fieldDragging ? "dragging" : "active-top",
20101
+ itemDragSurface: false,
20102
+ toolbarAlign: "left",
20103
+ chromeGap: 10,
20104
+ showHandle: true,
20105
+ dragHandleLabel: "Reorder field",
20106
+ onDragHandleDragStart: handleFieldDragStart,
20107
+ onDragHandleDragEnd: handleFieldDragEnd,
20108
+ toolbar: /* @__PURE__ */ jsx33(
20109
+ FormFieldToolbar,
21707
20110
  {
21708
- rect: toolbarRect,
21709
- elRef: glowElRef,
21710
- reorderHrefKey,
21711
- dragDisabled: reorderDragDisabled,
21712
- hideHandle: isItemDragging
20111
+ type: fieldPickState.type,
20112
+ required: fieldPickState.required,
20113
+ onTypeChange: handleFieldTypeChange,
20114
+ onRequiredToggle: handleFieldRequiredToggle,
20115
+ onDuplicate: handleFieldDuplicate,
20116
+ onDelete: handleFieldDelete
21713
20117
  }
21714
- ),
21715
- /* @__PURE__ */ jsx33(
21716
- FloatingToolbar,
20118
+ )
20119
+ }
20120
+ ),
20121
+ fieldDragging ? fieldDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
20122
+ "div",
20123
+ {
20124
+ className: "pointer-events-none fixed z-[2147483644]",
20125
+ style: { top: slot.top, left: slot.left, width: slot.width },
20126
+ children: /* @__PURE__ */ jsx33(
20127
+ DropIndicator,
21717
20128
  {
21718
- rect: toolbarRect,
21719
- parentScroll: parentScrollRef.current,
21720
- elRef: toolbarElRef,
21721
- onCommand: handleCommand,
21722
- activeCommands,
21723
- showEditLink,
21724
- onEditLink: openLinkPopoverForActive
20129
+ direction: "horizontal",
20130
+ state: fieldDropIndex === i ? "dragActive" : "dragIdle",
20131
+ className: "!w-full"
21725
20132
  }
21726
20133
  )
21727
- ] }),
21728
- maxBadge && /* @__PURE__ */ jsxs20(
20134
+ },
20135
+ `field-drop-${i}`
20136
+ )) : null,
20137
+ fieldTypePickerOpen && formPickRect ? (() => {
20138
+ const toolbar = document.querySelector("[data-ohw-form-toolbar]")?.getBoundingClientRect();
20139
+ return /* @__PURE__ */ jsx33(
21729
20140
  "div",
21730
20141
  {
21731
- "data-ohw-max-badge": "",
20142
+ className: "pointer-events-none fixed z-[2147483645]",
21732
20143
  style: {
21733
- position: "fixed",
21734
- top: maxBadge.rect.bottom + 4,
21735
- left: maxBadge.rect.right,
21736
- transform: "translateX(-100%)",
21737
- zIndex: 2147483647,
21738
- background: maxBadge.current > maxBadge.max ? "#FEF2F2" : "#F5F5F4",
21739
- color: maxBadge.current > maxBadge.max ? "#DC2626" : "#78716C",
21740
- border: `1px solid ${maxBadge.current > maxBadge.max ? "#FECACA" : "#E7E5E4"}`,
21741
- borderRadius: 4,
21742
- padding: "2px 6px",
21743
- fontSize: 11,
21744
- fontWeight: 500,
21745
- pointerEvents: "none"
20144
+ top: toolbar ? toolbar.bottom + 6 : formPickRect.top + 16,
20145
+ left: toolbar ? toolbar.left : formPickRect.left + 24
21746
20146
  },
21747
- children: [
21748
- maxBadge.current,
21749
- "/",
21750
- maxBadge.max
21751
- ]
20147
+ children: /* @__PURE__ */ jsx33(FieldTypePicker, { onPick: handleAddField })
21752
20148
  }
21753
- ),
21754
- toggleState && !linkPopover && /* @__PURE__ */ jsx33(
21755
- StateToggle,
20149
+ );
20150
+ })() : null,
20151
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer2(selectedElRef.current) && /* @__PURE__ */ jsx33(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
20152
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isFooterLinksContainer(selectedElRef.current) && /* @__PURE__ */ jsx33(
20153
+ FooterContainerChrome,
20154
+ {
20155
+ rect: toolbarRect,
20156
+ onAdd: handleAddFooterColumn,
20157
+ addDisabled: !canAddFooterColumn()
20158
+ }
20159
+ ),
20160
+ toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame" || toolbarVariant === "logo") && /* @__PURE__ */ jsx33(
20161
+ ItemInteractionLayer,
20162
+ {
20163
+ rect: isItemDragging && draggedItemRect && (footerDragRef.current?.wasSelected || navDragRef.current?.wasSelected) ? draggedItemRect : toolbarRect,
20164
+ toolbarBelowRect: selectedElRef.current && isNavigationItem2(selectedElRef.current) ? getOpenNavDropdownPanelRect(selectedElRef.current) : null,
20165
+ elRef: glowElRef,
20166
+ state: isItemDragging ? "dragging" : selectedElRef.current && isNavigationItem2(selectedElRef.current) && getOpenNavDropdownPanelRect(selectedElRef.current) ? "active-bottom" : resolveItemInteractionState(toolbarRect, parentScrollRef.current),
20167
+ showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey) || toolbarVariant === "select-frame" && isFooterFrameSelection,
20168
+ dragDisabled: reorderDragDisabled,
20169
+ dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
20170
+ onDragHandleDragStart: handleItemDragStart,
20171
+ onDragHandleDragEnd: handleItemDragEnd,
20172
+ onItemPointerDown: toolbarVariant === "logo" ? void 0 : handleItemChromePointerDown,
20173
+ onItemClick: toolbarVariant === "logo" ? void 0 : handleItemChromeClick,
20174
+ itemDragSurface: toolbarVariant !== "logo" && !isFooterFrameSelection && !selectedIsSocialsRow,
20175
+ toolbar: toolbarVariant === "link-action" && !isItemDragging || toolbarVariant === "select-frame" && (isFooterFrameSelection || selectedIsSocialsRow) && !isItemDragging ? /* @__PURE__ */ jsx33(
20176
+ ItemActionToolbar,
20177
+ {
20178
+ onEditLink: openLinkPopoverForSelected,
20179
+ onStyle: () => {
20180
+ const row = selectedElRef.current;
20181
+ if (!row) return;
20182
+ if (floatingPanel?.kind === "socials-display") closeFloatingPanelOnly();
20183
+ else openSocialsDisplayPanel(row);
20184
+ },
20185
+ showStyle: selectedIsSocialsRow,
20186
+ styleActive: floatingPanel?.kind === "socials-display",
20187
+ onAddItem: handleAddChildItem,
20188
+ onSelectParent: handleSelectParent,
20189
+ onDuplicate: handleDuplicateSelected,
20190
+ onDelete: handleDeleteSelected,
20191
+ addItemDisabled: isFooterFrameSelection && isFooterAddItemDisabled(selectedElRef.current) || selectedElRef.current !== null && (() => {
20192
+ const row = isSocialsRow(selectedElRef.current) ? selectedElRef.current : findSocialsRow(selectedElRef.current);
20193
+ return row ? !canAddSocialItem(row) : false;
20194
+ })(),
20195
+ editLinkDisabled: false,
20196
+ moreDisabled: false,
20197
+ deleteDisabled: selectedElRef.current !== null && (() => {
20198
+ const social = getSocialItem(selectedElRef.current);
20199
+ return social ? !canRemoveSocialItem(social) : false;
20200
+ })(),
20201
+ duplicateDisabled: isFooterFrameSelection || selectedIsSocialsRow || selectedElRef.current !== null && (() => {
20202
+ const social = getSocialItem(selectedElRef.current);
20203
+ const row = social ? findSocialsRow(social) : null;
20204
+ return row ? !canAddSocialItem(row) : false;
20205
+ })(),
20206
+ showEditLink: !isFooterFrameSelection && !selectedIsSocialsRow && navDropdownPreviewOpen === null,
20207
+ showAddItem: isFooterFrameSelection || selectedIsSocialsRow || !selectedIsCta && Boolean(
20208
+ selectedElRef.current && isNavbarHrefKey(selectedElRef.current.getAttribute("data-ohw-href-key")) && !isNestedNavChild(selectedElRef.current)
20209
+ ),
20210
+ showMore: (!selectedIsCta || isFooterFrameSelection) && !selectedIsSocialsRow,
20211
+ dropdownOpen: navDropdownPreviewOpen,
20212
+ onDropdownOpenChange: handleNavDropdownOpenChange,
20213
+ headingVisible: footerHeadingVisible,
20214
+ onHeadingVisibleChange: handleFooterHeadingVisibleChange
20215
+ }
20216
+ ) : void 0
20217
+ }
20218
+ ),
20219
+ toolbarRect && toolbarVariant === "rich-text" && !linkPopover && /* @__PURE__ */ jsxs20(Fragment8, { children: [
20220
+ /* @__PURE__ */ jsx33(
20221
+ EditGlowChrome,
21756
20222
  {
21757
- rect: toggleState.rect,
21758
- activeState: toggleState.activeState,
21759
- states: toggleState.states,
21760
- onStateChange: handleStateChange
20223
+ rect: toolbarRect,
20224
+ elRef: glowElRef,
20225
+ reorderHrefKey,
20226
+ dragDisabled: reorderDragDisabled,
20227
+ hideHandle: isItemDragging
21761
20228
  }
21762
20229
  ),
21763
- sectionGap && !linkPopover && /* @__PURE__ */ jsxs20(
21764
- "div",
20230
+ /* @__PURE__ */ jsx33(
20231
+ FloatingToolbar,
21765
20232
  {
21766
- "data-ohw-section-insert-line": "",
21767
- className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
21768
- style: { top: sectionGap.y, transform: "translateY(-50%)" },
21769
- children: [
21770
- /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
21771
- /* @__PURE__ */ jsx33(
21772
- Badge,
21773
- {
21774
- 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",
21775
- onClick: () => {
21776
- window.parent.postMessage(
21777
- {
21778
- type: "ow:add-section",
21779
- insertAfter: sectionGap.insertAfter,
21780
- insertBefore: sectionGap.insertBefore
21781
- },
21782
- "*"
21783
- );
21784
- },
21785
- children: "Add Section"
21786
- }
21787
- ),
21788
- /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-primary", style: { height: 3 } })
21789
- ]
20233
+ rect: toolbarRect,
20234
+ parentScroll: parentScrollRef.current,
20235
+ elRef: toolbarElRef,
20236
+ onCommand: handleCommand,
20237
+ activeCommands,
20238
+ showEditLink,
20239
+ onEditLink: openLinkPopoverForActive
21790
20240
  }
21791
- ),
21792
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx33(
21793
- LinkPopover,
21794
- {
21795
- panelRef: linkPopoverPanelRef,
21796
- portalContainer: dialogPortalContainer,
21797
- open: true,
21798
- mode: linkPopover.mode ?? "edit",
21799
- pages: sitePages,
21800
- sections: currentSections,
21801
- sectionsByPath,
21802
- initialTarget: linkPopover.target,
21803
- existingTargets: linkPopover.intent === "add-nav" ? getNavbarExistingTargets() : [],
21804
- onClose: closeLinkPopover,
21805
- onSubmit: handleLinkPopoverSubmit
20241
+ )
20242
+ ] }),
20243
+ maxBadge && /* @__PURE__ */ jsxs20(
20244
+ "div",
20245
+ {
20246
+ "data-ohw-max-badge": "",
20247
+ style: {
20248
+ position: "fixed",
20249
+ top: maxBadge.rect.bottom + 4,
20250
+ left: maxBadge.rect.right,
20251
+ transform: "translateX(-100%)",
20252
+ zIndex: 2147483647,
20253
+ background: maxBadge.current > maxBadge.max ? "#FEF2F2" : "#F5F5F4",
20254
+ color: maxBadge.current > maxBadge.max ? "#DC2626" : "#78716C",
20255
+ border: `1px solid ${maxBadge.current > maxBadge.max ? "#FECACA" : "#E7E5E4"}`,
20256
+ borderRadius: 4,
20257
+ padding: "2px 6px",
20258
+ fontSize: 11,
20259
+ fontWeight: 500,
20260
+ pointerEvents: "none"
21806
20261
  },
21807
- linkPopover.key
21808
- ) : null,
21809
- floatingPanel && floatingPanel.kind === "socials-display" ? /* @__PURE__ */ jsx33(
21810
- FloatingPanel,
21811
- {
21812
- open: true,
21813
- title: floatingPanel.title,
21814
- context: floatingPanel.context,
21815
- position: floatingPanelPos,
21816
- onPositionChange: setFloatingPanelPos,
21817
- parentScroll: parentScrollSnap ?? parentScrollRef.current,
21818
- onClose: closeFloatingPanelOnly,
21819
- children: /* @__PURE__ */ jsx33(
21820
- SocialsDisplayPanel,
20262
+ children: [
20263
+ maxBadge.current,
20264
+ "/",
20265
+ maxBadge.max
20266
+ ]
20267
+ }
20268
+ ),
20269
+ toggleState && !linkPopover && /* @__PURE__ */ jsx33(
20270
+ StateToggle,
20271
+ {
20272
+ rect: toggleState.rect,
20273
+ activeState: toggleState.activeState,
20274
+ states: toggleState.states,
20275
+ onStateChange: handleStateChange
20276
+ }
20277
+ ),
20278
+ sectionGap && !linkPopover && /* @__PURE__ */ jsxs20(
20279
+ "div",
20280
+ {
20281
+ "data-ohw-section-insert-line": "",
20282
+ className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
20283
+ style: { top: sectionGap.y, transform: "translateY(-50%)" },
20284
+ children: [
20285
+ /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
20286
+ /* @__PURE__ */ jsx33(
20287
+ Badge,
21821
20288
  {
21822
- display: socialsDisplayFor(floatingPanel.row, editContentRef.current),
21823
- onChange: (next) => {
21824
- changeSocialsDisplay(floatingPanel.row, next);
21825
- setFloatingPanel({ ...floatingPanel });
21826
- }
20289
+ 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",
20290
+ onClick: () => {
20291
+ window.parent.postMessage(
20292
+ {
20293
+ type: "ow:add-section",
20294
+ insertAfter: sectionGap.insertAfter,
20295
+ insertBefore: sectionGap.insertBefore
20296
+ },
20297
+ "*"
20298
+ );
20299
+ },
20300
+ children: "Add Section"
21827
20301
  }
21828
- )
21829
- }
21830
- ) : null
21831
- ] }),
21832
- bridgeRoot
21833
- ) : null
21834
- ] });
21835
- }
21836
-
21837
- // src/ui/EmptySection.tsx
21838
- import Link3 from "next/link";
21839
- import { Fragment as Fragment9, jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
21840
- function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey }) {
21841
- return /* @__PURE__ */ jsxs21(Fragment9, { children: [
21842
- /* @__PURE__ */ jsx34(
21843
- "p",
21844
- {
21845
- style: {
21846
- fontFamily: "var(--brand-font-body)",
21847
- fontSize: "0.75rem",
21848
- fontWeight: 500,
21849
- letterSpacing: "0.15em",
21850
- textTransform: "uppercase",
21851
- color: "var(--brand-accent)",
21852
- marginBottom: "1.5rem"
21853
- },
21854
- children: /* @__PURE__ */ jsx34(Link3, { href: homeHref, style: { color: "inherit" }, children: /* @__PURE__ */ jsx34("span", { ...eyebrowKey ? { "data-ohw-editable": "text", "data-ohw-key": eyebrowKey } : {}, children: "Home" }) })
21855
- }
21856
- ),
21857
- /* @__PURE__ */ jsx34(
21858
- "h1",
21859
- {
21860
- style: {
21861
- fontFamily: "var(--brand-font-heading)",
21862
- fontSize: "clamp(2rem, 3.5vw, 2.75rem)",
21863
- lineHeight: 1.1,
21864
- letterSpacing: "-0.025em",
21865
- color: "var(--brand-text)",
21866
- marginBottom: "1rem"
21867
- },
21868
- ...titleKey ? { "data-ohw-editable": "text", "data-ohw-key": titleKey, "data-ohw-max-length": 80 } : {},
21869
- children: title
21870
- }
21871
- ),
21872
- /* @__PURE__ */ jsx34(
21873
- "p",
21874
- {
21875
- style: {
21876
- fontFamily: "var(--brand-font-body)",
21877
- fontSize: "1rem",
21878
- lineHeight: 1.7,
21879
- fontWeight: 300,
21880
- color: "var(--brand-text-muted)",
21881
- maxWidth: "340px"
20302
+ ),
20303
+ /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-primary", style: { height: 3 } })
20304
+ ]
20305
+ }
20306
+ ),
20307
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx33(
20308
+ LinkPopover,
20309
+ {
20310
+ panelRef: linkPopoverPanelRef,
20311
+ portalContainer: dialogPortalContainer,
20312
+ open: true,
20313
+ mode: linkPopover.mode ?? "edit",
20314
+ pages: sitePages,
20315
+ sections: currentSections,
20316
+ sectionsByPath,
20317
+ initialTarget: linkPopover.target,
20318
+ existingTargets: linkPopover.intent === "add-nav" ? getNavbarExistingTargets() : [],
20319
+ onClose: closeLinkPopover,
20320
+ onSubmit: handleLinkPopoverSubmit
21882
20321
  },
21883
- ...subtitleKey ? { "data-ohw-editable": "text", "data-ohw-key": subtitleKey, "data-ohw-max-length": 160 } : {},
21884
- children: "This page doesn't have any content yet."
21885
- }
21886
- )
21887
- ] });
20322
+ linkPopover.key
20323
+ ) : null,
20324
+ floatingPanel && floatingPanel.kind === "socials-display" ? /* @__PURE__ */ jsx33(
20325
+ FloatingPanel,
20326
+ {
20327
+ open: true,
20328
+ title: floatingPanel.title,
20329
+ context: floatingPanel.context,
20330
+ position: floatingPanelPos,
20331
+ onPositionChange: setFloatingPanelPos,
20332
+ parentScroll: parentScrollSnap ?? parentScrollRef.current,
20333
+ onClose: closeFloatingPanelOnly,
20334
+ children: /* @__PURE__ */ jsx33(
20335
+ SocialsDisplayPanel,
20336
+ {
20337
+ display: socialsDisplayFor(floatingPanel.row, editContentRef.current),
20338
+ onChange: (next) => {
20339
+ changeSocialsDisplay(floatingPanel.row, next);
20340
+ setFloatingPanel({ ...floatingPanel });
20341
+ }
20342
+ }
20343
+ )
20344
+ }
20345
+ ) : null
20346
+ ] }),
20347
+ bridgeRoot
20348
+ ) : null;
21888
20349
  }
21889
20350
  export {
21890
20351
  AI_DEFAULT_BRAND,
@@ -21902,7 +20363,6 @@ export {
21902
20363
  DropdownMenuItem,
21903
20364
  DropdownMenuSeparator,
21904
20365
  DropdownMenuTrigger,
21905
- EmptySection,
21906
20366
  ItemActionToolbar,
21907
20367
  ItemInteractionLayer,
21908
20368
  LinkEditorPanel,