@ohhwells/bridge 0.1.67-next.196 → 0.1.67

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 React11, { useCallback as useCallback8, useEffect as useEffect13, useLayoutEffect as useLayoutEffect4, useRef as useRef10, useState as useState12 } from "react";
4
+ import React11, { useCallback as useCallback7, useEffect as useEffect12, useLayoutEffect as useLayoutEffect4, useRef as useRef9, useState as useState11 } 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
  }
@@ -507,8 +159,6 @@ var AI_RESPONSIVE_CSS = [
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
161
  " [data-ai-responsive] [data-ai-section-inner] { padding-left: 20px !important; padding-right: 20px !important; }",
510
- " [data-ai-responsive] { overflow-x: hidden; }",
511
- " [data-ai-responsive] img { max-width: 100%; }",
512
162
  "}"
513
163
  ].join("\n");
514
164
  var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
@@ -586,7 +236,7 @@ function ButtonEl({
586
236
  }) {
587
237
  const secondary = slots.variant === "secondary";
588
238
  const href = str(slots.href);
589
- 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`) } : {};
590
240
  return /* @__PURE__ */ jsx(
591
241
  "a",
592
242
  {
@@ -602,7 +252,7 @@ function ButtonEl({
602
252
  textDecoration: "none",
603
253
  cursor: "pointer",
604
254
  ...typeStyle(AI_TREE_TOKENS.type.button, ctx.brand.fonts.body),
605
- ...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 }
606
256
  },
607
257
  children: /* @__PURE__ */ jsx("span", { ...path ? textAttrs(ctx, `${path}.label`) : {}, children: str(slots.label) })
608
258
  }
@@ -1504,14 +1154,11 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1504
1154
  return null;
1505
1155
  }
1506
1156
  const resolvedBrand = brand ?? AI_DEFAULT_BRAND;
1507
- const band = (tree.settings ?? {}).sectionBackground === "accent" ? accentBandContext(resolvedBrand) : null;
1508
- const blockBrand = band?.brand ?? resolvedBrand;
1509
1157
  const ctx = {
1510
- brand: blockBrand,
1158
+ brand: resolvedBrand,
1511
1159
  resolveMedia: resolveMedia ?? (() => null),
1512
- cardSurface: blockBrand.palette.light.toUpperCase() === AI_DEFAULT_BRAND.palette.light.toUpperCase() ? "#ECECEB" : `color-mix(in srgb, ${blockBrand.palette.light} 90%, ${blockBrand.palette.dark})`,
1513
- keyFor: editKeyPrefix ? (path) => `${editKeyPrefix}.${path}` : null,
1514
- ...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
1515
1162
  };
1516
1163
  const settings = tree.settings ?? {};
1517
1164
  const pad = AI_TREE_TOKENS.sectionPadding[settings.spacing ?? "balanced"];
@@ -1519,20 +1166,6 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1519
1166
  const overlayAlpha = { low: 0.25, medium: 0.45, high: 0.65 }[settings.overlayIntensity ?? "medium"];
1520
1167
  const backgroundUrl = isOverlay && settings.backgroundMedia ? ctx.resolveMedia(settings.backgroundMedia) : null;
1521
1168
  const bgAttrs = ctx.keyFor && isOverlay && settings.backgroundMedia ? { "data-ohw-key": ctx.keyFor("settings.backgroundMedia"), "data-ohw-editable": "bg-image" } : {};
1522
- const toneBackground = (() => {
1523
- const { dark, primary, light } = resolvedBrand.palette;
1524
- switch (settings.sectionBackground) {
1525
- case "surface":
1526
- return `color-mix(in srgb, ${light} 94%, ${dark})`;
1527
- case "accent":
1528
- return primary;
1529
- case "accent-soft":
1530
- return `color-mix(in srgb, ${primary} 12%, ${light})`;
1531
- default:
1532
- return void 0;
1533
- }
1534
- })();
1535
- const distributed = !isOverlay && settings.textDistribution;
1536
1169
  return /* @__PURE__ */ jsxs(
1537
1170
  "section",
1538
1171
  {
@@ -1542,11 +1175,10 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1542
1175
  style: {
1543
1176
  position: "relative",
1544
1177
  padding: `${pad}px 0`,
1545
- background: toneBackground ?? (isOverlay && !backgroundUrl ? "#DBEAFE" : void 0),
1178
+ background: isOverlay && !backgroundUrl ? "#DBEAFE" : void 0,
1546
1179
  backgroundImage: backgroundUrl ? `url(${backgroundUrl})` : void 0,
1547
1180
  backgroundSize: "cover",
1548
- backgroundPosition: "center",
1549
- color: settings.sectionBackground === "accent" ? blockBrand.palette.dark : void 0
1181
+ backgroundPosition: "center"
1550
1182
  },
1551
1183
  children: [
1552
1184
  /* @__PURE__ */ jsx("style", { children: AI_RESPONSIVE_CSS }),
@@ -1570,24 +1202,10 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1570
1202
  display: "grid",
1571
1203
  gridTemplateColumns: "repeat(12, 1fr)",
1572
1204
  gap: AI_TREE_TOKENS.spacing6,
1573
- alignItems: distributed === "space-between" ? "stretch" : (distributed ?? settings.verticalPosition) === "top" ? "start" : "center",
1205
+ alignItems: settings.verticalPosition === "top" ? "start" : "center",
1574
1206
  marginTop: r2 > 0 ? AI_TREE_TOKENS.spacing8 : 0
1575
1207
  },
1576
- children: (row.blocks ?? []).map((block, b) => /* @__PURE__ */ jsx(
1577
- "div",
1578
- {
1579
- "data-ai-cell": "",
1580
- style: {
1581
- gridColumn: `span ${Math.min(12, Math.max(1, block.span))}`,
1582
- minWidth: 0,
1583
- // space-between: each column becomes a flex column whose content spreads over
1584
- // the full row height instead of clumping at the top.
1585
- ...distributed === "space-between" ? { display: "flex", flexDirection: "column", justifyContent: "space-between" } : {}
1586
- },
1587
- children: renderNode(block, ctx, `r${r2}.b${b}`)
1588
- },
1589
- b
1590
- ))
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))
1591
1209
  },
1592
1210
  r2
1593
1211
  ))
@@ -1603,36 +1221,17 @@ import { jsx as jsx2 } from "react/jsx-runtime";
1603
1221
  var CONTAINER_ATTR = "data-ohw-ai-generated";
1604
1222
  var REPLACED_ATTR = "data-ohw-ai-replaced-by";
1605
1223
  var REMOVED_ATTR = "data-ohw-ai-removed";
1606
- var TEMPLATE_HIDDEN_ATTR = "data-ohw-ai-template-hidden";
1607
- var CHROME_SECTION_IDS = /* @__PURE__ */ new Set(["navbar", "footer"]);
1608
- function readRootVar(name) {
1609
- if (typeof document === "undefined") return "";
1610
- return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
1611
- }
1612
- function deriveBrandOverride() {
1613
- const dark = readRootVar("--ohw-brand-dark");
1614
- const primary = readRootVar("--ohw-brand-primary");
1615
- const light = readRootVar("--ohw-brand-light");
1616
- if (!dark || !primary || !light) return null;
1617
- const accent = readRootVar("--ohw-brand-accent");
1618
- const heading = readRootVar("--font-heading") || readRootVar("--font-display");
1619
- const body = readRootVar("--font-body");
1620
- return {
1621
- palette: { dark, primary, accent: accent || dark, light },
1622
- fonts: {
1623
- heading: heading || AI_DEFAULT_BRAND.fonts.heading,
1624
- body: body || AI_DEFAULT_BRAND.fonts.body
1625
- }
1626
- };
1627
- }
1628
1224
  function deriveTemplateBrand() {
1629
- const dark = readRootVar("--color-dark");
1630
- const primary = readRootVar("--color-primary");
1631
- 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");
1632
1231
  if (!dark || !primary || !light) return null;
1633
- const accent = readRootVar("--color-accent");
1634
- const heading = readRootVar("--font-heading") || readRootVar("--font-display");
1635
- 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");
1636
1235
  return {
1637
1236
  palette: { dark, primary, accent: accent || dark, light },
1638
1237
  fonts: {
@@ -1704,24 +1303,6 @@ function syncRemovedSections(state) {
1704
1303
  }
1705
1304
  }
1706
1305
  }
1707
- function syncTemplateHidden(state, pageHasSections) {
1708
- const hide = state.hideTemplate === true && pageHasSections;
1709
- for (const el of Array.from(document.querySelectorAll(`[${TEMPLATE_HIDDEN_ATTR}]`))) {
1710
- if (!hide) {
1711
- el.style.removeProperty("display");
1712
- el.removeAttribute(TEMPLATE_HIDDEN_ATTR);
1713
- }
1714
- }
1715
- if (!hide) return;
1716
- for (const el of Array.from(document.querySelectorAll("[data-ohw-section]"))) {
1717
- if (el.hasAttribute(CONTAINER_ATTR)) continue;
1718
- if (CHROME_SECTION_IDS.has(el.getAttribute("data-ohw-section") ?? "")) continue;
1719
- if (el.parentElement?.closest("[data-ohw-section]")) continue;
1720
- if (el.hasAttribute(REPLACED_ATTR) || el.hasAttribute(REMOVED_ATTR)) continue;
1721
- el.style.display = "none";
1722
- el.setAttribute(TEMPLATE_HIDDEN_ATTR, "");
1723
- }
1724
- }
1725
1306
  function syncReplacedOriginals(state) {
1726
1307
  for (const el of document.querySelectorAll(`[${REPLACED_ATTR}]`)) {
1727
1308
  const byId = el.getAttribute(REPLACED_ATTR) ?? "";
@@ -1742,12 +1323,8 @@ function syncReplacedOriginals(state) {
1742
1323
  }
1743
1324
  function applyAiSectionsToDom(state, options) {
1744
1325
  if (typeof document === "undefined") return;
1745
- const brandOverride = deriveBrandOverride();
1746
1326
  const templateBrand = deriveTemplateBrand();
1747
- const brandKey = brandOverride ? JSON.stringify(brandOverride) : "";
1748
- const pagePath = window.location.pathname;
1749
- const pageSections = state.sections.filter((entry) => !entry.path || entry.path === pagePath);
1750
- const activeIds = new Set(pageSections.map((entry) => entry.id));
1327
+ const activeIds = new Set(state.sections.map((entry) => entry.id));
1751
1328
  for (const [id, section] of mounted) {
1752
1329
  if (!activeIds.has(id)) {
1753
1330
  section.root.unmount();
@@ -1755,8 +1332,8 @@ function applyAiSectionsToDom(state, options) {
1755
1332
  mounted.delete(id);
1756
1333
  }
1757
1334
  }
1758
- for (const entry of pageSections) {
1759
- const serialized = JSON.stringify(entry) + brandKey;
1335
+ for (const entry of state.sections) {
1336
+ const serialized = JSON.stringify(entry);
1760
1337
  const existing = mounted.get(entry.id);
1761
1338
  if (existing && existing.serialized === serialized && existing.container.isConnected) {
1762
1339
  continue;
@@ -1770,7 +1347,6 @@ function applyAiSectionsToDom(state, options) {
1770
1347
  mounted.delete(entry.id);
1771
1348
  }
1772
1349
  container.setAttribute("data-ohw-section", entry.id);
1773
- container.setAttribute("data-ohw-instance", entry.id);
1774
1350
  container.setAttribute("data-ohw-section-label", entry.label);
1775
1351
  placeContainer(container, entry);
1776
1352
  const root = mounted.get(entry.id)?.root ?? createRoot(container);
@@ -1781,7 +1357,7 @@ function applyAiSectionsToDom(state, options) {
1781
1357
  AiTreeRenderer,
1782
1358
  {
1783
1359
  tree: entry.tree,
1784
- brand: brandOverride ?? entry.brand ?? options?.brand ?? templateBrand ?? void 0,
1360
+ brand: entry.brand ?? options?.brand ?? templateBrand ?? void 0,
1785
1361
  resolveMedia,
1786
1362
  editKeyPrefix: `ai.${entry.id}`
1787
1363
  }
@@ -1792,7 +1368,6 @@ function applyAiSectionsToDom(state, options) {
1792
1368
  }
1793
1369
  syncReplacedOriginals(state);
1794
1370
  syncRemovedSections(state);
1795
- syncTemplateHidden(state, pageSections.length > 0);
1796
1371
  }
1797
1372
 
1798
1373
  // src/useLinkHrefGuardian.ts
@@ -2399,7 +1974,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
2399
1974
  const autoId = useId();
2400
1975
  const insertAfter = insertAfterProp ?? autoId;
2401
1976
  const [schedule, setSchedule] = useState2(null);
2402
- const [loading, setLoading] = useState2(initialScheduleId !== null);
1977
+ const [loading, setLoading] = useState2(true);
2403
1978
  const [inEditor, setInEditor] = useState2(false);
2404
1979
  const [isHovered, setIsHovered] = useState2(false);
2405
1980
  const [modalState, setModalState] = useState2(null);
@@ -2573,10 +2148,8 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
2573
2148
  "*"
2574
2149
  );
2575
2150
  };
2151
+ if (!inEditor && !loading && !schedule) return null;
2576
2152
  const sectionId = `scheduling-${insertAfter}`;
2577
- if (!inEditor && !loading && !schedule) {
2578
- return /* @__PURE__ */ jsx4("section", { "data-ohw-section": sectionId, "data-ohw-scheduling-anchor": insertAfter, style: { display: "none" } });
2579
- }
2580
2153
  return /* @__PURE__ */ jsxs3(
2581
2154
  "section",
2582
2155
  {
@@ -7107,7 +6680,6 @@ function applyFieldType(wrapper, type) {
7107
6680
  if (inputType) input.type = inputType;
7108
6681
  shedSize(input);
7109
6682
  applyDefaults(input);
7110
- requireIfEmail(wrapper, type);
7111
6683
  return;
7112
6684
  }
7113
6685
  const next = document.createElement(tag);
@@ -7120,11 +6692,6 @@ function applyFieldType(wrapper, type) {
7120
6692
  if (type === "long-text") next.setAttribute("rows", "4");
7121
6693
  input.replaceWith(next);
7122
6694
  applyDefaults(next);
7123
- requireIfEmail(wrapper, type);
7124
- }
7125
- function requireIfEmail(wrapper, type) {
7126
- if (type !== "email") return;
7127
- setFieldRequired(wrapper, true);
7128
6695
  }
7129
6696
  var REQUIRED_MARK_ATTR = "data-ohw-field-required";
7130
6697
  function fieldLabelText(label) {
@@ -7207,7 +6774,6 @@ function insertField(form, type) {
7207
6774
  const label = ensureFieldLabel(wrapper, key);
7208
6775
  label.textContent = defaults.label;
7209
6776
  label.setAttribute("data-ohw-key", `${key}-label`);
7210
- requireIfEmail(wrapper, type);
7211
6777
  return wrapper;
7212
6778
  }
7213
6779
  function duplicateField(form, wrapper) {
@@ -7349,7 +6915,7 @@ function FormFieldToolbar({
7349
6915
  "aria-pressed": required,
7350
6916
  onClick: onRequiredToggle,
7351
6917
  className: "flex h-7 items-center gap-1.5 whitespace-nowrap rounded-md px-2 text-[13px] font-medium transition-colors " + (required ? "bg-primary/10 text-primary" : "text-foreground hover:bg-muted/70"),
7352
- "data-ohw-field-required-toggle": "",
6918
+ "data-ohw-field-required": "",
7353
6919
  children: [
7354
6920
  /* @__PURE__ */ jsx13(Asterisk, { size: 14, strokeWidth: 2, "aria-hidden": true }),
7355
6921
  "Required"
@@ -7484,17 +7050,13 @@ function MediaOverlay({
7484
7050
  hover,
7485
7051
  isUploading,
7486
7052
  fadingOut = false,
7487
- selected = false,
7488
- hovered = false,
7489
7053
  onFadeOutComplete,
7490
7054
  onReplace,
7491
- onSelect,
7492
7055
  onVideoSettingsChange
7493
7056
  }) {
7494
7057
  const { rect } = hover;
7495
7058
  const skeletonRef = React7.useRef(null);
7496
7059
  const isVideo = hover.elementType === "video";
7497
- const showChrome = !selected || hovered;
7498
7060
  const autoplay = hover.videoAutoplay ?? true;
7499
7061
  const muted = hover.videoMuted ?? true;
7500
7062
  const box = {
@@ -7529,7 +7091,7 @@ function MediaOverlay({
7529
7091
  }
7530
7092
  );
7531
7093
  }
7532
- const settingsBar = isVideo && !hover.isDragOver && showChrome ? /* @__PURE__ */ jsxs7(
7094
+ const settingsBar = isVideo && !hover.isDragOver ? /* @__PURE__ */ jsxs7(
7533
7095
  "div",
7534
7096
  {
7535
7097
  "data-ohw-bridge": "",
@@ -7599,13 +7161,11 @@ function MediaOverlay({
7599
7161
  // in-document, pointer-events does it natively. The button below opts back in, so
7600
7162
  // Replace still works.
7601
7163
  pointerEvents: hover.hasTextOverlap ? "none" : "auto",
7602
- // Selected: a firm component ring with no wash, so the image reads as chosen rather
7603
- // than hovered. Hover keeps the existing tinted preview.
7604
- boxShadow: selected ? "inset 0 0 0 2px var(--color-primary)" : "inset 0 0 0 1.5px var(--color-primary)",
7605
- background: selected ? "transparent" : "color-mix(in srgb, var(--color-primary) 20%, transparent)"
7164
+ boxShadow: "inset 0 0 0 1.5px var(--color-primary)",
7165
+ background: "color-mix(in srgb, var(--color-primary) 20%, transparent)"
7606
7166
  },
7607
- onClick: () => onSelect && !selected ? onSelect(hover.key) : onReplace(hover.key),
7608
- children: showChrome && /* @__PURE__ */ jsxs7(
7167
+ onClick: () => onReplace(hover.key),
7168
+ children: /* @__PURE__ */ jsxs7(
7609
7169
  Button,
7610
7170
  {
7611
7171
  "data-ohw-media-overlay": "",
@@ -7694,9 +7254,6 @@ import { Check, X } from "lucide-react";
7694
7254
 
7695
7255
  // src/lib/sections.ts
7696
7256
  var LINK_PICKER_EXCLUDED_IDS = /* @__PURE__ */ new Set(["navbar", "footer"]);
7697
- function isChromeSection(el) {
7698
- return el.matches("header, nav, footer, aside");
7699
- }
7700
7257
  function titleCaseSectionId(id) {
7701
7258
  return id.split("-").filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
7702
7259
  }
@@ -7707,8 +7264,6 @@ function parseSectionsFromRoot(root) {
7707
7264
  const id = el.getAttribute("data-ohw-section") ?? "";
7708
7265
  if (!id || seen.has(id) || LINK_PICKER_EXCLUDED_IDS.has(id)) continue;
7709
7266
  if (el.parentElement?.closest("[data-ohw-section]")) continue;
7710
- if (el.hasAttribute("data-ohw-ai-template-hidden") || el.hasAttribute("data-ohw-ai-removed") || el.hasAttribute("data-ohw-ai-replaced-by"))
7711
- continue;
7712
7267
  seen.add(id);
7713
7268
  const label = el.getAttribute("data-ohw-section-label") ?? titleCaseSectionId(id);
7714
7269
  sections.push({ id, label });
@@ -7726,12 +7281,8 @@ function parseSectionsFromHtml(html) {
7726
7281
 
7727
7282
  // src/ui/ai-section/AiSectionOverlay.tsx
7728
7283
  import { Fragment as Fragment5, jsx as jsx17, jsxs as jsxs9 } from "react/jsx-runtime";
7729
- function findSectionElement(instanceId) {
7730
- const escaped = CSS.escape(instanceId);
7731
- return document.querySelector(`[data-ohw-instance="${escaped}"]`) ?? document.querySelector(`[data-ohw-section="${escaped}"]:not([data-ohw-instance])`);
7732
- }
7733
- function readRect(instanceId) {
7734
- const el = findSectionElement(instanceId);
7284
+ function readRect(sectionId) {
7285
+ const el = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
7735
7286
  if (!el) return null;
7736
7287
  const r2 = el.getBoundingClientRect();
7737
7288
  if (r2.width <= 0 || r2.height <= 0) return null;
@@ -7754,7 +7305,7 @@ function useLiveSectionRect(sectionId) {
7754
7305
  const opts = { capture: true, passive: true };
7755
7306
  window.addEventListener("scroll", update, opts);
7756
7307
  window.addEventListener("resize", update);
7757
- const el = findSectionElement(sectionId);
7308
+ const el = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
7758
7309
  const ro = el ? new ResizeObserver(update) : null;
7759
7310
  if (el && ro) ro.observe(el);
7760
7311
  const interval = setInterval(update, 500);
@@ -7767,14 +7318,6 @@ function useLiveSectionRect(sectionId) {
7767
7318
  }, [sectionId]);
7768
7319
  return rect;
7769
7320
  }
7770
- function computeSectionBoundaryFlags(instanceId) {
7771
- const topLevel = Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
7772
- (el) => !el.parentElement?.closest("[data-ohw-section]") && !isChromeSection(el)
7773
- );
7774
- const index = topLevel.findIndex((el) => (el.dataset.ohwInstance ?? el.dataset.ohwSection) === instanceId);
7775
- if (index === -1) return { isFirst: true, isLast: true };
7776
- return { isFirst: index === 0, isLast: index === topLevel.length - 1 };
7777
- }
7778
7321
  var PRIMARY2 = "#0885FE";
7779
7322
  function edgeAwareRadius(rect) {
7780
7323
  const container = window.innerWidth <= 480 ? 16 : 24;
@@ -7848,7 +7391,6 @@ function AiSectionOverlay({
7848
7391
  }) {
7849
7392
  const [selectedId, setSelectedId] = useState4(null);
7850
7393
  const [reviewId, setReviewId] = useState4(null);
7851
- const [reviewButtonsHidden, setReviewButtonsHidden] = useState4(false);
7852
7394
  const reviewIdRef = useRef4(null);
7853
7395
  reviewIdRef.current = reviewId;
7854
7396
  const selectedIdRef = useRef4(null);
@@ -7857,7 +7399,7 @@ function AiSectionOverlay({
7857
7399
  (el) => {
7858
7400
  postToParent2({
7859
7401
  type: "ow:section-selected",
7860
- sectionId: el ? el.dataset.ohwInstance ?? el.dataset.ohwSection ?? null : null,
7402
+ sectionId: el?.dataset.ohwSection ?? null,
7861
7403
  sectionLabel: el ? el.dataset.ohwSectionLabel ?? titleCaseSectionId(el.dataset.ohwSection ?? "") : null
7862
7404
  });
7863
7405
  },
@@ -7866,7 +7408,7 @@ function AiSectionOverlay({
7866
7408
  const selectFromElement = useCallback2(
7867
7409
  (el, options) => {
7868
7410
  const sectionEl = el?.closest("[data-ohw-section]") ?? null;
7869
- const id = sectionEl ? sectionEl.dataset.ohwInstance ?? sectionEl.dataset.ohwSection ?? null : null;
7411
+ const id = sectionEl?.dataset.ohwSection ?? null;
7870
7412
  if (id === selectedIdRef.current) return;
7871
7413
  setSelectedId(id);
7872
7414
  if (options?.report !== false) report(sectionEl);
@@ -7907,10 +7449,9 @@ function AiSectionOverlay({
7907
7449
  }
7908
7450
  const found = readRect(sectionId) != null;
7909
7451
  setReviewId(found ? sectionId : null);
7910
- setReviewButtonsHidden(e.data.hideButtons === true);
7911
7452
  postToParent2({ type: "ow:ai-review-started", sectionId, found });
7912
7453
  if (found) {
7913
- document.querySelector(`[data-ohw-instance="${CSS.escape(sectionId)}"]`)?.scrollIntoView({ behavior: "smooth", block: "nearest" });
7454
+ document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`)?.scrollIntoView({ behavior: "smooth", block: "nearest" });
7914
7455
  }
7915
7456
  }
7916
7457
  };
@@ -7929,7 +7470,7 @@ function AiSectionOverlay({
7929
7470
  return;
7930
7471
  }
7931
7472
  const sec = t.closest("[data-ohw-section]");
7932
- setHoveredId(sec ? sec.dataset.ohwInstance ?? sec.dataset.ohwSection ?? null : null);
7473
+ setHoveredId(sec?.dataset.ohwSection ?? null);
7933
7474
  };
7934
7475
  const onLeave = () => setHoveredId(null);
7935
7476
  document.addEventListener("mousemove", onMove, { passive: true });
@@ -7961,30 +7502,9 @@ function AiSectionOverlay({
7961
7502
  },
7962
7503
  [postToParent2]
7963
7504
  );
7964
- const activeSelectionId = reviewId ? null : selectedId;
7965
- const selectionRect = useLiveSectionRect(activeSelectionId);
7505
+ const selectionRect = useLiveSectionRect(reviewId ? null : selectedId);
7966
7506
  const reviewRect = useLiveSectionRect(reviewId);
7967
7507
  const hoverRect = useLiveSectionRect(reviewId || hoveredId === selectedId ? null : hoveredId);
7968
- useEffect4(() => {
7969
- const selectedEl = activeSelectionId ? findSectionElement(activeSelectionId) : null;
7970
- if (!activeSelectionId || !selectionRect || selectedEl && isChromeSection(selectedEl)) {
7971
- postToParent2({ type: "ow:section-rect", instanceId: null, rect: null });
7972
- return;
7973
- }
7974
- const { isFirst, isLast } = computeSectionBoundaryFlags(activeSelectionId);
7975
- postToParent2({
7976
- type: "ow:section-rect",
7977
- instanceId: activeSelectionId,
7978
- rect: {
7979
- top: selectionRect.top + window.scrollY,
7980
- left: selectionRect.left + window.scrollX,
7981
- width: selectionRect.width,
7982
- height: selectionRect.height
7983
- },
7984
- isFirst,
7985
- isLast
7986
- });
7987
- }, [activeSelectionId, selectionRect, postToParent2]);
7988
7508
  return /* @__PURE__ */ jsxs9(Fragment5, { children: [
7989
7509
  hoverRect && /* @__PURE__ */ jsx17(
7990
7510
  "div",
@@ -8035,16 +7555,13 @@ function AiSectionOverlay({
8035
7555
  border: `2px solid ${PRIMARY2}`,
8036
7556
  borderRadius: edgeAwareRadius(reviewRect),
8037
7557
  zIndex: 2147483200,
8038
- // The veil itself: swallows clicks so the section stays locked until decided. This
8039
- // stopPropagation only guards the bubble phase; the bridge's capture-phase click
8040
- // handlers additionally bail on `[data-ohw-ai-review]`, without which a click on
8041
- // Accept/Discard resolves to the media beneath and opens the file picker.
7558
+ // The veil itself: swallows clicks so the section stays locked until decided.
8042
7559
  background: "rgba(8, 133, 254, 0.04)",
8043
7560
  pointerEvents: "auto",
8044
7561
  cursor: "default"
8045
7562
  },
8046
7563
  onClick: (e) => e.stopPropagation(),
8047
- children: !reviewButtonsHidden && /* @__PURE__ */ jsxs9(
7564
+ children: /* @__PURE__ */ jsxs9(
8048
7565
  "div",
8049
7566
  {
8050
7567
  style: {
@@ -8069,58 +7586,6 @@ function AiSectionOverlay({
8069
7586
 
8070
7587
  // src/lib/section-instances.ts
8071
7588
  var SECTION_ORDER_KEY = "__ohw_section_order";
8072
- function topLevelSections() {
8073
- return Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
8074
- (el) => !el.parentElement?.closest("[data-ohw-section]") && !isChromeSection(el)
8075
- );
8076
- }
8077
- function instanceIdOf(el) {
8078
- return el.dataset.ohwInstance ?? el.dataset.ohwSection ?? "";
8079
- }
8080
- function planSectionMove(instanceId, targetIndex, currentPath) {
8081
- const sections = topLevelSections();
8082
- const index = sections.findIndex((el) => instanceIdOf(el) === instanceId);
8083
- if (index === -1) return null;
8084
- const dragged = sections[index];
8085
- const others = sections.filter((_, i) => i !== index);
8086
- const clamped = Math.max(0, Math.min(targetIndex, others.length));
8087
- const reordered = [...others.slice(0, clamped), dragged, ...others.slice(clamped)];
8088
- return reordered.map((el, order) => ({
8089
- instanceId: instanceIdOf(el),
8090
- type: el.getAttribute("data-ohw-section") ?? "",
8091
- order,
8092
- pagePath: currentPath
8093
- }));
8094
- }
8095
- function moveSectionInstance(instanceId, direction, currentPath) {
8096
- const sections = topLevelSections();
8097
- const index = sections.findIndex((el) => instanceIdOf(el) === instanceId);
8098
- if (index === -1) return null;
8099
- const siblingIndex = direction === "up" ? index - 1 : index + 1;
8100
- if (siblingIndex < 0 || siblingIndex >= sections.length) return null;
8101
- const entries = planSectionMove(instanceId, siblingIndex, currentPath);
8102
- if (!entries) return null;
8103
- applyPersistedOrder(entries);
8104
- return entries;
8105
- }
8106
- function applyPersistedOrder(entries) {
8107
- if (entries.length === 0) return;
8108
- const sections = topLevelSections();
8109
- if (sections.length === 0) return;
8110
- const orderIndex = new Map(entries.map((e) => [e.instanceId, e.order]));
8111
- const ordered = [...sections].sort((a, b) => {
8112
- const aOrder = orderIndex.get(instanceIdOf(a));
8113
- const bOrder = orderIndex.get(instanceIdOf(b));
8114
- if (aOrder === void 0 && bOrder === void 0) return 0;
8115
- if (aOrder === void 0) return 1;
8116
- if (bOrder === void 0) return -1;
8117
- return aOrder - bOrder;
8118
- });
8119
- const parent = sections[0].parentElement;
8120
- if (!parent) return;
8121
- const anchor = sections[sections.length - 1].nextSibling;
8122
- ordered.forEach((el) => parent.insertBefore(el, anchor));
8123
- }
8124
7589
  function getPageSectionOrderEntries(raw, currentPath) {
8125
7590
  if (!raw) return [];
8126
7591
  try {
@@ -8158,7 +7623,6 @@ function initSectionInstancesFromContent(content, currentPath) {
8158
7623
  rekeySectionSubtree(clone, entry.instanceId);
8159
7624
  original.insertAdjacentElement("afterend", clone);
8160
7625
  }
8161
- applyPersistedOrder(entries);
8162
7626
  }
8163
7627
 
8164
7628
  // src/OhhwellsBridge.tsx
@@ -12003,12 +11467,17 @@ function deleteFooterColumn(column) {
12003
11467
  }
12004
11468
 
12005
11469
  // src/lib/logo-identity.ts
12006
- var LOGO_TEXT_KEYS = ["nav-logo-text", "footer-logo-text", "logo-text"];
12007
- var LOGO_IMAGE_KEYS = ["nav-logo-image", "footer-logo", "footer-logo-image"];
11470
+ var LOGO_TEXT_KEYS = ["nav-logo-text", "footer-logo-text", "logo-text", "nav-logo", "logo"];
11471
+ var LOGO_IMAGE_KEYS = [
11472
+ "nav-logo-image",
11473
+ "footer-logo",
11474
+ "footer-logo-image",
11475
+ "logo-image",
11476
+ "footer-logo-img"
11477
+ ];
12008
11478
  var LOGO_HREF_KEYS = ["nav-logo-href", "footer-logo-href", "logo-href"];
12009
11479
  var LOGO_PLACEHOLDER_KEY = "logo-is-placeholder";
12010
11480
  var LOGO_ALT_KEY = "logo-alt";
12011
- var LOGO_IMAGE_URL_KEY = "nav-logo-image";
12012
11481
  var PLACEHOLDER_BUSINESS_NAME = "Business name";
12013
11482
  function resolveLogoDisplayText(text) {
12014
11483
  const trimmed = (text ?? "").trim();
@@ -12028,6 +11497,21 @@ function ensureLogoHrefKey(root) {
12028
11497
  if (root.hasAttribute("data-ohw-href-key")) return;
12029
11498
  root.setAttribute("data-ohw-href-key", isFooterLogoRoot(root) ? "footer-logo-href" : "nav-logo-href");
12030
11499
  }
11500
+ function logoTextElement(root) {
11501
+ const SELECTOR = '[data-ohw-editable="plain"], [data-ohw-editable="text"]';
11502
+ if (root.matches(SELECTOR)) return root;
11503
+ const marked = root.querySelector(SELECTOR);
11504
+ if (marked) return marked;
11505
+ const hasOwnText = Array.from(root.childNodes).some(
11506
+ (node) => node.nodeType === Node.TEXT_NODE && node.textContent?.trim()
11507
+ );
11508
+ return hasOwnText ? root : null;
11509
+ }
11510
+ function logoRootImage(root) {
11511
+ const img = root.querySelector("img");
11512
+ const src = img?.getAttribute("src")?.trim() ?? "";
11513
+ return img && src && img.style.display !== "none" ? img : null;
11514
+ }
12031
11515
  function applyLogoIdentity(text, isPlaceholder) {
12032
11516
  const display = resolveLogoDisplayText(text);
12033
11517
  const placeholder = isPlaceholder || !text.trim() || display === PLACEHOLDER_BUSINESS_NAME;
@@ -12045,6 +11529,9 @@ function applyLogoIdentity(text, isPlaceholder) {
12045
11529
  document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((el) => {
12046
11530
  if (placeholder) el.setAttribute("data-ohw-placeholder", "");
12047
11531
  else el.removeAttribute("data-ohw-placeholder");
11532
+ if (logoRootImage(el)) return;
11533
+ const textEl = logoTextElement(el);
11534
+ if (textEl && textEl.textContent !== display) textEl.textContent = display;
12048
11535
  });
12049
11536
  return display;
12050
11537
  }
@@ -12055,7 +11542,7 @@ function applyLogoImage(url, alt) {
12055
11542
  const imageKey = imageKeyForRoot(root);
12056
11543
  const textKey = textKeyForRoot(root);
12057
11544
  let img = root.querySelector(`img[data-ohw-key="${imageKey}"]`) ?? (root.querySelector(`[data-ohw-key="${imageKey}"]`) instanceof HTMLImageElement ? root.querySelector(`[data-ohw-key="${imageKey}"]`) : null) ?? root.querySelector("img");
12058
- let textEl = root.querySelector(`[data-ohw-key="${textKey}"]`) ?? root.querySelector('[data-ohw-key="logo-text"]');
11545
+ let textEl = root.querySelector(`[data-ohw-key="${textKey}"]`) ?? root.querySelector('[data-ohw-key="logo-text"]') ?? logoTextElement(root);
12059
11546
  if (url) {
12060
11547
  if (!img) {
12061
11548
  img = document.createElement("img");
@@ -12077,7 +11564,11 @@ function applyLogoImage(url, alt) {
12077
11564
  img.src = url;
12078
11565
  img.alt = displayAlt;
12079
11566
  img.style.display = "block";
12080
- if (textEl) textEl.style.display = "none";
11567
+ if (textEl === root) {
11568
+ Array.from(root.childNodes).filter((node) => node.nodeType === Node.TEXT_NODE).forEach((node) => node.remove());
11569
+ } else if (textEl) {
11570
+ textEl.style.display = "none";
11571
+ }
12081
11572
  root.removeAttribute("data-ohw-placeholder");
12082
11573
  return;
12083
11574
  }
@@ -12126,6 +11617,13 @@ function readLogoIdentityFromDom() {
12126
11617
  }
12127
11618
  let text = PLACEHOLDER_BUSINESS_NAME;
12128
11619
  let isPlaceholder = true;
11620
+ const rootWithText = Array.from(
11621
+ document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]')
11622
+ ).find((root) => logoTextElement(root)?.textContent?.trim());
11623
+ if (rootWithText) {
11624
+ text = logoTextElement(rootWithText).textContent.trim();
11625
+ isPlaceholder = rootWithText.hasAttribute("data-ohw-placeholder") || text === PLACEHOLDER_BUSINESS_NAME;
11626
+ }
12129
11627
  for (const key of LOGO_TEXT_KEYS) {
12130
11628
  const el = document.querySelector(`[data-ohw-key="${key}"]`);
12131
11629
  if (el?.textContent?.trim()) {
@@ -12156,7 +11654,7 @@ function applyLogoFromContent(content) {
12156
11654
  if (!hasLogoIdentity) return false;
12157
11655
  const logoText = content[LOGO_TEXT_KEYS[0]] ?? content[LOGO_TEXT_KEYS[1]] ?? readLogoIdentityFromDom().text;
12158
11656
  const logoAlt = content[LOGO_ALT_KEY] ?? logoText;
12159
- const rawLogoImage = content[LOGO_IMAGE_URL_KEY] ?? content["footer-logo"] ?? content["footer-logo-image"] ?? null;
11657
+ const rawLogoImage = LOGO_IMAGE_KEYS.map((key) => content[key]).find((value) => typeof value === "string" && value.trim()) ?? null;
12160
11658
  const logoImageUrl = typeof rawLogoImage === "string" && rawLogoImage.trim() ? rawLogoImage.trim() : null;
12161
11659
  const imageExplicitlyCleared = LOGO_IMAGE_KEYS.some((key) => key in content) && !logoImageUrl;
12162
11660
  const logoIsPlaceholder = LOGO_PLACEHOLDER_KEY in content ? content[LOGO_PLACEHOLDER_KEY] !== "false" : !logoImageUrl && (!logoText.trim() || logoText === PLACEHOLDER_BUSINESS_NAME);
@@ -12291,7 +11789,6 @@ function readLogoSizeState(content, placement) {
12291
11789
  function getLogoElement(el) {
12292
11790
  const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
12293
11791
  if (marked) return marked;
12294
- if (el.closest('[data-ohw-editable="icon"]')) return null;
12295
11792
  const root = el.closest("nav, [data-ohw-nav-root], footer");
12296
11793
  if (!root) return null;
12297
11794
  const anchor = el.closest("a");
@@ -13162,449 +12659,122 @@ function useNavItemDrag({
13162
12659
  };
13163
12660
  }
13164
12661
 
13165
- // src/useSectionDrag.ts
13166
- import { useCallback as useCallback7, useEffect as useEffect11, useRef as useRef9, useState as useState10 } from "react";
12662
+ // src/ui/footer-container-chrome.tsx
12663
+ import { Plus as Plus2 } from "lucide-react";
12664
+ import { jsx as jsx29, jsxs as jsxs19 } from "react/jsx-runtime";
12665
+ function FooterContainerChrome({
12666
+ rect,
12667
+ onAdd,
12668
+ addDisabled = false
12669
+ }) {
12670
+ const chromeGap = 6;
12671
+ const buttonMargin = 7;
12672
+ return /* @__PURE__ */ jsx29(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsx29(
12673
+ "div",
12674
+ {
12675
+ "data-ohw-footer-container-chrome": "",
12676
+ "data-ohw-bridge": "",
12677
+ className: "pointer-events-none fixed z-[2147483647]",
12678
+ style: {
12679
+ top: rect.top - chromeGap,
12680
+ left: rect.left - chromeGap,
12681
+ width: rect.width + chromeGap * 2,
12682
+ height: rect.height + chromeGap * 2
12683
+ },
12684
+ children: /* @__PURE__ */ jsxs19(Tooltip, { children: [
12685
+ /* @__PURE__ */ jsx29(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx29(
12686
+ "button",
12687
+ {
12688
+ type: "button",
12689
+ "data-ohw-footer-add-button": "",
12690
+ disabled: addDisabled,
12691
+ className: "pointer-events-auto absolute left-1/2 flex size-7 -translate-x-1/2 -translate-y-full items-center justify-center rounded-[10px] border border-border bg-background p-0.5 shadow-sm transition-colors hover:bg-muted/80 disabled:pointer-events-none disabled:opacity-40",
12692
+ style: { top: chromeGap - buttonMargin },
12693
+ "aria-label": "Add item",
12694
+ onMouseDown: (e) => {
12695
+ e.preventDefault();
12696
+ e.stopPropagation();
12697
+ },
12698
+ onClick: (e) => {
12699
+ e.preventDefault();
12700
+ e.stopPropagation();
12701
+ if (addDisabled) return;
12702
+ onAdd();
12703
+ },
12704
+ children: /* @__PURE__ */ jsx29(Plus2, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
12705
+ }
12706
+ ) }),
12707
+ /* @__PURE__ */ jsx29(TooltipContent, { side: "bottom", sideOffset: 9, children: addDisabled ? "Maximum columns reached" : "Add item" })
12708
+ ] })
12709
+ }
12710
+ ) });
12711
+ }
13167
12712
 
13168
- // src/lib/section-dnd.ts
13169
- function isFooterSection(el) {
13170
- return el.dataset.ohwSection === "footer";
12713
+ // src/lib/carousel.ts
12714
+ import { useEffect as useEffect11, useState as useState10 } from "react";
12715
+ var CAROUSEL_ATTR = "data-ohw-carousel";
12716
+ var CAROUSEL_VALUE_ATTR = "data-ohw-carousel-value";
12717
+ var CAROUSEL_SLIDE_ATTR = "data-ohw-carousel-slide";
12718
+ var CAROUSEL_EVENT = "ohw:carousel-change";
12719
+ function listCarouselKeys() {
12720
+ const keys = /* @__PURE__ */ new Set();
12721
+ document.querySelectorAll(`[${CAROUSEL_ATTR}]`).forEach((el) => {
12722
+ const key = el.getAttribute("data-ohw-key");
12723
+ if (key) keys.add(key);
12724
+ });
12725
+ return [...keys];
13171
12726
  }
13172
- function buildSectionDropSlots(draggedInstanceId) {
13173
- const sections = topLevelSections().filter(
13174
- (el) => instanceIdOf(el) !== draggedInstanceId && !isFooterSection(el)
12727
+ function containersForKey(key) {
12728
+ return Array.from(
12729
+ document.querySelectorAll(`[${CAROUSEL_ATTR}][data-ohw-key="${CSS.escape(key)}"]`)
13175
12730
  );
13176
- const slots = [];
13177
- if (sections.length === 0) return slots;
13178
- const left = 0;
13179
- const width = document.documentElement.clientWidth;
13180
- for (let i = 0; i <= sections.length; i++) {
13181
- let y;
13182
- if (i === 0) {
13183
- y = sections[0].getBoundingClientRect().top;
13184
- } else if (i === sections.length) {
13185
- y = sections[sections.length - 1].getBoundingClientRect().bottom;
13186
- } else {
13187
- const prev = sections[i - 1].getBoundingClientRect();
13188
- const next = sections[i].getBoundingClientRect();
13189
- y = (prev.bottom + next.top) / 2;
13190
- }
13191
- slots.push({ insertIndex: i, y, left, width });
12731
+ }
12732
+ function isCarouselKey(key) {
12733
+ return containersForKey(key).length > 0;
12734
+ }
12735
+ function parseSlides(raw) {
12736
+ if (!raw) return [];
12737
+ try {
12738
+ const parsed = JSON.parse(raw);
12739
+ if (!Array.isArray(parsed)) return [];
12740
+ return parsed.filter((s) => Boolean(s) && typeof s === "object").map((s) => ({ src: String(s.src ?? ""), alt: String(s.alt ?? "") }));
12741
+ } catch {
12742
+ return [];
13192
12743
  }
13193
- return slots;
13194
12744
  }
13195
- function hitTestSectionDropSlot(y, slots) {
13196
- let best = null;
13197
- for (const slot of slots) {
13198
- const dist = Math.abs(y - slot.y);
13199
- if (!best || dist < best.dist) best = { slot, dist };
12745
+ function readCarouselValue(key) {
12746
+ const container = containersForKey(key)[0];
12747
+ if (!container) return [];
12748
+ const fromAttr = parseSlides(container.getAttribute(CAROUSEL_VALUE_ATTR));
12749
+ if (fromAttr.length > 0) return fromAttr;
12750
+ return Array.from(container.querySelectorAll(`[${CAROUSEL_SLIDE_ATTR}]`)).filter((slide) => slide.closest(`[${CAROUSEL_ATTR}]`) === container).sort((a, b) => slideIndex(a) - slideIndex(b)).map((slide) => {
12751
+ const img = slide instanceof HTMLImageElement ? slide : slide.querySelector("img");
12752
+ return { src: img?.src ?? "", alt: img?.alt ?? "" };
12753
+ });
12754
+ }
12755
+ function slideIndex(el) {
12756
+ const raw = el.getAttribute(CAROUSEL_SLIDE_ATTR);
12757
+ const n = raw ? parseInt(raw, 10) : NaN;
12758
+ return Number.isFinite(n) ? n : 0;
12759
+ }
12760
+ function applyCarouselValue(key, slides) {
12761
+ const value = JSON.stringify(slides);
12762
+ for (const container of containersForKey(key)) {
12763
+ if (container.getAttribute(CAROUSEL_VALUE_ATTR) === value) continue;
12764
+ container.setAttribute(CAROUSEL_VALUE_ATTR, value);
12765
+ container.dispatchEvent(
12766
+ new CustomEvent(CAROUSEL_EVENT, { detail: { images: slides } })
12767
+ );
13200
12768
  }
13201
- return best?.slot ?? null;
13202
12769
  }
13203
-
13204
- // src/useSectionDrag.ts
13205
- var PRESS_THRESHOLD = 10;
13206
- var EDGE_ZONE = 60;
13207
- var MAX_AUTO_SCROLL_SPEED = 18;
13208
- var SECTION_DRAG_EXCLUDED_SELECTOR = [
13209
- "[data-ohw-toolbar]",
13210
- "[data-ohw-edit-chrome]",
13211
- "[data-ohw-item-interaction]",
13212
- "[data-ohw-drag-handle-container]",
13213
- '[data-slot="drag-handle"]',
13214
- "[data-ohw-item-toolbar-anchor]",
13215
- "[data-ohw-item-drag-surface]",
13216
- "[data-ohw-more-menu]",
13217
- '[data-slot="dropdown-menu-content"]',
13218
- '[data-slot="dropdown-menu-item"]',
13219
- "[data-ohw-state-toggle]",
13220
- "[data-ohw-max-badge]",
13221
- "[data-ohw-floating-panel]",
13222
- "[data-ohw-section-picker]",
13223
- "[data-ohw-link-popover-root]",
13224
- "[data-ohw-link-modal-root]",
13225
- "[data-ohw-link-page-dropdown]",
13226
- '[data-slot="popover-content"]',
13227
- '[data-slot="dialog-content"]',
13228
- '[data-slot="dialog-overlay"]',
13229
- "[data-ohw-ai-review]",
13230
- "[data-ohw-editable]",
13231
- "[data-ohw-editable-state]",
13232
- "[contenteditable]",
13233
- "[data-ohw-href-key]",
13234
- "[data-ohw-footer-col]",
13235
- "[data-ohw-social-label]",
13236
- "a",
13237
- "button",
13238
- '[role="button"]',
13239
- '[data-ohw-role="navbar-button"]',
13240
- '[data-ohw-role="button"]',
13241
- "[data-ohw-carousel]",
13242
- "[data-ohw-carousel-value]",
13243
- "[data-ohw-carousel-slide]",
13244
- "[data-ohw-carousel-overlay]",
13245
- "[data-ohw-media-chrome]",
13246
- "[data-ohw-media-overlay]",
13247
- "[data-ohw-media-skeleton]"
13248
- ].join(", ");
13249
- function visibleClip(ps) {
13250
- if (!ps) return null;
13251
- const top = Math.max(0, ps.headerH - ps.iframeOffsetTop);
13252
- const bottom = Math.min(window.innerHeight, ps.headerH + ps.canvasH - ps.iframeOffsetTop);
13253
- return { top, bottom: Math.max(top, bottom) };
13254
- }
13255
- function useSectionDrag({
13256
- isEditMode,
13257
- editContentRef,
13258
- postToParentRef,
13259
- parentScrollRef,
13260
- navDragRef,
13261
- footerDragRef,
13262
- suppressNextClickRef,
13263
- suppressClickUntilRef
13264
- }) {
13265
- const sectionDragRef = useRef9(null);
13266
- const [sectionDropSlots, setSectionDropSlots] = useState10([]);
13267
- const [activeSectionDropIndex, setActiveSectionDropIndex] = useState10(null);
13268
- const [isSectionDragging, setIsSectionDragging] = useState10(false);
13269
- const sectionPointerDragRef = useRef9(null);
13270
- const autoScrollRafRef = useRef9(null);
13271
- const autoScrollDeltaRef = useRef9(0);
13272
- const stopAutoScroll = useCallback7(() => {
13273
- if (autoScrollRafRef.current != null) {
13274
- cancelAnimationFrame(autoScrollRafRef.current);
13275
- autoScrollRafRef.current = null;
13276
- }
13277
- autoScrollDeltaRef.current = 0;
13278
- }, []);
13279
- const tickAutoScroll = useCallback7(() => {
13280
- if (!sectionDragRef.current) {
13281
- stopAutoScroll();
13282
- return;
13283
- }
13284
- if (autoScrollDeltaRef.current !== 0) {
13285
- postToParentRef.current({ type: "ow:request-scroll", deltaY: autoScrollDeltaRef.current });
13286
- }
13287
- autoScrollRafRef.current = requestAnimationFrame(tickAutoScroll);
13288
- }, [postToParentRef, stopAutoScroll]);
13289
- const updateAutoScroll = useCallback7(
13290
- (clientY) => {
13291
- const clip = visibleClip(parentScrollRef.current);
13292
- let delta = 0;
13293
- if (clip) {
13294
- const distTop = clientY - clip.top;
13295
- const distBottom = clip.bottom - clientY;
13296
- if (distTop >= 0 && distTop < EDGE_ZONE) {
13297
- delta = -MAX_AUTO_SCROLL_SPEED * (1 - distTop / EDGE_ZONE);
13298
- } else if (distBottom >= 0 && distBottom < EDGE_ZONE) {
13299
- delta = MAX_AUTO_SCROLL_SPEED * (1 - distBottom / EDGE_ZONE);
13300
- }
13301
- }
13302
- autoScrollDeltaRef.current = delta;
13303
- if (delta !== 0 && autoScrollRafRef.current == null) {
13304
- autoScrollRafRef.current = requestAnimationFrame(tickAutoScroll);
13305
- } else if (delta === 0) {
13306
- stopAutoScroll();
13307
- }
13308
- },
13309
- [parentScrollRef, stopAutoScroll, tickAutoScroll]
13310
- );
13311
- const clearSectionDragVisuals = useCallback7(() => {
13312
- sectionDragRef.current?.draggedEl.removeAttribute("data-ohw-section-dragging");
13313
- sectionDragRef.current = null;
13314
- setSectionDropSlots([]);
13315
- setActiveSectionDropIndex(null);
13316
- setIsSectionDragging(false);
13317
- stopAutoScroll();
13318
- document.documentElement.removeAttribute("data-ohw-section-dragging-root");
13319
- unlockItemDragInteraction();
13320
- }, [stopAutoScroll]);
13321
- const refreshSectionDragVisuals = useCallback7(
13322
- (session, clientX, clientY) => {
13323
- session.lastClientX = clientX;
13324
- session.lastClientY = clientY;
13325
- const slots = buildSectionDropSlots(session.instanceId);
13326
- const activeSlot = hitTestSectionDropSlot(clientY, slots);
13327
- session.activeSlot = activeSlot;
13328
- setSectionDropSlots(slots);
13329
- const activeIdx = activeSlot ? slots.findIndex((s) => s.insertIndex === activeSlot.insertIndex) : -1;
13330
- setActiveSectionDropIndex(activeIdx >= 0 ? activeIdx : null);
13331
- updateAutoScroll(clientY);
13332
- },
13333
- [updateAutoScroll]
13334
- );
13335
- const beginSectionDrag = useCallback7(
13336
- (session) => {
13337
- sectionDragRef.current = session;
13338
- setIsSectionDragging(true);
13339
- lockItemDuringDrag();
13340
- document.documentElement.setAttribute("data-ohw-section-dragging-root", "");
13341
- session.draggedEl.setAttribute("data-ohw-section-dragging", "");
13342
- refreshSectionDragVisuals(session, session.lastClientX, session.lastClientY);
13343
- },
13344
- [refreshSectionDragVisuals]
13345
- );
13346
- const commitSectionDrag = useCallback7(() => {
13347
- const session = sectionDragRef.current;
13348
- if (!session) {
13349
- clearSectionDragVisuals();
13350
- return;
13351
- }
13352
- const slot = session.activeSlot ?? hitTestSectionDropSlot(session.lastClientY, buildSectionDropSlots(session.instanceId));
13353
- const entries = slot ? planSectionMove(session.instanceId, slot.insertIndex, window.location.pathname) : null;
13354
- if (!entries) {
13355
- clearSectionDragVisuals();
13356
- return;
13357
- }
13358
- const orderJson = JSON.stringify(entries);
13359
- editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
13360
- postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
13361
- applyPersistedOrder(entries);
13362
- clearSectionDragVisuals();
13363
- requestAnimationFrame(() => {
13364
- if (editContentRef.current[SECTION_ORDER_KEY] === orderJson) {
13365
- applyPersistedOrder(entries);
13366
- }
13367
- requestAnimationFrame(() => {
13368
- window.dispatchEvent(new Event("resize"));
13369
- });
13370
- });
13371
- }, [clearSectionDragVisuals, editContentRef, postToParentRef]);
13372
- const startSectionPressDrag = useCallback7(
13373
- (el, clientX, clientY, pointerId) => {
13374
- if (navDragRef.current || footerDragRef.current || sectionDragRef.current) return false;
13375
- const instanceId = instanceIdOf(el);
13376
- if (!instanceId) return false;
13377
- sectionPointerDragRef.current = {
13378
- el,
13379
- instanceId,
13380
- startX: clientX,
13381
- startY: clientY,
13382
- pointerId,
13383
- started: false
13384
- };
13385
- return true;
13386
- },
13387
- [footerDragRef, navDragRef]
13388
- );
13389
- useEffect11(() => {
13390
- if (!isEditMode) return;
13391
- const onPointerDown = (e) => {
13392
- if (e.button !== 0) return;
13393
- if (navDragRef.current || footerDragRef.current || sectionDragRef.current) return;
13394
- if (sectionPointerDragRef.current) return;
13395
- const target = e.target;
13396
- if (!(target instanceof HTMLElement)) return;
13397
- if (target.closest(SECTION_DRAG_EXCLUDED_SELECTOR)) return;
13398
- const sectionEl = target.closest("[data-ohw-section]");
13399
- if (!sectionEl || isChromeSection(sectionEl) || sectionEl.dataset.ohwSection === "footer") return;
13400
- if (!topLevelSections().includes(sectionEl)) return;
13401
- startSectionPressDrag(sectionEl, e.clientX, e.clientY, e.pointerId);
13402
- };
13403
- const onPointerMove = (e) => {
13404
- const pending = sectionPointerDragRef.current;
13405
- if (!pending) return;
13406
- if (pending.started) {
13407
- e.preventDefault();
13408
- clearTextSelection();
13409
- const session = sectionDragRef.current;
13410
- if (!session) return;
13411
- refreshSectionDragVisuals(session, e.clientX, e.clientY);
13412
- return;
13413
- }
13414
- const dx = e.clientX - pending.startX;
13415
- const dy = e.clientY - pending.startY;
13416
- if (dx * dx + dy * dy < PRESS_THRESHOLD * PRESS_THRESHOLD) return;
13417
- e.preventDefault();
13418
- pending.started = true;
13419
- armItemPressDrag();
13420
- clearTextSelection();
13421
- try {
13422
- document.body.setPointerCapture(pending.pointerId);
13423
- } catch {
13424
- }
13425
- beginSectionDrag({
13426
- instanceId: pending.instanceId,
13427
- draggedEl: pending.el,
13428
- lastClientX: e.clientX,
13429
- lastClientY: e.clientY,
13430
- activeSlot: null
13431
- });
13432
- };
13433
- const endPointerDrag = (e) => {
13434
- const pending = sectionPointerDragRef.current;
13435
- sectionPointerDragRef.current = null;
13436
- try {
13437
- if (document.body.hasPointerCapture(e.pointerId)) {
13438
- document.body.releasePointerCapture(e.pointerId);
13439
- }
13440
- } catch {
13441
- }
13442
- if (!pending) return;
13443
- if (!pending.started) {
13444
- unlockItemDragInteraction();
13445
- return;
13446
- }
13447
- suppressNextClickRef.current = true;
13448
- suppressClickUntilRef.current = Date.now() + 500;
13449
- commitSectionDrag();
13450
- };
13451
- const onKeyDown = (e) => {
13452
- if (e.key !== "Escape") return;
13453
- if (!sectionDragRef.current && !sectionPointerDragRef.current) return;
13454
- sectionPointerDragRef.current = null;
13455
- clearSectionDragVisuals();
13456
- };
13457
- document.addEventListener("pointerdown", onPointerDown, true);
13458
- document.addEventListener("pointermove", onPointerMove, true);
13459
- document.addEventListener("pointerup", endPointerDrag, true);
13460
- document.addEventListener("pointercancel", endPointerDrag, true);
13461
- document.addEventListener("keydown", onKeyDown, true);
13462
- return () => {
13463
- document.removeEventListener("pointerdown", onPointerDown, true);
13464
- document.removeEventListener("pointermove", onPointerMove, true);
13465
- document.removeEventListener("pointerup", endPointerDrag, true);
13466
- document.removeEventListener("pointercancel", endPointerDrag, true);
13467
- document.removeEventListener("keydown", onKeyDown, true);
13468
- unlockItemDragInteraction();
13469
- stopAutoScroll();
13470
- };
13471
- }, [
13472
- beginSectionDrag,
13473
- clearSectionDragVisuals,
13474
- commitSectionDrag,
13475
- footerDragRef,
13476
- isEditMode,
13477
- navDragRef,
13478
- refreshSectionDragVisuals,
13479
- startSectionPressDrag,
13480
- stopAutoScroll,
13481
- suppressClickUntilRef,
13482
- suppressNextClickRef
13483
- ]);
13484
- return {
13485
- sectionDragRef,
13486
- sectionDropSlots,
13487
- activeSectionDropIndex,
13488
- isSectionDragging
13489
- };
13490
- }
13491
-
13492
- // src/ui/footer-container-chrome.tsx
13493
- import { Plus as Plus2 } from "lucide-react";
13494
- import { jsx as jsx29, jsxs as jsxs19 } from "react/jsx-runtime";
13495
- function FooterContainerChrome({
13496
- rect,
13497
- onAdd,
13498
- addDisabled = false
13499
- }) {
13500
- const chromeGap = 6;
13501
- const buttonMargin = 7;
13502
- return /* @__PURE__ */ jsx29(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsx29(
13503
- "div",
13504
- {
13505
- "data-ohw-footer-container-chrome": "",
13506
- "data-ohw-bridge": "",
13507
- className: "pointer-events-none fixed z-[2147483647]",
13508
- style: {
13509
- top: rect.top - chromeGap,
13510
- left: rect.left - chromeGap,
13511
- width: rect.width + chromeGap * 2,
13512
- height: rect.height + chromeGap * 2
13513
- },
13514
- children: /* @__PURE__ */ jsxs19(Tooltip, { children: [
13515
- /* @__PURE__ */ jsx29(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx29(
13516
- "button",
13517
- {
13518
- type: "button",
13519
- "data-ohw-footer-add-button": "",
13520
- disabled: addDisabled,
13521
- className: "pointer-events-auto absolute left-1/2 flex size-7 -translate-x-1/2 -translate-y-full items-center justify-center rounded-[10px] border border-border bg-background p-0.5 shadow-sm transition-colors hover:bg-muted/80 disabled:pointer-events-none disabled:opacity-40",
13522
- style: { top: chromeGap - buttonMargin },
13523
- "aria-label": "Add item",
13524
- onMouseDown: (e) => {
13525
- e.preventDefault();
13526
- e.stopPropagation();
13527
- },
13528
- onClick: (e) => {
13529
- e.preventDefault();
13530
- e.stopPropagation();
13531
- if (addDisabled) return;
13532
- onAdd();
13533
- },
13534
- children: /* @__PURE__ */ jsx29(Plus2, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
13535
- }
13536
- ) }),
13537
- /* @__PURE__ */ jsx29(TooltipContent, { side: "bottom", sideOffset: 9, children: addDisabled ? "Maximum columns reached" : "Add item" })
13538
- ] })
13539
- }
13540
- ) });
13541
- }
13542
-
13543
- // src/lib/carousel.ts
13544
- import { useEffect as useEffect12, useState as useState11 } from "react";
13545
- var CAROUSEL_ATTR = "data-ohw-carousel";
13546
- var CAROUSEL_VALUE_ATTR = "data-ohw-carousel-value";
13547
- var CAROUSEL_SLIDE_ATTR = "data-ohw-carousel-slide";
13548
- var CAROUSEL_EVENT = "ohw:carousel-change";
13549
- function listCarouselKeys() {
13550
- const keys = /* @__PURE__ */ new Set();
13551
- document.querySelectorAll(`[${CAROUSEL_ATTR}]`).forEach((el) => {
13552
- const key = el.getAttribute("data-ohw-key");
13553
- if (key) keys.add(key);
13554
- });
13555
- return [...keys];
13556
- }
13557
- function containersForKey(key) {
13558
- return Array.from(
13559
- document.querySelectorAll(`[${CAROUSEL_ATTR}][data-ohw-key="${CSS.escape(key)}"]`)
13560
- );
13561
- }
13562
- function isCarouselKey(key) {
13563
- return containersForKey(key).length > 0;
13564
- }
13565
- function parseSlides(raw) {
13566
- if (!raw) return [];
13567
- try {
13568
- const parsed = JSON.parse(raw);
13569
- if (!Array.isArray(parsed)) return [];
13570
- return parsed.filter((s) => Boolean(s) && typeof s === "object").map((s) => ({ src: String(s.src ?? ""), alt: String(s.alt ?? "") }));
13571
- } catch {
13572
- return [];
13573
- }
13574
- }
13575
- function readCarouselValue(key) {
13576
- const container = containersForKey(key)[0];
13577
- if (!container) return [];
13578
- const fromAttr = parseSlides(container.getAttribute(CAROUSEL_VALUE_ATTR));
13579
- if (fromAttr.length > 0) return fromAttr;
13580
- return Array.from(container.querySelectorAll(`[${CAROUSEL_SLIDE_ATTR}]`)).filter((slide) => slide.closest(`[${CAROUSEL_ATTR}]`) === container).sort((a, b) => slideIndex(a) - slideIndex(b)).map((slide) => {
13581
- const img = slide instanceof HTMLImageElement ? slide : slide.querySelector("img");
13582
- return { src: img?.src ?? "", alt: img?.alt ?? "" };
13583
- });
13584
- }
13585
- function slideIndex(el) {
13586
- const raw = el.getAttribute(CAROUSEL_SLIDE_ATTR);
13587
- const n = raw ? parseInt(raw, 10) : NaN;
13588
- return Number.isFinite(n) ? n : 0;
13589
- }
13590
- function applyCarouselValue(key, slides) {
13591
- const value = JSON.stringify(slides);
13592
- for (const container of containersForKey(key)) {
13593
- if (container.getAttribute(CAROUSEL_VALUE_ATTR) === value) continue;
13594
- container.setAttribute(CAROUSEL_VALUE_ATTR, value);
13595
- container.dispatchEvent(
13596
- new CustomEvent(CAROUSEL_EVENT, { detail: { images: slides } })
13597
- );
13598
- }
13599
- }
13600
- function applyCarouselNode(key, val) {
13601
- if (!isCarouselKey(key)) return false;
13602
- applyCarouselValue(key, parseSlides(val));
13603
- return true;
12770
+ function applyCarouselNode(key, val) {
12771
+ if (!isCarouselKey(key)) return false;
12772
+ applyCarouselValue(key, parseSlides(val));
12773
+ return true;
13604
12774
  }
13605
12775
  function useOhwCarousel(key, initial) {
13606
- const [images, setImages] = useState11(initial);
13607
- useEffect12(() => {
12776
+ const [images, setImages] = useState10(initial);
12777
+ useEffect11(() => {
13608
12778
  const el = document.querySelector(
13609
12779
  `[${CAROUSEL_ATTR}][data-ohw-key="${CSS.escape(key)}"]`
13610
12780
  );
@@ -13682,7 +12852,7 @@ function collectEditableNodes(extraContent, root = document) {
13682
12852
  nodes.push({ key, type: "link", text: href });
13683
12853
  }
13684
12854
  if (extraContent) {
13685
- for (const key of [NAV_ORDER_KEY, FOOTER_ORDER_KEY, NAV_COUNT_KEY, SECTION_ORDER_KEY]) {
12855
+ for (const key of [NAV_ORDER_KEY, FOOTER_ORDER_KEY, NAV_COUNT_KEY]) {
13686
12856
  const text = extraContent[key];
13687
12857
  if (typeof text === "string" && text.length > 0) {
13688
12858
  nodes.push({ key, type: "meta", text });
@@ -13719,18 +12889,6 @@ function collectEditableNodes(extraContent, root = document) {
13719
12889
  }
13720
12890
  if (extraContent && !isScoped) {
13721
12891
  applyNavFooterDeleteOverrides(byKey, extraContent);
13722
- for (const key of LOGO_IMAGE_KEYS) {
13723
- if (!(key in extraContent)) continue;
13724
- byKey.set(key, { key, type: "image", text: extraContent[key] ?? "" });
13725
- }
13726
- for (const key of [LOGO_PLACEHOLDER_KEY, LOGO_ALT_KEY]) {
13727
- if (!(key in extraContent)) continue;
13728
- byKey.set(key, { key, type: "meta", text: extraContent[key] ?? "" });
13729
- }
13730
- for (const key of LOGO_SIZE_KEYS) {
13731
- if (!(key in extraContent)) continue;
13732
- byKey.set(key, { key, type: "meta", text: extraContent[key] ?? "" });
13733
- }
13734
12892
  }
13735
12893
  return Array.from(byKey.values());
13736
12894
  }
@@ -14199,10 +13357,21 @@ function parseSchedulingInsertAfter(insertAfter) {
14199
13357
  insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
14200
13358
  };
14201
13359
  }
14202
- function resolveEntryAnchor(entry) {
14203
- if (entry.anchorId !== void 0) return { anchorId: entry.anchorId, beforeId: entry.beforeId ?? null };
14204
- const parsed = parseSchedulingInsertAfter(entry.insertAfter);
14205
- return { anchorId: parsed.anchor, beforeId: parsed.insertBefore };
13360
+ function resolveSchedulingInsert(insertAfter, explicitInsertBefore) {
13361
+ const parsed = parseSchedulingInsertAfter(insertAfter);
13362
+ const insertBefore = explicitInsertBefore ?? parsed.insertBefore;
13363
+ const effectiveInsertAfter = insertBefore && parsed.insertBefore === null ? `${insertAfter}${INSERT_BEFORE_MARKER}${insertBefore}` : insertAfter;
13364
+ return { effectiveInsertAfter, insertBefore };
13365
+ }
13366
+ function getSchedulingMountPoint(insertAfter) {
13367
+ const { anchor } = parseSchedulingInsertAfter(insertAfter);
13368
+ let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
13369
+ if (!anchorEl && anchor === "scheduling") {
13370
+ const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
13371
+ anchorEl = widgets.at(-1) ?? null;
13372
+ }
13373
+ if (!anchorEl) return null;
13374
+ return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
14206
13375
  }
14207
13376
  function schedulingMountDepth(insertAfter) {
14208
13377
  if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
@@ -14219,7 +13388,8 @@ function getPageSchedulingEntries(raw) {
14219
13388
  }
14220
13389
  }
14221
13390
  function isSchedulingWidgetMissing(entry) {
14222
- return !document.querySelector(`[data-ohw-section="${schedulingSectionId(entry.insertAfter)}"]`);
13391
+ const { effectiveInsertAfter } = resolveSchedulingInsert(entry.insertAfter);
13392
+ return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
14223
13393
  }
14224
13394
  function hasMissingSchedulingWidgets(entries) {
14225
13395
  return entries.some(isSchedulingWidgetMissing);
@@ -14249,17 +13419,16 @@ function initSectionsFromContent(content, removeExisting = false) {
14249
13419
  } catch {
14250
13420
  }
14251
13421
  }
14252
- function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, beforeId, existingWidgetId) {
14253
- const widgetId = existingWidgetId ?? (beforeId ? `${anchorId}${INSERT_BEFORE_MARKER}${beforeId}` : anchorId);
14254
- const sectionId = schedulingSectionId(widgetId);
13422
+ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId, explicitInsertBefore) {
13423
+ const { effectiveInsertAfter, insertBefore } = resolveSchedulingInsert(insertAfter, explicitInsertBefore);
13424
+ const sectionId = schedulingSectionId(effectiveInsertAfter);
14255
13425
  if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
14256
- const anchorEl = document.querySelector(`[data-ohw-section="${anchorId}"]`);
14257
- if (!anchorEl) return false;
14258
- const mountPoint = anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
13426
+ const mountPoint = getSchedulingMountPoint(effectiveInsertAfter);
13427
+ if (!mountPoint) return false;
14259
13428
  const container = document.createElement("div");
14260
13429
  container.dataset.ohwSectionContainer = "scheduling";
14261
- if (beforeId) {
14262
- const beforeAnchor = document.querySelector(`[data-ohw-section="${beforeId}"]`);
13430
+ if (insertBefore) {
13431
+ const beforeAnchor = document.querySelector(`[data-ohw-section="${insertBefore}"]`);
14263
13432
  const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
14264
13433
  if (!beforePoint) return false;
14265
13434
  beforePoint.insertAdjacentElement("beforebegin", container);
@@ -14270,25 +13439,19 @@ function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, be
14270
13439
  }
14271
13440
  tail.insertAdjacentElement("afterend", container);
14272
13441
  }
14273
- try {
14274
- const root = createRoot2(container);
14275
- flushSync2(() => {
14276
- root.render(
14277
- /* @__PURE__ */ jsx33(
14278
- SchedulingWidget,
14279
- {
14280
- notifyOnConnect,
14281
- initialScheduleId: scheduleId,
14282
- insertAfter: widgetId
14283
- }
14284
- )
14285
- );
14286
- });
14287
- } catch (err) {
14288
- console.error("[ow:scheduling] render threw", err);
14289
- container.remove();
14290
- return false;
14291
- }
13442
+ const root = createRoot2(container);
13443
+ flushSync2(() => {
13444
+ root.render(
13445
+ /* @__PURE__ */ jsx33(
13446
+ SchedulingWidget,
13447
+ {
13448
+ notifyOnConnect,
13449
+ initialScheduleId: scheduleId,
13450
+ insertAfter: effectiveInsertAfter
13451
+ }
13452
+ )
13453
+ );
13454
+ });
14292
13455
  const tracker = getSectionsTracker();
14293
13456
  let sections = [];
14294
13457
  try {
@@ -14296,12 +13459,10 @@ function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, be
14296
13459
  } catch {
14297
13460
  }
14298
13461
  const inEditor = typeof window !== "undefined" && window.self !== window.top;
14299
- if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === widgetId)) {
13462
+ if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === effectiveInsertAfter)) {
14300
13463
  sections.push({
14301
13464
  type: "scheduling",
14302
- insertAfter: widgetId,
14303
- anchorId,
14304
- beforeId: beforeId ?? null,
13465
+ insertAfter: effectiveInsertAfter,
14305
13466
  pagePath: window.location.pathname,
14306
13467
  ...scheduleId ? { scheduleId } : {}
14307
13468
  });
@@ -14315,8 +13476,7 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
14315
13476
  for (let i = pending.length - 1; i >= 0; i--) {
14316
13477
  const entry = pending[i];
14317
13478
  const shouldNotify = typeof notifyOnConnect === "function" ? notifyOnConnect(entry) : notifyOnConnect;
14318
- const { anchorId, beforeId } = resolveEntryAnchor(entry);
14319
- if (mountSchedulingWidget(anchorId, shouldNotify, entry.scheduleId ?? null, beforeId, entry.insertAfter)) {
13479
+ if (mountSchedulingWidget(entry.insertAfter, shouldNotify, entry.scheduleId)) {
14320
13480
  pending.splice(i, 1);
14321
13481
  }
14322
13482
  }
@@ -14464,13 +13624,6 @@ function isInsideLinkEditor(target) {
14464
13624
  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"]')
14465
13625
  );
14466
13626
  }
14467
- function isInsideFloatingPanel(target) {
14468
- return Boolean(target.closest("[data-ohw-floating-panel]"));
14469
- }
14470
- function isPointOverFloatingPanel(clientX, clientY) {
14471
- const el = document.elementFromPoint(clientX, clientY);
14472
- return Boolean(el instanceof Element && el.closest("[data-ohw-floating-panel]"));
14473
- }
14474
13627
  function getHrefKeyFromElement(el) {
14475
13628
  if (!el) return null;
14476
13629
  const anchor = el.closest("[data-ohw-href-key]");
@@ -14621,6 +13774,13 @@ function resolveFooterColumnSelectionTarget(target, clientX, clientY) {
14621
13774
  if (isPointOverNavItem(column, clientX, clientY)) return null;
14622
13775
  return column;
14623
13776
  }
13777
+ function isPointOverLogo(x, y) {
13778
+ for (const root of document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]')) {
13779
+ const r2 = root.getBoundingClientRect();
13780
+ if (r2.width > 0 && r2.height > 0 && x >= r2.left && x <= r2.right && y >= r2.top && y <= r2.bottom) return true;
13781
+ }
13782
+ return false;
13783
+ }
14624
13784
  function isPointOverNavigation(x, y) {
14625
13785
  const roots = /* @__PURE__ */ new Set();
14626
13786
  const navRoot = document.querySelector("[data-ohw-nav-root]") ?? document.querySelector("nav");
@@ -14709,7 +13869,7 @@ function getNavigationSelectionParent(el) {
14709
13869
  if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
14710
13870
  return getFooterLinksContainer();
14711
13871
  }
14712
- 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)) {
13872
+ if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
14713
13873
  return getNavigationRoot(el);
14714
13874
  }
14715
13875
  return null;
@@ -15302,45 +14462,6 @@ function StateToggle({
15302
14462
  );
15303
14463
  }
15304
14464
  var contentCache = /* @__PURE__ */ new Map();
15305
- var fetchedContentPaths = /* @__PURE__ */ new Set();
15306
- var OHW_LOADER_STYLE = {
15307
- position: "fixed",
15308
- inset: 0,
15309
- background: "#fff",
15310
- zIndex: 2147483646,
15311
- display: "flex",
15312
- alignItems: "center",
15313
- justifyContent: "center"
15314
- };
15315
- function OhwLoaderSpinner() {
15316
- return /* @__PURE__ */ jsxs20("svg", { width: "28", height: "28", viewBox: "0 0 28 28", fill: "none", "aria-hidden": true, children: [
15317
- /* @__PURE__ */ jsx33("circle", { cx: "14", cy: "14", r: "11", stroke: "#E7E5E4", strokeWidth: "3" }),
15318
- /* @__PURE__ */ jsx33(
15319
- "circle",
15320
- {
15321
- cx: "14",
15322
- cy: "14",
15323
- r: "11",
15324
- stroke: "#1C1917",
15325
- strokeWidth: "3",
15326
- strokeDasharray: "17 52",
15327
- strokeLinecap: "round",
15328
- children: /* @__PURE__ */ jsx33(
15329
- "animateTransform",
15330
- {
15331
- attributeName: "transform",
15332
- type: "rotate",
15333
- from: "0 14 14",
15334
- to: "360 14 14",
15335
- dur: "0.7s",
15336
- repeatCount: "indefinite"
15337
- }
15338
- )
15339
- }
15340
- )
15341
- ] });
15342
- }
15343
- 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){}})();`;
15344
14465
  function resolveSubdomain(subdomainFromQuery) {
15345
14466
  if (subdomainFromQuery) return subdomainFromQuery;
15346
14467
  if (typeof window !== "undefined") {
@@ -15363,8 +14484,8 @@ function OhhwellsBridge() {
15363
14484
  const router = useRouter3();
15364
14485
  const searchParams = useSearchParams();
15365
14486
  const isEditMode = isEditSessionActive();
15366
- const [bridgeRoot, setBridgeRoot] = useState12(null);
15367
- useEffect13(() => {
14487
+ const [bridgeRoot, setBridgeRoot] = useState11(null);
14488
+ useEffect12(() => {
15368
14489
  const figtreeFontId = "ohw-figtree-font";
15369
14490
  if (!document.getElementById(figtreeFontId)) {
15370
14491
  const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
@@ -15393,146 +14514,82 @@ function OhhwellsBridge() {
15393
14514
  const subdomain = resolveSubdomain(subdomainFromQuery);
15394
14515
  useLinkHrefGuardian(pathname, subdomain, isEditMode);
15395
14516
  useSavedLinkNavigation(isEditMode);
15396
- const postToParent2 = useCallback8((data) => {
14517
+ const postToParent2 = useCallback7((data) => {
15397
14518
  if (typeof window !== "undefined" && window.parent !== window) {
15398
14519
  window.parent.postMessage(data, "*");
15399
14520
  }
15400
14521
  }, []);
15401
- const [fetchState, setFetchState] = useState12("idle");
15402
- const autoSaveTimers = useRef10(/* @__PURE__ */ new Map());
15403
- const activeElRef = useRef10(null);
15404
- const pointerHeldRef = useRef10(false);
15405
- const selectedElRef = useRef10(null);
15406
- const selectedHrefKeyRef = useRef10(null);
15407
- const selectedFooterColAttrRef = useRef10(null);
15408
- const originalContentRef = useRef10(null);
15409
- const activeStateElRef = useRef10(null);
15410
- const parentScrollRef = useRef10(null);
15411
- const visibleViewportRef = useRef10(null);
15412
- const [dialogPortalContainer, setDialogPortalContainer] = useState12(null);
15413
- const attachVisibleViewport = useCallback8((node) => {
14522
+ const [fetchState, setFetchState] = useState11("idle");
14523
+ const autoSaveTimers = useRef9(/* @__PURE__ */ new Map());
14524
+ const activeElRef = useRef9(null);
14525
+ const pointerHeldRef = useRef9(false);
14526
+ const selectedElRef = useRef9(null);
14527
+ const selectedHrefKeyRef = useRef9(null);
14528
+ const selectedFooterColAttrRef = useRef9(null);
14529
+ const originalContentRef = useRef9(null);
14530
+ const activeStateElRef = useRef9(null);
14531
+ const parentScrollRef = useRef9(null);
14532
+ const visibleViewportRef = useRef9(null);
14533
+ const [dialogPortalContainer, setDialogPortalContainer] = useState11(null);
14534
+ const attachVisibleViewport = useCallback7((node) => {
15414
14535
  visibleViewportRef.current = node;
15415
14536
  setDialogPortalContainer(node);
15416
14537
  if (node) applyVisibleViewport(node, parentScrollRef.current);
15417
14538
  }, []);
15418
- const toolbarElRef = useRef10(null);
15419
- const glowElRef = useRef10(null);
15420
- const hoveredImageRef = useRef10(null);
15421
- const hoveredImageHasTextOverlapRef = useRef10(false);
15422
- const dragOverElRef = useRef10(null);
15423
- const [mediaHover, setMediaHover] = useState12(null);
15424
- const [selectedMedia, setSelectedMedia] = useState12(null);
15425
- const selectedMediaElRef = useRef10(null);
15426
- const clearMediaSelection = useCallback8(() => {
15427
- const prev = selectedMediaElRef.current;
15428
- selectedMediaElRef.current = null;
15429
- setSelectedMedia(null);
15430
- const sectionEl = prev?.closest("[data-ohw-section]") ?? null;
15431
- if (sectionEl) {
15432
- postToParentRef.current({
15433
- type: "ow:section-selected",
15434
- sectionId: sectionEl.dataset.ohwSection ?? null,
15435
- sectionLabel: sectionEl.dataset.ohwSectionLabel ?? titleCaseSectionId(sectionEl.dataset.ohwSection ?? ""),
15436
- key: null
15437
- });
15438
- }
15439
- }, []);
15440
- const clearMediaSelectionRef = useRef10(clearMediaSelection);
15441
- clearMediaSelectionRef.current = clearMediaSelection;
15442
- const selectMediaElement = useCallback8((el) => {
15443
- const r2 = el.getBoundingClientRect();
15444
- const video = el.dataset.ohwEditable === "video" ? el.querySelector("video") : null;
15445
- selectedMediaElRef.current = el;
15446
- setSelectedMedia({
15447
- key: el.dataset.ohwKey ?? "",
15448
- rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height },
15449
- elementType: el.dataset.ohwEditable ?? "image",
15450
- hasTextOverlap: false,
15451
- isDragOver: false,
15452
- ...video ? { videoAutoplay: video.autoplay, videoMuted: video.muted } : {}
15453
- });
15454
- const sectionEl = el.closest("[data-ohw-section]");
15455
- aiSectionApiRef.current?.selectFromElement(el, { report: false });
15456
- postToParentRef.current({
15457
- type: "ow:section-selected",
15458
- sectionId: sectionEl?.dataset.ohwSection ?? null,
15459
- sectionLabel: sectionEl ? sectionEl.dataset.ohwSectionLabel ?? titleCaseSectionId(sectionEl.dataset.ohwSection ?? "") : null,
15460
- key: el.dataset.ohwKey ?? null,
15461
- // Display name for the pill — the raw key prettifies into fragments ("Img"); the
15462
- // bridge knows what the node IS, so it names it.
15463
- keyLabel: el.dataset.ohwEditable === "video" ? "Video" : "Image"
15464
- });
15465
- }, []);
15466
- const selectMediaElementRef = useRef10(selectMediaElement);
15467
- selectMediaElementRef.current = selectMediaElement;
15468
- useEffect13(() => {
15469
- if (!selectedMedia) return;
15470
- const update = () => {
15471
- const el = selectedMediaElRef.current;
15472
- if (!el || !el.isConnected) {
15473
- clearMediaSelection();
15474
- return;
15475
- }
15476
- const r2 = el.getBoundingClientRect();
15477
- setSelectedMedia(
15478
- (prev) => prev ? { ...prev, rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height } } : prev
15479
- );
15480
- };
15481
- window.addEventListener("scroll", update, true);
15482
- window.addEventListener("resize", update);
15483
- return () => {
15484
- window.removeEventListener("scroll", update, true);
15485
- window.removeEventListener("resize", update);
15486
- };
15487
- }, [selectedMedia !== null]);
15488
- const [carouselHover, setCarouselHover] = useState12(null);
15489
- const [uploadingRects, setUploadingRects] = useState12({});
15490
- const hoveredGapRef = useRef10(null);
15491
- const imageUnhoverTimerRef = useRef10(null);
15492
- const imageShowTimerRef = useRef10(null);
15493
- const editStylesRef = useRef10(null);
15494
- const activateRef = useRef10(() => {
14539
+ const toolbarElRef = useRef9(null);
14540
+ const glowElRef = useRef9(null);
14541
+ const hoveredImageRef = useRef9(null);
14542
+ const hoveredImageHasTextOverlapRef = useRef9(false);
14543
+ const dragOverElRef = useRef9(null);
14544
+ const [mediaHover, setMediaHover] = useState11(null);
14545
+ const [carouselHover, setCarouselHover] = useState11(null);
14546
+ const [uploadingRects, setUploadingRects] = useState11({});
14547
+ const hoveredGapRef = useRef9(null);
14548
+ const imageUnhoverTimerRef = useRef9(null);
14549
+ const imageShowTimerRef = useRef9(null);
14550
+ const editStylesRef = useRef9(null);
14551
+ const activateRef = useRef9(() => {
15495
14552
  });
15496
- const deactivateRef = useRef10(() => {
14553
+ const deactivateRef = useRef9(() => {
15497
14554
  });
15498
- const selectRef = useRef10(() => {
14555
+ const selectRef = useRef9(() => {
15499
14556
  });
15500
- const selectFrameRef = useRef10(() => {
14557
+ const selectFrameRef = useRef9(() => {
15501
14558
  });
15502
- const selectLogoRef = useRef10(() => {
14559
+ const selectLogoRef = useRef9(() => {
15503
14560
  });
15504
- const openLogoSizePanelRef = useRef10(() => {
14561
+ const openLogoSizePanelRef = useRef9(() => {
15505
14562
  });
15506
- const deselectRef = useRef10(() => {
14563
+ const deselectRef = useRef9(() => {
15507
14564
  });
15508
- const closeFloatingPanelOnlyRef = useRef10(() => {
14565
+ const closeFloatingPanelOnlyRef = useRef9(() => {
15509
14566
  });
15510
- const reselectNavigationItemRef = useRef10(() => {
14567
+ const reselectNavigationItemRef = useRef9(() => {
15511
14568
  });
15512
- const commitNavigationTextEditRef = useRef10(() => {
14569
+ const commitNavigationTextEditRef = useRef9(() => {
15513
14570
  });
15514
- const handleDeleteSelectedRef = useRef10(() => false);
15515
- const runPendingDeleteUndoRef = useRef10(() => false);
15516
- const isFooterFrameSelectionRef = useRef10(false);
15517
- const refreshActiveCommandsRef = useRef10(() => {
14571
+ const handleDeleteSelectedRef = useRef9(() => false);
14572
+ const runPendingDeleteUndoRef = useRef9(() => false);
14573
+ const isFooterFrameSelectionRef = useRef9(false);
14574
+ const refreshActiveCommandsRef = useRef9(() => {
15518
14575
  });
15519
- const postToParentRef = useRef10(postToParent2);
14576
+ const postToParentRef = useRef9(postToParent2);
15520
14577
  postToParentRef.current = postToParent2;
15521
- const aiSectionApiRef = useRef10(null);
15522
- const sectionsLoadedRef = useRef10(false);
15523
- const pendingScheduleConfigRequests = useRef10([]);
15524
- const [toolbarRect, setToolbarRect] = useState12(null);
15525
- const [formPickRect, setFormPickRect] = useState12(null);
15526
- const formPickElRef = useRef10(null);
15527
- const [formViewState, setFormViewStateUi] = useState12("default");
15528
- const [formPickCount, setFormPickCount] = useState12(null);
15529
- const [formHoverRect, setFormHoverRect] = useState12(null);
15530
- const formHoverElRef = useRef10(null);
15531
- const [fieldPickRect, setFieldPickRect] = useState12(null);
15532
- const fieldPickElRef = useRef10(null);
15533
- const [fieldPickState, setFieldPickState] = useState12(null);
15534
- const [fieldTypePickerOpen, setFieldTypePickerOpen] = useState12(false);
15535
- const clearFormPick = useCallback8(() => {
14578
+ const aiSectionApiRef = useRef9(null);
14579
+ const sectionsLoadedRef = useRef9(false);
14580
+ const pendingScheduleConfigRequests = useRef9([]);
14581
+ const [toolbarRect, setToolbarRect] = useState11(null);
14582
+ const [formPickRect, setFormPickRect] = useState11(null);
14583
+ const formPickElRef = useRef9(null);
14584
+ const [formViewState, setFormViewStateUi] = useState11("default");
14585
+ const [formPickCount, setFormPickCount] = useState11(null);
14586
+ const [formHoverRect, setFormHoverRect] = useState11(null);
14587
+ const formHoverElRef = useRef9(null);
14588
+ const [fieldPickRect, setFieldPickRect] = useState11(null);
14589
+ const fieldPickElRef = useRef9(null);
14590
+ const [fieldPickState, setFieldPickState] = useState11(null);
14591
+ const [fieldTypePickerOpen, setFieldTypePickerOpen] = useState11(false);
14592
+ const clearFormPick = useCallback7(() => {
15536
14593
  const form = formPickElRef.current;
15537
14594
  const editing = fieldPickElRef.current;
15538
14595
  if (commitPlaceholderEdit(editing) && editing) {
@@ -15552,7 +14609,7 @@ function OhhwellsBridge() {
15552
14609
  formPickElRef.current = null;
15553
14610
  setFormPickRect(null);
15554
14611
  }, []);
15555
- const clearFieldPick = useCallback8(() => {
14612
+ const clearFieldPick = useCallback7(() => {
15556
14613
  const wrapper = fieldPickElRef.current;
15557
14614
  if (commitPlaceholderEdit(wrapper) && wrapper) {
15558
14615
  const form = wrapper.closest('[data-ohw-editable="form"]');
@@ -15562,9 +14619,9 @@ function OhhwellsBridge() {
15562
14619
  setFieldPickRect(null);
15563
14620
  setFieldPickState(null);
15564
14621
  }, []);
15565
- const persistFieldsRef = useRef10(() => {
14622
+ const persistFieldsRef = useRef9(() => {
15566
14623
  });
15567
- const persistFields = useCallback8(
14624
+ const persistFields = useCallback7(
15568
14625
  (form) => {
15569
14626
  const key = formKeyOf(form);
15570
14627
  if (!key) return;
@@ -15575,7 +14632,7 @@ function OhhwellsBridge() {
15575
14632
  []
15576
14633
  );
15577
14634
  persistFieldsRef.current = persistFields;
15578
- const selectField = useCallback8((wrapper) => {
14635
+ const selectField = useCallback7((wrapper) => {
15579
14636
  if (fieldPickElRef.current && fieldPickElRef.current !== wrapper) {
15580
14637
  commitPlaceholderEdit(fieldPickElRef.current);
15581
14638
  }
@@ -15588,7 +14645,7 @@ function OhhwellsBridge() {
15588
14645
  setFieldPickState({ type: fieldTypeOf(wrapper), required: isFieldRequired(wrapper) });
15589
14646
  setFieldTypePickerOpen(false);
15590
14647
  }, []);
15591
- const withSelectedField = useCallback8(
14648
+ const withSelectedField = useCallback7(
15592
14649
  (run) => {
15593
14650
  const wrapper = fieldPickElRef.current;
15594
14651
  const form = formPickElRef.current;
@@ -15601,28 +14658,28 @@ function OhhwellsBridge() {
15601
14658
  },
15602
14659
  [persistFields]
15603
14660
  );
15604
- const handleFieldTypeChange = useCallback8(
14661
+ const handleFieldTypeChange = useCallback7(
15605
14662
  (type) => withSelectedField((_form, wrapper) => {
15606
14663
  applyFieldType(wrapper, type);
15607
14664
  selectField(wrapper);
15608
14665
  }),
15609
14666
  [selectField, withSelectedField]
15610
14667
  );
15611
- const handleFieldRequiredToggle = useCallback8(
14668
+ const handleFieldRequiredToggle = useCallback7(
15612
14669
  () => withSelectedField((_form, wrapper) => {
15613
14670
  setFieldRequired(wrapper, !isFieldRequired(wrapper));
15614
14671
  selectField(wrapper);
15615
14672
  }),
15616
14673
  [selectField, withSelectedField]
15617
14674
  );
15618
- const handleFieldDuplicate = useCallback8(
14675
+ const handleFieldDuplicate = useCallback7(
15619
14676
  () => withSelectedField((form, wrapper) => {
15620
14677
  const copy = duplicateField(form, wrapper);
15621
14678
  selectField(copy);
15622
14679
  }),
15623
14680
  [selectField, withSelectedField]
15624
14681
  );
15625
- const handleFieldDelete = useCallback8(
14682
+ const handleFieldDelete = useCallback7(
15626
14683
  () => withSelectedField((_form, wrapper) => {
15627
14684
  removeField(wrapper);
15628
14685
  clearFieldPick();
@@ -15630,7 +14687,7 @@ function OhhwellsBridge() {
15630
14687
  }),
15631
14688
  [clearFieldPick, withSelectedField]
15632
14689
  );
15633
- const handleAddField = useCallback8(
14690
+ const handleAddField = useCallback7(
15634
14691
  (type) => {
15635
14692
  const form = formPickElRef.current;
15636
14693
  if (!form) return;
@@ -15646,8 +14703,8 @@ function OhhwellsBridge() {
15646
14703
  },
15647
14704
  [persistFields, selectField]
15648
14705
  );
15649
- const fieldDragRef = useRef10(null);
15650
- const buildFieldDropSlots = useCallback8((form, draggedKey) => {
14706
+ const fieldDragRef = useRef9(null);
14707
+ const buildFieldDropSlots = useCallback7((form, draggedKey) => {
15651
14708
  const others = listFieldWrappers(form).filter((el) => fieldKeyOf(el) !== draggedKey);
15652
14709
  const slots = others.map((el) => {
15653
14710
  const rect = el.getBoundingClientRect();
@@ -15660,7 +14717,7 @@ function OhhwellsBridge() {
15660
14717
  }
15661
14718
  return slots;
15662
14719
  }, []);
15663
- const handleFieldDragStart = useCallback8(() => {
14720
+ const handleFieldDragStart = useCallback7(() => {
15664
14721
  const wrapper = fieldPickElRef.current;
15665
14722
  const form = formPickElRef.current;
15666
14723
  if (!wrapper || !form) return;
@@ -15669,18 +14726,18 @@ function OhhwellsBridge() {
15669
14726
  setFieldDragging(true);
15670
14727
  setFieldDropSlots(buildFieldDropSlots(form, key));
15671
14728
  }, [buildFieldDropSlots]);
15672
- const handleFieldDragEnd = useCallback8(() => {
14729
+ const handleFieldDragEnd = useCallback7(() => {
15673
14730
  fieldDragRef.current = null;
15674
14731
  setFieldDropIndex(null);
15675
14732
  setFieldDropSlots([]);
15676
14733
  setFieldDragging(false);
15677
14734
  }, []);
15678
- const [fieldDropIndex, setFieldDropIndex] = useState12(null);
15679
- const [fieldDropSlots, setFieldDropSlots] = useState12([]);
15680
- const [fieldDragging, setFieldDragging] = useState12(false);
15681
- const clearFormPickRef = useRef10(clearFormPick);
14735
+ const [fieldDropIndex, setFieldDropIndex] = useState11(null);
14736
+ const [fieldDropSlots, setFieldDropSlots] = useState11([]);
14737
+ const [fieldDragging, setFieldDragging] = useState11(false);
14738
+ const clearFormPickRef = useRef9(clearFormPick);
15682
14739
  clearFormPickRef.current = clearFormPick;
15683
- useEffect13(() => {
14740
+ useEffect12(() => {
15684
14741
  const el = fieldPickElRef.current;
15685
14742
  if (!el || fieldPickRect === null) return;
15686
14743
  const observer = new ResizeObserver(() => {
@@ -15689,7 +14746,7 @@ function OhhwellsBridge() {
15689
14746
  observer.observe(el);
15690
14747
  return () => observer.disconnect();
15691
14748
  }, [fieldPickRect !== null, fieldPickState]);
15692
- useEffect13(() => {
14749
+ useEffect12(() => {
15693
14750
  const el = formPickElRef.current;
15694
14751
  if (!el || formPickRect === null) return;
15695
14752
  const observer = new ResizeObserver(() => {
@@ -15698,25 +14755,25 @@ function OhhwellsBridge() {
15698
14755
  observer.observe(el);
15699
14756
  return () => observer.disconnect();
15700
14757
  }, [formPickRect !== null, formViewState]);
15701
- const [toolbarVariant, setToolbarVariant] = useState12("none");
15702
- const toolbarVariantRef = useRef10("none");
14758
+ const [toolbarVariant, setToolbarVariant] = useState11("none");
14759
+ const toolbarVariantRef = useRef9("none");
15703
14760
  toolbarVariantRef.current = toolbarVariant;
15704
- const [selectedIsCta, setSelectedIsCta] = useState12(false);
15705
- const [selectedIsSocial, setSelectedIsSocial] = useState12(false);
15706
- const [selectedIsSocialsRow, setSelectedIsSocialsRow] = useState12(false);
15707
- const [reorderHrefKey, setReorderHrefKey] = useState12(null);
15708
- const [reorderDragDisabled, setReorderDragDisabled] = useState12(false);
15709
- const [toggleState, setToggleState] = useState12(null);
15710
- const [maxBadge, setMaxBadge] = useState12(null);
15711
- const [activeCommands, setActiveCommands] = useState12(/* @__PURE__ */ new Set());
15712
- const [sectionGap, setSectionGap] = useState12(null);
15713
- const [toolbarShowEditLink, setToolbarShowEditLink] = useState12(false);
15714
- const hoveredNavContainerRef = useRef10(null);
15715
- const [hoveredNavContainerRect, setHoveredNavContainerRect] = useState12(null);
15716
- const hoveredItemElRef = useRef10(null);
15717
- const [hoveredItemRect, setHoveredItemRect] = useState12(null);
15718
- const [hoveredTextRect, setHoveredTextRect] = useState12(null);
15719
- useEffect13(() => {
14761
+ const [selectedIsCta, setSelectedIsCta] = useState11(false);
14762
+ const [selectedIsSocial, setSelectedIsSocial] = useState11(false);
14763
+ const [selectedIsSocialsRow, setSelectedIsSocialsRow] = useState11(false);
14764
+ const [reorderHrefKey, setReorderHrefKey] = useState11(null);
14765
+ const [reorderDragDisabled, setReorderDragDisabled] = useState11(false);
14766
+ const [toggleState, setToggleState] = useState11(null);
14767
+ const [maxBadge, setMaxBadge] = useState11(null);
14768
+ const [activeCommands, setActiveCommands] = useState11(/* @__PURE__ */ new Set());
14769
+ const [sectionGap, setSectionGap] = useState11(null);
14770
+ const [toolbarShowEditLink, setToolbarShowEditLink] = useState11(false);
14771
+ const hoveredNavContainerRef = useRef9(null);
14772
+ const [hoveredNavContainerRect, setHoveredNavContainerRect] = useState11(null);
14773
+ const hoveredItemElRef = useRef9(null);
14774
+ const [hoveredItemRect, setHoveredItemRect] = useState11(null);
14775
+ const [hoveredTextRect, setHoveredTextRect] = useState11(null);
14776
+ useEffect12(() => {
15720
14777
  const sync = () => {
15721
14778
  const el = document.querySelector(
15722
14779
  '[data-ohw-hovered]:not([contenteditable]):not([data-ohw-href-key]):not([data-ohw-editable="form"] *)'
@@ -15737,56 +14794,43 @@ function OhhwellsBridge() {
15737
14794
  });
15738
14795
  return () => observer.disconnect();
15739
14796
  }, []);
15740
- const siblingHintElRef = useRef10(null);
15741
- const [siblingHintRect, setSiblingHintRect] = useState12(null);
15742
- const [siblingHintRects, setSiblingHintRects] = useState12([]);
15743
- const [isItemDragging, setIsItemDragging] = useState12(false);
15744
- const [isFooterFrameSelection, setIsFooterFrameSelection] = useState12(false);
14797
+ const siblingHintElRef = useRef9(null);
14798
+ const [siblingHintRect, setSiblingHintRect] = useState11(null);
14799
+ const [siblingHintRects, setSiblingHintRects] = useState11([]);
14800
+ const [isItemDragging, setIsItemDragging] = useState11(false);
14801
+ const [isFooterFrameSelection, setIsFooterFrameSelection] = useState11(false);
15745
14802
  isFooterFrameSelectionRef.current = isFooterFrameSelection;
15746
- const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = useState12(null);
15747
- const [footerHeadingVisible, setFooterHeadingVisible] = useState12(null);
15748
- const footerDragRef = useRef10(null);
15749
- const [footerDropSlots, setFooterDropSlots] = useState12([]);
15750
- const [activeFooterDropIndex, setActiveFooterDropIndex] = useState12(null);
15751
- const [draggedItemRect, setDraggedItemRect] = useState12(null);
15752
- const footerPointerDragRef = useRef10(null);
15753
- const suppressNextClickRef = useRef10(false);
15754
- const suppressClickUntilRef = useRef10(0);
15755
- const [linkPopover, setLinkPopover] = useState12(null);
15756
- const linkPopoverSessionRef = useRef10(null);
15757
- const addNavAfterAnchorRef = useRef10(null);
15758
- const editContentRef = useRef10({});
15759
- const aiSectionsRef = useRef10("");
15760
- const brandKitRef = useRef10("");
15761
- const stylesRef = useRef10("");
15762
- const pendingDeleteUndoRef = useRef10(null);
15763
- const [floatingPanel, setFloatingPanel] = useState12(null);
15764
- const floatingPanelOpenRef = useRef10(false);
15765
- const setFloatingPanelRef = useRef10(setFloatingPanel);
15766
- const [floatingPanelPos, setFloatingPanelPos] = useState12(null);
15767
- const [logoSizeDraft, setLogoSizeDraft] = useState12(null);
15768
- const [editorViewport, setEditorViewport] = useState12("desktop");
15769
- const [parentScrollSnap, setParentScrollSnap] = useState12(null);
15770
- const [sitePages, setSitePages] = useState12([]);
15771
- const [sectionsByPath, setSectionsByPath] = useState12({});
15772
- const sectionsPrefetchGenRef = useRef10(0);
15773
- const setLinkPopoverRef = useRef10(setLinkPopover);
15774
- const linkPopoverPanelRef = useRef10(null);
15775
- const linkPopoverOpenRef = useRef10(false);
15776
- const linkPopoverGraceUntilRef = useRef10(0);
14803
+ const [floatingPanel, setFloatingPanel] = useState11(null);
14804
+ const floatingPanelOpenRef = useRef9(false);
14805
+ floatingPanelOpenRef.current = floatingPanel !== null;
14806
+ const [floatingPanelPos, setFloatingPanelPos] = useState11(null);
14807
+ const [logoSizeDraft, setLogoSizeDraft] = useState11(null);
14808
+ const [editorViewport, setEditorViewport] = useState11("desktop");
14809
+ const [parentScrollSnap, setParentScrollSnap] = useState11(null);
14810
+ const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = useState11(null);
14811
+ const [footerHeadingVisible, setFooterHeadingVisible] = useState11(null);
14812
+ const footerDragRef = useRef9(null);
14813
+ const [footerDropSlots, setFooterDropSlots] = useState11([]);
14814
+ const [activeFooterDropIndex, setActiveFooterDropIndex] = useState11(null);
14815
+ const [draggedItemRect, setDraggedItemRect] = useState11(null);
14816
+ const footerPointerDragRef = useRef9(null);
14817
+ const suppressNextClickRef = useRef9(false);
14818
+ const suppressClickUntilRef = useRef9(0);
14819
+ const [linkPopover, setLinkPopover] = useState11(null);
14820
+ const linkPopoverSessionRef = useRef9(null);
14821
+ const addNavAfterAnchorRef = useRef9(null);
14822
+ const editContentRef = useRef9({});
14823
+ const aiSectionsRef = useRef9("");
14824
+ const pendingDeleteUndoRef = useRef9(null);
14825
+ const [sitePages, setSitePages] = useState11([]);
14826
+ const [sectionsByPath, setSectionsByPath] = useState11({});
14827
+ const sectionsPrefetchGenRef = useRef9(0);
14828
+ const setLinkPopoverRef = useRef9(setLinkPopover);
14829
+ const linkPopoverPanelRef = useRef9(null);
14830
+ const linkPopoverOpenRef = useRef9(false);
14831
+ const linkPopoverGraceUntilRef = useRef9(0);
15777
14832
  setLinkPopoverRef.current = setLinkPopover;
15778
- setFloatingPanelRef.current = setFloatingPanel;
15779
14833
  linkPopoverSessionRef.current = linkPopover;
15780
- floatingPanelOpenRef.current = Boolean(floatingPanel);
15781
- useEffect13(() => {
15782
- const syncViewport = () => {
15783
- const next = window.innerWidth <= 480 ? "mobile" : "desktop";
15784
- setEditorViewport((prev) => prev === next ? prev : next);
15785
- };
15786
- syncViewport();
15787
- window.addEventListener("resize", syncViewport);
15788
- return () => window.removeEventListener("resize", syncViewport);
15789
- }, []);
15790
14834
  const {
15791
14835
  navDragRef,
15792
14836
  navDropSlots,
@@ -15819,20 +14863,10 @@ function OhhwellsBridge() {
15819
14863
  getNavigationItemAnchor,
15820
14864
  isDragHandleDisabled
15821
14865
  });
15822
- const { sectionDropSlots, activeSectionDropIndex, isSectionDragging } = useSectionDrag({
15823
- isEditMode,
15824
- editContentRef,
15825
- postToParentRef,
15826
- parentScrollRef,
15827
- navDragRef,
15828
- footerDragRef,
15829
- suppressNextClickRef,
15830
- suppressClickUntilRef
15831
- });
15832
14866
  const bumpLinkPopoverGrace = () => {
15833
14867
  linkPopoverGraceUntilRef.current = Date.now() + 350;
15834
14868
  };
15835
- const runSectionsPrefetch = useCallback8((pages) => {
14869
+ const runSectionsPrefetch = useCallback7((pages) => {
15836
14870
  if (!isEditMode || shouldUseDevFixtures() || pages.length === 0) return;
15837
14871
  const gen = ++sectionsPrefetchGenRef.current;
15838
14872
  const paths = pages.map((p) => p.path);
@@ -15851,9 +14885,9 @@ function OhhwellsBridge() {
15851
14885
  );
15852
14886
  });
15853
14887
  }, [isEditMode, pathname]);
15854
- const runSectionsPrefetchRef = useRef10(runSectionsPrefetch);
14888
+ const runSectionsPrefetchRef = useRef9(runSectionsPrefetch);
15855
14889
  runSectionsPrefetchRef.current = runSectionsPrefetch;
15856
- useEffect13(() => {
14890
+ useEffect12(() => {
15857
14891
  if (!linkPopover) {
15858
14892
  document.documentElement.removeAttribute("data-ohw-link-popover-open");
15859
14893
  return;
@@ -15881,7 +14915,7 @@ function OhhwellsBridge() {
15881
14915
  document.documentElement.removeAttribute("data-ohw-link-popover-open");
15882
14916
  };
15883
14917
  }, [linkPopover, postToParent2]);
15884
- useEffect13(() => {
14918
+ useEffect12(() => {
15885
14919
  if (!isEditMode) return;
15886
14920
  const useFixtures = shouldUseDevFixtures();
15887
14921
  if (useFixtures) {
@@ -15905,14 +14939,14 @@ function OhhwellsBridge() {
15905
14939
  if (!useFixtures) postToParent2({ type: "ow:request-site-pages" });
15906
14940
  return () => window.removeEventListener("message", onSitePages);
15907
14941
  }, [isEditMode, postToParent2]);
15908
- useEffect13(() => {
14942
+ useEffect12(() => {
15909
14943
  if (!isEditMode || shouldUseDevFixtures()) return;
15910
14944
  void loadAllSectionsManifest().then((manifest) => {
15911
14945
  if (Object.keys(manifest).length === 0) return;
15912
14946
  setSectionsByPath((prev) => ({ ...manifest, ...prev }));
15913
14947
  });
15914
14948
  }, [isEditMode]);
15915
- useEffect13(() => {
14949
+ useEffect12(() => {
15916
14950
  const update = () => {
15917
14951
  const el = activeElRef.current ?? selectedElRef.current;
15918
14952
  if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
@@ -15936,10 +14970,10 @@ function OhhwellsBridge() {
15936
14970
  vvp.removeEventListener("resize", update);
15937
14971
  };
15938
14972
  }, []);
15939
- const refreshStateRules = useCallback8(() => {
14973
+ const refreshStateRules = useCallback7(() => {
15940
14974
  editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
15941
14975
  }, []);
15942
- const processConfigRequest = useCallback8((insertAfterVal) => {
14976
+ const processConfigRequest = useCallback7((insertAfterVal) => {
15943
14977
  const tracker = getSectionsTracker();
15944
14978
  let entries = [];
15945
14979
  try {
@@ -15962,7 +14996,7 @@ function OhhwellsBridge() {
15962
14996
  }
15963
14997
  window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: null }, "*");
15964
14998
  }, [isEditMode]);
15965
- const deactivate = useCallback8(() => {
14999
+ const deactivate = useCallback7(() => {
15966
15000
  const el = activeElRef.current;
15967
15001
  if (!el) return;
15968
15002
  const isFormBlock = el.dataset.ohwEditable === "form";
@@ -16003,12 +15037,12 @@ function OhhwellsBridge() {
16003
15037
  setToolbarShowEditLink(false);
16004
15038
  postToParent2({ type: "ow:exit-edit" });
16005
15039
  }, [postToParent2]);
16006
- const clearSelectedAttr = useCallback8(() => {
15040
+ const clearSelectedAttr = useCallback7(() => {
16007
15041
  document.querySelectorAll("[data-ohw-selected]").forEach((el) => {
16008
15042
  el.removeAttribute("data-ohw-selected");
16009
15043
  });
16010
15044
  }, []);
16011
- const deselect = useCallback8(() => {
15045
+ const deselect = useCallback7(() => {
16012
15046
  clearSelectedAttr();
16013
15047
  selectedElRef.current = null;
16014
15048
  selectedHrefKeyRef.current = null;
@@ -16037,11 +15071,11 @@ function OhhwellsBridge() {
16037
15071
  setToolbarVariant("none");
16038
15072
  }
16039
15073
  }, [clearSelectedAttr]);
16040
- const markSelected = useCallback8((el) => {
15074
+ const markSelected = useCallback7((el) => {
16041
15075
  clearSelectedAttr();
16042
15076
  el.setAttribute("data-ohw-selected", "");
16043
15077
  }, [clearSelectedAttr]);
16044
- const resolveHrefKeyElement = useCallback8((hrefKey) => {
15078
+ const resolveHrefKeyElement = useCallback7((hrefKey) => {
16045
15079
  if (isFooterHrefKey(hrefKey)) {
16046
15080
  return document.querySelector(
16047
15081
  `footer [data-ohw-href-key="${CSS.escape(hrefKey)}"]`
@@ -16056,7 +15090,7 @@ function OhhwellsBridge() {
16056
15090
  `[data-ohw-href-key="${CSS.escape(hrefKey)}"]`
16057
15091
  );
16058
15092
  }, []);
16059
- const resyncSelectedNavigationItem = useCallback8(() => {
15093
+ const resyncSelectedNavigationItem = useCallback7(() => {
16060
15094
  const hrefKey = selectedHrefKeyRef.current;
16061
15095
  if (hrefKey) {
16062
15096
  const link = resolveHrefKeyElement(hrefKey);
@@ -16094,7 +15128,7 @@ function OhhwellsBridge() {
16094
15128
  );
16095
15129
  }
16096
15130
  }, [resolveHrefKeyElement]);
16097
- const reselectNavigationItem = useCallback8((navAnchor) => {
15131
+ const reselectNavigationItem = useCallback7((navAnchor) => {
16098
15132
  selectedElRef.current = navAnchor;
16099
15133
  selectedHrefKeyRef.current = navAnchor.getAttribute("data-ohw-href-key");
16100
15134
  selectedFooterColAttrRef.current = null;
@@ -16125,7 +15159,7 @@ function OhhwellsBridge() {
16125
15159
  setToolbarShowEditLink(false);
16126
15160
  setActiveCommands(/* @__PURE__ */ new Set());
16127
15161
  }, [markSelected]);
16128
- const commitNavigationTextEdit = useCallback8((navAnchor) => {
15162
+ const commitNavigationTextEdit = useCallback7((navAnchor) => {
16129
15163
  const el = activeElRef.current;
16130
15164
  if (!el) return;
16131
15165
  const key = el.dataset.ohwKey;
@@ -16158,7 +15192,7 @@ function OhhwellsBridge() {
16158
15192
  postToParent2({ type: "ow:exit-edit" });
16159
15193
  reselectNavigationItem(navAnchor);
16160
15194
  }, [postToParent2, reselectNavigationItem]);
16161
- const handleAddTopLevelNavItem = useCallback8(() => {
15195
+ const handleAddTopLevelNavItem = useCallback7(() => {
16162
15196
  const items = listNavbarRootItems();
16163
15197
  addNavAfterAnchorRef.current = items[items.length - 1] ?? null;
16164
15198
  deselectRef.current();
@@ -16170,7 +15204,7 @@ function OhhwellsBridge() {
16170
15204
  intent: "add-nav"
16171
15205
  });
16172
15206
  }, []);
16173
- const maybeWarnNavLinkDropdownConflict = useCallback8(
15207
+ const maybeWarnNavLinkDropdownConflict = useCallback7(
16174
15208
  (anchor) => {
16175
15209
  if (!isNavbarHrefKey(anchor.getAttribute("data-ohw-href-key"))) return;
16176
15210
  if (!navDropdownsOpenOnClick()) return;
@@ -16183,7 +15217,7 @@ function OhhwellsBridge() {
16183
15217
  },
16184
15218
  [postToParent2]
16185
15219
  );
16186
- const handleNavDropdownOpenChange = useCallback8((open) => {
15220
+ const handleNavDropdownOpenChange = useCallback7((open) => {
16187
15221
  const selected = selectedElRef.current;
16188
15222
  if (!selected || !isNavigationItem2(selected)) return;
16189
15223
  setNavGroupForceOpen(selected, open);
@@ -16195,7 +15229,7 @@ function OhhwellsBridge() {
16195
15229
  }
16196
15230
  });
16197
15231
  }, []);
16198
- const handleFooterHeadingVisibleChange = useCallback8(
15232
+ const handleFooterHeadingVisibleChange = useCallback7(
16199
15233
  (visible) => {
16200
15234
  const selected = selectedElRef.current;
16201
15235
  if (!selected || !isFooterFrameSelectionRef.current) return;
@@ -16219,7 +15253,7 @@ function OhhwellsBridge() {
16219
15253
  },
16220
15254
  [postToParent2]
16221
15255
  );
16222
- const enterEditOnNewItem = useCallback8((anchor) => {
15256
+ const enterEditOnNewItem = useCallback7((anchor) => {
16223
15257
  const label = anchor.querySelector('[data-ohw-editable="text"]');
16224
15258
  if (!label) {
16225
15259
  selectRef.current(anchor);
@@ -16228,7 +15262,7 @@ function OhhwellsBridge() {
16228
15262
  setNavGroupForceOpen(anchor, true);
16229
15263
  activateRef.current(label);
16230
15264
  }, []);
16231
- const handleAddChildItem = useCallback8(() => {
15265
+ const handleAddChildItem = useCallback7(() => {
16232
15266
  const selected = selectedElRef.current;
16233
15267
  if (!selected) return;
16234
15268
  const socialsRow = isSocialsRow(selected) ? selected : findSocialsRow(selected);
@@ -16340,7 +15374,7 @@ function OhhwellsBridge() {
16340
15374
  enterEditOnNewItem(result.anchor);
16341
15375
  });
16342
15376
  }, [enterEditOnNewItem, isFooterFrameSelection, maybeWarnNavLinkDropdownConflict, postToParent2]);
16343
- const handleAddFooterColumn = useCallback8(() => {
15377
+ const handleAddFooterColumn = useCallback7(() => {
16344
15378
  if (!canAddFooterColumn()) {
16345
15379
  postToParent2({
16346
15380
  type: "ow:toast",
@@ -16361,7 +15395,7 @@ function OhhwellsBridge() {
16361
15395
  selectRef.current(result.firstLink);
16362
15396
  });
16363
15397
  }, [postToParent2]);
16364
- const clearFooterDragVisuals = useCallback8(() => {
15398
+ const clearFooterDragVisuals = useCallback7(() => {
16365
15399
  footerDragRef.current = null;
16366
15400
  setSiblingHintRects([]);
16367
15401
  setFooterDropSlots([]);
@@ -16370,7 +15404,7 @@ function OhhwellsBridge() {
16370
15404
  setIsItemDragging(false);
16371
15405
  unlockFooterDragInteraction();
16372
15406
  }, []);
16373
- const refreshFooterDragVisuals = useCallback8((session, activeSlot, clientX, clientY) => {
15407
+ const refreshFooterDragVisuals = useCallback7((session, activeSlot, clientX, clientY) => {
16374
15408
  const dragged = session.draggedEl;
16375
15409
  setDraggedItemRect(dragged.getBoundingClientRect());
16376
15410
  if (typeof clientX === "number" && typeof clientY === "number") {
@@ -16402,13 +15436,13 @@ function OhhwellsBridge() {
16402
15436
  const activeIdx = activeSlot ? slots.findIndex((s) => s.insertIndex === activeSlot.insertIndex) : -1;
16403
15437
  setActiveFooterDropIndex(activeIdx >= 0 ? activeIdx : null);
16404
15438
  }, []);
16405
- const refreshFooterDragVisualsRef = useRef10(refreshFooterDragVisuals);
15439
+ const refreshFooterDragVisualsRef = useRef9(refreshFooterDragVisuals);
16406
15440
  refreshFooterDragVisualsRef.current = refreshFooterDragVisuals;
16407
- const commitFooterDragRef = useRef10(() => {
15441
+ const commitFooterDragRef = useRef9(() => {
16408
15442
  });
16409
- const beginFooterDragRef = useRef10(() => {
15443
+ const beginFooterDragRef = useRef9(() => {
16410
15444
  });
16411
- const beginFooterDrag = useCallback8(
15445
+ const beginFooterDrag = useCallback7(
16412
15446
  (session) => {
16413
15447
  const rect = session.draggedEl.getBoundingClientRect();
16414
15448
  session.lastClientX = session.lastClientX || rect.left + rect.width / 2;
@@ -16428,7 +15462,7 @@ function OhhwellsBridge() {
16428
15462
  [refreshFooterDragVisuals]
16429
15463
  );
16430
15464
  beginFooterDragRef.current = beginFooterDrag;
16431
- const commitFooterDrag = useCallback8(
15465
+ const commitFooterDrag = useCallback7(
16432
15466
  (clientX, clientY) => {
16433
15467
  const session = footerDragRef.current;
16434
15468
  if (!session) {
@@ -16556,7 +15590,7 @@ function OhhwellsBridge() {
16556
15590
  [clearFooterDragVisuals, resolveHrefKeyElement, resyncSelectedNavigationItem]
16557
15591
  );
16558
15592
  commitFooterDragRef.current = commitFooterDrag;
16559
- const startFooterLinkDrag = useCallback8(
15593
+ const startFooterLinkDrag = useCallback7(
16560
15594
  (anchor, clientX, clientY, wasSelected) => {
16561
15595
  const hrefKey = anchor.getAttribute("data-ohw-href-key");
16562
15596
  if (!hrefKey) return false;
@@ -16592,7 +15626,7 @@ function OhhwellsBridge() {
16592
15626
  },
16593
15627
  [beginFooterDrag]
16594
15628
  );
16595
- const startFooterColumnDrag = useCallback8(
15629
+ const startFooterColumnDrag = useCallback7(
16596
15630
  (columnEl, clientX, clientY, wasSelected) => {
16597
15631
  const columns = listFooterColumns();
16598
15632
  const idx = columns.indexOf(columnEl);
@@ -16612,7 +15646,7 @@ function OhhwellsBridge() {
16612
15646
  },
16613
15647
  [beginFooterDrag]
16614
15648
  );
16615
- const handleItemDragStart = useCallback8(
15649
+ const handleItemDragStart = useCallback7(
16616
15650
  (e) => {
16617
15651
  const selected = selectedElRef.current;
16618
15652
  if (!selected) {
@@ -16632,7 +15666,7 @@ function OhhwellsBridge() {
16632
15666
  },
16633
15667
  [startFooterColumnDrag, startFooterLinkDrag, startNavLinkDrag]
16634
15668
  );
16635
- const handleItemDragEnd = useCallback8(
15669
+ const handleItemDragEnd = useCallback7(
16636
15670
  (e) => {
16637
15671
  if (footerDragRef.current) {
16638
15672
  const x = e?.clientX;
@@ -16658,7 +15692,7 @@ function OhhwellsBridge() {
16658
15692
  },
16659
15693
  [commitFooterDrag, commitNavDrag, navDragRef]
16660
15694
  );
16661
- const handleItemChromePointerDown = useCallback8((e) => {
15695
+ const handleItemChromePointerDown = useCallback7((e) => {
16662
15696
  if (e.button !== 0) return;
16663
15697
  const selected = selectedElRef.current;
16664
15698
  if (!selected) return;
@@ -16689,7 +15723,7 @@ function OhhwellsBridge() {
16689
15723
  }
16690
15724
  if (armNavPressFromChrome(selected, e.clientX, e.clientY, e.pointerId)) return;
16691
15725
  }, [armNavPressFromChrome]);
16692
- const handleItemChromeClick = useCallback8((clientX, clientY) => {
15726
+ const handleItemChromeClick = useCallback7((clientX, clientY) => {
16693
15727
  if (suppressNextClickRef.current || Date.now() < suppressClickUntilRef.current) {
16694
15728
  suppressNextClickRef.current = false;
16695
15729
  return;
@@ -16702,7 +15736,7 @@ function OhhwellsBridge() {
16702
15736
  }, []);
16703
15737
  reselectNavigationItemRef.current = reselectNavigationItem;
16704
15738
  commitNavigationTextEditRef.current = commitNavigationTextEdit;
16705
- const select = useCallback8((anchor) => {
15739
+ const select = useCallback7((anchor) => {
16706
15740
  if (!isNavigationItem2(anchor)) return;
16707
15741
  if (activeElRef.current) deactivate();
16708
15742
  aiSectionApiRef.current?.selectFromElement(anchor);
@@ -16745,7 +15779,7 @@ function OhhwellsBridge() {
16745
15779
  setFloatingPanel(null);
16746
15780
  setLogoSizeDraft(null);
16747
15781
  }, [deactivate, markSelected]);
16748
- const selectFrame = useCallback8((el) => {
15782
+ const selectFrame = useCallback7((el) => {
16749
15783
  if (!isNavigationContainer(el)) return;
16750
15784
  if (activeElRef.current) deactivate();
16751
15785
  aiSectionApiRef.current?.selectFromElement(el);
@@ -16796,7 +15830,7 @@ function OhhwellsBridge() {
16796
15830
  setFloatingPanel(null);
16797
15831
  setLogoSizeDraft(null);
16798
15832
  }, [deactivate, markSelected, postToParent2]);
16799
- const selectLogo = useCallback8(
15833
+ const selectLogo = useCallback7(
16800
15834
  (logoEl) => {
16801
15835
  if (activeElRef.current) deactivate();
16802
15836
  selectedElRef.current = logoEl;
@@ -16825,7 +15859,7 @@ function OhhwellsBridge() {
16825
15859
  },
16826
15860
  [deactivate, markSelected]
16827
15861
  );
16828
- const openLogoSizePanel = useCallback8((logoEl) => {
15862
+ const openLogoSizePanel = useCallback7((logoEl) => {
16829
15863
  const placement = getLogoPlacement(logoEl);
16830
15864
  const draft = readLogoSizeState(editContentRef.current, placement);
16831
15865
  setLogoSizeDraft(draft);
@@ -16838,7 +15872,7 @@ function OhhwellsBridge() {
16838
15872
  placement
16839
15873
  });
16840
15874
  }, []);
16841
- const openSocialsDisplayPanel = useCallback8((row) => {
15875
+ const openSocialsDisplayPanel = useCallback7((row) => {
16842
15876
  setParentScrollSnap(parentScrollRef.current);
16843
15877
  setFloatingPanel({
16844
15878
  key: "socials-display",
@@ -16848,7 +15882,7 @@ function OhhwellsBridge() {
16848
15882
  row
16849
15883
  });
16850
15884
  }, []);
16851
- const changeSocialsDisplay = useCallback8(
15885
+ const changeSocialsDisplay = useCallback7(
16852
15886
  (row, next) => {
16853
15887
  if (next.icon) {
16854
15888
  const missing = socialsMissingIcons(row);
@@ -16871,17 +15905,17 @@ function OhhwellsBridge() {
16871
15905
  },
16872
15906
  []
16873
15907
  );
16874
- const closeFloatingPanelOnly = useCallback8(() => {
15908
+ const closeFloatingPanelOnly = useCallback7(() => {
16875
15909
  setFloatingPanel(null);
16876
15910
  setLogoSizeDraft(null);
16877
15911
  }, []);
16878
15912
  closeFloatingPanelOnlyRef.current = closeFloatingPanelOnly;
16879
- const closeFloatingPanelAndDeselect = useCallback8(() => {
15913
+ const closeFloatingPanelAndDeselect = useCallback7(() => {
16880
15914
  setFloatingPanel(null);
16881
15915
  setLogoSizeDraft(null);
16882
15916
  deselectRef.current();
16883
15917
  }, []);
16884
- useEffect13(() => {
15918
+ useEffect12(() => {
16885
15919
  const session = floatingPanel && floatingPanel.kind === "logo-size" ? floatingPanel : null;
16886
15920
  if (!session || !logoSizeDraft) {
16887
15921
  postToParentRef.current({ type: "ow:logo-size-panel", open: false });
@@ -16900,7 +15934,7 @@ function OhhwellsBridge() {
16900
15934
  max: LOGO_SIZE_MAX
16901
15935
  });
16902
15936
  }, [floatingPanel, logoSizeDraft, editorViewport]);
16903
- const persistLogoSizeDraft = useCallback8(
15937
+ const persistLogoSizeDraft = useCallback7(
16904
15938
  (placement, draft) => {
16905
15939
  const desktopKey = LOGO_SIZE_DESKTOP_KEYS[placement];
16906
15940
  const mobileKey = LOGO_SIZE_MOBILE_KEYS[placement];
@@ -16940,7 +15974,7 @@ function OhhwellsBridge() {
16940
15974
  },
16941
15975
  [postToParent2]
16942
15976
  );
16943
- const activate = useCallback8((el, options) => {
15977
+ const activate = useCallback7((el, options) => {
16944
15978
  if (activeElRef.current === el) return;
16945
15979
  if (isIconEditable(el)) return;
16946
15980
  if (el.hasAttribute("data-ohw-social-label")) return;
@@ -17024,8 +16058,8 @@ function OhhwellsBridge() {
17024
16058
  openLogoSizePanelRef.current = openLogoSizePanel;
17025
16059
  deselectRef.current = deselect;
17026
16060
  closeFloatingPanelOnlyRef.current = closeFloatingPanelOnly;
17027
- const lastSiteWideScopeRef = useRef10(null);
17028
- useEffect13(() => {
16061
+ const lastSiteWideScopeRef = useRef9(null);
16062
+ useEffect12(() => {
17029
16063
  if (!isEditMode) {
17030
16064
  if (lastSiteWideScopeRef.current !== false) {
17031
16065
  lastSiteWideScopeRef.current = false;
@@ -17058,27 +16092,15 @@ function OhhwellsBridge() {
17058
16092
  }
17059
16093
  const applyContent = (content) => {
17060
16094
  const imageLoads = [];
17061
- if (typeof content[BRAND_KIT_KEY] === "string") {
17062
- brandKitRef.current = content[BRAND_KIT_KEY];
17063
- applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
17064
- }
17065
16095
  if (typeof content[AI_SECTIONS_KEY] === "string") {
17066
16096
  aiSectionsRef.current = content[AI_SECTIONS_KEY];
17067
16097
  applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
17068
16098
  }
17069
- if (typeof content[STYLE_STORE_KEY] === "string") {
17070
- stylesRef.current = content[STYLE_STORE_KEY];
17071
- applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
17072
- }
17073
- applyBrandChrome(content);
17074
16099
  for (const [key, val] of Object.entries(content)) {
17075
16100
  if (key === "__ohw_sections") continue;
17076
16101
  if (key === AI_SECTIONS_KEY) continue;
17077
16102
  if (key === LOGO_PLACEHOLDER_KEY) continue;
17078
16103
  if (LOGO_IMAGE_KEYS.includes(key)) continue;
17079
- if (key === BRAND_KIT_KEY) continue;
17080
- if (key === STYLE_STORE_KEY) continue;
17081
- if (BRAND_CHROME_KEYS.has(key)) continue;
17082
16104
  if (applyVideoSettingNode(key, val)) continue;
17083
16105
  if (applyCarouselNode(key, val)) continue;
17084
16106
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -17136,9 +16158,7 @@ function OhhwellsBridge() {
17136
16158
  let cancelled = false;
17137
16159
  setFetchState("loading");
17138
16160
  const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
17139
- const initialPath = pathname;
17140
- fetchedContentPaths.add(`${subdomain}::${initialPath}`);
17141
- fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
16161
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
17142
16162
  if (cancelled) return;
17143
16163
  const content = data?.content ?? {};
17144
16164
  contentCache.set(subdomain, content);
@@ -17151,7 +16171,7 @@ function OhhwellsBridge() {
17151
16171
  cancelled = true;
17152
16172
  };
17153
16173
  }, [subdomain, isEditMode]);
17154
- useEffect13(() => {
16174
+ useEffect12(() => {
17155
16175
  if (!isEditMode) return;
17156
16176
  const resolveIndex = (form, clientY) => {
17157
16177
  const wrappers = listFieldWrappers(form).filter((el) => fieldKeyOf(el) !== fieldDragRef.current?.key);
@@ -17192,7 +16212,7 @@ function OhhwellsBridge() {
17192
16212
  window.removeEventListener("drop", onDrop, true);
17193
16213
  };
17194
16214
  }, [buildFieldDropSlots, isEditMode, persistFields, selectField]);
17195
- useEffect13(() => {
16215
+ useEffect12(() => {
17196
16216
  if (!isEditMode) return;
17197
16217
  const mark = () => document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
17198
16218
  markFormFields(form);
@@ -17204,7 +16224,7 @@ function OhhwellsBridge() {
17204
16224
  });
17205
16225
  return () => observer.disconnect();
17206
16226
  }, [isEditMode, fetchState, pathname]);
17207
- useEffect13(() => {
16227
+ useEffect12(() => {
17208
16228
  if (!isEditMode) return;
17209
16229
  let saveTimer = null;
17210
16230
  const onInput = (e) => {
@@ -17226,14 +16246,14 @@ function OhhwellsBridge() {
17226
16246
  document.addEventListener("input", onInput, true);
17227
16247
  return () => document.removeEventListener("input", onInput, true);
17228
16248
  }, [isEditMode, persistFields]);
17229
- useEffect13(() => {
16249
+ useEffect12(() => {
17230
16250
  if (isEditMode || fetchState !== "done") return;
17231
16251
  const content = contentCache.get(subdomain) ?? {};
17232
16252
  document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
17233
16253
  reconcileFieldsFromContent(form, content);
17234
16254
  });
17235
16255
  }, [isEditMode, fetchState, subdomain]);
17236
- useEffect13(() => {
16256
+ useEffect12(() => {
17237
16257
  if (!isEditMode) return;
17238
16258
  const swallow = (e) => {
17239
16259
  const target = e.target;
@@ -17242,12 +16262,12 @@ function OhhwellsBridge() {
17242
16262
  document.addEventListener("submit", swallow, true);
17243
16263
  return () => document.removeEventListener("submit", swallow, true);
17244
16264
  }, [isEditMode]);
17245
- useEffect13(() => {
16265
+ useEffect12(() => {
17246
16266
  if (isEditMode || fetchState !== "done") return;
17247
16267
  const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
17248
16268
  bindPublishedForms(apiUrl, subdomain, contentCache.get(subdomain) ?? {});
17249
16269
  }, [isEditMode, fetchState, subdomain]);
17250
- useEffect13(() => {
16270
+ useEffect12(() => {
17251
16271
  if (!subdomain || isEditMode) return;
17252
16272
  let debounceTimer = null;
17253
16273
  let observer = null;
@@ -17258,21 +16278,10 @@ function OhhwellsBridge() {
17258
16278
  initSectionInstancesFromContent(content, window.location.pathname);
17259
16279
  observer?.disconnect();
17260
16280
  try {
17261
- applyBrandChrome(content);
17262
- if (typeof content[BRAND_KIT_KEY] === "string") {
17263
- applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
17264
- }
17265
- if (typeof content[STYLE_STORE_KEY] === "string") {
17266
- applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
17267
- }
17268
16281
  for (const [key, val] of Object.entries(content)) {
17269
16282
  if (key === "__ohw_sections") continue;
17270
16283
  if (key === LOGO_PLACEHOLDER_KEY) continue;
17271
16284
  if (LOGO_IMAGE_KEYS.includes(key)) continue;
17272
- if (key === BRAND_KIT_KEY) continue;
17273
- if (key === STYLE_STORE_KEY) continue;
17274
- if (key === STYLE_STORE_KEY) continue;
17275
- if (BRAND_CHROME_KEYS.has(key)) continue;
17276
16285
  if (applyVideoSettingNode(key, val)) continue;
17277
16286
  if (applyCarouselNode(key, val)) continue;
17278
16287
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -17315,17 +16324,6 @@ function OhhwellsBridge() {
17315
16324
  debounceTimer = setTimeout(applyFromCache, 150);
17316
16325
  };
17317
16326
  applyFromCache();
17318
- const pathCacheKey = `${subdomain}::${pathname}`;
17319
- if (!fetchedContentPaths.has(pathCacheKey)) {
17320
- fetchedContentPaths.add(pathCacheKey);
17321
- const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
17322
- fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(pathname)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
17323
- if (!data?.content) return;
17324
- contentCache.set(subdomain, data.content);
17325
- applyFromCache();
17326
- }).catch(() => {
17327
- });
17328
- }
17329
16327
  observer = new MutationObserver(scheduleApply);
17330
16328
  observer.observe(document.body, { childList: true, subtree: true });
17331
16329
  return () => {
@@ -17339,10 +16337,10 @@ function OhhwellsBridge() {
17339
16337
  const visible = Boolean(subdomain) && fetchState !== "done";
17340
16338
  el.style.display = visible ? "flex" : "none";
17341
16339
  }, [subdomain, fetchState]);
17342
- useEffect13(() => {
16340
+ useEffect12(() => {
17343
16341
  postToParent2({ type: "ow:navigation", path: pathname });
17344
16342
  }, [pathname, postToParent2]);
17345
- useEffect13(() => {
16343
+ useEffect12(() => {
17346
16344
  if (!isEditMode) return;
17347
16345
  if (linkPopoverSessionRef.current?.intent === "add-nav") return;
17348
16346
  if (document.querySelector("[data-ohw-section-picker]")) return;
@@ -17350,7 +16348,7 @@ function OhhwellsBridge() {
17350
16348
  deselectRef.current();
17351
16349
  deactivateRef.current();
17352
16350
  }, [pathname, isEditMode]);
17353
- useEffect13(() => {
16351
+ useEffect12(() => {
17354
16352
  const contentForNav = () => {
17355
16353
  if (isEditMode) return editContentRef.current;
17356
16354
  if (!subdomain) return {};
@@ -17417,36 +16415,31 @@ function OhhwellsBridge() {
17417
16415
  observer?.disconnect();
17418
16416
  };
17419
16417
  }, [isEditMode, pathname, subdomain, fetchState, resyncSelectedNavigationItem]);
17420
- useEffect13(() => {
16418
+ useEffect12(() => {
17421
16419
  if (!isEditMode) return;
17422
- let lastPosted = 0;
17423
16420
  const measure = () => {
17424
16421
  const h = document.body.scrollHeight;
17425
- if (h > 50 && Math.abs(h - lastPosted) > 1) {
17426
- lastPosted = h;
17427
- postToParent2({ type: "ow:height", height: h });
17428
- }
17429
- };
17430
- let raf = null;
17431
- const schedule = () => {
17432
- if (raf != null) return;
17433
- raf = requestAnimationFrame(() => {
17434
- raf = null;
17435
- measure();
17436
- });
16422
+ if (h > 50) postToParent2({ type: "ow:height", height: h });
17437
16423
  };
17438
16424
  const t1 = setTimeout(measure, 50);
17439
16425
  const t2 = setTimeout(measure, 500);
17440
- const ro = new ResizeObserver(schedule);
17441
- ro.observe(document.body);
16426
+ let lastWidth = window.innerWidth;
16427
+ let resizeTimer = null;
16428
+ const handleResize = () => {
16429
+ if (window.innerWidth === lastWidth) return;
16430
+ lastWidth = window.innerWidth;
16431
+ if (resizeTimer) clearTimeout(resizeTimer);
16432
+ resizeTimer = setTimeout(measure, 150);
16433
+ };
16434
+ window.addEventListener("resize", handleResize);
17442
16435
  return () => {
17443
16436
  clearTimeout(t1);
17444
16437
  clearTimeout(t2);
17445
- if (raf != null) cancelAnimationFrame(raf);
17446
- ro.disconnect();
16438
+ if (resizeTimer) clearTimeout(resizeTimer);
16439
+ window.removeEventListener("resize", handleResize);
17447
16440
  };
17448
16441
  }, [pathname, isEditMode, postToParent2]);
17449
- useEffect13(() => {
16442
+ useEffect12(() => {
17450
16443
  if (!subdomainFromQuery || isEditMode) return;
17451
16444
  const handleClick = (e) => {
17452
16445
  const anchor = e.target.closest("a");
@@ -17462,7 +16455,7 @@ function OhhwellsBridge() {
17462
16455
  document.addEventListener("click", handleClick, true);
17463
16456
  return () => document.removeEventListener("click", handleClick, true);
17464
16457
  }, [subdomainFromQuery, isEditMode, router]);
17465
- useEffect13(() => {
16458
+ useEffect12(() => {
17466
16459
  if (!isEditMode) {
17467
16460
  editStylesRef.current?.base.remove();
17468
16461
  editStylesRef.current?.forceHover.remove();
@@ -17624,21 +16617,16 @@ function OhhwellsBridge() {
17624
16617
  return;
17625
16618
  }
17626
16619
  const target = e.target;
17627
- if (target.closest("[data-ohw-ai-review]")) return;
17628
16620
  if (target.closest("[data-ohw-toolbar]")) return;
17629
16621
  if (target.closest("[data-ohw-state-toggle]")) return;
17630
16622
  if (target.closest("[data-ohw-max-badge]")) return;
17631
16623
  if (isInsideLinkEditor(target)) return;
17632
- if (isInsideFloatingPanel(target)) return;
17633
16624
  if (target.closest("[data-ohw-form-toolbar]")) return;
17634
16625
  if (target.closest(
17635
16626
  '[data-ohw-field-toolbar], [data-ohw-field-type-picker], [data-radix-popper-content-wrapper], [role="menu"], [data-slot="dropdown-menu-content"]'
17636
16627
  )) {
17637
16628
  return;
17638
16629
  }
17639
- if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
17640
- clearMediaSelectionRef.current();
17641
- }
17642
16630
  {
17643
16631
  const formEl = getFormElement(target);
17644
16632
  const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
@@ -17786,11 +16774,8 @@ function OhhwellsBridge() {
17786
16774
  if (isMediaEditable(editable) && !buttonOnMedia) {
17787
16775
  e.preventDefault();
17788
16776
  e.stopPropagation();
17789
- if (selectedMediaElRef.current === editable) {
17790
- postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "", elementType: editable.dataset.ohwEditable ?? "image" });
17791
- } else {
17792
- selectMediaElementRef.current(editable);
17793
- }
16777
+ aiSectionApiRef.current?.selectFromElement(editable);
16778
+ postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "", elementType: editable.dataset.ohwEditable ?? "image" });
17794
16779
  return;
17795
16780
  }
17796
16781
  const hrefLookupTarget = buttonOnMedia ? clickedButton : editable;
@@ -17909,12 +16894,10 @@ function OhhwellsBridge() {
17909
16894
  };
17910
16895
  const handleDblClick = (e) => {
17911
16896
  const target = e.target;
17912
- if (target.closest("[data-ohw-ai-review]")) return;
17913
16897
  if (target.closest("[data-ohw-toolbar]")) return;
17914
16898
  if (target.closest("[data-ohw-state-toggle]")) return;
17915
16899
  if (target.closest("[data-ohw-max-badge]")) return;
17916
16900
  if (isInsideLinkEditor(target)) return;
17917
- if (isInsideFloatingPanel(target)) return;
17918
16901
  if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
17919
16902
  return;
17920
16903
  }
@@ -17955,14 +16938,11 @@ function OhhwellsBridge() {
17955
16938
  setSiblingHintRects([]);
17956
16939
  return;
17957
16940
  }
17958
- if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || isInsideFloatingPanel(target) || isPointOverFloatingPanel(e.clientX, e.clientY)) {
16941
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || target.closest("[data-ohw-floating-panel]")) {
17959
16942
  hoveredItemElRef.current = null;
17960
16943
  setHoveredItemRect(null);
17961
16944
  hoveredNavContainerRef.current = null;
17962
16945
  setHoveredNavContainerRect(null);
17963
- siblingHintElRef.current = null;
17964
- setSiblingHintRect(null);
17965
- setSiblingHintRects([]);
17966
16946
  return;
17967
16947
  }
17968
16948
  {
@@ -18071,6 +17051,7 @@ function OhhwellsBridge() {
18071
17051
  hoveredNavContainerRef.current = null;
18072
17052
  setHoveredNavContainerRect(null);
18073
17053
  hoveredItemElRef.current = editable;
17054
+ setHoveredItemRect(editable.getBoundingClientRect());
18074
17055
  }
18075
17056
  }
18076
17057
  }
@@ -18367,7 +17348,7 @@ function OhhwellsBridge() {
18367
17348
  }
18368
17349
  };
18369
17350
  const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
18370
- if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
17351
+ if (linkPopoverOpenRef.current) {
18371
17352
  if (hoveredImageRef.current) {
18372
17353
  hoveredImageRef.current = null;
18373
17354
  hoveredImageHasTextOverlapRef.current = false;
@@ -18383,6 +17364,11 @@ function OhhwellsBridge() {
18383
17364
  probeNavigationHoverAt(x, y);
18384
17365
  return;
18385
17366
  }
17367
+ if (isPointOverLogo(x, y)) {
17368
+ dismissImageHover();
17369
+ probeNavigationHoverAt(x, y);
17370
+ return;
17371
+ }
18386
17372
  const overMoreMenu = document.elementFromPoint(x, y)?.closest?.(
18387
17373
  '[data-ohw-more-menu], [data-slot="dropdown-menu-content"], [data-slot="dropdown-menu-item"]'
18388
17374
  );
@@ -18621,7 +17607,7 @@ function OhhwellsBridge() {
18621
17607
  }
18622
17608
  };
18623
17609
  const probeHoverCardsAt = (clientX, clientY, fromParentViewport = false) => {
18624
- if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY) || document.documentElement.hasAttribute("data-ohw-section-picking")) {
17610
+ if (linkPopoverOpenRef.current || document.documentElement.hasAttribute("data-ohw-section-picking")) {
18625
17611
  if (activeStateElRef.current) {
18626
17612
  activeStateElRef.current.removeAttribute("data-ohw-state-hovered");
18627
17613
  activeStateElRef.current = null;
@@ -18667,9 +17653,7 @@ function OhhwellsBridge() {
18667
17653
  return;
18668
17654
  }
18669
17655
  const { y } = toProbeCoords(clientX, clientY, fromParentViewport);
18670
- const sections = Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
18671
- (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
18672
- ).sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);
17656
+ const sections = Array.from(document.querySelectorAll("[data-ohw-section]")).sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);
18673
17657
  const ZONE = 20;
18674
17658
  for (let i = 0; i < sections.length; i++) {
18675
17659
  const a = sections[i];
@@ -18698,7 +17682,8 @@ function OhhwellsBridge() {
18698
17682
  };
18699
17683
  const handleMouseMove = (e) => {
18700
17684
  const { clientX, clientY } = e;
18701
- if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY) || isOverEditorChrome(clientX, clientY)) {
17685
+ if (pointOwnedByFloatingPanel(clientX, clientY)) return;
17686
+ if (isOverEditorChrome(clientX, clientY)) {
18702
17687
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
18703
17688
  formHoverElRef.current = null;
18704
17689
  setFormHoverRect(null);
@@ -18706,12 +17691,6 @@ function OhhwellsBridge() {
18706
17691
  setHoveredItemRect(null);
18707
17692
  hoveredNavContainerRef.current = null;
18708
17693
  setHoveredNavContainerRect(null);
18709
- siblingHintElRef.current = null;
18710
- setSiblingHintRect(null);
18711
- setSiblingHintRects([]);
18712
- dismissImageHover();
18713
- clearImageHover();
18714
- setSectionGap(null);
18715
17694
  return;
18716
17695
  }
18717
17696
  probeSectionGapAt(clientX, clientY);
@@ -18722,11 +17701,7 @@ function OhhwellsBridge() {
18722
17701
  if (e.data?.type !== "ow:pointer-sync") return;
18723
17702
  const { clientX, clientY } = e.data;
18724
17703
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
18725
- if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
18726
- dismissImageHover();
18727
- clearImageHover();
18728
- return;
18729
- }
17704
+ if (pointOwnedByFloatingPanel(clientX, clientY)) return;
18730
17705
  probeSectionGapAt(clientX, clientY);
18731
17706
  probeImageAt(clientX, clientY);
18732
17707
  probeHoverCardsAt(clientX, clientY);
@@ -18972,19 +17947,10 @@ function OhhwellsBridge() {
18972
17947
  if (e.data?.type !== "ow:hydrate") return;
18973
17948
  const content = e.data.content;
18974
17949
  if (!content) return;
18975
- if (typeof content[BRAND_KIT_KEY] === "string") {
18976
- brandKitRef.current = content[BRAND_KIT_KEY];
18977
- applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
18978
- }
18979
17950
  if (typeof content[AI_SECTIONS_KEY] === "string") {
18980
17951
  aiSectionsRef.current = content[AI_SECTIONS_KEY];
18981
17952
  applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
18982
17953
  }
18983
- if (typeof content[STYLE_STORE_KEY] === "string") {
18984
- stylesRef.current = content[STYLE_STORE_KEY];
18985
- applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
18986
- }
18987
- applyBrandChrome(content);
18988
17954
  let sectionsJson = null;
18989
17955
  for (const [key, val] of Object.entries(content)) {
18990
17956
  if (key === "__ohw_sections") {
@@ -18994,9 +17960,6 @@ function OhhwellsBridge() {
18994
17960
  if (key === AI_SECTIONS_KEY) continue;
18995
17961
  if (key === LOGO_PLACEHOLDER_KEY) continue;
18996
17962
  if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
18997
- if (key === BRAND_KIT_KEY) continue;
18998
- if (key === STYLE_STORE_KEY) continue;
18999
- if (BRAND_CHROME_KEYS.has(key)) continue;
19000
17963
  if (applyVideoSettingNode(key, val)) continue;
19001
17964
  if (applyCarouselNode(key, val)) continue;
19002
17965
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -19093,21 +18056,12 @@ function OhhwellsBridge() {
19093
18056
  nodes: collectEditableNodes(editContentRef.current)
19094
18057
  });
19095
18058
  };
19096
- const clearInteractionChrome = () => {
19097
- deactivateRef.current();
19098
- deselectRef.current();
19099
- clearMediaSelectionRef.current();
19100
- };
19101
18059
  const handleAiApplyTree = (e) => {
19102
18060
  if (e.data?.type !== "ow:ai-apply-tree") return;
19103
18061
  const payload = e.data.payload;
19104
18062
  if (!payload || typeof payload.id !== "string" || !isRenderableTree(payload.tree)) return;
19105
- clearInteractionChrome();
19106
18063
  const previous = aiSectionsRef.current;
19107
- const nextState = applyTreeToState(parseAiSectionsState(previous), {
19108
- ...payload,
19109
- path: payload.path ?? window.location.pathname
19110
- });
18064
+ const nextState = applyTreeToState(parseAiSectionsState(previous), payload);
19111
18065
  const nextValue = serializeAiSectionsState(nextState);
19112
18066
  aiSectionsRef.current = nextValue;
19113
18067
  applyAiSectionsToDom(nextState);
@@ -19127,7 +18081,6 @@ function OhhwellsBridge() {
19127
18081
  if (!sectionId || sectionId === "navbar" || sectionId === "footer") return;
19128
18082
  const exists = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
19129
18083
  if (!exists) return;
19130
- clearInteractionChrome();
19131
18084
  const previous = aiSectionsRef.current;
19132
18085
  const nextState = deleteSectionFromState(parseAiSectionsState(previous), sectionId);
19133
18086
  const nextValue = serializeAiSectionsState(nextState);
@@ -19143,58 +18096,14 @@ function OhhwellsBridge() {
19143
18096
  const handleAiSetSections = (e) => {
19144
18097
  if (e.data?.type !== "ow:ai-set-sections") return;
19145
18098
  const value = typeof e.data.value === "string" ? e.data.value : "";
19146
- clearInteractionChrome();
19147
18099
  aiSectionsRef.current = value;
19148
18100
  applyAiSectionsToDom(parseAiSectionsState(value));
19149
- applyStylesToDom(parseStyleStore(stylesRef.current));
19150
18101
  const restoredHeight = document.documentElement.scrollHeight;
19151
18102
  if (restoredHeight > 50) postToParentRef.current({ type: "ow:height", height: restoredHeight });
19152
18103
  postToParentRef.current({ type: "ow:change", nodes: [{ key: AI_SECTIONS_KEY, text: value }] });
19153
18104
  postAiSectionsChanged();
19154
18105
  };
19155
18106
  window.addEventListener("message", handleAiSetSections);
19156
- const handleMoveSection = (e) => {
19157
- if (e.data?.type !== "ow:move-section") return;
19158
- const instanceId = typeof e.data.instanceId === "string" ? e.data.instanceId : "";
19159
- const direction = e.data.direction === "up" || e.data.direction === "down" ? e.data.direction : null;
19160
- if (!instanceId || !direction) return;
19161
- const entries = moveSectionInstance(instanceId, direction, window.location.pathname);
19162
- if (!entries) return;
19163
- const orderJson = JSON.stringify(entries);
19164
- editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
19165
- postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
19166
- window.dispatchEvent(new Event("resize"));
19167
- };
19168
- window.addEventListener("message", handleMoveSection);
19169
- const handleAiSetBrand = (e) => {
19170
- if (e.data?.type !== "ow:ai-set-brand") return;
19171
- const value = typeof e.data.value === "string" ? e.data.value : "";
19172
- const previous = brandKitRef.current;
19173
- brandKitRef.current = value;
19174
- applyBrandToDom(parseBrandKit(value));
19175
- if (aiSectionsRef.current) applyAiSectionsToDom(parseAiSectionsState(aiSectionsRef.current));
19176
- applyStylesToDom(parseStyleStore(stylesRef.current));
19177
- postToParentRef.current({ type: "ow:change", nodes: [{ key: BRAND_KIT_KEY, text: value }] });
19178
- postToParentRef.current({ type: "ow:ai-brand-applied", previous, value });
19179
- };
19180
- window.addEventListener("message", handleAiSetBrand);
19181
- const handleAiSetStyles = (e) => {
19182
- if (e.data?.type !== "ow:ai-set-styles") return;
19183
- const value = typeof e.data.value === "string" ? e.data.value : "";
19184
- const previous = stylesRef.current;
19185
- stylesRef.current = value;
19186
- applyStylesToDom(parseStyleStore(value));
19187
- postToParentRef.current({ type: "ow:change", nodes: [{ key: STYLE_STORE_KEY, text: value }] });
19188
- postToParentRef.current({ type: "ow:ai-styles-applied", previous, value });
19189
- };
19190
- window.addEventListener("message", handleAiSetStyles);
19191
- const handleGetBrand = (e) => {
19192
- if (e.data?.type !== "ow:get-brand") return;
19193
- const template = deriveTemplateBrand();
19194
- const value = brandKitRef.current || (template ? JSON.stringify(template) : "");
19195
- postToParentRef.current({ type: "ow:brand-value", value });
19196
- };
19197
- window.addEventListener("message", handleGetBrand);
19198
18107
  const handleDeactivate = (e) => {
19199
18108
  if (e.data?.type !== "ow:deactivate") return;
19200
18109
  if (Date.now() < linkPopoverGraceUntilRef.current) return;
@@ -19203,12 +18112,6 @@ function OhhwellsBridge() {
19203
18112
  closeLinkPopoverRef.current();
19204
18113
  return;
19205
18114
  }
19206
- if (floatingPanelOpenRef.current) {
19207
- setFloatingPanelRef.current(null);
19208
- deselectRef.current();
19209
- deactivateRef.current();
19210
- return;
19211
- }
19212
18115
  deselectRef.current();
19213
18116
  deactivateRef.current();
19214
18117
  };
@@ -19296,10 +18199,6 @@ function OhhwellsBridge() {
19296
18199
  const handleKeyDown = (e) => {
19297
18200
  if (e.key === "Escape" && document.querySelector("[data-ohw-section-picker]")) return;
19298
18201
  if (e.key === "Escape" && document.querySelector("[data-ohw-more-menu]")) return;
19299
- if (e.key === "Escape" && selectedMediaElRef.current) {
19300
- clearMediaSelectionRef.current();
19301
- return;
19302
- }
19303
18202
  if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "a" && activeElRef.current) {
19304
18203
  e.preventDefault();
19305
18204
  selectAllTextInEditable(activeElRef.current);
@@ -19459,12 +18358,6 @@ function OhhwellsBridge() {
19459
18358
  if (aiSectionsRef.current) {
19460
18359
  nodes.push({ key: AI_SECTIONS_KEY, type: "sections", text: aiSectionsRef.current });
19461
18360
  }
19462
- if (stylesRef.current) {
19463
- nodes.push({ key: STYLE_STORE_KEY, type: "styles", text: stylesRef.current });
19464
- }
19465
- if (brandKitRef.current) {
19466
- nodes.push({ key: BRAND_KIT_KEY, type: "brand", text: brandKitRef.current });
19467
- }
19468
18361
  document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
19469
18362
  const formKey = formKeyOf(form);
19470
18363
  if (!formKey) return;
@@ -19482,12 +18375,8 @@ function OhhwellsBridge() {
19482
18375
  if (inserted) {
19483
18376
  const tracker = getSectionsTracker();
19484
18377
  postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
19485
- const reportHeight = () => {
19486
- const h = document.body.scrollHeight;
19487
- if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
19488
- };
19489
- reportHeight();
19490
- setTimeout(reportHeight, 500);
18378
+ const h = document.documentElement.scrollHeight;
18379
+ if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
19491
18380
  }
19492
18381
  };
19493
18382
  const handleSwitchSchedule = (e) => {
@@ -19874,10 +18763,6 @@ function OhhwellsBridge() {
19874
18763
  window.removeEventListener("message", handleAiApplyTree);
19875
18764
  window.removeEventListener("message", handleAiDeleteSection);
19876
18765
  window.removeEventListener("message", handleAiSetSections);
19877
- window.removeEventListener("message", handleMoveSection);
19878
- window.removeEventListener("message", handleAiSetBrand);
19879
- window.removeEventListener("message", handleAiSetStyles);
19880
- window.removeEventListener("message", handleGetBrand);
19881
18766
  window.removeEventListener("message", handleDeactivate);
19882
18767
  window.removeEventListener("message", handleToastAction);
19883
18768
  window.removeEventListener("message", handleFormCount);
@@ -19888,7 +18773,7 @@ function OhhwellsBridge() {
19888
18773
  if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
19889
18774
  };
19890
18775
  }, [isEditMode, refreshStateRules]);
19891
- useEffect13(() => {
18776
+ useEffect12(() => {
19892
18777
  if (!isEditMode) return;
19893
18778
  const THRESHOLD = 10;
19894
18779
  const resolveWasSelected = (el) => {
@@ -20044,7 +18929,7 @@ function OhhwellsBridge() {
20044
18929
  unlockFooterDragInteraction();
20045
18930
  };
20046
18931
  }, [isEditMode]);
20047
- useEffect13(() => {
18932
+ useEffect12(() => {
20048
18933
  const handler = (e) => {
20049
18934
  if (e.data?.type !== "ow:request-schedule-config") return;
20050
18935
  const insertAfterVal = e.data.insertAfter;
@@ -20060,7 +18945,7 @@ function OhhwellsBridge() {
20060
18945
  window.addEventListener("message", handler);
20061
18946
  return () => window.removeEventListener("message", handler);
20062
18947
  }, [processConfigRequest]);
20063
- useEffect13(() => {
18948
+ useEffect12(() => {
20064
18949
  if (!isEditMode) return;
20065
18950
  document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
20066
18951
  el.removeAttribute("data-ohw-active-state");
@@ -20084,7 +18969,7 @@ function OhhwellsBridge() {
20084
18969
  postToParent2({
20085
18970
  type: "ow:ready",
20086
18971
  version: "1",
20087
- bridgeVersion: "0.1.67",
18972
+ bridgeVersion: "0.1.66",
20088
18973
  path: pathname,
20089
18974
  nodes: collectEditableNodes(editContentRef.current),
20090
18975
  sections
@@ -20096,13 +18981,13 @@ function OhhwellsBridge() {
20096
18981
  clearTimeout(timer);
20097
18982
  };
20098
18983
  }, [pathname, isEditMode, refreshStateRules, postToParent2]);
20099
- useEffect13(() => {
18984
+ useEffect12(() => {
20100
18985
  scrollToHashSectionWhenReady();
20101
18986
  const onHashChange = () => scrollToHashSectionWhenReady();
20102
18987
  window.addEventListener("hashchange", onHashChange);
20103
18988
  return () => window.removeEventListener("hashchange", onHashChange);
20104
18989
  }, [pathname]);
20105
- const handleCommand = useCallback8((cmd) => {
18990
+ const handleCommand = useCallback7((cmd) => {
20106
18991
  const el = activeElRef.current;
20107
18992
  const selBefore = window.getSelection();
20108
18993
  let savedOffsets = null;
@@ -20138,7 +19023,7 @@ function OhhwellsBridge() {
20138
19023
  if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
20139
19024
  refreshActiveCommandsRef.current();
20140
19025
  }, []);
20141
- useEffect13(() => {
19026
+ useEffect12(() => {
20142
19027
  const session = floatingPanel && floatingPanel.kind === "logo-size" ? floatingPanel : null;
20143
19028
  if (!session || !logoSizeDraft) return;
20144
19029
  const onPanelAction = (e) => {
@@ -20176,7 +19061,7 @@ function OhhwellsBridge() {
20176
19061
  window.addEventListener("message", onPanelAction);
20177
19062
  return () => window.removeEventListener("message", onPanelAction);
20178
19063
  }, [floatingPanel, logoSizeDraft, editorViewport, persistLogoSizeDraft, closeFloatingPanelAndDeselect]);
20179
- const handleStateChange = useCallback8((state) => {
19064
+ const handleStateChange = useCallback7((state) => {
20180
19065
  if (!activeStateElRef.current) return;
20181
19066
  const el = activeStateElRef.current;
20182
19067
  if (state === "Default") {
@@ -20189,7 +19074,7 @@ function OhhwellsBridge() {
20189
19074
  }
20190
19075
  setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
20191
19076
  }, [deactivate]);
20192
- const reselectAfterLinkPopover = useCallback8(
19077
+ const reselectAfterLinkPopover = useCallback7(
20193
19078
  (hrefKey) => {
20194
19079
  requestAnimationFrame(() => {
20195
19080
  const el = resolveHrefKeyElement(hrefKey);
@@ -20198,7 +19083,7 @@ function OhhwellsBridge() {
20198
19083
  },
20199
19084
  [resolveHrefKeyElement]
20200
19085
  );
20201
- const closeLinkPopover = useCallback8(() => {
19086
+ const closeLinkPopover = useCallback7(() => {
20202
19087
  const session = linkPopoverSessionRef.current;
20203
19088
  addNavAfterAnchorRef.current = null;
20204
19089
  setLinkPopover(null);
@@ -20206,9 +19091,9 @@ function OhhwellsBridge() {
20206
19091
  reselectAfterLinkPopover(session.key);
20207
19092
  }
20208
19093
  }, [reselectAfterLinkPopover]);
20209
- const closeLinkPopoverRef = useRef10(closeLinkPopover);
19094
+ const closeLinkPopoverRef = useRef9(closeLinkPopover);
20210
19095
  closeLinkPopoverRef.current = closeLinkPopover;
20211
- const openLinkPopoverForActive = useCallback8(() => {
19096
+ const openLinkPopoverForActive = useCallback7(() => {
20212
19097
  const hrefCtx = getHrefKeyFromElement(activeElRef.current);
20213
19098
  if (!hrefCtx) return;
20214
19099
  bumpLinkPopoverGrace();
@@ -20219,7 +19104,7 @@ function OhhwellsBridge() {
20219
19104
  });
20220
19105
  deactivate();
20221
19106
  }, [deactivate]);
20222
- const openLinkPopoverForSelected = useCallback8(() => {
19107
+ const openLinkPopoverForSelected = useCallback7(() => {
20223
19108
  const anchor = selectedElRef.current;
20224
19109
  if (!anchor) return;
20225
19110
  const key = anchor.getAttribute("data-ohw-href-key");
@@ -20236,7 +19121,7 @@ function OhhwellsBridge() {
20236
19121
  });
20237
19122
  deselect();
20238
19123
  }, [deselect]);
20239
- const handleSelectParent = useCallback8(() => {
19124
+ const handleSelectParent = useCallback7(() => {
20240
19125
  const selected = selectedElRef.current;
20241
19126
  if (!selected) return;
20242
19127
  if (toolbarVariantRef.current === "select-frame") {
@@ -20263,7 +19148,7 @@ function OhhwellsBridge() {
20263
19148
  }
20264
19149
  deselectRef.current();
20265
19150
  }, []);
20266
- const handleDuplicateSelected = useCallback8(() => {
19151
+ const handleDuplicateSelected = useCallback7(() => {
20267
19152
  const selected = selectedElRef.current;
20268
19153
  if (!selected || !isNavigationItem2(selected)) return;
20269
19154
  const hrefKey = selected.getAttribute("data-ohw-href-key");
@@ -20379,7 +19264,7 @@ function OhhwellsBridge() {
20379
19264
  });
20380
19265
  }
20381
19266
  }, [postToParent2]);
20382
- const runPendingDeleteUndo = useCallback8(() => {
19267
+ const runPendingDeleteUndo = useCallback7(() => {
20383
19268
  const pending = pendingDeleteUndoRef.current;
20384
19269
  if (!pending) return false;
20385
19270
  pendingDeleteUndoRef.current = null;
@@ -20387,7 +19272,7 @@ function OhhwellsBridge() {
20387
19272
  enforceLinkHrefs();
20388
19273
  return true;
20389
19274
  }, []);
20390
- const handleDeleteSelected = useCallback8(() => {
19275
+ const handleDeleteSelected = useCallback7(() => {
20391
19276
  const selected = selectedElRef.current;
20392
19277
  if (!selected) return false;
20393
19278
  return deleteSelectedNavFooterItem({
@@ -20408,7 +19293,7 @@ function OhhwellsBridge() {
20408
19293
  }, [postToParent2]);
20409
19294
  handleDeleteSelectedRef.current = handleDeleteSelected;
20410
19295
  runPendingDeleteUndoRef.current = runPendingDeleteUndo;
20411
- const handleLinkPopoverSubmit = useCallback8(
19296
+ const handleLinkPopoverSubmit = useCallback7(
20412
19297
  (target) => {
20413
19298
  const session = linkPopoverSessionRef.current;
20414
19299
  if (!session) return;
@@ -20474,30 +19359,19 @@ function OhhwellsBridge() {
20474
19359
  const showEditLink = toolbarShowEditLink;
20475
19360
  const currentSections = sectionsByPath[pathname] ?? [];
20476
19361
  linkPopoverOpenRef.current = linkPopover !== null;
20477
- const handleMediaSelect = useCallback8((key) => {
20478
- const el = hoveredImageRef.current?.dataset.ohwKey === key ? hoveredImageRef.current : Array.from(document.querySelectorAll(MEDIA_SELECTOR)).find(
20479
- (m) => (m.dataset.ohwKey ?? "") === key
20480
- ) ?? null;
20481
- if (!el) return;
20482
- selectMediaElementRef.current(el);
20483
- }, []);
20484
- const handleMediaReplace = useCallback8(
19362
+ const handleMediaReplace = useCallback7(
20485
19363
  (key) => {
20486
- postToParent2({
20487
- type: "ow:image-pick",
20488
- key,
20489
- elementType: mediaHover?.elementType ?? selectedMedia?.elementType ?? "image"
20490
- });
19364
+ postToParent2({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
20491
19365
  },
20492
- [postToParent2, mediaHover?.elementType, selectedMedia?.elementType]
19366
+ [postToParent2, mediaHover?.elementType]
20493
19367
  );
20494
- const handleEditCarousel = useCallback8(
19368
+ const handleEditCarousel = useCallback7(
20495
19369
  (key) => {
20496
19370
  postToParent2({ type: "ow:carousel-open", key, images: readCarouselValue(key) });
20497
19371
  },
20498
19372
  [postToParent2]
20499
19373
  );
20500
- const handleMediaFadeOutComplete = useCallback8((key) => {
19374
+ const handleMediaFadeOutComplete = useCallback7((key) => {
20501
19375
  setUploadingRects((prev) => {
20502
19376
  if (!(key in prev)) return prev;
20503
19377
  const next = { ...prev };
@@ -20505,7 +19379,7 @@ function OhhwellsBridge() {
20505
19379
  return next;
20506
19380
  });
20507
19381
  }, []);
20508
- const handleVideoSettingsChange = useCallback8(
19382
+ const handleVideoSettingsChange = useCallback7(
20509
19383
  (key, settings) => {
20510
19384
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
20511
19385
  const video = getVideoEl2(el);
@@ -20527,506 +19401,414 @@ function OhhwellsBridge() {
20527
19401
  },
20528
19402
  [postToParent2]
20529
19403
  );
20530
- return /* @__PURE__ */ jsxs20(Fragment8, { children: [
20531
- /* @__PURE__ */ jsx33("div", { id: "ohw-loader", suppressHydrationWarning: true, style: { ...OHW_LOADER_STYLE, display: "none" }, children: /* @__PURE__ */ jsx33(OhwLoaderSpinner, {}) }),
20532
- /* @__PURE__ */ jsx33("script", { suppressHydrationWarning: true, dangerouslySetInnerHTML: { __html: OHW_LOADER_PREHYDRATE_SCRIPT } }),
20533
- bridgeRoot ? createPortal2(
20534
- /* @__PURE__ */ jsxs20(Fragment8, { children: [
20535
- /* @__PURE__ */ jsx33("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
20536
- isEditMode && /* @__PURE__ */ jsx33(AiSectionOverlay, { postToParent: postToParent2, apiRef: aiSectionApiRef }),
20537
- isSectionDragging && sectionDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
20538
- "div",
20539
- {
20540
- className: "pointer-events-none fixed z-2147483646",
20541
- style: { left: slot.left, top: slot.y, width: slot.width, height: 3, transform: "translateY(-50%)" },
20542
- children: /* @__PURE__ */ jsx33(
20543
- DropIndicator,
20544
- {
20545
- direction: "horizontal",
20546
- state: activeSectionDropIndex === i ? "dragActive" : "dragIdle",
20547
- className: "!h-full !w-full"
20548
- }
20549
- )
20550
- },
20551
- `section-drop-${slot.insertIndex}-${i}`
20552
- )),
20553
- Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ jsx33(
20554
- MediaOverlay,
20555
- {
20556
- hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
20557
- isUploading: true,
20558
- fadingOut,
20559
- onFadeOutComplete: handleMediaFadeOutComplete,
20560
- onReplace: handleMediaReplace
20561
- },
20562
- `uploading-${key}`
20563
- )),
20564
- mediaHover && !(mediaHover.key in uploadingRects) && mediaHover.key !== selectedMedia?.key && /* @__PURE__ */ jsx33(
20565
- MediaOverlay,
20566
- {
20567
- hover: mediaHover,
20568
- isUploading: false,
20569
- onReplace: handleMediaReplace,
20570
- onSelect: handleMediaSelect,
20571
- onVideoSettingsChange: handleVideoSettingsChange
20572
- }
20573
- ),
20574
- selectedMedia && !(selectedMedia.key in uploadingRects) && !linkPopover && /* @__PURE__ */ jsx33(
20575
- MediaOverlay,
20576
- {
20577
- hover: selectedMedia,
20578
- selected: true,
20579
- hovered: mediaHover?.key === selectedMedia.key,
20580
- isUploading: false,
20581
- onReplace: handleMediaReplace,
20582
- onSelect: handleMediaSelect,
20583
- onVideoSettingsChange: handleVideoSettingsChange
20584
- }
20585
- ),
20586
- carouselHover && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx33(CarouselOverlay, { hover: carouselHover, onEdit: handleEditCarousel }),
20587
- siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
20588
- siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
20589
- isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
20590
- isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
20591
- "div",
20592
- {
20593
- className: "pointer-events-none fixed z-2147483646",
20594
- style: {
20595
- left: slot.left,
20596
- top: slot.top,
20597
- width: slot.width,
20598
- height: slot.height
20599
- },
20600
- children: /* @__PURE__ */ jsx33(
20601
- DropIndicator,
20602
- {
20603
- direction: slot.direction,
20604
- state: activeFooterDropIndex === i ? "dragActive" : "dragIdle",
20605
- className: "!h-full !w-full"
20606
- }
20607
- )
20608
- },
20609
- `footer-drop-${slot.direction}-${slot.columnIndex}-${slot.insertIndex}-${i}`
20610
- )),
20611
- isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
20612
- "div",
20613
- {
20614
- className: "pointer-events-none fixed z-2147483646",
20615
- style: {
20616
- left: slot.left,
20617
- top: slot.top,
20618
- width: slot.width,
20619
- height: slot.height
20620
- },
20621
- children: /* @__PURE__ */ jsx33(
20622
- DropIndicator,
20623
- {
20624
- direction: slot.direction,
20625
- state: activeNavDropIndex === i ? "dragActive" : "dragIdle",
20626
- className: "!h-full !w-full"
20627
- }
20628
- )
19404
+ return bridgeRoot ? createPortal2(
19405
+ /* @__PURE__ */ jsxs20(Fragment8, { children: [
19406
+ /* @__PURE__ */ jsx33("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
19407
+ isEditMode && /* @__PURE__ */ jsx33(AiSectionOverlay, { postToParent: postToParent2, apiRef: aiSectionApiRef }),
19408
+ Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ jsx33(
19409
+ MediaOverlay,
19410
+ {
19411
+ hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
19412
+ isUploading: true,
19413
+ fadingOut,
19414
+ onFadeOutComplete: handleMediaFadeOutComplete,
19415
+ onReplace: handleMediaReplace
19416
+ },
19417
+ `uploading-${key}`
19418
+ )),
19419
+ mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ jsx33(
19420
+ MediaOverlay,
19421
+ {
19422
+ hover: mediaHover,
19423
+ isUploading: false,
19424
+ onReplace: handleMediaReplace,
19425
+ onVideoSettingsChange: handleVideoSettingsChange
19426
+ }
19427
+ ),
19428
+ carouselHover && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx33(CarouselOverlay, { hover: carouselHover, onEdit: handleEditCarousel }),
19429
+ siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
19430
+ siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
19431
+ isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
19432
+ isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
19433
+ "div",
19434
+ {
19435
+ className: "pointer-events-none fixed z-2147483646",
19436
+ style: {
19437
+ left: slot.left,
19438
+ top: slot.top,
19439
+ width: slot.width,
19440
+ height: slot.height
20629
19441
  },
20630
- `nav-drop-${slot.direction}-${slot.parentId ?? "root"}-${slot.insertIndex}-${i}`
20631
- )),
20632
- hoveredNavContainerRect && (toolbarVariant !== "select-frame" || isFooterFrameSelection) && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
20633
- hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
20634
- hoveredTextRect && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredTextRect, state: "hover" }),
20635
- formPickRect && !isItemDragging && /* @__PURE__ */ jsx33(
20636
- ItemInteractionLayer,
20637
- {
20638
- rect: formPickRect,
20639
- state: "active-top",
20640
- itemDragSurface: false,
20641
- toolbarAlign: "left",
20642
- chromeGap: 24,
20643
- toolbar: fieldPickRect ? void 0 : /* @__PURE__ */ jsxs20(
20644
- "div",
20645
- {
20646
- "data-ohw-form-toolbar": "",
20647
- className: "pointer-events-auto flex items-center gap-0.5 whitespace-nowrap rounded-lg border border-border bg-background p-1 shadow-md",
20648
- children: [
20649
- /* @__PURE__ */ jsx33(
20650
- "button",
20651
- {
20652
- type: "button",
20653
- "aria-label": "Add field",
20654
- className: "flex h-7 w-7 items-center justify-center rounded-md text-foreground transition-colors hover:bg-muted/80",
20655
- onClick: () => setFieldTypePickerOpen((open) => !open),
20656
- "data-ohw-add-field": "",
20657
- children: /* @__PURE__ */ jsx33(Plus4, { size: 15, "aria-hidden": true })
20658
- }
20659
- ),
20660
- /* @__PURE__ */ jsx33("div", { className: "mx-0.5 h-5 w-px bg-border" }),
20661
- /* @__PURE__ */ jsxs20(
20662
- "button",
20663
- {
20664
- type: "button",
20665
- 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",
20666
- onClick: () => {
20667
- const form = formPickElRef.current;
20668
- if (!form) return;
20669
- postToParent2({
20670
- type: "ow:form-pick",
20671
- formKey: formKeyOf(form),
20672
- hasLongText: formHasLongText(form)
20673
- });
20674
- },
20675
- children: [
20676
- /* @__PURE__ */ jsx33(Settings, { size: 14, "aria-hidden": true }),
20677
- "Form settings",
20678
- formPickCount ? (
20679
- // Counter pill, per the design — not a text suffix.
20680
- /* @__PURE__ */ jsx33(
20681
- "span",
20682
- {
20683
- "data-ohw-form-count": "",
20684
- 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",
20685
- children: formPickCount
20686
- }
20687
- )
20688
- ) : null
20689
- ]
20690
- }
20691
- ),
20692
- /* @__PURE__ */ jsx33("div", { className: "mx-0.5 h-5 w-px bg-border" }),
20693
- /* @__PURE__ */ jsx33("div", { className: "flex items-center rounded-md bg-muted/70 p-0.5", children: ["default", "success"].map((state) => /* @__PURE__ */ jsx33(
20694
- "button",
20695
- {
20696
- type: "button",
20697
- "aria-pressed": formViewState === state,
20698
- 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"),
20699
- onClick: () => {
20700
- const form = formPickElRef.current;
20701
- const key = form ? formKeyOf(form) : null;
20702
- if (!form || !key) return;
20703
- const initial = successInitialFor(form, key, editContentRef.current);
20704
- setFormViewState(form, key, state, initial);
20705
- setFormViewStateUi(state);
20706
- setFormPickRect(form.getBoundingClientRect());
20707
- if (state === "success") {
20708
- const successEl = form.querySelector(`[${SUCCESS_TEXT_ATTR}]`);
20709
- if (successEl) requestAnimationFrame(() => activateRef.current(successEl));
20710
- } else {
20711
- deactivateRef.current();
20712
- }
20713
- },
20714
- children: state
20715
- },
20716
- state
20717
- )) })
20718
- ]
20719
- }
20720
- )
20721
- }
20722
- ),
20723
- formHoverRect && !isItemDragging && /* @__PURE__ */ jsx33(
20724
- ItemInteractionLayer,
20725
- {
20726
- rect: formHoverRect,
20727
- state: "hover",
20728
- chromeGap: formHoverElRef.current && getFieldWrapper(formHoverElRef.current) ? 8 : 24
20729
- }
20730
- ),
20731
- fieldPickRect && fieldPickState && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ jsx33(
20732
- ItemInteractionLayer,
20733
- {
20734
- rect: fieldPickRect,
20735
- state: fieldDragging ? "dragging" : "active-top",
20736
- itemDragSurface: false,
20737
- toolbarAlign: "left",
20738
- chromeGap: 10,
20739
- showHandle: true,
20740
- dragHandleLabel: "Reorder field",
20741
- onDragHandleDragStart: handleFieldDragStart,
20742
- onDragHandleDragEnd: handleFieldDragEnd,
20743
- toolbar: /* @__PURE__ */ jsx33(
20744
- FormFieldToolbar,
20745
- {
20746
- type: fieldPickState.type,
20747
- required: fieldPickState.required,
20748
- onTypeChange: handleFieldTypeChange,
20749
- onRequiredToggle: handleFieldRequiredToggle,
20750
- onDuplicate: handleFieldDuplicate,
20751
- onDelete: handleFieldDelete
20752
- }
20753
- )
20754
- }
20755
- ),
20756
- fieldDragging ? fieldDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
20757
- "div",
20758
- {
20759
- className: "pointer-events-none fixed z-[2147483644]",
20760
- style: { top: slot.top, left: slot.left, width: slot.width },
20761
- children: /* @__PURE__ */ jsx33(
20762
- DropIndicator,
20763
- {
20764
- direction: "horizontal",
20765
- state: fieldDropIndex === i ? "dragActive" : "dragIdle",
20766
- className: "!w-full"
20767
- }
20768
- )
19442
+ children: /* @__PURE__ */ jsx33(
19443
+ DropIndicator,
19444
+ {
19445
+ direction: slot.direction,
19446
+ state: activeFooterDropIndex === i ? "dragActive" : "dragIdle",
19447
+ className: "!h-full !w-full"
19448
+ }
19449
+ )
19450
+ },
19451
+ `footer-drop-${slot.direction}-${slot.columnIndex}-${slot.insertIndex}-${i}`
19452
+ )),
19453
+ isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
19454
+ "div",
19455
+ {
19456
+ className: "pointer-events-none fixed z-2147483646",
19457
+ style: {
19458
+ left: slot.left,
19459
+ top: slot.top,
19460
+ width: slot.width,
19461
+ height: slot.height
20769
19462
  },
20770
- `field-drop-${i}`
20771
- )) : null,
20772
- fieldTypePickerOpen && formPickRect ? (() => {
20773
- const toolbar = document.querySelector("[data-ohw-form-toolbar]")?.getBoundingClientRect();
20774
- return /* @__PURE__ */ jsx33(
20775
- "div",
19463
+ children: /* @__PURE__ */ jsx33(
19464
+ DropIndicator,
20776
19465
  {
20777
- className: "pointer-events-none fixed z-[2147483645]",
20778
- style: {
20779
- top: toolbar ? toolbar.bottom + 6 : formPickRect.top + 16,
20780
- left: toolbar ? toolbar.left : formPickRect.left + 24
20781
- },
20782
- children: /* @__PURE__ */ jsx33(FieldTypePicker, { onPick: handleAddField })
19466
+ direction: slot.direction,
19467
+ state: activeNavDropIndex === i ? "dragActive" : "dragIdle",
19468
+ className: "!h-full !w-full"
20783
19469
  }
20784
- );
20785
- })() : null,
20786
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer2(selectedElRef.current) && /* @__PURE__ */ jsx33(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
20787
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isFooterLinksContainer(selectedElRef.current) && /* @__PURE__ */ jsx33(
20788
- FooterContainerChrome,
20789
- {
20790
- rect: toolbarRect,
20791
- onAdd: handleAddFooterColumn,
20792
- addDisabled: !canAddFooterColumn()
20793
- }
20794
- ),
20795
- toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame" || toolbarVariant === "logo") && /* @__PURE__ */ jsx33(
20796
- ItemInteractionLayer,
20797
- {
20798
- rect: isItemDragging && draggedItemRect && (footerDragRef.current?.wasSelected || navDragRef.current?.wasSelected) ? draggedItemRect : toolbarRect,
20799
- toolbarBelowRect: selectedElRef.current && isNavigationItem2(selectedElRef.current) ? getOpenNavDropdownPanelRect(selectedElRef.current) : null,
20800
- elRef: glowElRef,
20801
- state: isItemDragging ? "dragging" : selectedElRef.current && isNavigationItem2(selectedElRef.current) && getOpenNavDropdownPanelRect(selectedElRef.current) ? "active-bottom" : resolveItemInteractionState(toolbarRect, parentScrollRef.current),
20802
- showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey) || toolbarVariant === "select-frame" && isFooterFrameSelection,
20803
- dragDisabled: reorderDragDisabled,
20804
- dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
20805
- onDragHandleDragStart: handleItemDragStart,
20806
- onDragHandleDragEnd: handleItemDragEnd,
20807
- onItemPointerDown: toolbarVariant === "logo" ? void 0 : handleItemChromePointerDown,
20808
- onItemClick: toolbarVariant === "logo" ? void 0 : handleItemChromeClick,
20809
- itemDragSurface: toolbarVariant !== "logo" && !isFooterFrameSelection,
20810
- toolbar: toolbarVariant === "link-action" && !isItemDragging || toolbarVariant === "select-frame" && (isFooterFrameSelection || selectedIsSocialsRow) && !isItemDragging ? /* @__PURE__ */ jsx33(
20811
- ItemActionToolbar,
20812
- {
20813
- onEditLink: openLinkPopoverForSelected,
20814
- onStyle: () => {
20815
- const row = selectedElRef.current;
20816
- if (!row) return;
20817
- if (floatingPanel?.kind === "socials-display") closeFloatingPanelOnly();
20818
- else openSocialsDisplayPanel(row);
20819
- },
20820
- showStyle: selectedIsSocialsRow,
20821
- styleActive: floatingPanel?.kind === "socials-display",
20822
- onAddItem: handleAddChildItem,
20823
- onSelectParent: handleSelectParent,
20824
- onDuplicate: handleDuplicateSelected,
20825
- onDelete: handleDeleteSelected,
20826
- addItemDisabled: isFooterFrameSelection && isFooterAddItemDisabled(selectedElRef.current) || selectedElRef.current !== null && (() => {
20827
- const row = isSocialsRow(selectedElRef.current) ? selectedElRef.current : findSocialsRow(selectedElRef.current);
20828
- return row ? !canAddSocialItem(row) : false;
20829
- })(),
20830
- editLinkDisabled: false,
20831
- moreDisabled: false,
20832
- duplicateDisabled: isFooterFrameSelection || selectedIsSocialsRow,
20833
- showEditLink: !isFooterFrameSelection && !selectedIsSocialsRow && navDropdownPreviewOpen === null,
20834
- showAddItem: isFooterFrameSelection || selectedIsSocialsRow || !selectedIsCta && Boolean(
20835
- selectedElRef.current && isNavbarHrefKey(selectedElRef.current.getAttribute("data-ohw-href-key")) && !isNestedNavChild(selectedElRef.current)
19470
+ )
19471
+ },
19472
+ `nav-drop-${slot.direction}-${slot.parentId ?? "root"}-${slot.insertIndex}-${i}`
19473
+ )),
19474
+ hoveredNavContainerRect && (toolbarVariant !== "select-frame" || isFooterFrameSelection) && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
19475
+ hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
19476
+ hoveredTextRect && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredTextRect, state: "hover" }),
19477
+ formPickRect && !isItemDragging && /* @__PURE__ */ jsx33(
19478
+ ItemInteractionLayer,
19479
+ {
19480
+ rect: formPickRect,
19481
+ state: "active-top",
19482
+ itemDragSurface: false,
19483
+ toolbarAlign: "left",
19484
+ chromeGap: 24,
19485
+ toolbar: fieldPickRect ? void 0 : /* @__PURE__ */ jsxs20(
19486
+ "div",
19487
+ {
19488
+ "data-ohw-form-toolbar": "",
19489
+ className: "pointer-events-auto flex items-center gap-0.5 whitespace-nowrap rounded-lg border border-border bg-background p-1 shadow-md",
19490
+ children: [
19491
+ /* @__PURE__ */ jsx33(
19492
+ "button",
19493
+ {
19494
+ type: "button",
19495
+ "aria-label": "Add field",
19496
+ className: "flex h-7 w-7 items-center justify-center rounded-md text-foreground transition-colors hover:bg-muted/80",
19497
+ onClick: () => setFieldTypePickerOpen((open) => !open),
19498
+ "data-ohw-add-field": "",
19499
+ children: /* @__PURE__ */ jsx33(Plus4, { size: 15, "aria-hidden": true })
19500
+ }
20836
19501
  ),
20837
- showMore: (!selectedIsCta || isFooterFrameSelection) && !selectedIsSocialsRow,
20838
- dropdownOpen: navDropdownPreviewOpen,
20839
- onDropdownOpenChange: handleNavDropdownOpenChange,
20840
- headingVisible: footerHeadingVisible,
20841
- onHeadingVisibleChange: handleFooterHeadingVisibleChange
20842
- }
20843
- ) : void 0
20844
- }
20845
- ),
20846
- toolbarRect && toolbarVariant === "rich-text" && !linkPopover && /* @__PURE__ */ jsxs20(Fragment8, { children: [
20847
- /* @__PURE__ */ jsx33(
20848
- EditGlowChrome,
19502
+ /* @__PURE__ */ jsx33("div", { className: "mx-0.5 h-5 w-px bg-border" }),
19503
+ /* @__PURE__ */ jsxs20(
19504
+ "button",
19505
+ {
19506
+ type: "button",
19507
+ 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",
19508
+ onClick: () => {
19509
+ const form = formPickElRef.current;
19510
+ if (!form) return;
19511
+ postToParent2({
19512
+ type: "ow:form-pick",
19513
+ formKey: formKeyOf(form),
19514
+ hasLongText: formHasLongText(form)
19515
+ });
19516
+ },
19517
+ children: [
19518
+ /* @__PURE__ */ jsx33(Settings, { size: 14, "aria-hidden": true }),
19519
+ "Form settings",
19520
+ formPickCount ? (
19521
+ // Counter pill, per the design — not a text suffix.
19522
+ /* @__PURE__ */ jsx33(
19523
+ "span",
19524
+ {
19525
+ "data-ohw-form-count": "",
19526
+ 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",
19527
+ children: formPickCount
19528
+ }
19529
+ )
19530
+ ) : null
19531
+ ]
19532
+ }
19533
+ ),
19534
+ /* @__PURE__ */ jsx33("div", { className: "mx-0.5 h-5 w-px bg-border" }),
19535
+ /* @__PURE__ */ jsx33("div", { className: "flex items-center rounded-md bg-muted/70 p-0.5", children: ["default", "success"].map((state) => /* @__PURE__ */ jsx33(
19536
+ "button",
19537
+ {
19538
+ type: "button",
19539
+ "aria-pressed": formViewState === state,
19540
+ 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"),
19541
+ onClick: () => {
19542
+ const form = formPickElRef.current;
19543
+ const key = form ? formKeyOf(form) : null;
19544
+ if (!form || !key) return;
19545
+ const initial = successInitialFor(form, key, editContentRef.current);
19546
+ setFormViewState(form, key, state, initial);
19547
+ setFormViewStateUi(state);
19548
+ setFormPickRect(form.getBoundingClientRect());
19549
+ if (state === "success") {
19550
+ const successEl = form.querySelector(`[${SUCCESS_TEXT_ATTR}]`);
19551
+ if (successEl) requestAnimationFrame(() => activateRef.current(successEl));
19552
+ } else {
19553
+ deactivateRef.current();
19554
+ }
19555
+ },
19556
+ children: state
19557
+ },
19558
+ state
19559
+ )) })
19560
+ ]
19561
+ }
19562
+ )
19563
+ }
19564
+ ),
19565
+ formHoverRect && !isItemDragging && /* @__PURE__ */ jsx33(
19566
+ ItemInteractionLayer,
19567
+ {
19568
+ rect: formHoverRect,
19569
+ state: "hover",
19570
+ chromeGap: formHoverElRef.current && getFieldWrapper(formHoverElRef.current) ? 8 : 24
19571
+ }
19572
+ ),
19573
+ fieldPickRect && fieldPickState && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ jsx33(
19574
+ ItemInteractionLayer,
19575
+ {
19576
+ rect: fieldPickRect,
19577
+ state: fieldDragging ? "dragging" : "active-top",
19578
+ itemDragSurface: false,
19579
+ toolbarAlign: "left",
19580
+ chromeGap: 10,
19581
+ showHandle: true,
19582
+ dragHandleLabel: "Reorder field",
19583
+ onDragHandleDragStart: handleFieldDragStart,
19584
+ onDragHandleDragEnd: handleFieldDragEnd,
19585
+ toolbar: /* @__PURE__ */ jsx33(
19586
+ FormFieldToolbar,
20849
19587
  {
20850
- rect: toolbarRect,
20851
- elRef: glowElRef,
20852
- reorderHrefKey,
20853
- dragDisabled: reorderDragDisabled,
20854
- hideHandle: isItemDragging
19588
+ type: fieldPickState.type,
19589
+ required: fieldPickState.required,
19590
+ onTypeChange: handleFieldTypeChange,
19591
+ onRequiredToggle: handleFieldRequiredToggle,
19592
+ onDuplicate: handleFieldDuplicate,
19593
+ onDelete: handleFieldDelete
20855
19594
  }
20856
- ),
20857
- /* @__PURE__ */ jsx33(
20858
- FloatingToolbar,
19595
+ )
19596
+ }
19597
+ ),
19598
+ fieldDragging ? fieldDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
19599
+ "div",
19600
+ {
19601
+ className: "pointer-events-none fixed z-[2147483644]",
19602
+ style: { top: slot.top, left: slot.left, width: slot.width },
19603
+ children: /* @__PURE__ */ jsx33(
19604
+ DropIndicator,
20859
19605
  {
20860
- rect: toolbarRect,
20861
- parentScroll: parentScrollRef.current,
20862
- elRef: toolbarElRef,
20863
- onCommand: handleCommand,
20864
- activeCommands,
20865
- showEditLink,
20866
- onEditLink: openLinkPopoverForActive
19606
+ direction: "horizontal",
19607
+ state: fieldDropIndex === i ? "dragActive" : "dragIdle",
19608
+ className: "!w-full"
20867
19609
  }
20868
19610
  )
20869
- ] }),
20870
- maxBadge && /* @__PURE__ */ jsxs20(
20871
- "div",
20872
- {
20873
- "data-ohw-max-badge": "",
20874
- style: {
20875
- position: "fixed",
20876
- top: maxBadge.rect.bottom + 4,
20877
- left: maxBadge.rect.right,
20878
- transform: "translateX(-100%)",
20879
- zIndex: 2147483647,
20880
- background: maxBadge.current > maxBadge.max ? "#FEF2F2" : "#F5F5F4",
20881
- color: maxBadge.current > maxBadge.max ? "#DC2626" : "#78716C",
20882
- border: `1px solid ${maxBadge.current > maxBadge.max ? "#FECACA" : "#E7E5E4"}`,
20883
- borderRadius: 4,
20884
- padding: "2px 6px",
20885
- fontSize: 11,
20886
- fontWeight: 500,
20887
- pointerEvents: "none"
20888
- },
20889
- children: [
20890
- maxBadge.current,
20891
- "/",
20892
- maxBadge.max
20893
- ]
20894
- }
20895
- ),
20896
- toggleState && !linkPopover && /* @__PURE__ */ jsx33(
20897
- StateToggle,
19611
+ },
19612
+ `field-drop-${i}`
19613
+ )) : null,
19614
+ fieldTypePickerOpen && formPickRect && /* @__PURE__ */ jsx33(
19615
+ "div",
19616
+ {
19617
+ className: "pointer-events-none fixed z-[2147483645]",
19618
+ style: { top: formPickRect.top + 16, left: formPickRect.left + 24 },
19619
+ children: /* @__PURE__ */ jsx33(FieldTypePicker, { onPick: handleAddField })
19620
+ }
19621
+ ),
19622
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer2(selectedElRef.current) && /* @__PURE__ */ jsx33(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
19623
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isFooterLinksContainer(selectedElRef.current) && /* @__PURE__ */ jsx33(
19624
+ FooterContainerChrome,
19625
+ {
19626
+ rect: toolbarRect,
19627
+ onAdd: handleAddFooterColumn,
19628
+ addDisabled: !canAddFooterColumn()
19629
+ }
19630
+ ),
19631
+ toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame" || toolbarVariant === "logo") && /* @__PURE__ */ jsx33(
19632
+ ItemInteractionLayer,
19633
+ {
19634
+ rect: isItemDragging && draggedItemRect && (footerDragRef.current?.wasSelected || navDragRef.current?.wasSelected) ? draggedItemRect : toolbarRect,
19635
+ toolbarBelowRect: selectedElRef.current && isNavigationItem2(selectedElRef.current) ? getOpenNavDropdownPanelRect(selectedElRef.current) : null,
19636
+ elRef: glowElRef,
19637
+ state: isItemDragging ? "dragging" : selectedElRef.current && isNavigationItem2(selectedElRef.current) && getOpenNavDropdownPanelRect(selectedElRef.current) ? "active-bottom" : resolveItemInteractionState(toolbarRect, parentScrollRef.current),
19638
+ showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey) || toolbarVariant === "select-frame" && isFooterFrameSelection,
19639
+ dragDisabled: reorderDragDisabled,
19640
+ dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
19641
+ onDragHandleDragStart: handleItemDragStart,
19642
+ onDragHandleDragEnd: handleItemDragEnd,
19643
+ onItemPointerDown: toolbarVariant === "logo" ? void 0 : handleItemChromePointerDown,
19644
+ onItemClick: toolbarVariant === "logo" ? void 0 : handleItemChromeClick,
19645
+ itemDragSurface: toolbarVariant !== "logo" && !isFooterFrameSelection,
19646
+ toolbar: toolbarVariant === "link-action" && !isItemDragging || toolbarVariant === "select-frame" && (isFooterFrameSelection || selectedIsSocialsRow) && !isItemDragging ? /* @__PURE__ */ jsx33(
19647
+ ItemActionToolbar,
19648
+ {
19649
+ onEditLink: openLinkPopoverForSelected,
19650
+ onStyle: () => {
19651
+ const row = selectedElRef.current;
19652
+ if (!row) return;
19653
+ if (floatingPanel?.kind === "socials-display") closeFloatingPanelOnly();
19654
+ else openSocialsDisplayPanel(row);
19655
+ },
19656
+ showStyle: selectedIsSocialsRow,
19657
+ styleActive: floatingPanel?.kind === "socials-display",
19658
+ onAddItem: handleAddChildItem,
19659
+ onSelectParent: handleSelectParent,
19660
+ onDuplicate: handleDuplicateSelected,
19661
+ onDelete: handleDeleteSelected,
19662
+ addItemDisabled: isFooterFrameSelection && isFooterAddItemDisabled(selectedElRef.current) || selectedElRef.current !== null && (() => {
19663
+ const row = isSocialsRow(selectedElRef.current) ? selectedElRef.current : findSocialsRow(selectedElRef.current);
19664
+ return row ? !canAddSocialItem(row) : false;
19665
+ })(),
19666
+ editLinkDisabled: false,
19667
+ moreDisabled: false,
19668
+ duplicateDisabled: isFooterFrameSelection || selectedIsSocialsRow,
19669
+ showEditLink: !isFooterFrameSelection && !selectedIsSocialsRow && navDropdownPreviewOpen === null,
19670
+ showAddItem: isFooterFrameSelection || selectedIsSocialsRow || !selectedIsCta && Boolean(
19671
+ selectedElRef.current && isNavbarHrefKey(selectedElRef.current.getAttribute("data-ohw-href-key")) && !isNestedNavChild(selectedElRef.current)
19672
+ ),
19673
+ showMore: (!selectedIsCta || isFooterFrameSelection) && !selectedIsSocialsRow,
19674
+ dropdownOpen: navDropdownPreviewOpen,
19675
+ onDropdownOpenChange: handleNavDropdownOpenChange,
19676
+ headingVisible: footerHeadingVisible,
19677
+ onHeadingVisibleChange: handleFooterHeadingVisibleChange
19678
+ }
19679
+ ) : void 0
19680
+ }
19681
+ ),
19682
+ toolbarRect && toolbarVariant === "rich-text" && !linkPopover && /* @__PURE__ */ jsxs20(Fragment8, { children: [
19683
+ /* @__PURE__ */ jsx33(
19684
+ EditGlowChrome,
20898
19685
  {
20899
- rect: toggleState.rect,
20900
- activeState: toggleState.activeState,
20901
- states: toggleState.states,
20902
- onStateChange: handleStateChange
19686
+ rect: toolbarRect,
19687
+ elRef: glowElRef,
19688
+ reorderHrefKey,
19689
+ dragDisabled: reorderDragDisabled,
19690
+ hideHandle: isItemDragging
20903
19691
  }
20904
19692
  ),
20905
- sectionGap && !linkPopover && /* @__PURE__ */ jsxs20(
20906
- "div",
19693
+ /* @__PURE__ */ jsx33(
19694
+ FloatingToolbar,
20907
19695
  {
20908
- "data-ohw-section-insert-line": "",
20909
- className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
20910
- style: { top: sectionGap.y, transform: "translateY(-50%)" },
20911
- children: [
20912
- /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
20913
- /* @__PURE__ */ jsx33(
20914
- Badge,
20915
- {
20916
- 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",
20917
- onClick: () => {
20918
- window.parent.postMessage(
20919
- {
20920
- type: "ow:add-section",
20921
- insertAfter: sectionGap.insertAfter,
20922
- insertBefore: sectionGap.insertBefore
20923
- },
20924
- "*"
20925
- );
20926
- },
20927
- children: "Add Section"
20928
- }
20929
- ),
20930
- /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-primary", style: { height: 3 } })
20931
- ]
19696
+ rect: toolbarRect,
19697
+ parentScroll: parentScrollRef.current,
19698
+ elRef: toolbarElRef,
19699
+ onCommand: handleCommand,
19700
+ activeCommands,
19701
+ showEditLink,
19702
+ onEditLink: openLinkPopoverForActive
20932
19703
  }
20933
- ),
20934
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx33(
20935
- LinkPopover,
20936
- {
20937
- panelRef: linkPopoverPanelRef,
20938
- portalContainer: dialogPortalContainer,
20939
- open: true,
20940
- mode: linkPopover.mode ?? "edit",
20941
- pages: sitePages,
20942
- sections: currentSections,
20943
- sectionsByPath,
20944
- initialTarget: linkPopover.target,
20945
- existingTargets: linkPopover.intent === "add-nav" ? getNavbarExistingTargets() : [],
20946
- onClose: closeLinkPopover,
20947
- onSubmit: handleLinkPopoverSubmit
19704
+ )
19705
+ ] }),
19706
+ maxBadge && /* @__PURE__ */ jsxs20(
19707
+ "div",
19708
+ {
19709
+ "data-ohw-max-badge": "",
19710
+ style: {
19711
+ position: "fixed",
19712
+ top: maxBadge.rect.bottom + 4,
19713
+ left: maxBadge.rect.right,
19714
+ transform: "translateX(-100%)",
19715
+ zIndex: 2147483647,
19716
+ background: maxBadge.current > maxBadge.max ? "#FEF2F2" : "#F5F5F4",
19717
+ color: maxBadge.current > maxBadge.max ? "#DC2626" : "#78716C",
19718
+ border: `1px solid ${maxBadge.current > maxBadge.max ? "#FECACA" : "#E7E5E4"}`,
19719
+ borderRadius: 4,
19720
+ padding: "2px 6px",
19721
+ fontSize: 11,
19722
+ fontWeight: 500,
19723
+ pointerEvents: "none"
20948
19724
  },
20949
- linkPopover.key
20950
- ) : null,
20951
- floatingPanel && floatingPanel.kind === "socials-display" ? /* @__PURE__ */ jsx33(
20952
- FloatingPanel,
20953
- {
20954
- open: true,
20955
- title: floatingPanel.title,
20956
- context: floatingPanel.context,
20957
- position: floatingPanelPos,
20958
- onPositionChange: setFloatingPanelPos,
20959
- parentScroll: parentScrollSnap ?? parentScrollRef.current,
20960
- onClose: closeFloatingPanelOnly,
20961
- children: /* @__PURE__ */ jsx33(
20962
- SocialsDisplayPanel,
19725
+ children: [
19726
+ maxBadge.current,
19727
+ "/",
19728
+ maxBadge.max
19729
+ ]
19730
+ }
19731
+ ),
19732
+ toggleState && !linkPopover && /* @__PURE__ */ jsx33(
19733
+ StateToggle,
19734
+ {
19735
+ rect: toggleState.rect,
19736
+ activeState: toggleState.activeState,
19737
+ states: toggleState.states,
19738
+ onStateChange: handleStateChange
19739
+ }
19740
+ ),
19741
+ sectionGap && !linkPopover && /* @__PURE__ */ jsxs20(
19742
+ "div",
19743
+ {
19744
+ "data-ohw-section-insert-line": "",
19745
+ className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
19746
+ style: { top: sectionGap.y, transform: "translateY(-50%)" },
19747
+ children: [
19748
+ /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
19749
+ /* @__PURE__ */ jsx33(
19750
+ Badge,
20963
19751
  {
20964
- display: socialsDisplayFor(floatingPanel.row, editContentRef.current),
20965
- onChange: (next) => {
20966
- changeSocialsDisplay(floatingPanel.row, next);
20967
- setFloatingPanel({ ...floatingPanel });
20968
- }
19752
+ 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",
19753
+ onClick: () => {
19754
+ window.parent.postMessage(
19755
+ {
19756
+ type: "ow:add-section",
19757
+ insertAfter: sectionGap.insertAfter,
19758
+ insertBefore: sectionGap.insertBefore
19759
+ },
19760
+ "*"
19761
+ );
19762
+ },
19763
+ children: "Add Section"
20969
19764
  }
20970
- )
20971
- }
20972
- ) : null
20973
- ] }),
20974
- bridgeRoot
20975
- ) : null
20976
- ] });
20977
- }
20978
-
20979
- // src/ui/EmptySection.tsx
20980
- import Link3 from "next/link";
20981
- import { Fragment as Fragment9, jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
20982
- function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey }) {
20983
- return /* @__PURE__ */ jsxs21(Fragment9, { children: [
20984
- /* @__PURE__ */ jsx34(
20985
- "p",
20986
- {
20987
- style: {
20988
- fontFamily: "var(--brand-font-body)",
20989
- fontSize: "0.75rem",
20990
- fontWeight: 500,
20991
- letterSpacing: "0.15em",
20992
- textTransform: "uppercase",
20993
- color: "var(--brand-accent)",
20994
- marginBottom: "1.5rem"
20995
- },
20996
- children: /* @__PURE__ */ jsx34(Link3, { href: homeHref, style: { color: "inherit" }, children: /* @__PURE__ */ jsx34("span", { ...eyebrowKey ? { "data-ohw-editable": "text", "data-ohw-key": eyebrowKey } : {}, children: "Home" }) })
20997
- }
20998
- ),
20999
- /* @__PURE__ */ jsx34(
21000
- "h1",
21001
- {
21002
- style: {
21003
- fontFamily: "var(--brand-font-heading)",
21004
- fontSize: "clamp(2rem, 3.5vw, 2.75rem)",
21005
- lineHeight: 1.1,
21006
- letterSpacing: "-0.025em",
21007
- color: "var(--brand-text)",
21008
- marginBottom: "1rem"
21009
- },
21010
- ...titleKey ? { "data-ohw-editable": "text", "data-ohw-key": titleKey, "data-ohw-max-length": 80 } : {},
21011
- children: title
21012
- }
21013
- ),
21014
- /* @__PURE__ */ jsx34(
21015
- "p",
21016
- {
21017
- style: {
21018
- fontFamily: "var(--brand-font-body)",
21019
- fontSize: "1rem",
21020
- lineHeight: 1.7,
21021
- fontWeight: 300,
21022
- color: "var(--brand-text-muted)",
21023
- maxWidth: "340px"
19765
+ ),
19766
+ /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-primary", style: { height: 3 } })
19767
+ ]
19768
+ }
19769
+ ),
19770
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx33(
19771
+ LinkPopover,
19772
+ {
19773
+ panelRef: linkPopoverPanelRef,
19774
+ portalContainer: dialogPortalContainer,
19775
+ open: true,
19776
+ mode: linkPopover.mode ?? "edit",
19777
+ pages: sitePages,
19778
+ sections: currentSections,
19779
+ sectionsByPath,
19780
+ initialTarget: linkPopover.target,
19781
+ existingTargets: linkPopover.intent === "add-nav" ? getNavbarExistingTargets() : [],
19782
+ onClose: closeLinkPopover,
19783
+ onSubmit: handleLinkPopoverSubmit
21024
19784
  },
21025
- ...subtitleKey ? { "data-ohw-editable": "text", "data-ohw-key": subtitleKey, "data-ohw-max-length": 160 } : {},
21026
- children: "This page doesn't have any content yet."
21027
- }
21028
- )
21029
- ] });
19785
+ linkPopover.key
19786
+ ) : null,
19787
+ floatingPanel && floatingPanel.kind === "socials-display" ? /* @__PURE__ */ jsx33(
19788
+ FloatingPanel,
19789
+ {
19790
+ open: true,
19791
+ title: floatingPanel.title,
19792
+ context: floatingPanel.context,
19793
+ position: floatingPanelPos,
19794
+ onPositionChange: setFloatingPanelPos,
19795
+ parentScroll: parentScrollSnap ?? parentScrollRef.current,
19796
+ onClose: closeFloatingPanelOnly,
19797
+ children: /* @__PURE__ */ jsx33(
19798
+ SocialsDisplayPanel,
19799
+ {
19800
+ display: socialsDisplayFor(floatingPanel.row, editContentRef.current),
19801
+ onChange: (next) => {
19802
+ changeSocialsDisplay(floatingPanel.row, next);
19803
+ setFloatingPanel({ ...floatingPanel });
19804
+ }
19805
+ }
19806
+ )
19807
+ }
19808
+ ) : null
19809
+ ] }),
19810
+ bridgeRoot
19811
+ ) : null;
21030
19812
  }
21031
19813
  export {
21032
19814
  AI_DEFAULT_BRAND,
@@ -21044,7 +19826,6 @@ export {
21044
19826
  DropdownMenuItem,
21045
19827
  DropdownMenuSeparator,
21046
19828
  DropdownMenuTrigger,
21047
- EmptySection,
21048
19829
  ItemActionToolbar,
21049
19830
  ItemInteractionLayer,
21050
19831
  LinkEditorPanel,