@ohhwells/bridge 0.1.70-next.212 → 0.1.70

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  // src/OhhwellsBridge.tsx
4
- import React12, { useCallback as useCallback8, useEffect as useEffect13, useLayoutEffect as useLayoutEffect4, useRef as useRef10, useState as useState12 } from "react";
4
+ import React12, { 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
  }
@@ -506,12 +158,7 @@ var AI_RESPONSIVE_CSS = [
506
158
  "@media (max-width: 640px) {",
507
159
  " [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
508
160
  " [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
509
- // Group containers flatten to a column on phones; span placements come along for free.
510
- " [data-ai-responsive] [data-ai-group] { display: flex !important; flex-direction: column !important; }",
511
- " [data-ai-responsive] [data-ai-group] > * { grid-column: auto !important; }",
512
161
  " [data-ai-responsive] [data-ai-section-inner] { padding-left: 20px !important; padding-right: 20px !important; }",
513
- " [data-ai-responsive] { overflow-x: hidden; }",
514
- " [data-ai-responsive] img { max-width: 100%; }",
515
162
  "}"
516
163
  ].join("\n");
517
164
  var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
@@ -589,7 +236,7 @@ function ButtonEl({
589
236
  }) {
590
237
  const secondary = slots.variant === "secondary";
591
238
  const href = str(slots.href);
592
- const linkAttrs = ctx.keyFor && path ? { "data-ohw-href-key": ctx.keyFor(`${path}.href`), "data-ohw-role": "button" } : {};
239
+ const linkAttrs = ctx.keyFor && path ? { "data-ohw-href-key": ctx.keyFor(`${path}.href`) } : {};
593
240
  return /* @__PURE__ */ jsx(
594
241
  "a",
595
242
  {
@@ -605,7 +252,7 @@ function ButtonEl({
605
252
  textDecoration: "none",
606
253
  cursor: "pointer",
607
254
  ...typeStyle(AI_TREE_TOKENS.type.button, ctx.brand.fonts.body),
608
- ...secondary ? { border: `1px solid ${ctx.brand.palette.dark}`, color: ctx.brand.palette.dark } : { background: ctx.brand.palette.primary, color: ctx.buttonLabel ?? AI_TREE_TOKENS.textPrimaryForeground }
255
+ ...secondary ? { border: `1px solid ${ctx.brand.palette.dark}`, color: ctx.brand.palette.dark } : { background: ctx.brand.palette.primary, color: AI_TREE_TOKENS.textPrimaryForeground }
609
256
  },
610
257
  children: /* @__PURE__ */ jsx("span", { ...path ? textAttrs(ctx, `${path}.label`) : {}, children: str(slots.label) })
611
258
  }
@@ -1111,24 +758,7 @@ function CardBlock({ node, ctx, path }) {
1111
758
  minWidth: 0
1112
759
  },
1113
760
  children: [
1114
- media && (horizontal ? /* @__PURE__ */ jsx(
1115
- "div",
1116
- {
1117
- style: (
1118
- // An icon hugs its glyph — flex:1 gave a 48px icon half the card and pushed the
1119
- // text to the far side. Photos keep the half-and-half split. The inset has no
1120
- // inner padding (the photo split absorbed that), so the icon carries its own gap.
1121
- /^(lucide|simple):/.test(mediaRef) ? {
1122
- flexShrink: 0,
1123
- display: "flex",
1124
- alignItems: "center",
1125
- padding: mediaInset,
1126
- [mediaPosition === "right" ? "marginLeft" : "marginRight"]: 20
1127
- } : { flex: 1, minWidth: 0, display: "flex", alignItems: "center", padding: mediaInset }
1128
- ),
1129
- children: media
1130
- }
1131
- ) : /* @__PURE__ */ jsx(
761
+ media && (horizontal ? /* @__PURE__ */ jsx("div", { style: { flex: 1, minWidth: 0, display: "flex", alignItems: "center", padding: mediaInset }, children: media }) : /* @__PURE__ */ jsx(
1132
762
  "div",
1133
763
  {
1134
764
  style: /^(lucide|simple):/.test(mediaRef) ? { padding: `${AI_TREE_TOKENS.paddingBlock}px ${AI_TREE_TOKENS.paddingBlock}px 0` } : hasBg ? void 0 : { borderRadius: AI_TREE_TOKENS.radiusCard, overflow: "hidden" },
@@ -1392,49 +1022,6 @@ function renderNode(node, ctx, path) {
1392
1022
  switch (node.type) {
1393
1023
  case "text":
1394
1024
  return /* @__PURE__ */ jsx(TextBlock, { slots, ctx, path });
1395
- // Layout container: arranges child blocks, contributes no content of its own. `grid` is a
1396
- // nested 12-column grid the children span (a collage is 3–5 media on it, bottom-aligned so
1397
- // mixed aspects read as a composition); `split` is exactly two children at a ratio; `stack`
1398
- // is a column. Children render through this same dispatcher, so edit markers, media
1399
- // resolution, and copy paths all work unchanged inside a group.
1400
- case "group": {
1401
- const layout = str(slots.layout);
1402
- const gap = slots.spacing === "tight" ? AI_TREE_TOKENS.spacing3 : slots.spacing === "airy" ? AI_TREE_TOKENS.spacing8 : AI_TREE_TOKENS.spacing6;
1403
- const kids = (node.children ?? []).map((child, i) => /* @__PURE__ */ jsx(
1404
- "div",
1405
- {
1406
- style: layout === "grid" ? {
1407
- gridColumn: `span ${typeof child.span === "number" ? Math.min(12, Math.max(1, child.span)) : 12}`,
1408
- minWidth: 0
1409
- } : { minWidth: 0 },
1410
- children: renderNode(child, ctx, `${path}.c${i}`)
1411
- },
1412
- i
1413
- ));
1414
- if (layout === "grid") {
1415
- return /* @__PURE__ */ jsx(
1416
- "div",
1417
- {
1418
- "data-ai-group": "grid",
1419
- style: { display: "grid", gridTemplateColumns: "repeat(12, 1fr)", gap, alignItems: "end" },
1420
- children: kids
1421
- }
1422
- );
1423
- }
1424
- if (layout === "split") {
1425
- const ratio = str(slots.ratio);
1426
- const cols = ratio === "3:5" ? "3fr 5fr" : ratio === "5:3" ? "5fr 3fr" : "1fr 1fr";
1427
- return /* @__PURE__ */ jsx(
1428
- "div",
1429
- {
1430
- "data-ai-group": "split",
1431
- style: { display: "grid", gridTemplateColumns: cols, gap, alignItems: "center" },
1432
- children: kids
1433
- }
1434
- );
1435
- }
1436
- return /* @__PURE__ */ jsx("div", { "data-ai-group": "stack", style: { display: "flex", flexDirection: "column", gap }, children: kids });
1437
- }
1438
1025
  case "button":
1439
1026
  return /* @__PURE__ */ jsx(ButtonEl, { slots, ctx, path });
1440
1027
  case "button-row":
@@ -1567,14 +1154,11 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1567
1154
  return null;
1568
1155
  }
1569
1156
  const resolvedBrand = brand ?? AI_DEFAULT_BRAND;
1570
- const band = (tree.settings ?? {}).sectionBackground === "accent" ? accentBandContext(resolvedBrand) : null;
1571
- const blockBrand = band?.brand ?? resolvedBrand;
1572
1157
  const ctx = {
1573
- brand: blockBrand,
1158
+ brand: resolvedBrand,
1574
1159
  resolveMedia: resolveMedia ?? (() => null),
1575
- cardSurface: blockBrand.palette.light.toUpperCase() === AI_DEFAULT_BRAND.palette.light.toUpperCase() ? "#ECECEB" : `color-mix(in srgb, ${blockBrand.palette.light} 90%, ${blockBrand.palette.dark})`,
1576
- keyFor: editKeyPrefix ? (path) => `${editKeyPrefix}.${path}` : null,
1577
- ...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
1578
1162
  };
1579
1163
  const settings = tree.settings ?? {};
1580
1164
  const pad = AI_TREE_TOKENS.sectionPadding[settings.spacing ?? "balanced"];
@@ -1582,20 +1166,6 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1582
1166
  const overlayAlpha = { low: 0.25, medium: 0.45, high: 0.65 }[settings.overlayIntensity ?? "medium"];
1583
1167
  const backgroundUrl = isOverlay && settings.backgroundMedia ? ctx.resolveMedia(settings.backgroundMedia) : null;
1584
1168
  const bgAttrs = ctx.keyFor && isOverlay && settings.backgroundMedia ? { "data-ohw-key": ctx.keyFor("settings.backgroundMedia"), "data-ohw-editable": "bg-image" } : {};
1585
- const toneBackground = (() => {
1586
- const { dark, primary, light } = resolvedBrand.palette;
1587
- switch (settings.sectionBackground) {
1588
- case "surface":
1589
- return `color-mix(in srgb, ${light} 94%, ${dark})`;
1590
- case "accent":
1591
- return primary;
1592
- case "accent-soft":
1593
- return `color-mix(in srgb, ${primary} 12%, ${light})`;
1594
- default:
1595
- return void 0;
1596
- }
1597
- })();
1598
- const distributed = !isOverlay && settings.textDistribution;
1599
1169
  return /* @__PURE__ */ jsxs(
1600
1170
  "section",
1601
1171
  {
@@ -1605,11 +1175,10 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1605
1175
  style: {
1606
1176
  position: "relative",
1607
1177
  padding: `${pad}px 0`,
1608
- background: toneBackground ?? (isOverlay && !backgroundUrl ? "#DBEAFE" : void 0),
1178
+ background: isOverlay && !backgroundUrl ? "#DBEAFE" : void 0,
1609
1179
  backgroundImage: backgroundUrl ? `url(${backgroundUrl})` : void 0,
1610
1180
  backgroundSize: "cover",
1611
- backgroundPosition: "center",
1612
- color: settings.sectionBackground === "accent" ? blockBrand.palette.dark : void 0
1181
+ backgroundPosition: "center"
1613
1182
  },
1614
1183
  children: [
1615
1184
  /* @__PURE__ */ jsx("style", { children: AI_RESPONSIVE_CSS }),
@@ -1633,24 +1202,10 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1633
1202
  display: "grid",
1634
1203
  gridTemplateColumns: "repeat(12, 1fr)",
1635
1204
  gap: AI_TREE_TOKENS.spacing6,
1636
- alignItems: distributed === "space-between" ? "stretch" : (distributed ?? settings.verticalPosition) === "top" ? "start" : "center",
1205
+ alignItems: settings.verticalPosition === "top" ? "start" : "center",
1637
1206
  marginTop: r2 > 0 ? AI_TREE_TOKENS.spacing8 : 0
1638
1207
  },
1639
- children: (row.blocks ?? []).map((block, b) => /* @__PURE__ */ jsx(
1640
- "div",
1641
- {
1642
- "data-ai-cell": "",
1643
- style: {
1644
- gridColumn: `span ${Math.min(12, Math.max(1, block.span))}`,
1645
- minWidth: 0,
1646
- // space-between: each column becomes a flex column whose content spreads over
1647
- // the full row height instead of clumping at the top.
1648
- ...distributed === "space-between" ? { display: "flex", flexDirection: "column", justifyContent: "space-between" } : {}
1649
- },
1650
- children: renderNode(block, ctx, `r${r2}.b${b}`)
1651
- },
1652
- b
1653
- ))
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))
1654
1209
  },
1655
1210
  r2
1656
1211
  ))
@@ -1666,36 +1221,17 @@ import { jsx as jsx2 } from "react/jsx-runtime";
1666
1221
  var CONTAINER_ATTR = "data-ohw-ai-generated";
1667
1222
  var REPLACED_ATTR = "data-ohw-ai-replaced-by";
1668
1223
  var REMOVED_ATTR = "data-ohw-ai-removed";
1669
- var TEMPLATE_HIDDEN_ATTR = "data-ohw-ai-template-hidden";
1670
- var CHROME_SECTION_IDS = /* @__PURE__ */ new Set(["navbar", "footer"]);
1671
- function readRootVar(name) {
1672
- if (typeof document === "undefined") return "";
1673
- return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
1674
- }
1675
- function deriveBrandOverride() {
1676
- const dark = readRootVar("--ohw-brand-dark");
1677
- const primary = readRootVar("--ohw-brand-primary");
1678
- const light = readRootVar("--ohw-brand-light");
1679
- if (!dark || !primary || !light) return null;
1680
- const accent = readRootVar("--ohw-brand-accent");
1681
- const heading = readRootVar("--font-heading") || readRootVar("--font-display");
1682
- const body = readRootVar("--font-body");
1683
- return {
1684
- palette: { dark, primary, accent: accent || dark, light },
1685
- fonts: {
1686
- heading: heading || AI_DEFAULT_BRAND.fonts.heading,
1687
- body: body || AI_DEFAULT_BRAND.fonts.body
1688
- }
1689
- };
1690
- }
1691
1224
  function deriveTemplateBrand() {
1692
- const dark = readRootVar("--color-dark");
1693
- const primary = readRootVar("--color-primary");
1694
- 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");
1695
1231
  if (!dark || !primary || !light) return null;
1696
- const accent = readRootVar("--color-accent");
1697
- const heading = readRootVar("--font-heading") || readRootVar("--font-display");
1698
- 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");
1699
1235
  return {
1700
1236
  palette: { dark, primary, accent: accent || dark, light },
1701
1237
  fonts: {
@@ -1767,24 +1303,6 @@ function syncRemovedSections(state) {
1767
1303
  }
1768
1304
  }
1769
1305
  }
1770
- function syncTemplateHidden(state, pageHasSections) {
1771
- const hide = state.hideTemplate === true && pageHasSections;
1772
- for (const el of Array.from(document.querySelectorAll(`[${TEMPLATE_HIDDEN_ATTR}]`))) {
1773
- if (!hide) {
1774
- el.style.removeProperty("display");
1775
- el.removeAttribute(TEMPLATE_HIDDEN_ATTR);
1776
- }
1777
- }
1778
- if (!hide) return;
1779
- for (const el of Array.from(document.querySelectorAll("[data-ohw-section]"))) {
1780
- if (el.hasAttribute(CONTAINER_ATTR)) continue;
1781
- if (CHROME_SECTION_IDS.has(el.getAttribute("data-ohw-section") ?? "")) continue;
1782
- if (el.parentElement?.closest("[data-ohw-section]")) continue;
1783
- if (el.hasAttribute(REPLACED_ATTR) || el.hasAttribute(REMOVED_ATTR)) continue;
1784
- el.style.display = "none";
1785
- el.setAttribute(TEMPLATE_HIDDEN_ATTR, "");
1786
- }
1787
- }
1788
1306
  function syncReplacedOriginals(state) {
1789
1307
  for (const el of document.querySelectorAll(`[${REPLACED_ATTR}]`)) {
1790
1308
  const byId = el.getAttribute(REPLACED_ATTR) ?? "";
@@ -1805,12 +1323,8 @@ function syncReplacedOriginals(state) {
1805
1323
  }
1806
1324
  function applyAiSectionsToDom(state, options) {
1807
1325
  if (typeof document === "undefined") return;
1808
- const brandOverride = deriveBrandOverride();
1809
1326
  const templateBrand = deriveTemplateBrand();
1810
- const brandKey = brandOverride ? JSON.stringify(brandOverride) : "";
1811
- const pagePath = window.location.pathname;
1812
- const pageSections = state.sections.filter((entry) => (entry.path ?? "/") === pagePath);
1813
- const activeIds = new Set(pageSections.map((entry) => entry.id));
1327
+ const activeIds = new Set(state.sections.map((entry) => entry.id));
1814
1328
  for (const [id, section] of mounted) {
1815
1329
  if (!activeIds.has(id)) {
1816
1330
  section.root.unmount();
@@ -1818,8 +1332,8 @@ function applyAiSectionsToDom(state, options) {
1818
1332
  mounted.delete(id);
1819
1333
  }
1820
1334
  }
1821
- for (const entry of pageSections) {
1822
- const serialized = JSON.stringify(entry) + brandKey;
1335
+ for (const entry of state.sections) {
1336
+ const serialized = JSON.stringify(entry);
1823
1337
  const existing = mounted.get(entry.id);
1824
1338
  if (existing && existing.serialized === serialized && existing.container.isConnected) {
1825
1339
  continue;
@@ -1833,7 +1347,6 @@ function applyAiSectionsToDom(state, options) {
1833
1347
  mounted.delete(entry.id);
1834
1348
  }
1835
1349
  container.setAttribute("data-ohw-section", entry.id);
1836
- container.setAttribute("data-ohw-instance", entry.id);
1837
1350
  container.setAttribute("data-ohw-section-label", entry.label);
1838
1351
  placeContainer(container, entry);
1839
1352
  const root = mounted.get(entry.id)?.root ?? createRoot(container);
@@ -1844,7 +1357,7 @@ function applyAiSectionsToDom(state, options) {
1844
1357
  AiTreeRenderer,
1845
1358
  {
1846
1359
  tree: entry.tree,
1847
- brand: brandOverride ?? entry.brand ?? options?.brand ?? templateBrand ?? void 0,
1360
+ brand: entry.brand ?? options?.brand ?? templateBrand ?? void 0,
1848
1361
  resolveMedia,
1849
1362
  editKeyPrefix: `ai.${entry.id}`
1850
1363
  }
@@ -1853,20 +1366,8 @@ function applyAiSectionsToDom(state, options) {
1853
1366
  });
1854
1367
  mounted.set(entry.id, { root, container, serialized });
1855
1368
  }
1856
- if (state.hideTemplate === true) {
1857
- let prev = null;
1858
- for (const entry of pageSections) {
1859
- const el = mounted.get(entry.id)?.container;
1860
- if (!el) continue;
1861
- if (prev && !(prev.compareDocumentPosition(el) & Node.DOCUMENT_POSITION_FOLLOWING)) {
1862
- prev.insertAdjacentElement("afterend", el);
1863
- }
1864
- prev = el;
1865
- }
1866
- }
1867
1369
  syncReplacedOriginals(state);
1868
1370
  syncRemovedSections(state);
1869
- syncTemplateHidden(state, pageSections.length > 0);
1870
1371
  }
1871
1372
 
1872
1373
  // src/useLinkHrefGuardian.ts
@@ -2473,7 +1974,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
2473
1974
  const autoId = useId();
2474
1975
  const insertAfter = insertAfterProp ?? autoId;
2475
1976
  const [schedule, setSchedule] = useState2(null);
2476
- const [loading, setLoading] = useState2(initialScheduleId !== null);
1977
+ const [loading, setLoading] = useState2(true);
2477
1978
  const [inEditor, setInEditor] = useState2(false);
2478
1979
  const [isHovered, setIsHovered] = useState2(false);
2479
1980
  const [modalState, setModalState] = useState2(null);
@@ -2647,10 +2148,8 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
2647
2148
  "*"
2648
2149
  );
2649
2150
  };
2151
+ if (!inEditor && !loading && !schedule) return null;
2650
2152
  const sectionId = `scheduling-${insertAfter}`;
2651
- if (!inEditor && !loading && !schedule) {
2652
- return /* @__PURE__ */ jsx4("section", { "data-ohw-section": sectionId, "data-ohw-scheduling-anchor": insertAfter, style: { display: "none" } });
2653
- }
2654
2153
  return /* @__PURE__ */ jsxs3(
2655
2154
  "section",
2656
2155
  {
@@ -7556,17 +7055,13 @@ function MediaOverlay({
7556
7055
  hover,
7557
7056
  isUploading,
7558
7057
  fadingOut = false,
7559
- selected = false,
7560
- hovered = false,
7561
7058
  onFadeOutComplete,
7562
7059
  onReplace,
7563
- onSelect,
7564
7060
  onVideoSettingsChange
7565
7061
  }) {
7566
7062
  const { rect } = hover;
7567
7063
  const skeletonRef = React8.useRef(null);
7568
7064
  const isVideo = hover.elementType === "video";
7569
- const showChrome = !selected || hovered;
7570
7065
  const autoplay = hover.videoAutoplay ?? true;
7571
7066
  const muted = hover.videoMuted ?? true;
7572
7067
  const box = {
@@ -7601,7 +7096,7 @@ function MediaOverlay({
7601
7096
  }
7602
7097
  );
7603
7098
  }
7604
- const settingsBar = isVideo && !hover.isDragOver && showChrome ? /* @__PURE__ */ jsxs7(
7099
+ const settingsBar = isVideo && !hover.isDragOver ? /* @__PURE__ */ jsxs7(
7605
7100
  "div",
7606
7101
  {
7607
7102
  "data-ohw-bridge": "",
@@ -7671,13 +7166,11 @@ function MediaOverlay({
7671
7166
  // in-document, pointer-events does it natively. The button below opts back in, so
7672
7167
  // Replace still works.
7673
7168
  pointerEvents: hover.hasTextOverlap ? "none" : "auto",
7674
- // Selected: a firm component ring with no wash, so the image reads as chosen rather
7675
- // than hovered. Hover keeps the existing tinted preview.
7676
- boxShadow: selected ? "inset 0 0 0 2px var(--color-primary)" : "inset 0 0 0 1.5px var(--color-primary)",
7677
- background: selected ? "transparent" : "color-mix(in srgb, var(--color-primary) 20%, transparent)"
7169
+ boxShadow: "inset 0 0 0 1.5px var(--color-primary)",
7170
+ background: "color-mix(in srgb, var(--color-primary) 20%, transparent)"
7678
7171
  },
7679
- onClick: () => onSelect && !selected ? onSelect(hover.key) : onReplace(hover.key),
7680
- children: showChrome && /* @__PURE__ */ jsxs7(
7172
+ onClick: () => onReplace(hover.key),
7173
+ children: /* @__PURE__ */ jsxs7(
7681
7174
  Button,
7682
7175
  {
7683
7176
  "data-ohw-media-overlay": "",
@@ -7696,7 +7189,7 @@ function MediaOverlay({
7696
7189
  },
7697
7190
  children: [
7698
7191
  isVideo ? /* @__PURE__ */ jsx15(Film, { size: 14 }) : /* @__PURE__ */ jsx15(ImageIcon, { size: 14 }),
7699
- isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image"
7192
+ isVideo ? "Replace video" : "Replace image"
7700
7193
  ]
7701
7194
  }
7702
7195
  )
@@ -7766,9 +7259,6 @@ import { Check, X } from "lucide-react";
7766
7259
 
7767
7260
  // src/lib/sections.ts
7768
7261
  var LINK_PICKER_EXCLUDED_IDS = /* @__PURE__ */ new Set(["navbar", "footer"]);
7769
- function isChromeSection(el) {
7770
- return el.matches("header, nav, footer, aside");
7771
- }
7772
7262
  function titleCaseSectionId(id) {
7773
7263
  return id.split("-").filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
7774
7264
  }
@@ -7779,8 +7269,6 @@ function parseSectionsFromRoot(root) {
7779
7269
  const id = el.getAttribute("data-ohw-section") ?? "";
7780
7270
  if (!id || seen.has(id) || LINK_PICKER_EXCLUDED_IDS.has(id)) continue;
7781
7271
  if (el.parentElement?.closest("[data-ohw-section]")) continue;
7782
- if (el.hasAttribute("data-ohw-ai-template-hidden") || el.hasAttribute("data-ohw-ai-removed") || el.hasAttribute("data-ohw-ai-replaced-by"))
7783
- continue;
7784
7272
  seen.add(id);
7785
7273
  const label = el.getAttribute("data-ohw-section-label") ?? titleCaseSectionId(id);
7786
7274
  sections.push({ id, label });
@@ -7798,12 +7286,8 @@ function parseSectionsFromHtml(html) {
7798
7286
 
7799
7287
  // src/ui/ai-section/AiSectionOverlay.tsx
7800
7288
  import { Fragment as Fragment5, jsx as jsx17, jsxs as jsxs9 } from "react/jsx-runtime";
7801
- function findSectionElement(instanceId) {
7802
- const escaped = CSS.escape(instanceId);
7803
- return document.querySelector(`[data-ohw-instance="${escaped}"]`) ?? document.querySelector(`[data-ohw-section="${escaped}"]:not([data-ohw-instance])`);
7804
- }
7805
- function readRect(instanceId) {
7806
- const el = findSectionElement(instanceId);
7289
+ function readRect(sectionId) {
7290
+ const el = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
7807
7291
  if (!el) return null;
7808
7292
  const r2 = el.getBoundingClientRect();
7809
7293
  if (r2.width <= 0 || r2.height <= 0) return null;
@@ -7826,7 +7310,7 @@ function useLiveSectionRect(sectionId) {
7826
7310
  const opts = { capture: true, passive: true };
7827
7311
  window.addEventListener("scroll", update, opts);
7828
7312
  window.addEventListener("resize", update);
7829
- const el = findSectionElement(sectionId);
7313
+ const el = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
7830
7314
  const ro = el ? new ResizeObserver(update) : null;
7831
7315
  if (el && ro) ro.observe(el);
7832
7316
  const interval = setInterval(update, 500);
@@ -7839,14 +7323,6 @@ function useLiveSectionRect(sectionId) {
7839
7323
  }, [sectionId]);
7840
7324
  return rect;
7841
7325
  }
7842
- function computeSectionBoundaryFlags(instanceId) {
7843
- const topLevel = Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
7844
- (el) => !el.parentElement?.closest("[data-ohw-section]") && !isChromeSection(el)
7845
- );
7846
- const index = topLevel.findIndex((el) => (el.dataset.ohwInstance ?? el.dataset.ohwSection) === instanceId);
7847
- if (index === -1) return { isFirst: true, isLast: true };
7848
- return { isFirst: index === 0, isLast: index === topLevel.length - 1 };
7849
- }
7850
7326
  var PRIMARY2 = "#0885FE";
7851
7327
  function edgeAwareRadius(rect) {
7852
7328
  const container = window.innerWidth <= 480 ? 16 : 24;
@@ -7920,7 +7396,6 @@ function AiSectionOverlay({
7920
7396
  }) {
7921
7397
  const [selectedId, setSelectedId] = useState4(null);
7922
7398
  const [reviewId, setReviewId] = useState4(null);
7923
- const [reviewButtonsHidden, setReviewButtonsHidden] = useState4(false);
7924
7399
  const reviewIdRef = useRef4(null);
7925
7400
  reviewIdRef.current = reviewId;
7926
7401
  const selectedIdRef = useRef4(null);
@@ -7929,7 +7404,7 @@ function AiSectionOverlay({
7929
7404
  (el) => {
7930
7405
  postToParent2({
7931
7406
  type: "ow:section-selected",
7932
- sectionId: el ? el.dataset.ohwInstance ?? el.dataset.ohwSection ?? null : null,
7407
+ sectionId: el?.dataset.ohwSection ?? null,
7933
7408
  sectionLabel: el ? el.dataset.ohwSectionLabel ?? titleCaseSectionId(el.dataset.ohwSection ?? "") : null
7934
7409
  });
7935
7410
  },
@@ -7938,7 +7413,7 @@ function AiSectionOverlay({
7938
7413
  const selectFromElement = useCallback2(
7939
7414
  (el, options) => {
7940
7415
  const sectionEl = el?.closest("[data-ohw-section]") ?? null;
7941
- const id = sectionEl ? sectionEl.dataset.ohwInstance ?? sectionEl.dataset.ohwSection ?? null : null;
7416
+ const id = sectionEl?.dataset.ohwSection ?? null;
7942
7417
  if (id === selectedIdRef.current) return;
7943
7418
  setSelectedId(id);
7944
7419
  if (options?.report !== false) report(sectionEl);
@@ -7979,10 +7454,9 @@ function AiSectionOverlay({
7979
7454
  }
7980
7455
  const found = readRect(sectionId) != null;
7981
7456
  setReviewId(found ? sectionId : null);
7982
- setReviewButtonsHidden(e.data.hideButtons === true);
7983
7457
  postToParent2({ type: "ow:ai-review-started", sectionId, found });
7984
7458
  if (found) {
7985
- document.querySelector(`[data-ohw-instance="${CSS.escape(sectionId)}"]`)?.scrollIntoView({ behavior: "smooth", block: "nearest" });
7459
+ document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`)?.scrollIntoView({ behavior: "smooth", block: "nearest" });
7986
7460
  }
7987
7461
  }
7988
7462
  };
@@ -8001,7 +7475,7 @@ function AiSectionOverlay({
8001
7475
  return;
8002
7476
  }
8003
7477
  const sec = t.closest("[data-ohw-section]");
8004
- setHoveredId(sec ? sec.dataset.ohwInstance ?? sec.dataset.ohwSection ?? null : null);
7478
+ setHoveredId(sec?.dataset.ohwSection ?? null);
8005
7479
  };
8006
7480
  const onLeave = () => setHoveredId(null);
8007
7481
  document.addEventListener("mousemove", onMove, { passive: true });
@@ -8033,30 +7507,9 @@ function AiSectionOverlay({
8033
7507
  },
8034
7508
  [postToParent2]
8035
7509
  );
8036
- const activeSelectionId = reviewId ? null : selectedId;
8037
- const selectionRect = useLiveSectionRect(activeSelectionId);
7510
+ const selectionRect = useLiveSectionRect(reviewId ? null : selectedId);
8038
7511
  const reviewRect = useLiveSectionRect(reviewId);
8039
7512
  const hoverRect = useLiveSectionRect(reviewId || hoveredId === selectedId ? null : hoveredId);
8040
- useEffect4(() => {
8041
- const selectedEl = activeSelectionId ? findSectionElement(activeSelectionId) : null;
8042
- if (!activeSelectionId || !selectionRect || selectedEl && isChromeSection(selectedEl)) {
8043
- postToParent2({ type: "ow:section-rect", instanceId: null, rect: null });
8044
- return;
8045
- }
8046
- const { isFirst, isLast } = computeSectionBoundaryFlags(activeSelectionId);
8047
- postToParent2({
8048
- type: "ow:section-rect",
8049
- instanceId: activeSelectionId,
8050
- rect: {
8051
- top: selectionRect.top + window.scrollY,
8052
- left: selectionRect.left + window.scrollX,
8053
- width: selectionRect.width,
8054
- height: selectionRect.height
8055
- },
8056
- isFirst,
8057
- isLast
8058
- });
8059
- }, [activeSelectionId, selectionRect, postToParent2]);
8060
7513
  return /* @__PURE__ */ jsxs9(Fragment5, { children: [
8061
7514
  hoverRect && /* @__PURE__ */ jsx17(
8062
7515
  "div",
@@ -8107,16 +7560,13 @@ function AiSectionOverlay({
8107
7560
  border: `2px solid ${PRIMARY2}`,
8108
7561
  borderRadius: edgeAwareRadius(reviewRect),
8109
7562
  zIndex: 2147483200,
8110
- // The veil itself: swallows clicks so the section stays locked until decided. This
8111
- // stopPropagation only guards the bubble phase; the bridge's capture-phase click
8112
- // handlers additionally bail on `[data-ohw-ai-review]`, without which a click on
8113
- // Accept/Discard resolves to the media beneath and opens the file picker.
7563
+ // The veil itself: swallows clicks so the section stays locked until decided.
8114
7564
  background: "rgba(8, 133, 254, 0.04)",
8115
7565
  pointerEvents: "auto",
8116
7566
  cursor: "default"
8117
7567
  },
8118
7568
  onClick: (e) => e.stopPropagation(),
8119
- children: !reviewButtonsHidden && /* @__PURE__ */ jsxs9(
7569
+ children: /* @__PURE__ */ jsxs9(
8120
7570
  "div",
8121
7571
  {
8122
7572
  style: {
@@ -8141,59 +7591,6 @@ function AiSectionOverlay({
8141
7591
 
8142
7592
  // src/lib/section-instances.ts
8143
7593
  var SECTION_ORDER_KEY = "__ohw_section_order";
8144
- function topLevelSections() {
8145
- return Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
8146
- (el) => !el.parentElement?.closest("[data-ohw-section]") && !isChromeSection(el)
8147
- );
8148
- }
8149
- function instanceIdOf(el) {
8150
- return el.dataset.ohwInstance ?? el.dataset.ohwSection ?? "";
8151
- }
8152
- function planSectionMove(instanceId, targetIndex, currentPath) {
8153
- const sections = topLevelSections();
8154
- const index = sections.findIndex((el) => instanceIdOf(el) === instanceId);
8155
- if (index === -1) return null;
8156
- const dragged = sections[index];
8157
- const others = sections.filter((_, i) => i !== index);
8158
- const clamped = Math.max(0, Math.min(targetIndex, others.length));
8159
- const reordered = [...others.slice(0, clamped), dragged, ...others.slice(clamped)];
8160
- return reordered.map((el, order) => ({
8161
- instanceId: instanceIdOf(el),
8162
- type: el.getAttribute("data-ohw-section") ?? "",
8163
- order,
8164
- pagePath: currentPath
8165
- }));
8166
- }
8167
- function moveSectionInstance(instanceId, direction, currentPath) {
8168
- const sections = topLevelSections();
8169
- const index = sections.findIndex((el) => instanceIdOf(el) === instanceId);
8170
- if (index === -1) return null;
8171
- const siblingIndex = direction === "up" ? index - 1 : index + 1;
8172
- if (siblingIndex < 0 || siblingIndex >= sections.length) return null;
8173
- const entries = planSectionMove(instanceId, siblingIndex, currentPath);
8174
- if (!entries) return null;
8175
- applyPersistedOrder(entries);
8176
- return entries;
8177
- }
8178
- function applyPersistedOrder(entries) {
8179
- if (entries.length === 0) return;
8180
- const sections = topLevelSections();
8181
- if (sections.length === 0) return;
8182
- const orderIndex = new Map(entries.map((e) => [e.instanceId, e.order]));
8183
- const ordered = [...sections].sort((a, b) => {
8184
- const aOrder = orderIndex.get(instanceIdOf(a));
8185
- const bOrder = orderIndex.get(instanceIdOf(b));
8186
- if (aOrder === void 0 && bOrder === void 0) return 0;
8187
- if (aOrder === void 0) return 1;
8188
- if (bOrder === void 0) return -1;
8189
- return aOrder - bOrder;
8190
- });
8191
- let prev = null;
8192
- for (const el of ordered) {
8193
- if (prev) prev.after(el);
8194
- prev = el;
8195
- }
8196
- }
8197
7594
  function getPageSectionOrderEntries(raw, currentPath) {
8198
7595
  if (!raw) return [];
8199
7596
  try {
@@ -8231,7 +7628,6 @@ function initSectionInstancesFromContent(content, currentPath) {
8231
7628
  rekeySectionSubtree(clone, entry.instanceId);
8232
7629
  original.insertAdjacentElement("afterend", clone);
8233
7630
  }
8234
- applyPersistedOrder(entries);
8235
7631
  }
8236
7632
 
8237
7633
  // src/OhhwellsBridge.tsx
@@ -10778,13 +10174,8 @@ function referenceBox(slot) {
10778
10174
  const neighbour = row ? Array.from(row.querySelectorAll('[data-ohw-editable="icon"]')).find(
10779
10175
  (el) => el !== slot && !el.hasAttribute("data-ohw-social-icon-placeholder")
10780
10176
  ) : null;
10781
- if (neighbour) {
10782
- const box2 = neighbour.querySelector(GLYPH_SELECTOR)?.getBoundingClientRect();
10783
- if (box2?.width && box2.height) return box2;
10784
- }
10785
- const own = slot.getBoundingClientRect();
10786
- if (own.width && own.height) return own;
10787
- const box = slot.querySelector(GLYPH_SELECTOR)?.getBoundingClientRect() ?? null;
10177
+ const source = (neighbour ?? slot).querySelector(GLYPH_SELECTOR);
10178
+ const box = source?.getBoundingClientRect() ?? null;
10788
10179
  return box?.width && box.height ? box : null;
10789
10180
  }
10790
10181
  function iconMarkupSizedFor(slot, markup) {
@@ -10939,6 +10330,10 @@ function markSocialsRows(root = document) {
10939
10330
  item.setAttribute(SOCIALS_ITEM_ATTR, String(index));
10940
10331
  }
10941
10332
  const iconKey = socialIconKey(item) ?? socialHrefKey(item)?.replace(/-href$/, "");
10333
+ if (iconKey && !item.querySelector(ICON_SELECTOR)) {
10334
+ const drawn = item.querySelector("svg, img");
10335
+ if (drawn) adoptAsIconSlot(drawn, iconKey);
10336
+ }
10942
10337
  if (iconKey) ensureLabelSlot(item, iconKey);
10943
10338
  });
10944
10339
  });
@@ -11370,12 +10765,30 @@ function socialsMissingIcons(row) {
11370
10765
  label: (socialLabelElement(item)?.textContent ?? item.getAttribute("aria-label") ?? "").trim()
11371
10766
  })).filter((entry) => Boolean(entry.hrefKey));
11372
10767
  }
10768
+ function adoptAsIconSlot(drawn, base) {
10769
+ const parent = drawn.parentElement;
10770
+ if (!parent) return null;
10771
+ const taken = drawn.ownerDocument.querySelector(`[data-ohw-key="${base}"]`);
10772
+ const iconKey = taken ? `${base}-icon` : base;
10773
+ const slot = drawn.ownerDocument.createElement("span");
10774
+ slot.setAttribute("data-ohw-key", iconKey);
10775
+ slot.setAttribute("data-ohw-editable", "icon");
10776
+ slot.style.display = "inline-flex";
10777
+ parent.insertBefore(slot, drawn);
10778
+ slot.appendChild(drawn);
10779
+ return iconKey;
10780
+ }
11373
10781
  function ensureIconSlot(item) {
11374
10782
  const existing = item.querySelector(ICON_SELECTOR);
11375
10783
  if (existing) return existing.dataset.ohwKey ?? null;
11376
10784
  const hrefKey = socialHrefKey(item);
11377
10785
  if (!hrefKey) return null;
11378
10786
  const base = hrefKey.replace(/-href$/, "");
10787
+ const drawn = item.querySelector("svg, img");
10788
+ if (drawn) {
10789
+ const adopted = adoptAsIconSlot(drawn, base);
10790
+ if (adopted) return adopted;
10791
+ }
11379
10792
  const taken = document.querySelector(`[data-ohw-key="${base}"]`);
11380
10793
  const iconKey = taken && taken !== item ? `${base}-icon` : base;
11381
10794
  const slot = document.createElement("span");
@@ -12563,7 +11976,6 @@ function readLogoSizeState(content, placement) {
12563
11976
  function getLogoElement(el) {
12564
11977
  const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
12565
11978
  if (marked) return marked;
12566
- if (el.closest('[data-ohw-editable="icon"]')) return null;
12567
11979
  const root = el.closest("nav, [data-ohw-nav-root], footer");
12568
11980
  if (!root) return null;
12569
11981
  const anchor = el.closest("a");
@@ -13443,415 +12855,88 @@ function useNavItemDrag({
13443
12855
  };
13444
12856
  }
13445
12857
 
13446
- // src/useSectionDrag.ts
13447
- import { useCallback as useCallback7, useEffect as useEffect11, useRef as useRef9, useState as useState10 } from "react";
12858
+ // src/ui/footer-container-chrome.tsx
12859
+ import { Plus as Plus2 } from "lucide-react";
12860
+ import { jsx as jsx29, jsxs as jsxs19 } from "react/jsx-runtime";
12861
+ function FooterContainerChrome({
12862
+ rect,
12863
+ onAdd,
12864
+ addDisabled = false
12865
+ }) {
12866
+ const chromeGap = 6;
12867
+ const buttonMargin = 7;
12868
+ return /* @__PURE__ */ jsx29(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsx29(
12869
+ "div",
12870
+ {
12871
+ "data-ohw-footer-container-chrome": "",
12872
+ "data-ohw-bridge": "",
12873
+ className: "pointer-events-none fixed z-[2147483647]",
12874
+ style: {
12875
+ top: rect.top - chromeGap,
12876
+ left: rect.left - chromeGap,
12877
+ width: rect.width + chromeGap * 2,
12878
+ height: rect.height + chromeGap * 2
12879
+ },
12880
+ children: /* @__PURE__ */ jsxs19(Tooltip, { children: [
12881
+ /* @__PURE__ */ jsx29(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx29(
12882
+ "button",
12883
+ {
12884
+ type: "button",
12885
+ "data-ohw-footer-add-button": "",
12886
+ disabled: addDisabled,
12887
+ 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",
12888
+ style: { top: chromeGap - buttonMargin },
12889
+ "aria-label": "Add item",
12890
+ onMouseDown: (e) => {
12891
+ e.preventDefault();
12892
+ e.stopPropagation();
12893
+ },
12894
+ onClick: (e) => {
12895
+ e.preventDefault();
12896
+ e.stopPropagation();
12897
+ if (addDisabled) return;
12898
+ onAdd();
12899
+ },
12900
+ children: /* @__PURE__ */ jsx29(Plus2, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
12901
+ }
12902
+ ) }),
12903
+ /* @__PURE__ */ jsx29(TooltipContent, { side: "bottom", sideOffset: 9, children: addDisabled ? "Maximum columns reached" : "Add item" })
12904
+ ] })
12905
+ }
12906
+ ) });
12907
+ }
13448
12908
 
13449
- // src/lib/section-dnd.ts
13450
- function isFooterSection(el) {
13451
- return el.dataset.ohwSection === "footer";
12909
+ // src/lib/carousel.ts
12910
+ import { useEffect as useEffect11, useState as useState10 } from "react";
12911
+ var CAROUSEL_ATTR = "data-ohw-carousel";
12912
+ var CAROUSEL_VALUE_ATTR = "data-ohw-carousel-value";
12913
+ var CAROUSEL_SLIDE_ATTR = "data-ohw-carousel-slide";
12914
+ var CAROUSEL_EVENT = "ohw:carousel-change";
12915
+ function listCarouselKeys() {
12916
+ const keys = /* @__PURE__ */ new Set();
12917
+ document.querySelectorAll(`[${CAROUSEL_ATTR}]`).forEach((el) => {
12918
+ const key = el.getAttribute("data-ohw-key");
12919
+ if (key) keys.add(key);
12920
+ });
12921
+ return [...keys];
13452
12922
  }
13453
- function buildSectionDropSlots(draggedInstanceId) {
13454
- const sections = topLevelSections().filter(
13455
- (el) => instanceIdOf(el) !== draggedInstanceId && !isFooterSection(el)
12923
+ function containersForKey(key) {
12924
+ return Array.from(
12925
+ document.querySelectorAll(`[${CAROUSEL_ATTR}][data-ohw-key="${CSS.escape(key)}"]`)
13456
12926
  );
13457
- const slots = [];
13458
- if (sections.length === 0) return slots;
13459
- const left = 0;
13460
- const width = document.documentElement.clientWidth;
13461
- for (let i = 0; i <= sections.length; i++) {
13462
- let y;
13463
- if (i === 0) {
13464
- y = sections[0].getBoundingClientRect().top;
13465
- } else if (i === sections.length) {
13466
- y = sections[sections.length - 1].getBoundingClientRect().bottom;
13467
- } else {
13468
- const prev = sections[i - 1].getBoundingClientRect();
13469
- const next = sections[i].getBoundingClientRect();
13470
- y = (prev.bottom + next.top) / 2;
13471
- }
13472
- slots.push({ insertIndex: i, y, left, width });
13473
- }
13474
- return slots;
13475
12927
  }
13476
- function hitTestSectionDropSlot(y, slots) {
13477
- let best = null;
13478
- for (const slot of slots) {
13479
- const dist = Math.abs(y - slot.y);
13480
- if (!best || dist < best.dist) best = { slot, dist };
13481
- }
13482
- return best?.slot ?? null;
12928
+ function isCarouselKey(key) {
12929
+ return containersForKey(key).length > 0;
13483
12930
  }
13484
-
13485
- // src/useSectionDrag.ts
13486
- var PRESS_THRESHOLD = 10;
13487
- var EDGE_ZONE = 60;
13488
- var MAX_AUTO_SCROLL_SPEED = 18;
13489
- var SECTION_DRAG_EXCLUDED_SELECTOR = [
13490
- "[data-ohw-toolbar]",
13491
- "[data-ohw-edit-chrome]",
13492
- "[data-ohw-item-interaction]",
13493
- "[data-ohw-drag-handle-container]",
13494
- '[data-slot="drag-handle"]',
13495
- "[data-ohw-item-toolbar-anchor]",
13496
- "[data-ohw-item-drag-surface]",
13497
- "[data-ohw-more-menu]",
13498
- '[data-slot="dropdown-menu-content"]',
13499
- '[data-slot="dropdown-menu-item"]',
13500
- "[data-ohw-state-toggle]",
13501
- "[data-ohw-max-badge]",
13502
- "[data-ohw-floating-panel]",
13503
- "[data-ohw-section-picker]",
13504
- "[data-ohw-link-popover-root]",
13505
- "[data-ohw-link-modal-root]",
13506
- "[data-ohw-link-page-dropdown]",
13507
- '[data-slot="popover-content"]',
13508
- '[data-slot="dialog-content"]',
13509
- '[data-slot="dialog-overlay"]',
13510
- "[data-ohw-ai-review]",
13511
- "[data-ohw-editable]",
13512
- "[data-ohw-editable-state]",
13513
- "[contenteditable]",
13514
- "[data-ohw-href-key]",
13515
- "[data-ohw-footer-col]",
13516
- "[data-ohw-social-label]",
13517
- "a",
13518
- "button",
13519
- '[role="button"]',
13520
- '[data-ohw-role="navbar-button"]',
13521
- '[data-ohw-role="button"]',
13522
- "[data-ohw-carousel]",
13523
- "[data-ohw-carousel-value]",
13524
- "[data-ohw-carousel-slide]",
13525
- "[data-ohw-carousel-overlay]",
13526
- "[data-ohw-media-chrome]",
13527
- "[data-ohw-media-overlay]",
13528
- "[data-ohw-media-skeleton]"
13529
- ].join(", ");
13530
- function visibleClip(ps) {
13531
- if (!ps) return null;
13532
- const top = Math.max(0, ps.headerH - ps.iframeOffsetTop);
13533
- const bottom = Math.min(window.innerHeight, ps.headerH + ps.canvasH - ps.iframeOffsetTop);
13534
- return { top, bottom: Math.max(top, bottom) };
13535
- }
13536
- function useSectionDrag({
13537
- isEditMode,
13538
- editContentRef,
13539
- postToParentRef,
13540
- parentScrollRef,
13541
- navDragRef,
13542
- footerDragRef,
13543
- suppressNextClickRef,
13544
- suppressClickUntilRef
13545
- }) {
13546
- const sectionDragRef = useRef9(null);
13547
- const [sectionDropSlots, setSectionDropSlots] = useState10([]);
13548
- const [activeSectionDropIndex, setActiveSectionDropIndex] = useState10(null);
13549
- const [isSectionDragging, setIsSectionDragging] = useState10(false);
13550
- const sectionPointerDragRef = useRef9(null);
13551
- const autoScrollRafRef = useRef9(null);
13552
- const autoScrollDeltaRef = useRef9(0);
13553
- const stopAutoScroll = useCallback7(() => {
13554
- if (autoScrollRafRef.current != null) {
13555
- cancelAnimationFrame(autoScrollRafRef.current);
13556
- autoScrollRafRef.current = null;
13557
- }
13558
- autoScrollDeltaRef.current = 0;
13559
- }, []);
13560
- const tickAutoScroll = useCallback7(() => {
13561
- if (!sectionDragRef.current) {
13562
- stopAutoScroll();
13563
- return;
13564
- }
13565
- if (autoScrollDeltaRef.current !== 0) {
13566
- postToParentRef.current({ type: "ow:request-scroll", deltaY: autoScrollDeltaRef.current });
13567
- }
13568
- autoScrollRafRef.current = requestAnimationFrame(tickAutoScroll);
13569
- }, [postToParentRef, stopAutoScroll]);
13570
- const updateAutoScroll = useCallback7(
13571
- (clientY) => {
13572
- const clip = visibleClip(parentScrollRef.current);
13573
- let delta = 0;
13574
- if (clip) {
13575
- const distTop = clientY - clip.top;
13576
- const distBottom = clip.bottom - clientY;
13577
- if (distTop >= 0 && distTop < EDGE_ZONE) {
13578
- delta = -MAX_AUTO_SCROLL_SPEED * (1 - distTop / EDGE_ZONE);
13579
- } else if (distBottom >= 0 && distBottom < EDGE_ZONE) {
13580
- delta = MAX_AUTO_SCROLL_SPEED * (1 - distBottom / EDGE_ZONE);
13581
- }
13582
- }
13583
- autoScrollDeltaRef.current = delta;
13584
- if (delta !== 0 && autoScrollRafRef.current == null) {
13585
- autoScrollRafRef.current = requestAnimationFrame(tickAutoScroll);
13586
- } else if (delta === 0) {
13587
- stopAutoScroll();
13588
- }
13589
- },
13590
- [parentScrollRef, stopAutoScroll, tickAutoScroll]
13591
- );
13592
- const clearSectionDragVisuals = useCallback7(() => {
13593
- sectionDragRef.current?.draggedEl.removeAttribute("data-ohw-section-dragging");
13594
- sectionDragRef.current = null;
13595
- setSectionDropSlots([]);
13596
- setActiveSectionDropIndex(null);
13597
- setIsSectionDragging(false);
13598
- stopAutoScroll();
13599
- document.documentElement.removeAttribute("data-ohw-section-dragging-root");
13600
- unlockItemDragInteraction();
13601
- }, [stopAutoScroll]);
13602
- const refreshSectionDragVisuals = useCallback7(
13603
- (session, clientX, clientY) => {
13604
- session.lastClientX = clientX;
13605
- session.lastClientY = clientY;
13606
- const slots = buildSectionDropSlots(session.instanceId);
13607
- const activeSlot = hitTestSectionDropSlot(clientY, slots);
13608
- session.activeSlot = activeSlot;
13609
- setSectionDropSlots(slots);
13610
- const activeIdx = activeSlot ? slots.findIndex((s) => s.insertIndex === activeSlot.insertIndex) : -1;
13611
- setActiveSectionDropIndex(activeIdx >= 0 ? activeIdx : null);
13612
- updateAutoScroll(clientY);
13613
- },
13614
- [updateAutoScroll]
13615
- );
13616
- const beginSectionDrag = useCallback7(
13617
- (session) => {
13618
- sectionDragRef.current = session;
13619
- setIsSectionDragging(true);
13620
- lockItemDuringDrag();
13621
- document.documentElement.setAttribute("data-ohw-section-dragging-root", "");
13622
- session.draggedEl.setAttribute("data-ohw-section-dragging", "");
13623
- refreshSectionDragVisuals(session, session.lastClientX, session.lastClientY);
13624
- },
13625
- [refreshSectionDragVisuals]
13626
- );
13627
- const commitSectionDrag = useCallback7(() => {
13628
- const session = sectionDragRef.current;
13629
- if (!session) {
13630
- clearSectionDragVisuals();
13631
- return;
13632
- }
13633
- const slot = session.activeSlot ?? hitTestSectionDropSlot(session.lastClientY, buildSectionDropSlots(session.instanceId));
13634
- const entries = slot ? planSectionMove(session.instanceId, slot.insertIndex, window.location.pathname) : null;
13635
- if (!entries) {
13636
- clearSectionDragVisuals();
13637
- return;
13638
- }
13639
- const orderJson = JSON.stringify(entries);
13640
- editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
13641
- postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
13642
- applyPersistedOrder(entries);
13643
- clearSectionDragVisuals();
13644
- requestAnimationFrame(() => {
13645
- if (editContentRef.current[SECTION_ORDER_KEY] === orderJson) {
13646
- applyPersistedOrder(entries);
13647
- }
13648
- requestAnimationFrame(() => {
13649
- window.dispatchEvent(new Event("resize"));
13650
- });
13651
- });
13652
- }, [clearSectionDragVisuals, editContentRef, postToParentRef]);
13653
- const startSectionPressDrag = useCallback7(
13654
- (el, clientX, clientY, pointerId) => {
13655
- if (navDragRef.current || footerDragRef.current || sectionDragRef.current) return false;
13656
- const instanceId = instanceIdOf(el);
13657
- if (!instanceId) return false;
13658
- sectionPointerDragRef.current = {
13659
- el,
13660
- instanceId,
13661
- startX: clientX,
13662
- startY: clientY,
13663
- pointerId,
13664
- started: false
13665
- };
13666
- return true;
13667
- },
13668
- [footerDragRef, navDragRef]
13669
- );
13670
- useEffect11(() => {
13671
- if (!isEditMode) return;
13672
- const onPointerDown = (e) => {
13673
- if (e.button !== 0) return;
13674
- if (navDragRef.current || footerDragRef.current || sectionDragRef.current) return;
13675
- if (sectionPointerDragRef.current) return;
13676
- const target = e.target;
13677
- if (!(target instanceof HTMLElement)) return;
13678
- if (target.closest(SECTION_DRAG_EXCLUDED_SELECTOR)) return;
13679
- const sectionEl = target.closest("[data-ohw-section]");
13680
- if (!sectionEl || isChromeSection(sectionEl) || sectionEl.dataset.ohwSection === "footer") return;
13681
- if (!topLevelSections().includes(sectionEl)) return;
13682
- startSectionPressDrag(sectionEl, e.clientX, e.clientY, e.pointerId);
13683
- };
13684
- const onPointerMove = (e) => {
13685
- const pending = sectionPointerDragRef.current;
13686
- if (!pending) return;
13687
- if (pending.started) {
13688
- e.preventDefault();
13689
- clearTextSelection();
13690
- const session = sectionDragRef.current;
13691
- if (!session) return;
13692
- refreshSectionDragVisuals(session, e.clientX, e.clientY);
13693
- return;
13694
- }
13695
- const dx = e.clientX - pending.startX;
13696
- const dy = e.clientY - pending.startY;
13697
- if (dx * dx + dy * dy < PRESS_THRESHOLD * PRESS_THRESHOLD) return;
13698
- e.preventDefault();
13699
- pending.started = true;
13700
- armItemPressDrag();
13701
- clearTextSelection();
13702
- try {
13703
- document.body.setPointerCapture(pending.pointerId);
13704
- } catch {
13705
- }
13706
- beginSectionDrag({
13707
- instanceId: pending.instanceId,
13708
- draggedEl: pending.el,
13709
- lastClientX: e.clientX,
13710
- lastClientY: e.clientY,
13711
- activeSlot: null
13712
- });
13713
- };
13714
- const endPointerDrag = (e) => {
13715
- const pending = sectionPointerDragRef.current;
13716
- sectionPointerDragRef.current = null;
13717
- try {
13718
- if (document.body.hasPointerCapture(e.pointerId)) {
13719
- document.body.releasePointerCapture(e.pointerId);
13720
- }
13721
- } catch {
13722
- }
13723
- if (!pending) return;
13724
- if (!pending.started) {
13725
- unlockItemDragInteraction();
13726
- return;
13727
- }
13728
- suppressNextClickRef.current = true;
13729
- suppressClickUntilRef.current = Date.now() + 500;
13730
- commitSectionDrag();
13731
- };
13732
- const onKeyDown = (e) => {
13733
- if (e.key !== "Escape") return;
13734
- if (!sectionDragRef.current && !sectionPointerDragRef.current) return;
13735
- sectionPointerDragRef.current = null;
13736
- clearSectionDragVisuals();
13737
- };
13738
- document.addEventListener("pointerdown", onPointerDown, true);
13739
- document.addEventListener("pointermove", onPointerMove, true);
13740
- document.addEventListener("pointerup", endPointerDrag, true);
13741
- document.addEventListener("pointercancel", endPointerDrag, true);
13742
- document.addEventListener("keydown", onKeyDown, true);
13743
- return () => {
13744
- document.removeEventListener("pointerdown", onPointerDown, true);
13745
- document.removeEventListener("pointermove", onPointerMove, true);
13746
- document.removeEventListener("pointerup", endPointerDrag, true);
13747
- document.removeEventListener("pointercancel", endPointerDrag, true);
13748
- document.removeEventListener("keydown", onKeyDown, true);
13749
- unlockItemDragInteraction();
13750
- stopAutoScroll();
13751
- };
13752
- }, [
13753
- beginSectionDrag,
13754
- clearSectionDragVisuals,
13755
- commitSectionDrag,
13756
- footerDragRef,
13757
- isEditMode,
13758
- navDragRef,
13759
- refreshSectionDragVisuals,
13760
- startSectionPressDrag,
13761
- stopAutoScroll,
13762
- suppressClickUntilRef,
13763
- suppressNextClickRef
13764
- ]);
13765
- return {
13766
- sectionDragRef,
13767
- sectionDropSlots,
13768
- activeSectionDropIndex,
13769
- isSectionDragging
13770
- };
13771
- }
13772
-
13773
- // src/ui/footer-container-chrome.tsx
13774
- import { Plus as Plus2 } from "lucide-react";
13775
- import { jsx as jsx29, jsxs as jsxs19 } from "react/jsx-runtime";
13776
- function FooterContainerChrome({
13777
- rect,
13778
- onAdd,
13779
- addDisabled = false
13780
- }) {
13781
- const chromeGap = 6;
13782
- const buttonMargin = 7;
13783
- return /* @__PURE__ */ jsx29(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsx29(
13784
- "div",
13785
- {
13786
- "data-ohw-footer-container-chrome": "",
13787
- "data-ohw-bridge": "",
13788
- className: "pointer-events-none fixed z-[2147483647]",
13789
- style: {
13790
- top: rect.top - chromeGap,
13791
- left: rect.left - chromeGap,
13792
- width: rect.width + chromeGap * 2,
13793
- height: rect.height + chromeGap * 2
13794
- },
13795
- children: /* @__PURE__ */ jsxs19(Tooltip, { children: [
13796
- /* @__PURE__ */ jsx29(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx29(
13797
- "button",
13798
- {
13799
- type: "button",
13800
- "data-ohw-footer-add-button": "",
13801
- disabled: addDisabled,
13802
- 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",
13803
- style: { top: chromeGap - buttonMargin },
13804
- "aria-label": "Add item",
13805
- onMouseDown: (e) => {
13806
- e.preventDefault();
13807
- e.stopPropagation();
13808
- },
13809
- onClick: (e) => {
13810
- e.preventDefault();
13811
- e.stopPropagation();
13812
- if (addDisabled) return;
13813
- onAdd();
13814
- },
13815
- children: /* @__PURE__ */ jsx29(Plus2, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
13816
- }
13817
- ) }),
13818
- /* @__PURE__ */ jsx29(TooltipContent, { side: "bottom", sideOffset: 9, children: addDisabled ? "Maximum columns reached" : "Add item" })
13819
- ] })
13820
- }
13821
- ) });
13822
- }
13823
-
13824
- // src/lib/carousel.ts
13825
- import { useEffect as useEffect12, useState as useState11 } from "react";
13826
- var CAROUSEL_ATTR = "data-ohw-carousel";
13827
- var CAROUSEL_VALUE_ATTR = "data-ohw-carousel-value";
13828
- var CAROUSEL_SLIDE_ATTR = "data-ohw-carousel-slide";
13829
- var CAROUSEL_EVENT = "ohw:carousel-change";
13830
- function listCarouselKeys() {
13831
- const keys = /* @__PURE__ */ new Set();
13832
- document.querySelectorAll(`[${CAROUSEL_ATTR}]`).forEach((el) => {
13833
- const key = el.getAttribute("data-ohw-key");
13834
- if (key) keys.add(key);
13835
- });
13836
- return [...keys];
13837
- }
13838
- function containersForKey(key) {
13839
- return Array.from(
13840
- document.querySelectorAll(`[${CAROUSEL_ATTR}][data-ohw-key="${CSS.escape(key)}"]`)
13841
- );
13842
- }
13843
- function isCarouselKey(key) {
13844
- return containersForKey(key).length > 0;
13845
- }
13846
- function parseSlides(raw) {
13847
- if (!raw) return [];
13848
- try {
13849
- const parsed = JSON.parse(raw);
13850
- if (!Array.isArray(parsed)) return [];
13851
- return parsed.filter((s) => Boolean(s) && typeof s === "object").map((s) => ({ src: String(s.src ?? ""), alt: String(s.alt ?? "") }));
13852
- } catch {
13853
- return [];
13854
- }
12931
+ function parseSlides(raw) {
12932
+ if (!raw) return [];
12933
+ try {
12934
+ const parsed = JSON.parse(raw);
12935
+ if (!Array.isArray(parsed)) return [];
12936
+ return parsed.filter((s) => Boolean(s) && typeof s === "object").map((s) => ({ src: String(s.src ?? ""), alt: String(s.alt ?? "") }));
12937
+ } catch {
12938
+ return [];
12939
+ }
13855
12940
  }
13856
12941
  function readCarouselValue(key) {
13857
12942
  const container = containersForKey(key)[0];
@@ -13884,8 +12969,8 @@ function applyCarouselNode(key, val) {
13884
12969
  return true;
13885
12970
  }
13886
12971
  function useOhwCarousel(key, initial) {
13887
- const [images, setImages] = useState11(initial);
13888
- useEffect12(() => {
12972
+ const [images, setImages] = useState10(initial);
12973
+ useEffect11(() => {
13889
12974
  const el = document.querySelector(
13890
12975
  `[${CAROUSEL_ATTR}][data-ohw-key="${CSS.escape(key)}"]`
13891
12976
  );
@@ -13967,7 +13052,6 @@ function collectEditableNodes(extraContent, root = document) {
13967
13052
  NAV_ORDER_KEY,
13968
13053
  FOOTER_ORDER_KEY,
13969
13054
  NAV_COUNT_KEY,
13970
- SECTION_ORDER_KEY,
13971
13055
  // A socials row's order and its icons-vs-words setting live under keys no element carries,
13972
13056
  // so collecting the DOM alone left them behind: the draft knew the row was showing icons and
13973
13057
  // had gained an item, and the published page went back to the template's own (OHH-736).
@@ -14015,18 +13099,6 @@ function collectEditableNodes(extraContent, root = document) {
14015
13099
  }
14016
13100
  if (extraContent && !isScoped) {
14017
13101
  applyNavFooterDeleteOverrides(byKey, extraContent);
14018
- for (const key of LOGO_IMAGE_KEYS) {
14019
- if (!(key in extraContent)) continue;
14020
- byKey.set(key, { key, type: "image", text: extraContent[key] ?? "" });
14021
- }
14022
- for (const key of [LOGO_PLACEHOLDER_KEY, LOGO_ALT_KEY]) {
14023
- if (!(key in extraContent)) continue;
14024
- byKey.set(key, { key, type: "meta", text: extraContent[key] ?? "" });
14025
- }
14026
- for (const key of LOGO_SIZE_KEYS) {
14027
- if (!(key in extraContent)) continue;
14028
- byKey.set(key, { key, type: "meta", text: extraContent[key] ?? "" });
14029
- }
14030
13102
  }
14031
13103
  return Array.from(byKey.values());
14032
13104
  }
@@ -14431,7 +13503,6 @@ function fadeInImageElement(img, onReady) {
14431
13503
  function applyEditableImageSrc(img, url) {
14432
13504
  img.removeAttribute("srcset");
14433
13505
  img.removeAttribute("sizes");
14434
- if (img.loading === "lazy") img.loading = "eager";
14435
13506
  img.src = url;
14436
13507
  }
14437
13508
  function fadeInBgImage(el, url, onReady) {
@@ -14496,10 +13567,21 @@ function parseSchedulingInsertAfter(insertAfter) {
14496
13567
  insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
14497
13568
  };
14498
13569
  }
14499
- function resolveEntryAnchor(entry) {
14500
- if (entry.anchorId !== void 0) return { anchorId: entry.anchorId, beforeId: entry.beforeId ?? null };
14501
- const parsed = parseSchedulingInsertAfter(entry.insertAfter);
14502
- return { anchorId: parsed.anchor, beforeId: parsed.insertBefore };
13570
+ function resolveSchedulingInsert(insertAfter, explicitInsertBefore) {
13571
+ const parsed = parseSchedulingInsertAfter(insertAfter);
13572
+ const insertBefore = explicitInsertBefore ?? parsed.insertBefore;
13573
+ const effectiveInsertAfter = insertBefore && parsed.insertBefore === null ? `${insertAfter}${INSERT_BEFORE_MARKER}${insertBefore}` : insertAfter;
13574
+ return { effectiveInsertAfter, insertBefore };
13575
+ }
13576
+ function getSchedulingMountPoint(insertAfter) {
13577
+ const { anchor } = parseSchedulingInsertAfter(insertAfter);
13578
+ let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
13579
+ if (!anchorEl && anchor === "scheduling") {
13580
+ const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
13581
+ anchorEl = widgets.at(-1) ?? null;
13582
+ }
13583
+ if (!anchorEl) return null;
13584
+ return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
14503
13585
  }
14504
13586
  function schedulingMountDepth(insertAfter) {
14505
13587
  if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
@@ -14516,7 +13598,8 @@ function getPageSchedulingEntries(raw) {
14516
13598
  }
14517
13599
  }
14518
13600
  function isSchedulingWidgetMissing(entry) {
14519
- return !document.querySelector(`[data-ohw-section="${schedulingSectionId(entry.insertAfter)}"]`);
13601
+ const { effectiveInsertAfter } = resolveSchedulingInsert(entry.insertAfter);
13602
+ return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
14520
13603
  }
14521
13604
  function hasMissingSchedulingWidgets(entries) {
14522
13605
  return entries.some(isSchedulingWidgetMissing);
@@ -14546,17 +13629,16 @@ function initSectionsFromContent(content, removeExisting = false) {
14546
13629
  } catch {
14547
13630
  }
14548
13631
  }
14549
- function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, beforeId, existingWidgetId) {
14550
- const widgetId = existingWidgetId ?? (beforeId ? `${anchorId}${INSERT_BEFORE_MARKER}${beforeId}` : anchorId);
14551
- const sectionId = schedulingSectionId(widgetId);
13632
+ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId, explicitInsertBefore) {
13633
+ const { effectiveInsertAfter, insertBefore } = resolveSchedulingInsert(insertAfter, explicitInsertBefore);
13634
+ const sectionId = schedulingSectionId(effectiveInsertAfter);
14552
13635
  if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
14553
- const anchorEl = document.querySelector(`[data-ohw-section="${anchorId}"]`);
14554
- if (!anchorEl) return false;
14555
- const mountPoint = anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
13636
+ const mountPoint = getSchedulingMountPoint(effectiveInsertAfter);
13637
+ if (!mountPoint) return false;
14556
13638
  const container = document.createElement("div");
14557
13639
  container.dataset.ohwSectionContainer = "scheduling";
14558
- if (beforeId) {
14559
- const beforeAnchor = document.querySelector(`[data-ohw-section="${beforeId}"]`);
13640
+ if (insertBefore) {
13641
+ const beforeAnchor = document.querySelector(`[data-ohw-section="${insertBefore}"]`);
14560
13642
  const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
14561
13643
  if (!beforePoint) return false;
14562
13644
  beforePoint.insertAdjacentElement("beforebegin", container);
@@ -14567,25 +13649,19 @@ function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, be
14567
13649
  }
14568
13650
  tail.insertAdjacentElement("afterend", container);
14569
13651
  }
14570
- try {
14571
- const root = createRoot2(container);
14572
- flushSync2(() => {
14573
- root.render(
14574
- /* @__PURE__ */ jsx33(
14575
- SchedulingWidget,
14576
- {
14577
- notifyOnConnect,
14578
- initialScheduleId: scheduleId,
14579
- insertAfter: widgetId
14580
- }
14581
- )
14582
- );
14583
- });
14584
- } catch (err) {
14585
- console.error("[ow:scheduling] render threw", err);
14586
- container.remove();
14587
- return false;
14588
- }
13652
+ const root = createRoot2(container);
13653
+ flushSync2(() => {
13654
+ root.render(
13655
+ /* @__PURE__ */ jsx33(
13656
+ SchedulingWidget,
13657
+ {
13658
+ notifyOnConnect,
13659
+ initialScheduleId: scheduleId,
13660
+ insertAfter: effectiveInsertAfter
13661
+ }
13662
+ )
13663
+ );
13664
+ });
14589
13665
  const tracker = getSectionsTracker();
14590
13666
  let sections = [];
14591
13667
  try {
@@ -14593,12 +13669,10 @@ function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, be
14593
13669
  } catch {
14594
13670
  }
14595
13671
  const inEditor = typeof window !== "undefined" && window.self !== window.top;
14596
- if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === widgetId)) {
13672
+ if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === effectiveInsertAfter)) {
14597
13673
  sections.push({
14598
13674
  type: "scheduling",
14599
- insertAfter: widgetId,
14600
- anchorId,
14601
- beforeId: beforeId ?? null,
13675
+ insertAfter: effectiveInsertAfter,
14602
13676
  pagePath: window.location.pathname,
14603
13677
  ...scheduleId ? { scheduleId } : {}
14604
13678
  });
@@ -14612,8 +13686,7 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
14612
13686
  for (let i = pending.length - 1; i >= 0; i--) {
14613
13687
  const entry = pending[i];
14614
13688
  const shouldNotify = typeof notifyOnConnect === "function" ? notifyOnConnect(entry) : notifyOnConnect;
14615
- const { anchorId, beforeId } = resolveEntryAnchor(entry);
14616
- if (mountSchedulingWidget(anchorId, shouldNotify, entry.scheduleId ?? null, beforeId, entry.insertAfter)) {
13689
+ if (mountSchedulingWidget(entry.insertAfter, shouldNotify, entry.scheduleId)) {
14617
13690
  pending.splice(i, 1);
14618
13691
  }
14619
13692
  }
@@ -14759,11 +13832,6 @@ function applyLinkByKey(key, val) {
14759
13832
  hrefAnchors.forEach((el) => applyLinkHref(el, val));
14760
13833
  }
14761
13834
  }
14762
- function isInsideLinkEditor(target) {
14763
- return Boolean(
14764
- 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"]')
14765
- );
14766
- }
14767
13835
  function isInsideFloatingPanel(target) {
14768
13836
  return Boolean(target.closest("[data-ohw-floating-panel]"));
14769
13837
  }
@@ -14771,6 +13839,11 @@ function isPointOverFloatingPanel(clientX, clientY) {
14771
13839
  const el = document.elementFromPoint(clientX, clientY);
14772
13840
  return Boolean(el instanceof Element && el.closest("[data-ohw-floating-panel]"));
14773
13841
  }
13842
+ function isInsideLinkEditor(target) {
13843
+ return Boolean(
13844
+ 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"]')
13845
+ );
13846
+ }
14774
13847
  function getHrefKeyFromElement(el) {
14775
13848
  if (!el) return null;
14776
13849
  const anchor = el.closest("[data-ohw-href-key]");
@@ -15029,7 +14102,7 @@ function getNavigationSelectionParent(el) {
15029
14102
  if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
15030
14103
  return getFooterLinksContainer();
15031
14104
  }
15032
- 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)) {
14105
+ if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
15033
14106
  return getNavigationRoot(el);
15034
14107
  }
15035
14108
  return null;
@@ -15622,45 +14695,6 @@ function StateToggle({
15622
14695
  );
15623
14696
  }
15624
14697
  var contentCache = /* @__PURE__ */ new Map();
15625
- var fetchedContentPaths = /* @__PURE__ */ new Set();
15626
- var OHW_LOADER_STYLE = {
15627
- position: "fixed",
15628
- inset: 0,
15629
- background: "#fff",
15630
- zIndex: 2147483646,
15631
- display: "flex",
15632
- alignItems: "center",
15633
- justifyContent: "center"
15634
- };
15635
- function OhwLoaderSpinner() {
15636
- return /* @__PURE__ */ jsxs20("svg", { width: "28", height: "28", viewBox: "0 0 28 28", fill: "none", "aria-hidden": true, children: [
15637
- /* @__PURE__ */ jsx33("circle", { cx: "14", cy: "14", r: "11", stroke: "#E7E5E4", strokeWidth: "3" }),
15638
- /* @__PURE__ */ jsx33(
15639
- "circle",
15640
- {
15641
- cx: "14",
15642
- cy: "14",
15643
- r: "11",
15644
- stroke: "#1C1917",
15645
- strokeWidth: "3",
15646
- strokeDasharray: "17 52",
15647
- strokeLinecap: "round",
15648
- children: /* @__PURE__ */ jsx33(
15649
- "animateTransform",
15650
- {
15651
- attributeName: "transform",
15652
- type: "rotate",
15653
- from: "0 14 14",
15654
- to: "360 14 14",
15655
- dur: "0.7s",
15656
- repeatCount: "indefinite"
15657
- }
15658
- )
15659
- }
15660
- )
15661
- ] });
15662
- }
15663
- 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){}})();`;
15664
14698
  function resolveSubdomain(subdomainFromQuery) {
15665
14699
  if (subdomainFromQuery) return subdomainFromQuery;
15666
14700
  if (typeof window !== "undefined") {
@@ -15683,8 +14717,8 @@ function OhhwellsBridge() {
15683
14717
  const router = useRouter3();
15684
14718
  const searchParams = useSearchParams();
15685
14719
  const isEditMode = isEditSessionActive();
15686
- const [bridgeRoot, setBridgeRoot] = useState12(null);
15687
- useEffect13(() => {
14720
+ const [bridgeRoot, setBridgeRoot] = useState11(null);
14721
+ useEffect12(() => {
15688
14722
  const figtreeFontId = "ohw-figtree-font";
15689
14723
  if (!document.getElementById(figtreeFontId)) {
15690
14724
  const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
@@ -15713,151 +14747,87 @@ function OhhwellsBridge() {
15713
14747
  const subdomain = resolveSubdomain(subdomainFromQuery);
15714
14748
  useLinkHrefGuardian(pathname, subdomain, isEditMode);
15715
14749
  useSavedLinkNavigation(isEditMode);
15716
- const postToParent2 = useCallback8((data) => {
14750
+ const postToParent2 = useCallback7((data) => {
15717
14751
  if (typeof window !== "undefined" && window.parent !== window) {
15718
14752
  window.parent.postMessage(data, "*");
15719
14753
  }
15720
14754
  }, []);
15721
- const [fetchState, setFetchState] = useState12("idle");
15722
- const autoSaveTimers = useRef10(/* @__PURE__ */ new Map());
15723
- const activeElRef = useRef10(null);
15724
- const pointerHeldRef = useRef10(false);
15725
- const selectedElRef = useRef10(null);
15726
- const selectedHrefKeyRef = useRef10(null);
15727
- const selectedFooterColAttrRef = useRef10(null);
15728
- const originalContentRef = useRef10(null);
15729
- const activeStateElRef = useRef10(null);
15730
- const parentScrollRef = useRef10(null);
15731
- const visibleViewportRef = useRef10(null);
15732
- const [dialogPortalContainer, setDialogPortalContainer] = useState12(null);
15733
- const attachVisibleViewport = useCallback8((node) => {
14755
+ const [fetchState, setFetchState] = useState11("idle");
14756
+ const autoSaveTimers = useRef9(/* @__PURE__ */ new Map());
14757
+ const activeElRef = useRef9(null);
14758
+ const pointerHeldRef = useRef9(false);
14759
+ const selectedElRef = useRef9(null);
14760
+ const selectedHrefKeyRef = useRef9(null);
14761
+ const selectedFooterColAttrRef = useRef9(null);
14762
+ const originalContentRef = useRef9(null);
14763
+ const activeStateElRef = useRef9(null);
14764
+ const parentScrollRef = useRef9(null);
14765
+ const visibleViewportRef = useRef9(null);
14766
+ const [dialogPortalContainer, setDialogPortalContainer] = useState11(null);
14767
+ const attachVisibleViewport = useCallback7((node) => {
15734
14768
  visibleViewportRef.current = node;
15735
14769
  setDialogPortalContainer(node);
15736
14770
  if (node) applyVisibleViewport(node, parentScrollRef.current);
15737
14771
  }, []);
15738
- const toolbarElRef = useRef10(null);
15739
- const glowElRef = useRef10(null);
15740
- const hoveredImageRef = useRef10(null);
15741
- const hoveredImageHasTextOverlapRef = useRef10(false);
15742
- const dragOverElRef = useRef10(null);
15743
- const [mediaHover, setMediaHover] = useState12(null);
15744
- const [selectedMedia, setSelectedMedia] = useState12(null);
15745
- const selectedMediaElRef = useRef10(null);
15746
- const clearMediaSelection = useCallback8(() => {
15747
- const prev = selectedMediaElRef.current;
15748
- selectedMediaElRef.current = null;
15749
- setSelectedMedia(null);
15750
- const sectionEl = prev?.closest("[data-ohw-section]") ?? null;
15751
- if (sectionEl) {
15752
- postToParentRef.current({
15753
- type: "ow:section-selected",
15754
- sectionId: sectionEl.dataset.ohwSection ?? null,
15755
- sectionLabel: sectionEl.dataset.ohwSectionLabel ?? titleCaseSectionId(sectionEl.dataset.ohwSection ?? ""),
15756
- key: null
15757
- });
15758
- }
15759
- }, []);
15760
- const clearMediaSelectionRef = useRef10(clearMediaSelection);
15761
- clearMediaSelectionRef.current = clearMediaSelection;
15762
- const selectMediaElement = useCallback8((el) => {
15763
- const r2 = el.getBoundingClientRect();
15764
- const video = el.dataset.ohwEditable === "video" ? el.querySelector("video") : null;
15765
- selectedMediaElRef.current = el;
15766
- setSelectedMedia({
15767
- key: el.dataset.ohwKey ?? "",
15768
- rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height },
15769
- elementType: el.dataset.ohwEditable ?? "image",
15770
- hasTextOverlap: false,
15771
- isDragOver: false,
15772
- ...video ? { videoAutoplay: video.autoplay, videoMuted: video.muted } : {}
15773
- });
15774
- const sectionEl = el.closest("[data-ohw-section]");
15775
- aiSectionApiRef.current?.selectFromElement(el, { report: false });
15776
- postToParentRef.current({
15777
- type: "ow:section-selected",
15778
- sectionId: sectionEl?.dataset.ohwSection ?? null,
15779
- sectionLabel: sectionEl ? sectionEl.dataset.ohwSectionLabel ?? titleCaseSectionId(sectionEl.dataset.ohwSection ?? "") : null,
15780
- key: el.dataset.ohwKey ?? null,
15781
- // Display name for the pill — the raw key prettifies into fragments ("Img"); the
15782
- // bridge knows what the node IS, so it names it.
15783
- keyLabel: el.dataset.ohwEditable === "video" ? "Video" : el.dataset.ohwEditable === "bg-image" ? "Background" : "Image"
15784
- });
15785
- }, []);
15786
- const selectMediaElementRef = useRef10(selectMediaElement);
15787
- selectMediaElementRef.current = selectMediaElement;
15788
- useEffect13(() => {
15789
- if (!selectedMedia) return;
15790
- const update = () => {
15791
- const el = selectedMediaElRef.current;
15792
- if (!el || !el.isConnected) {
15793
- clearMediaSelection();
15794
- return;
15795
- }
15796
- const r2 = el.getBoundingClientRect();
15797
- setSelectedMedia(
15798
- (prev) => prev ? { ...prev, rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height } } : prev
15799
- );
15800
- };
15801
- window.addEventListener("scroll", update, true);
15802
- window.addEventListener("resize", update);
15803
- return () => {
15804
- window.removeEventListener("scroll", update, true);
15805
- window.removeEventListener("resize", update);
15806
- };
15807
- }, [selectedMedia !== null]);
15808
- const [carouselHover, setCarouselHover] = useState12(null);
15809
- const [uploadingRects, setUploadingRects] = useState12({});
15810
- const hoveredGapRef = useRef10(null);
15811
- const imageUnhoverTimerRef = useRef10(null);
15812
- const imageShowTimerRef = useRef10(null);
15813
- const editStylesRef = useRef10(null);
15814
- const activateRef = useRef10(() => {
15815
- });
15816
- const deactivateRef = useRef10(() => {
15817
- });
15818
- const selectRef = useRef10(() => {
15819
- });
15820
- const selectFrameRef = useRef10(() => {
15821
- });
15822
- const selectLogoRef = useRef10(() => {
15823
- });
15824
- const openLogoSizePanelRef = useRef10(() => {
15825
- });
15826
- const deselectRef = useRef10(() => {
15827
- });
15828
- const closeFloatingPanelOnlyRef = useRef10(() => {
15829
- });
15830
- const reselectNavigationItemRef = useRef10(() => {
15831
- });
15832
- const commitNavigationTextEditRef = useRef10(() => {
15833
- });
15834
- const handleDeleteSelectedRef = useRef10(() => false);
15835
- const runPendingDeleteUndoRef = useRef10(() => false);
15836
- const isFooterFrameSelectionRef = useRef10(false);
15837
- const refreshActiveCommandsRef = useRef10(() => {
15838
- });
15839
- const postToParentRef = useRef10(postToParent2);
15840
- postToParentRef.current = postToParent2;
15841
- const aiSectionApiRef = useRef10(null);
15842
- const sectionsLoadedRef = useRef10(false);
15843
- const pendingScheduleConfigRequests = useRef10([]);
15844
- const [toolbarRect, setToolbarRect] = useState12(null);
15845
- const [formPickRect, setFormPickRect] = useState12(null);
15846
- const formPickElRef = useRef10(null);
15847
- const [formViewState, setFormViewStateUi] = useState12("default");
15848
- const [formPickCount, setFormPickCount] = useState12(null);
15849
- const [formHoverRect, setFormHoverRect] = useState12(null);
15850
- const formHoverElRef = useRef10(null);
15851
- const [fieldPickRect, setFieldPickRect] = useState12(null);
15852
- const fieldPickElRef = useRef10(null);
15853
- const [fieldPickState, setFieldPickState] = useState12(null);
15854
- const [fieldTypePickerOpen, setFieldTypePickerOpen] = useState12(false);
15855
- const clearFormPick = useCallback8(() => {
15856
- const form = formPickElRef.current;
15857
- const editing = fieldPickElRef.current;
15858
- if (commitPlaceholderEdit(editing) && editing) {
15859
- const owner = editing.closest('[data-ohw-editable="form"]');
15860
- if (owner) persistFieldsRef.current(owner);
14772
+ const toolbarElRef = useRef9(null);
14773
+ const glowElRef = useRef9(null);
14774
+ const hoveredImageRef = useRef9(null);
14775
+ const hoveredImageHasTextOverlapRef = useRef9(false);
14776
+ const dragOverElRef = useRef9(null);
14777
+ const [mediaHover, setMediaHover] = useState11(null);
14778
+ const [carouselHover, setCarouselHover] = useState11(null);
14779
+ const [uploadingRects, setUploadingRects] = useState11({});
14780
+ const hoveredGapRef = useRef9(null);
14781
+ const imageUnhoverTimerRef = useRef9(null);
14782
+ const imageShowTimerRef = useRef9(null);
14783
+ const editStylesRef = useRef9(null);
14784
+ const activateRef = useRef9(() => {
14785
+ });
14786
+ const deactivateRef = useRef9(() => {
14787
+ });
14788
+ const selectRef = useRef9(() => {
14789
+ });
14790
+ const selectFrameRef = useRef9(() => {
14791
+ });
14792
+ const selectLogoRef = useRef9(() => {
14793
+ });
14794
+ const openLogoSizePanelRef = useRef9(() => {
14795
+ });
14796
+ const deselectRef = useRef9(() => {
14797
+ });
14798
+ const closeFloatingPanelOnlyRef = useRef9(() => {
14799
+ });
14800
+ const reselectNavigationItemRef = useRef9(() => {
14801
+ });
14802
+ const commitNavigationTextEditRef = useRef9(() => {
14803
+ });
14804
+ const handleDeleteSelectedRef = useRef9(() => false);
14805
+ const runPendingDeleteUndoRef = useRef9(() => false);
14806
+ const isFooterFrameSelectionRef = useRef9(false);
14807
+ const refreshActiveCommandsRef = useRef9(() => {
14808
+ });
14809
+ const postToParentRef = useRef9(postToParent2);
14810
+ postToParentRef.current = postToParent2;
14811
+ const aiSectionApiRef = useRef9(null);
14812
+ const sectionsLoadedRef = useRef9(false);
14813
+ const pendingScheduleConfigRequests = useRef9([]);
14814
+ const [toolbarRect, setToolbarRect] = useState11(null);
14815
+ const [formPickRect, setFormPickRect] = useState11(null);
14816
+ const formPickElRef = useRef9(null);
14817
+ const [formViewState, setFormViewStateUi] = useState11("default");
14818
+ const [formPickCount, setFormPickCount] = useState11(null);
14819
+ const [formHoverRect, setFormHoverRect] = useState11(null);
14820
+ const formHoverElRef = useRef9(null);
14821
+ const [fieldPickRect, setFieldPickRect] = useState11(null);
14822
+ const fieldPickElRef = useRef9(null);
14823
+ const [fieldPickState, setFieldPickState] = useState11(null);
14824
+ const [fieldTypePickerOpen, setFieldTypePickerOpen] = useState11(false);
14825
+ const clearFormPick = useCallback7(() => {
14826
+ const form = formPickElRef.current;
14827
+ const editing = fieldPickElRef.current;
14828
+ if (commitPlaceholderEdit(editing) && editing) {
14829
+ const owner = editing.closest('[data-ohw-editable="form"]');
14830
+ if (owner) persistFieldsRef.current(owner);
15861
14831
  }
15862
14832
  if (form) {
15863
14833
  const key = formKeyOf(form);
@@ -15872,7 +14842,7 @@ function OhhwellsBridge() {
15872
14842
  formPickElRef.current = null;
15873
14843
  setFormPickRect(null);
15874
14844
  }, []);
15875
- const clearFieldPick = useCallback8(() => {
14845
+ const clearFieldPick = useCallback7(() => {
15876
14846
  const wrapper = fieldPickElRef.current;
15877
14847
  if (commitPlaceholderEdit(wrapper) && wrapper) {
15878
14848
  const form = wrapper.closest('[data-ohw-editable="form"]');
@@ -15882,9 +14852,9 @@ function OhhwellsBridge() {
15882
14852
  setFieldPickRect(null);
15883
14853
  setFieldPickState(null);
15884
14854
  }, []);
15885
- const persistFieldsRef = useRef10(() => {
14855
+ const persistFieldsRef = useRef9(() => {
15886
14856
  });
15887
- const persistFields = useCallback8(
14857
+ const persistFields = useCallback7(
15888
14858
  (form) => {
15889
14859
  const key = formKeyOf(form);
15890
14860
  if (!key) return;
@@ -15895,7 +14865,7 @@ function OhhwellsBridge() {
15895
14865
  []
15896
14866
  );
15897
14867
  persistFieldsRef.current = persistFields;
15898
- const selectField = useCallback8((wrapper) => {
14868
+ const selectField = useCallback7((wrapper) => {
15899
14869
  if (fieldPickElRef.current && fieldPickElRef.current !== wrapper) {
15900
14870
  commitPlaceholderEdit(fieldPickElRef.current);
15901
14871
  }
@@ -15908,7 +14878,7 @@ function OhhwellsBridge() {
15908
14878
  setFieldPickState({ type: fieldTypeOf(wrapper), required: isFieldRequired(wrapper) });
15909
14879
  setFieldTypePickerOpen(false);
15910
14880
  }, []);
15911
- const withSelectedField = useCallback8(
14881
+ const withSelectedField = useCallback7(
15912
14882
  (run) => {
15913
14883
  const wrapper = fieldPickElRef.current;
15914
14884
  const form = formPickElRef.current;
@@ -15921,28 +14891,28 @@ function OhhwellsBridge() {
15921
14891
  },
15922
14892
  [persistFields]
15923
14893
  );
15924
- const handleFieldTypeChange = useCallback8(
14894
+ const handleFieldTypeChange = useCallback7(
15925
14895
  (type) => withSelectedField((_form, wrapper) => {
15926
14896
  applyFieldType(wrapper, type);
15927
14897
  selectField(wrapper);
15928
14898
  }),
15929
14899
  [selectField, withSelectedField]
15930
14900
  );
15931
- const handleFieldRequiredToggle = useCallback8(
14901
+ const handleFieldRequiredToggle = useCallback7(
15932
14902
  () => withSelectedField((_form, wrapper) => {
15933
14903
  setFieldRequired(wrapper, !isFieldRequired(wrapper));
15934
14904
  selectField(wrapper);
15935
14905
  }),
15936
14906
  [selectField, withSelectedField]
15937
14907
  );
15938
- const handleFieldDuplicate = useCallback8(
14908
+ const handleFieldDuplicate = useCallback7(
15939
14909
  () => withSelectedField((form, wrapper) => {
15940
14910
  const copy = duplicateField(form, wrapper);
15941
14911
  selectField(copy);
15942
14912
  }),
15943
14913
  [selectField, withSelectedField]
15944
14914
  );
15945
- const handleFieldDelete = useCallback8(
14915
+ const handleFieldDelete = useCallback7(
15946
14916
  () => withSelectedField((_form, wrapper) => {
15947
14917
  removeField(wrapper);
15948
14918
  clearFieldPick();
@@ -15950,7 +14920,7 @@ function OhhwellsBridge() {
15950
14920
  }),
15951
14921
  [clearFieldPick, withSelectedField]
15952
14922
  );
15953
- const handleAddField = useCallback8(
14923
+ const handleAddField = useCallback7(
15954
14924
  (type) => {
15955
14925
  const form = formPickElRef.current;
15956
14926
  if (!form) return;
@@ -15966,8 +14936,8 @@ function OhhwellsBridge() {
15966
14936
  },
15967
14937
  [persistFields, selectField]
15968
14938
  );
15969
- const fieldDragRef = useRef10(null);
15970
- const buildFieldDropSlots = useCallback8((form, draggedKey) => {
14939
+ const fieldDragRef = useRef9(null);
14940
+ const buildFieldDropSlots = useCallback7((form, draggedKey) => {
15971
14941
  const others = listFieldWrappers(form).filter((el) => fieldKeyOf(el) !== draggedKey);
15972
14942
  const slots = others.map((el) => {
15973
14943
  const rect = el.getBoundingClientRect();
@@ -15980,7 +14950,7 @@ function OhhwellsBridge() {
15980
14950
  }
15981
14951
  return slots;
15982
14952
  }, []);
15983
- const handleFieldDragStart = useCallback8(() => {
14953
+ const handleFieldDragStart = useCallback7(() => {
15984
14954
  const wrapper = fieldPickElRef.current;
15985
14955
  const form = formPickElRef.current;
15986
14956
  if (!wrapper || !form) return;
@@ -15989,18 +14959,18 @@ function OhhwellsBridge() {
15989
14959
  setFieldDragging(true);
15990
14960
  setFieldDropSlots(buildFieldDropSlots(form, key));
15991
14961
  }, [buildFieldDropSlots]);
15992
- const handleFieldDragEnd = useCallback8(() => {
14962
+ const handleFieldDragEnd = useCallback7(() => {
15993
14963
  fieldDragRef.current = null;
15994
14964
  setFieldDropIndex(null);
15995
14965
  setFieldDropSlots([]);
15996
14966
  setFieldDragging(false);
15997
14967
  }, []);
15998
- const [fieldDropIndex, setFieldDropIndex] = useState12(null);
15999
- const [fieldDropSlots, setFieldDropSlots] = useState12([]);
16000
- const [fieldDragging, setFieldDragging] = useState12(false);
16001
- const clearFormPickRef = useRef10(clearFormPick);
14968
+ const [fieldDropIndex, setFieldDropIndex] = useState11(null);
14969
+ const [fieldDropSlots, setFieldDropSlots] = useState11([]);
14970
+ const [fieldDragging, setFieldDragging] = useState11(false);
14971
+ const clearFormPickRef = useRef9(clearFormPick);
16002
14972
  clearFormPickRef.current = clearFormPick;
16003
- useEffect13(() => {
14973
+ useEffect12(() => {
16004
14974
  const el = fieldPickElRef.current;
16005
14975
  if (!el || fieldPickRect === null) return;
16006
14976
  const observer = new ResizeObserver(() => {
@@ -16009,7 +14979,7 @@ function OhhwellsBridge() {
16009
14979
  observer.observe(el);
16010
14980
  return () => observer.disconnect();
16011
14981
  }, [fieldPickRect !== null, fieldPickState]);
16012
- useEffect13(() => {
14982
+ useEffect12(() => {
16013
14983
  const el = formPickElRef.current;
16014
14984
  if (!el || formPickRect === null) return;
16015
14985
  const observer = new ResizeObserver(() => {
@@ -16018,25 +14988,25 @@ function OhhwellsBridge() {
16018
14988
  observer.observe(el);
16019
14989
  return () => observer.disconnect();
16020
14990
  }, [formPickRect !== null, formViewState]);
16021
- const [toolbarVariant, setToolbarVariant] = useState12("none");
16022
- const toolbarVariantRef = useRef10("none");
14991
+ const [toolbarVariant, setToolbarVariant] = useState11("none");
14992
+ const toolbarVariantRef = useRef9("none");
16023
14993
  toolbarVariantRef.current = toolbarVariant;
16024
- const [selectedIsCta, setSelectedIsCta] = useState12(false);
16025
- const [selectedIsSocial, setSelectedIsSocial] = useState12(false);
16026
- const [selectedIsSocialsRow, setSelectedIsSocialsRow] = useState12(false);
16027
- const [reorderHrefKey, setReorderHrefKey] = useState12(null);
16028
- const [reorderDragDisabled, setReorderDragDisabled] = useState12(false);
16029
- const [toggleState, setToggleState] = useState12(null);
16030
- const [maxBadge, setMaxBadge] = useState12(null);
16031
- const [activeCommands, setActiveCommands] = useState12(/* @__PURE__ */ new Set());
16032
- const [sectionGap, setSectionGap] = useState12(null);
16033
- const [toolbarShowEditLink, setToolbarShowEditLink] = useState12(false);
16034
- const hoveredNavContainerRef = useRef10(null);
16035
- const [hoveredNavContainerRect, setHoveredNavContainerRect] = useState12(null);
16036
- const hoveredItemElRef = useRef10(null);
16037
- const [hoveredItemRect, setHoveredItemRect] = useState12(null);
16038
- const [hoveredTextRect, setHoveredTextRect] = useState12(null);
16039
- useEffect13(() => {
14994
+ const [selectedIsCta, setSelectedIsCta] = useState11(false);
14995
+ const [selectedIsSocial, setSelectedIsSocial] = useState11(false);
14996
+ const [selectedIsSocialsRow, setSelectedIsSocialsRow] = useState11(false);
14997
+ const [reorderHrefKey, setReorderHrefKey] = useState11(null);
14998
+ const [reorderDragDisabled, setReorderDragDisabled] = useState11(false);
14999
+ const [toggleState, setToggleState] = useState11(null);
15000
+ const [maxBadge, setMaxBadge] = useState11(null);
15001
+ const [activeCommands, setActiveCommands] = useState11(/* @__PURE__ */ new Set());
15002
+ const [sectionGap, setSectionGap] = useState11(null);
15003
+ const [toolbarShowEditLink, setToolbarShowEditLink] = useState11(false);
15004
+ const hoveredNavContainerRef = useRef9(null);
15005
+ const [hoveredNavContainerRect, setHoveredNavContainerRect] = useState11(null);
15006
+ const hoveredItemElRef = useRef9(null);
15007
+ const [hoveredItemRect, setHoveredItemRect] = useState11(null);
15008
+ const [hoveredTextRect, setHoveredTextRect] = useState11(null);
15009
+ useEffect12(() => {
16040
15010
  const sync = () => {
16041
15011
  const el = document.querySelector(
16042
15012
  '[data-ohw-hovered]:not([contenteditable]):not([data-ohw-href-key]):not([data-ohw-editable="form"] *)'
@@ -16057,56 +15027,43 @@ function OhhwellsBridge() {
16057
15027
  });
16058
15028
  return () => observer.disconnect();
16059
15029
  }, []);
16060
- const siblingHintElRef = useRef10(null);
16061
- const [siblingHintRect, setSiblingHintRect] = useState12(null);
16062
- const [siblingHintRects, setSiblingHintRects] = useState12([]);
16063
- const [isItemDragging, setIsItemDragging] = useState12(false);
16064
- const [isFooterFrameSelection, setIsFooterFrameSelection] = useState12(false);
15030
+ const siblingHintElRef = useRef9(null);
15031
+ const [siblingHintRect, setSiblingHintRect] = useState11(null);
15032
+ const [siblingHintRects, setSiblingHintRects] = useState11([]);
15033
+ const [isItemDragging, setIsItemDragging] = useState11(false);
15034
+ const [isFooterFrameSelection, setIsFooterFrameSelection] = useState11(false);
16065
15035
  isFooterFrameSelectionRef.current = isFooterFrameSelection;
16066
- const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = useState12(null);
16067
- const [footerHeadingVisible, setFooterHeadingVisible] = useState12(null);
16068
- const footerDragRef = useRef10(null);
16069
- const [footerDropSlots, setFooterDropSlots] = useState12([]);
16070
- const [activeFooterDropIndex, setActiveFooterDropIndex] = useState12(null);
16071
- const [draggedItemRect, setDraggedItemRect] = useState12(null);
16072
- const footerPointerDragRef = useRef10(null);
16073
- const suppressNextClickRef = useRef10(false);
16074
- const suppressClickUntilRef = useRef10(0);
16075
- const [linkPopover, setLinkPopover] = useState12(null);
16076
- const linkPopoverSessionRef = useRef10(null);
16077
- const addNavAfterAnchorRef = useRef10(null);
16078
- const editContentRef = useRef10({});
16079
- const aiSectionsRef = useRef10("");
16080
- const brandKitRef = useRef10("");
16081
- const stylesRef = useRef10("");
16082
- const pendingDeleteUndoRef = useRef10(null);
16083
- const [floatingPanel, setFloatingPanel] = useState12(null);
16084
- const floatingPanelOpenRef = useRef10(false);
16085
- const setFloatingPanelRef = useRef10(setFloatingPanel);
16086
- const [floatingPanelPos, setFloatingPanelPos] = useState12(null);
16087
- const [logoSizeDraft, setLogoSizeDraft] = useState12(null);
16088
- const [editorViewport, setEditorViewport] = useState12("desktop");
16089
- const [parentScrollSnap, setParentScrollSnap] = useState12(null);
16090
- const [sitePages, setSitePages] = useState12([]);
16091
- const [sectionsByPath, setSectionsByPath] = useState12({});
16092
- const sectionsPrefetchGenRef = useRef10(0);
16093
- const setLinkPopoverRef = useRef10(setLinkPopover);
16094
- const linkPopoverPanelRef = useRef10(null);
16095
- const linkPopoverOpenRef = useRef10(false);
16096
- const linkPopoverGraceUntilRef = useRef10(0);
15036
+ const [floatingPanel, setFloatingPanel] = useState11(null);
15037
+ const floatingPanelOpenRef = useRef9(false);
15038
+ floatingPanelOpenRef.current = floatingPanel !== null;
15039
+ const [floatingPanelPos, setFloatingPanelPos] = useState11(null);
15040
+ const [logoSizeDraft, setLogoSizeDraft] = useState11(null);
15041
+ const [editorViewport, setEditorViewport] = useState11("desktop");
15042
+ const [parentScrollSnap, setParentScrollSnap] = useState11(null);
15043
+ const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = useState11(null);
15044
+ const [footerHeadingVisible, setFooterHeadingVisible] = useState11(null);
15045
+ const footerDragRef = useRef9(null);
15046
+ const [footerDropSlots, setFooterDropSlots] = useState11([]);
15047
+ const [activeFooterDropIndex, setActiveFooterDropIndex] = useState11(null);
15048
+ const [draggedItemRect, setDraggedItemRect] = useState11(null);
15049
+ const footerPointerDragRef = useRef9(null);
15050
+ const suppressNextClickRef = useRef9(false);
15051
+ const suppressClickUntilRef = useRef9(0);
15052
+ const [linkPopover, setLinkPopover] = useState11(null);
15053
+ const linkPopoverSessionRef = useRef9(null);
15054
+ const addNavAfterAnchorRef = useRef9(null);
15055
+ const editContentRef = useRef9({});
15056
+ const aiSectionsRef = useRef9("");
15057
+ const pendingDeleteUndoRef = useRef9(null);
15058
+ const [sitePages, setSitePages] = useState11([]);
15059
+ const [sectionsByPath, setSectionsByPath] = useState11({});
15060
+ const sectionsPrefetchGenRef = useRef9(0);
15061
+ const setLinkPopoverRef = useRef9(setLinkPopover);
15062
+ const linkPopoverPanelRef = useRef9(null);
15063
+ const linkPopoverOpenRef = useRef9(false);
15064
+ const linkPopoverGraceUntilRef = useRef9(0);
16097
15065
  setLinkPopoverRef.current = setLinkPopover;
16098
- setFloatingPanelRef.current = setFloatingPanel;
16099
15066
  linkPopoverSessionRef.current = linkPopover;
16100
- floatingPanelOpenRef.current = Boolean(floatingPanel);
16101
- useEffect13(() => {
16102
- const syncViewport = () => {
16103
- const next = window.innerWidth <= 480 ? "mobile" : "desktop";
16104
- setEditorViewport((prev) => prev === next ? prev : next);
16105
- };
16106
- syncViewport();
16107
- window.addEventListener("resize", syncViewport);
16108
- return () => window.removeEventListener("resize", syncViewport);
16109
- }, []);
16110
15067
  const {
16111
15068
  navDragRef,
16112
15069
  navDropSlots,
@@ -16139,20 +15096,10 @@ function OhhwellsBridge() {
16139
15096
  getNavigationItemAnchor,
16140
15097
  isDragHandleDisabled
16141
15098
  });
16142
- const { sectionDropSlots, activeSectionDropIndex, isSectionDragging } = useSectionDrag({
16143
- isEditMode,
16144
- editContentRef,
16145
- postToParentRef,
16146
- parentScrollRef,
16147
- navDragRef,
16148
- footerDragRef,
16149
- suppressNextClickRef,
16150
- suppressClickUntilRef
16151
- });
16152
15099
  const bumpLinkPopoverGrace = () => {
16153
15100
  linkPopoverGraceUntilRef.current = Date.now() + 350;
16154
15101
  };
16155
- const runSectionsPrefetch = useCallback8((pages) => {
15102
+ const runSectionsPrefetch = useCallback7((pages) => {
16156
15103
  if (!isEditMode || shouldUseDevFixtures() || pages.length === 0) return;
16157
15104
  const gen = ++sectionsPrefetchGenRef.current;
16158
15105
  const paths = pages.map((p) => p.path);
@@ -16171,9 +15118,9 @@ function OhhwellsBridge() {
16171
15118
  );
16172
15119
  });
16173
15120
  }, [isEditMode, pathname]);
16174
- const runSectionsPrefetchRef = useRef10(runSectionsPrefetch);
15121
+ const runSectionsPrefetchRef = useRef9(runSectionsPrefetch);
16175
15122
  runSectionsPrefetchRef.current = runSectionsPrefetch;
16176
- useEffect13(() => {
15123
+ useEffect12(() => {
16177
15124
  if (!linkPopover) {
16178
15125
  document.documentElement.removeAttribute("data-ohw-link-popover-open");
16179
15126
  return;
@@ -16201,7 +15148,7 @@ function OhhwellsBridge() {
16201
15148
  document.documentElement.removeAttribute("data-ohw-link-popover-open");
16202
15149
  };
16203
15150
  }, [linkPopover, postToParent2]);
16204
- useEffect13(() => {
15151
+ useEffect12(() => {
16205
15152
  if (!isEditMode) return;
16206
15153
  const useFixtures = shouldUseDevFixtures();
16207
15154
  if (useFixtures) {
@@ -16225,14 +15172,14 @@ function OhhwellsBridge() {
16225
15172
  if (!useFixtures) postToParent2({ type: "ow:request-site-pages" });
16226
15173
  return () => window.removeEventListener("message", onSitePages);
16227
15174
  }, [isEditMode, postToParent2]);
16228
- useEffect13(() => {
15175
+ useEffect12(() => {
16229
15176
  if (!isEditMode || shouldUseDevFixtures()) return;
16230
15177
  void loadAllSectionsManifest().then((manifest) => {
16231
15178
  if (Object.keys(manifest).length === 0) return;
16232
15179
  setSectionsByPath((prev) => ({ ...manifest, ...prev }));
16233
15180
  });
16234
15181
  }, [isEditMode]);
16235
- useEffect13(() => {
15182
+ useEffect12(() => {
16236
15183
  const update = () => {
16237
15184
  const el = activeElRef.current ?? selectedElRef.current;
16238
15185
  if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
@@ -16256,10 +15203,10 @@ function OhhwellsBridge() {
16256
15203
  vvp.removeEventListener("resize", update);
16257
15204
  };
16258
15205
  }, []);
16259
- const refreshStateRules = useCallback8(() => {
15206
+ const refreshStateRules = useCallback7(() => {
16260
15207
  editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
16261
15208
  }, []);
16262
- const processConfigRequest = useCallback8((insertAfterVal) => {
15209
+ const processConfigRequest = useCallback7((insertAfterVal) => {
16263
15210
  const tracker = getSectionsTracker();
16264
15211
  let entries = [];
16265
15212
  try {
@@ -16282,7 +15229,7 @@ function OhhwellsBridge() {
16282
15229
  }
16283
15230
  window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: null }, "*");
16284
15231
  }, [isEditMode]);
16285
- const deactivate = useCallback8(() => {
15232
+ const deactivate = useCallback7(() => {
16286
15233
  const el = activeElRef.current;
16287
15234
  if (!el) return;
16288
15235
  const isFormBlock = el.dataset.ohwEditable === "form";
@@ -16323,12 +15270,12 @@ function OhhwellsBridge() {
16323
15270
  setToolbarShowEditLink(false);
16324
15271
  postToParent2({ type: "ow:exit-edit" });
16325
15272
  }, [postToParent2]);
16326
- const clearSelectedAttr = useCallback8(() => {
15273
+ const clearSelectedAttr = useCallback7(() => {
16327
15274
  document.querySelectorAll("[data-ohw-selected]").forEach((el) => {
16328
15275
  el.removeAttribute("data-ohw-selected");
16329
15276
  });
16330
15277
  }, []);
16331
- const deselect = useCallback8(() => {
15278
+ const deselect = useCallback7(() => {
16332
15279
  clearSelectedAttr();
16333
15280
  selectedElRef.current = null;
16334
15281
  selectedHrefKeyRef.current = null;
@@ -16357,12 +15304,12 @@ function OhhwellsBridge() {
16357
15304
  setToolbarVariant("none");
16358
15305
  }
16359
15306
  }, [clearSelectedAttr]);
16360
- const markSelected = useCallback8((el) => {
15307
+ const markSelected = useCallback7((el) => {
16361
15308
  clearSelectedAttr();
16362
15309
  el.removeAttribute("data-ohw-hovered");
16363
15310
  el.setAttribute("data-ohw-selected", "");
16364
15311
  }, [clearSelectedAttr]);
16365
- const resolveHrefKeyElement = useCallback8((hrefKey) => {
15312
+ const resolveHrefKeyElement = useCallback7((hrefKey) => {
16366
15313
  if (isFooterHrefKey(hrefKey)) {
16367
15314
  return document.querySelector(
16368
15315
  `footer [data-ohw-href-key="${CSS.escape(hrefKey)}"]`
@@ -16377,7 +15324,7 @@ function OhhwellsBridge() {
16377
15324
  `[data-ohw-href-key="${CSS.escape(hrefKey)}"]`
16378
15325
  );
16379
15326
  }, []);
16380
- const resyncSelectedNavigationItem = useCallback8(() => {
15327
+ const resyncSelectedNavigationItem = useCallback7(() => {
16381
15328
  const hrefKey = selectedHrefKeyRef.current;
16382
15329
  if (hrefKey) {
16383
15330
  const link = resolveHrefKeyElement(hrefKey);
@@ -16415,7 +15362,7 @@ function OhhwellsBridge() {
16415
15362
  );
16416
15363
  }
16417
15364
  }, [resolveHrefKeyElement]);
16418
- const reselectNavigationItem = useCallback8((navAnchor) => {
15365
+ const reselectNavigationItem = useCallback7((navAnchor) => {
16419
15366
  selectedElRef.current = navAnchor;
16420
15367
  selectedHrefKeyRef.current = navAnchor.getAttribute("data-ohw-href-key");
16421
15368
  selectedFooterColAttrRef.current = null;
@@ -16446,7 +15393,7 @@ function OhhwellsBridge() {
16446
15393
  setToolbarShowEditLink(false);
16447
15394
  setActiveCommands(/* @__PURE__ */ new Set());
16448
15395
  }, [markSelected]);
16449
- const commitNavigationTextEdit = useCallback8((navAnchor) => {
15396
+ const commitNavigationTextEdit = useCallback7((navAnchor) => {
16450
15397
  const el = activeElRef.current;
16451
15398
  if (!el) return;
16452
15399
  const key = el.dataset.ohwKey;
@@ -16479,7 +15426,7 @@ function OhhwellsBridge() {
16479
15426
  postToParent2({ type: "ow:exit-edit" });
16480
15427
  reselectNavigationItem(navAnchor);
16481
15428
  }, [postToParent2, reselectNavigationItem]);
16482
- const handleAddTopLevelNavItem = useCallback8(() => {
15429
+ const handleAddTopLevelNavItem = useCallback7(() => {
16483
15430
  const items = listNavbarRootItems();
16484
15431
  addNavAfterAnchorRef.current = items[items.length - 1] ?? null;
16485
15432
  deselectRef.current();
@@ -16491,7 +15438,7 @@ function OhhwellsBridge() {
16491
15438
  intent: "add-nav"
16492
15439
  });
16493
15440
  }, []);
16494
- const maybeWarnNavLinkDropdownConflict = useCallback8(
15441
+ const maybeWarnNavLinkDropdownConflict = useCallback7(
16495
15442
  (anchor) => {
16496
15443
  if (!isNavbarHrefKey(anchor.getAttribute("data-ohw-href-key"))) return;
16497
15444
  if (!navDropdownsOpenOnClick()) return;
@@ -16504,7 +15451,7 @@ function OhhwellsBridge() {
16504
15451
  },
16505
15452
  [postToParent2]
16506
15453
  );
16507
- const handleNavDropdownOpenChange = useCallback8((open) => {
15454
+ const handleNavDropdownOpenChange = useCallback7((open) => {
16508
15455
  const selected = selectedElRef.current;
16509
15456
  if (!selected || !isNavigationItem2(selected)) return;
16510
15457
  setNavGroupForceOpen(selected, open);
@@ -16516,7 +15463,7 @@ function OhhwellsBridge() {
16516
15463
  }
16517
15464
  });
16518
15465
  }, []);
16519
- const handleFooterHeadingVisibleChange = useCallback8(
15466
+ const handleFooterHeadingVisibleChange = useCallback7(
16520
15467
  (visible) => {
16521
15468
  const selected = selectedElRef.current;
16522
15469
  if (!selected || !isFooterFrameSelectionRef.current) return;
@@ -16540,7 +15487,7 @@ function OhhwellsBridge() {
16540
15487
  },
16541
15488
  [postToParent2]
16542
15489
  );
16543
- const enterEditOnNewItem = useCallback8((anchor) => {
15490
+ const enterEditOnNewItem = useCallback7((anchor) => {
16544
15491
  const label = anchor.querySelector('[data-ohw-editable="text"]');
16545
15492
  if (!label) {
16546
15493
  selectRef.current(anchor);
@@ -16549,8 +15496,8 @@ function OhhwellsBridge() {
16549
15496
  setNavGroupForceOpen(anchor, true);
16550
15497
  activateRef.current(label);
16551
15498
  }, []);
16552
- const pendingSocialAddRef = useRef10(null);
16553
- const handleAddChildItem = useCallback8(() => {
15499
+ const pendingSocialAddRef = useRef9(null);
15500
+ const handleAddChildItem = useCallback7(() => {
16554
15501
  const selected = selectedElRef.current;
16555
15502
  if (!selected) return;
16556
15503
  const socialsRow = isSocialsRow(selected) ? selected : findSocialsRow(selected);
@@ -16659,7 +15606,7 @@ function OhhwellsBridge() {
16659
15606
  enterEditOnNewItem(result.anchor);
16660
15607
  });
16661
15608
  }, [enterEditOnNewItem, isFooterFrameSelection, maybeWarnNavLinkDropdownConflict, postToParent2]);
16662
- const handleAddFooterColumn = useCallback8(() => {
15609
+ const handleAddFooterColumn = useCallback7(() => {
16663
15610
  if (!canAddFooterColumn()) {
16664
15611
  postToParent2({
16665
15612
  type: "ow:toast",
@@ -16680,7 +15627,7 @@ function OhhwellsBridge() {
16680
15627
  selectRef.current(result.firstLink);
16681
15628
  });
16682
15629
  }, [postToParent2]);
16683
- const clearFooterDragVisuals = useCallback8(() => {
15630
+ const clearFooterDragVisuals = useCallback7(() => {
16684
15631
  footerDragRef.current = null;
16685
15632
  setSiblingHintRects([]);
16686
15633
  setFooterDropSlots([]);
@@ -16689,7 +15636,7 @@ function OhhwellsBridge() {
16689
15636
  setIsItemDragging(false);
16690
15637
  unlockFooterDragInteraction();
16691
15638
  }, []);
16692
- const refreshFooterDragVisuals = useCallback8((session, activeSlot, clientX, clientY) => {
15639
+ const refreshFooterDragVisuals = useCallback7((session, activeSlot, clientX, clientY) => {
16693
15640
  const dragged = session.draggedEl;
16694
15641
  setDraggedItemRect(dragged.getBoundingClientRect());
16695
15642
  if (typeof clientX === "number" && typeof clientY === "number") {
@@ -16721,13 +15668,13 @@ function OhhwellsBridge() {
16721
15668
  const activeIdx = activeSlot ? slots.findIndex((s) => s.insertIndex === activeSlot.insertIndex) : -1;
16722
15669
  setActiveFooterDropIndex(activeIdx >= 0 ? activeIdx : null);
16723
15670
  }, []);
16724
- const refreshFooterDragVisualsRef = useRef10(refreshFooterDragVisuals);
15671
+ const refreshFooterDragVisualsRef = useRef9(refreshFooterDragVisuals);
16725
15672
  refreshFooterDragVisualsRef.current = refreshFooterDragVisuals;
16726
- const commitFooterDragRef = useRef10(() => {
15673
+ const commitFooterDragRef = useRef9(() => {
16727
15674
  });
16728
- const beginFooterDragRef = useRef10(() => {
15675
+ const beginFooterDragRef = useRef9(() => {
16729
15676
  });
16730
- const beginFooterDrag = useCallback8(
15677
+ const beginFooterDrag = useCallback7(
16731
15678
  (session) => {
16732
15679
  const rect = session.draggedEl.getBoundingClientRect();
16733
15680
  session.lastClientX = session.lastClientX || rect.left + rect.width / 2;
@@ -16747,7 +15694,7 @@ function OhhwellsBridge() {
16747
15694
  [refreshFooterDragVisuals]
16748
15695
  );
16749
15696
  beginFooterDragRef.current = beginFooterDrag;
16750
- const commitFooterDrag = useCallback8(
15697
+ const commitFooterDrag = useCallback7(
16751
15698
  (clientX, clientY) => {
16752
15699
  const session = footerDragRef.current;
16753
15700
  if (!session) {
@@ -16876,7 +15823,7 @@ function OhhwellsBridge() {
16876
15823
  [clearFooterDragVisuals, resolveHrefKeyElement, resyncSelectedNavigationItem]
16877
15824
  );
16878
15825
  commitFooterDragRef.current = commitFooterDrag;
16879
- const startFooterLinkDrag = useCallback8(
15826
+ const startFooterLinkDrag = useCallback7(
16880
15827
  (anchor, clientX, clientY, wasSelected) => {
16881
15828
  const hrefKey = anchor.getAttribute("data-ohw-href-key");
16882
15829
  if (!hrefKey) return false;
@@ -16912,7 +15859,7 @@ function OhhwellsBridge() {
16912
15859
  },
16913
15860
  [beginFooterDrag]
16914
15861
  );
16915
- const startFooterColumnDrag = useCallback8(
15862
+ const startFooterColumnDrag = useCallback7(
16916
15863
  (columnEl, clientX, clientY, wasSelected) => {
16917
15864
  const columns = listFooterColumns();
16918
15865
  const idx = columns.indexOf(columnEl);
@@ -16932,7 +15879,7 @@ function OhhwellsBridge() {
16932
15879
  },
16933
15880
  [beginFooterDrag]
16934
15881
  );
16935
- const handleItemDragStart = useCallback8(
15882
+ const handleItemDragStart = useCallback7(
16936
15883
  (e) => {
16937
15884
  const selected = selectedElRef.current;
16938
15885
  if (!selected) {
@@ -16952,7 +15899,7 @@ function OhhwellsBridge() {
16952
15899
  },
16953
15900
  [startFooterColumnDrag, startFooterLinkDrag, startNavLinkDrag]
16954
15901
  );
16955
- const handleItemDragEnd = useCallback8(
15902
+ const handleItemDragEnd = useCallback7(
16956
15903
  (e) => {
16957
15904
  if (footerDragRef.current) {
16958
15905
  const x = e?.clientX;
@@ -16978,7 +15925,7 @@ function OhhwellsBridge() {
16978
15925
  },
16979
15926
  [commitFooterDrag, commitNavDrag, navDragRef]
16980
15927
  );
16981
- const handleItemChromePointerDown = useCallback8((e) => {
15928
+ const handleItemChromePointerDown = useCallback7((e) => {
16982
15929
  if (e.button !== 0) return;
16983
15930
  const selected = selectedElRef.current;
16984
15931
  if (!selected) return;
@@ -17009,7 +15956,7 @@ function OhhwellsBridge() {
17009
15956
  }
17010
15957
  if (armNavPressFromChrome(selected, e.clientX, e.clientY, e.pointerId)) return;
17011
15958
  }, [armNavPressFromChrome]);
17012
- const handleItemChromeClick = useCallback8((clientX, clientY) => {
15959
+ const handleItemChromeClick = useCallback7((clientX, clientY) => {
17013
15960
  if (suppressNextClickRef.current || Date.now() < suppressClickUntilRef.current) {
17014
15961
  suppressNextClickRef.current = false;
17015
15962
  return;
@@ -17022,7 +15969,7 @@ function OhhwellsBridge() {
17022
15969
  }, []);
17023
15970
  reselectNavigationItemRef.current = reselectNavigationItem;
17024
15971
  commitNavigationTextEditRef.current = commitNavigationTextEdit;
17025
- const select = useCallback8((anchor) => {
15972
+ const select = useCallback7((anchor) => {
17026
15973
  if (!isNavigationItem2(anchor)) return;
17027
15974
  if (activeElRef.current) deactivate();
17028
15975
  aiSectionApiRef.current?.selectFromElement(anchor);
@@ -17065,7 +16012,7 @@ function OhhwellsBridge() {
17065
16012
  setFloatingPanel(null);
17066
16013
  setLogoSizeDraft(null);
17067
16014
  }, [deactivate, markSelected]);
17068
- const selectFrame = useCallback8((el) => {
16015
+ const selectFrame = useCallback7((el) => {
17069
16016
  if (!isNavigationContainer(el)) return;
17070
16017
  if (activeElRef.current) deactivate();
17071
16018
  aiSectionApiRef.current?.selectFromElement(el);
@@ -17116,7 +16063,7 @@ function OhhwellsBridge() {
17116
16063
  setFloatingPanel(null);
17117
16064
  setLogoSizeDraft(null);
17118
16065
  }, [deactivate, markSelected, postToParent2]);
17119
- const selectLogo = useCallback8(
16066
+ const selectLogo = useCallback7(
17120
16067
  (logoEl) => {
17121
16068
  if (activeElRef.current) deactivate();
17122
16069
  selectedElRef.current = logoEl;
@@ -17145,7 +16092,7 @@ function OhhwellsBridge() {
17145
16092
  },
17146
16093
  [deactivate, markSelected]
17147
16094
  );
17148
- const openLogoSizePanel = useCallback8((logoEl) => {
16095
+ const openLogoSizePanel = useCallback7((logoEl) => {
17149
16096
  const placement = getLogoPlacement(logoEl);
17150
16097
  const draft = readLogoSizeState(editContentRef.current, placement);
17151
16098
  setLogoSizeDraft(draft);
@@ -17158,7 +16105,7 @@ function OhhwellsBridge() {
17158
16105
  placement
17159
16106
  });
17160
16107
  }, []);
17161
- const openSocialsDisplayPanel = useCallback8((row) => {
16108
+ const openSocialsDisplayPanel = useCallback7((row) => {
17162
16109
  setParentScrollSnap(parentScrollRef.current);
17163
16110
  setFloatingPanel({
17164
16111
  key: "socials-display",
@@ -17168,11 +16115,11 @@ function OhhwellsBridge() {
17168
16115
  row
17169
16116
  });
17170
16117
  }, []);
17171
- const isEditModeRef = useRef10(false);
17172
- const requestMissingSocialIconsRef = useRef10(() => {
16118
+ const isEditModeRef = useRef9(false);
16119
+ const requestMissingSocialIconsRef = useRef9(() => {
17173
16120
  });
17174
- const askedSocialIconsRef = useRef10(/* @__PURE__ */ new Set());
17175
- const requestMissingSocialIcons = useCallback8(() => {
16121
+ const askedSocialIconsRef = useRef9(/* @__PURE__ */ new Set());
16122
+ const requestMissingSocialIcons = useCallback7(() => {
17176
16123
  const items = Array.from(document.querySelectorAll(`[${SOCIALS_ROW_ATTR}]`)).filter((row) => socialsDisplayFor(row, editContentRef.current).icon).flatMap((row) => {
17177
16124
  const missing = socialsMissingIcons(row);
17178
16125
  listSocialItems(row).forEach((item) => ensureIconSlot(item));
@@ -17184,7 +16131,7 @@ function OhhwellsBridge() {
17184
16131
  }, []);
17185
16132
  requestMissingSocialIconsRef.current = requestMissingSocialIcons;
17186
16133
  isEditModeRef.current = isEditMode;
17187
- const changeSocialsDisplay = useCallback8(
16134
+ const changeSocialsDisplay = useCallback7(
17188
16135
  (row, next) => {
17189
16136
  if (next.icon) {
17190
16137
  const missing = socialsMissingIcons(row);
@@ -17208,17 +16155,17 @@ function OhhwellsBridge() {
17208
16155
  },
17209
16156
  []
17210
16157
  );
17211
- const closeFloatingPanelOnly = useCallback8(() => {
16158
+ const closeFloatingPanelOnly = useCallback7(() => {
17212
16159
  setFloatingPanel(null);
17213
16160
  setLogoSizeDraft(null);
17214
16161
  }, []);
17215
16162
  closeFloatingPanelOnlyRef.current = closeFloatingPanelOnly;
17216
- const closeFloatingPanelAndDeselect = useCallback8(() => {
16163
+ const closeFloatingPanelAndDeselect = useCallback7(() => {
17217
16164
  setFloatingPanel(null);
17218
16165
  setLogoSizeDraft(null);
17219
16166
  deselectRef.current();
17220
16167
  }, []);
17221
- useEffect13(() => {
16168
+ useEffect12(() => {
17222
16169
  const session = floatingPanel && floatingPanel.kind === "logo-size" ? floatingPanel : null;
17223
16170
  if (!session || !logoSizeDraft) {
17224
16171
  postToParentRef.current({ type: "ow:logo-size-panel", open: false });
@@ -17237,7 +16184,7 @@ function OhhwellsBridge() {
17237
16184
  max: LOGO_SIZE_MAX
17238
16185
  });
17239
16186
  }, [floatingPanel, logoSizeDraft, editorViewport]);
17240
- const persistLogoSizeDraft = useCallback8(
16187
+ const persistLogoSizeDraft = useCallback7(
17241
16188
  (placement, draft) => {
17242
16189
  const desktopKey = LOGO_SIZE_DESKTOP_KEYS[placement];
17243
16190
  const mobileKey = LOGO_SIZE_MOBILE_KEYS[placement];
@@ -17277,7 +16224,7 @@ function OhhwellsBridge() {
17277
16224
  },
17278
16225
  [postToParent2]
17279
16226
  );
17280
- const activate = useCallback8((el, options) => {
16227
+ const activate = useCallback7((el, options) => {
17281
16228
  if (activeElRef.current === el) return;
17282
16229
  if (isIconEditable(el)) {
17283
16230
  const social = getSocialItem(el);
@@ -17369,8 +16316,8 @@ function OhhwellsBridge() {
17369
16316
  openLogoSizePanelRef.current = openLogoSizePanel;
17370
16317
  deselectRef.current = deselect;
17371
16318
  closeFloatingPanelOnlyRef.current = closeFloatingPanelOnly;
17372
- const lastSiteWideScopeRef = useRef10(null);
17373
- useEffect13(() => {
16319
+ const lastSiteWideScopeRef = useRef9(null);
16320
+ useEffect12(() => {
17374
16321
  if (!isEditMode) {
17375
16322
  if (lastSiteWideScopeRef.current !== false) {
17376
16323
  lastSiteWideScopeRef.current = false;
@@ -17403,27 +16350,15 @@ function OhhwellsBridge() {
17403
16350
  }
17404
16351
  const applyContent = (content) => {
17405
16352
  const imageLoads = [];
17406
- if (typeof content[BRAND_KIT_KEY] === "string") {
17407
- brandKitRef.current = content[BRAND_KIT_KEY];
17408
- applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
17409
- }
17410
16353
  if (typeof content[AI_SECTIONS_KEY] === "string") {
17411
16354
  aiSectionsRef.current = content[AI_SECTIONS_KEY];
17412
16355
  applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
17413
16356
  }
17414
- if (typeof content[STYLE_STORE_KEY] === "string") {
17415
- stylesRef.current = content[STYLE_STORE_KEY];
17416
- applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
17417
- }
17418
- applyBrandChrome(content);
17419
16357
  for (const [key, val] of Object.entries(content)) {
17420
16358
  if (key === "__ohw_sections") continue;
17421
16359
  if (key === AI_SECTIONS_KEY) continue;
17422
16360
  if (key === LOGO_PLACEHOLDER_KEY) continue;
17423
16361
  if (LOGO_IMAGE_KEYS.includes(key)) continue;
17424
- if (key === BRAND_KIT_KEY) continue;
17425
- if (key === STYLE_STORE_KEY) continue;
17426
- if (BRAND_CHROME_KEYS.has(key)) continue;
17427
16362
  if (applyVideoSettingNode(key, val)) continue;
17428
16363
  if (applyCarouselNode(key, val)) continue;
17429
16364
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -17484,9 +16419,7 @@ function OhhwellsBridge() {
17484
16419
  let cancelled = false;
17485
16420
  setFetchState("loading");
17486
16421
  const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
17487
- const initialPath = pathname;
17488
- fetchedContentPaths.add(`${subdomain}::${initialPath}`);
17489
- fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
16422
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
17490
16423
  if (cancelled) return;
17491
16424
  const content = data?.content ?? {};
17492
16425
  contentCache.set(subdomain, content);
@@ -17499,7 +16432,7 @@ function OhhwellsBridge() {
17499
16432
  cancelled = true;
17500
16433
  };
17501
16434
  }, [subdomain, isEditMode]);
17502
- useEffect13(() => {
16435
+ useEffect12(() => {
17503
16436
  if (!isEditMode) return;
17504
16437
  const resolveIndex = (form, clientY) => {
17505
16438
  const wrappers = listFieldWrappers(form).filter((el) => fieldKeyOf(el) !== fieldDragRef.current?.key);
@@ -17540,7 +16473,7 @@ function OhhwellsBridge() {
17540
16473
  window.removeEventListener("drop", onDrop, true);
17541
16474
  };
17542
16475
  }, [buildFieldDropSlots, isEditMode, persistFields, selectField]);
17543
- useEffect13(() => {
16476
+ useEffect12(() => {
17544
16477
  if (!isEditMode) return;
17545
16478
  const mark = () => document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
17546
16479
  markFormFields(form);
@@ -17552,7 +16485,7 @@ function OhhwellsBridge() {
17552
16485
  });
17553
16486
  return () => observer.disconnect();
17554
16487
  }, [isEditMode, fetchState, pathname]);
17555
- useEffect13(() => {
16488
+ useEffect12(() => {
17556
16489
  if (!isEditMode) return;
17557
16490
  let saveTimer = null;
17558
16491
  const onInput = (e) => {
@@ -17574,14 +16507,14 @@ function OhhwellsBridge() {
17574
16507
  document.addEventListener("input", onInput, true);
17575
16508
  return () => document.removeEventListener("input", onInput, true);
17576
16509
  }, [isEditMode, persistFields]);
17577
- useEffect13(() => {
16510
+ useEffect12(() => {
17578
16511
  if (isEditMode || fetchState !== "done") return;
17579
16512
  const content = contentCache.get(subdomain) ?? {};
17580
16513
  document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
17581
16514
  reconcileFieldsFromContent(form, content);
17582
16515
  });
17583
16516
  }, [isEditMode, fetchState, subdomain]);
17584
- useEffect13(() => {
16517
+ useEffect12(() => {
17585
16518
  if (!isEditMode) return;
17586
16519
  const swallow = (e) => {
17587
16520
  const target = e.target;
@@ -17590,12 +16523,12 @@ function OhhwellsBridge() {
17590
16523
  document.addEventListener("submit", swallow, true);
17591
16524
  return () => document.removeEventListener("submit", swallow, true);
17592
16525
  }, [isEditMode]);
17593
- useEffect13(() => {
16526
+ useEffect12(() => {
17594
16527
  if (isEditMode || fetchState !== "done") return;
17595
16528
  const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
17596
16529
  bindPublishedForms(apiUrl, subdomain, contentCache.get(subdomain) ?? {});
17597
16530
  }, [isEditMode, fetchState, subdomain]);
17598
- useEffect13(() => {
16531
+ useEffect12(() => {
17599
16532
  if (!subdomain || isEditMode) return;
17600
16533
  let debounceTimer = null;
17601
16534
  let observer = null;
@@ -17606,25 +16539,10 @@ function OhhwellsBridge() {
17606
16539
  initSectionInstancesFromContent(content, window.location.pathname);
17607
16540
  observer?.disconnect();
17608
16541
  try {
17609
- applyBrandChrome(content);
17610
- if (typeof content[BRAND_KIT_KEY] === "string") {
17611
- applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
17612
- }
17613
- if (typeof content[AI_SECTIONS_KEY] === "string") {
17614
- applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
17615
- }
17616
- if (typeof content[STYLE_STORE_KEY] === "string") {
17617
- applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
17618
- }
17619
16542
  for (const [key, val] of Object.entries(content)) {
17620
16543
  if (key === "__ohw_sections") continue;
17621
- if (key === AI_SECTIONS_KEY) continue;
17622
16544
  if (key === LOGO_PLACEHOLDER_KEY) continue;
17623
16545
  if (LOGO_IMAGE_KEYS.includes(key)) continue;
17624
- if (key === BRAND_KIT_KEY) continue;
17625
- if (key === STYLE_STORE_KEY) continue;
17626
- if (key === STYLE_STORE_KEY) continue;
17627
- if (BRAND_CHROME_KEYS.has(key)) continue;
17628
16546
  if (applyVideoSettingNode(key, val)) continue;
17629
16547
  if (applyCarouselNode(key, val)) continue;
17630
16548
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -17670,17 +16588,6 @@ function OhhwellsBridge() {
17670
16588
  debounceTimer = setTimeout(applyFromCache, 150);
17671
16589
  };
17672
16590
  applyFromCache();
17673
- const pathCacheKey = `${subdomain}::${pathname}`;
17674
- if (!fetchedContentPaths.has(pathCacheKey)) {
17675
- fetchedContentPaths.add(pathCacheKey);
17676
- const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
17677
- fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(pathname)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
17678
- if (!data?.content) return;
17679
- contentCache.set(subdomain, data.content);
17680
- applyFromCache();
17681
- }).catch(() => {
17682
- });
17683
- }
17684
16591
  observer = new MutationObserver(scheduleApply);
17685
16592
  observer.observe(document.body, { childList: true, subtree: true });
17686
16593
  return () => {
@@ -17694,10 +16601,10 @@ function OhhwellsBridge() {
17694
16601
  const visible = Boolean(subdomain) && fetchState !== "done";
17695
16602
  el.style.display = visible ? "flex" : "none";
17696
16603
  }, [subdomain, fetchState]);
17697
- useEffect13(() => {
16604
+ useEffect12(() => {
17698
16605
  postToParent2({ type: "ow:navigation", path: pathname });
17699
16606
  }, [pathname, postToParent2]);
17700
- useEffect13(() => {
16607
+ useEffect12(() => {
17701
16608
  if (!isEditMode) return;
17702
16609
  if (linkPopoverSessionRef.current?.intent === "add-nav") return;
17703
16610
  if (document.querySelector("[data-ohw-section-picker]")) return;
@@ -17705,7 +16612,7 @@ function OhhwellsBridge() {
17705
16612
  deselectRef.current();
17706
16613
  deactivateRef.current();
17707
16614
  }, [pathname, isEditMode]);
17708
- useEffect13(() => {
16615
+ useEffect12(() => {
17709
16616
  const contentForNav = () => {
17710
16617
  if (isEditMode) return editContentRef.current;
17711
16618
  if (!subdomain) return {};
@@ -17774,36 +16681,31 @@ function OhhwellsBridge() {
17774
16681
  observer?.disconnect();
17775
16682
  };
17776
16683
  }, [isEditMode, pathname, subdomain, fetchState, resyncSelectedNavigationItem]);
17777
- useEffect13(() => {
16684
+ useEffect12(() => {
17778
16685
  if (!isEditMode) return;
17779
- let lastPosted = 0;
17780
16686
  const measure = () => {
17781
16687
  const h = document.body.scrollHeight;
17782
- if (h > 50 && Math.abs(h - lastPosted) > 1) {
17783
- lastPosted = h;
17784
- postToParent2({ type: "ow:height", height: h });
17785
- }
17786
- };
17787
- let raf = null;
17788
- const schedule = () => {
17789
- if (raf != null) return;
17790
- raf = requestAnimationFrame(() => {
17791
- raf = null;
17792
- measure();
17793
- });
16688
+ if (h > 50) postToParent2({ type: "ow:height", height: h });
17794
16689
  };
17795
16690
  const t1 = setTimeout(measure, 50);
17796
16691
  const t2 = setTimeout(measure, 500);
17797
- const ro = new ResizeObserver(schedule);
17798
- ro.observe(document.body);
16692
+ let lastWidth = window.innerWidth;
16693
+ let resizeTimer = null;
16694
+ const handleResize = () => {
16695
+ if (window.innerWidth === lastWidth) return;
16696
+ lastWidth = window.innerWidth;
16697
+ if (resizeTimer) clearTimeout(resizeTimer);
16698
+ resizeTimer = setTimeout(measure, 150);
16699
+ };
16700
+ window.addEventListener("resize", handleResize);
17799
16701
  return () => {
17800
16702
  clearTimeout(t1);
17801
16703
  clearTimeout(t2);
17802
- if (raf != null) cancelAnimationFrame(raf);
17803
- ro.disconnect();
16704
+ if (resizeTimer) clearTimeout(resizeTimer);
16705
+ window.removeEventListener("resize", handleResize);
17804
16706
  };
17805
16707
  }, [pathname, isEditMode, postToParent2]);
17806
- useEffect13(() => {
16708
+ useEffect12(() => {
17807
16709
  if (!subdomainFromQuery || isEditMode) return;
17808
16710
  const handleClick = (e) => {
17809
16711
  const anchor = e.target.closest("a");
@@ -17819,7 +16721,7 @@ function OhhwellsBridge() {
17819
16721
  document.addEventListener("click", handleClick, true);
17820
16722
  return () => document.removeEventListener("click", handleClick, true);
17821
16723
  }, [subdomainFromQuery, isEditMode, router]);
17822
- useEffect13(() => {
16724
+ useEffect12(() => {
17823
16725
  if (!isEditMode) {
17824
16726
  editStylesRef.current?.base.remove();
17825
16727
  editStylesRef.current?.forceHover.remove();
@@ -18041,7 +16943,6 @@ function OhhwellsBridge() {
18041
16943
  return;
18042
16944
  }
18043
16945
  const target = e.target;
18044
- if (target.closest("[data-ohw-ai-review]")) return;
18045
16946
  if (target.closest("[data-ohw-toolbar]")) return;
18046
16947
  if (target.closest("[data-ohw-state-toggle]")) return;
18047
16948
  if (target.closest("[data-ohw-max-badge]")) return;
@@ -18053,9 +16954,6 @@ function OhhwellsBridge() {
18053
16954
  )) {
18054
16955
  return;
18055
16956
  }
18056
- if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
18057
- clearMediaSelectionRef.current();
18058
- }
18059
16957
  {
18060
16958
  const formEl = getFormElement(target);
18061
16959
  const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
@@ -18210,11 +17108,8 @@ function OhhwellsBridge() {
18210
17108
  if (isMediaEditable(editable) && !buttonOnMedia) {
18211
17109
  e.preventDefault();
18212
17110
  e.stopPropagation();
18213
- if (selectedMediaElRef.current === editable) {
18214
- postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "", elementType: editable.dataset.ohwEditable ?? "image" });
18215
- } else {
18216
- selectMediaElementRef.current(editable);
18217
- }
17111
+ aiSectionApiRef.current?.selectFromElement(editable);
17112
+ postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "", elementType: editable.dataset.ohwEditable ?? "image" });
18218
17113
  return;
18219
17114
  }
18220
17115
  const socialItem = getSocialItem(editable);
@@ -18350,7 +17245,6 @@ function OhhwellsBridge() {
18350
17245
  };
18351
17246
  const handleDblClick = (e) => {
18352
17247
  const target = e.target;
18353
- if (target.closest("[data-ohw-ai-review]")) return;
18354
17248
  if (target.closest("[data-ohw-toolbar]")) return;
18355
17249
  if (target.closest("[data-ohw-state-toggle]")) return;
18356
17250
  if (target.closest("[data-ohw-max-badge]")) return;
@@ -18402,9 +17296,6 @@ function OhhwellsBridge() {
18402
17296
  setHoveredItemRect(null);
18403
17297
  hoveredNavContainerRef.current = null;
18404
17298
  setHoveredNavContainerRect(null);
18405
- siblingHintElRef.current = null;
18406
- setSiblingHintRect(null);
18407
- setSiblingHintRects([]);
18408
17299
  return;
18409
17300
  }
18410
17301
  {
@@ -18523,6 +17414,7 @@ function OhhwellsBridge() {
18523
17414
  hoveredNavContainerRef.current = null;
18524
17415
  setHoveredNavContainerRect(null);
18525
17416
  hoveredItemElRef.current = editable;
17417
+ setHoveredItemRect(editable.getBoundingClientRect());
18526
17418
  }
18527
17419
  }
18528
17420
  }
@@ -18819,7 +17711,7 @@ function OhhwellsBridge() {
18819
17711
  }
18820
17712
  };
18821
17713
  const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
18822
- if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
17714
+ if (linkPopoverOpenRef.current) {
18823
17715
  if (hoveredImageRef.current) {
18824
17716
  hoveredImageRef.current = null;
18825
17717
  hoveredImageHasTextOverlapRef.current = false;
@@ -19153,9 +18045,7 @@ function OhhwellsBridge() {
19153
18045
  return;
19154
18046
  }
19155
18047
  const { y } = toProbeCoords(clientX, clientY, fromParentViewport);
19156
- const sections = Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
19157
- (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
19158
- ).sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);
18048
+ const sections = Array.from(document.querySelectorAll("[data-ohw-section]")).sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);
19159
18049
  const ZONE = 20;
19160
18050
  for (let i = 0; i < sections.length; i++) {
19161
18051
  const a = sections[i];
@@ -19184,7 +18074,8 @@ function OhhwellsBridge() {
19184
18074
  };
19185
18075
  const handleMouseMove = (e) => {
19186
18076
  const { clientX, clientY } = e;
19187
- if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY) || isOverEditorChrome(clientX, clientY)) {
18077
+ if (pointOwnedByFloatingPanel(clientX, clientY)) return;
18078
+ if (isOverEditorChrome(clientX, clientY)) {
19188
18079
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
19189
18080
  formHoverElRef.current = null;
19190
18081
  setFormHoverRect(null);
@@ -19192,12 +18083,6 @@ function OhhwellsBridge() {
19192
18083
  setHoveredItemRect(null);
19193
18084
  hoveredNavContainerRef.current = null;
19194
18085
  setHoveredNavContainerRect(null);
19195
- siblingHintElRef.current = null;
19196
- setSiblingHintRect(null);
19197
- setSiblingHintRects([]);
19198
- dismissImageHover();
19199
- clearImageHover();
19200
- setSectionGap(null);
19201
18086
  return;
19202
18087
  }
19203
18088
  if (probeSocialsRowAt(clientX, clientY)) return;
@@ -19209,11 +18094,7 @@ function OhhwellsBridge() {
19209
18094
  if (e.data?.type !== "ow:pointer-sync") return;
19210
18095
  const { clientX, clientY } = e.data;
19211
18096
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
19212
- if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
19213
- dismissImageHover();
19214
- clearImageHover();
19215
- return;
19216
- }
18097
+ if (pointOwnedByFloatingPanel(clientX, clientY)) return;
19217
18098
  if (probeSocialsRowAt(clientX, clientY)) return;
19218
18099
  probeSectionGapAt(clientX, clientY);
19219
18100
  probeImageAt(clientX, clientY);
@@ -19496,19 +18377,10 @@ function OhhwellsBridge() {
19496
18377
  if (e.data?.type !== "ow:hydrate") return;
19497
18378
  const content = e.data.content;
19498
18379
  if (!content) return;
19499
- if (typeof content[BRAND_KIT_KEY] === "string") {
19500
- brandKitRef.current = content[BRAND_KIT_KEY];
19501
- applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
19502
- }
19503
18380
  if (typeof content[AI_SECTIONS_KEY] === "string") {
19504
18381
  aiSectionsRef.current = content[AI_SECTIONS_KEY];
19505
18382
  applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
19506
18383
  }
19507
- if (typeof content[STYLE_STORE_KEY] === "string") {
19508
- stylesRef.current = content[STYLE_STORE_KEY];
19509
- applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
19510
- }
19511
- applyBrandChrome(content);
19512
18384
  let sectionsJson = null;
19513
18385
  for (const [key, val] of Object.entries(content)) {
19514
18386
  if (key === "__ohw_sections") {
@@ -19518,9 +18390,6 @@ function OhhwellsBridge() {
19518
18390
  if (key === AI_SECTIONS_KEY) continue;
19519
18391
  if (key === LOGO_PLACEHOLDER_KEY) continue;
19520
18392
  if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
19521
- if (key === BRAND_KIT_KEY) continue;
19522
- if (key === STYLE_STORE_KEY) continue;
19523
- if (BRAND_CHROME_KEYS.has(key)) continue;
19524
18393
  if (applyVideoSettingNode(key, val)) continue;
19525
18394
  if (applyCarouselNode(key, val)) continue;
19526
18395
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -19534,8 +18403,6 @@ function OhhwellsBridge() {
19534
18403
  if (video && video.src !== val) applyVideoSrc(video, val);
19535
18404
  } else if (el.dataset.ohwEditable === "link") {
19536
18405
  applyLinkHref(el, val);
19537
- } else if (el.dataset.ohwEditable === "icon") {
19538
- applyIconMarkup(el, val);
19539
18406
  } else if (isIconMarkupValue(val)) {
19540
18407
  } else {
19541
18408
  el.innerHTML = val;
@@ -19620,21 +18487,12 @@ function OhhwellsBridge() {
19620
18487
  nodes: collectEditableNodes(editContentRef.current)
19621
18488
  });
19622
18489
  };
19623
- const clearInteractionChrome = () => {
19624
- deactivateRef.current();
19625
- deselectRef.current();
19626
- clearMediaSelectionRef.current();
19627
- };
19628
18490
  const handleAiApplyTree = (e) => {
19629
18491
  if (e.data?.type !== "ow:ai-apply-tree") return;
19630
18492
  const payload = e.data.payload;
19631
18493
  if (!payload || typeof payload.id !== "string" || !isRenderableTree(payload.tree)) return;
19632
- clearInteractionChrome();
19633
18494
  const previous = aiSectionsRef.current;
19634
- const nextState = applyTreeToState(parseAiSectionsState(previous), {
19635
- ...payload,
19636
- path: payload.path ?? window.location.pathname
19637
- });
18495
+ const nextState = applyTreeToState(parseAiSectionsState(previous), payload);
19638
18496
  const nextValue = serializeAiSectionsState(nextState);
19639
18497
  aiSectionsRef.current = nextValue;
19640
18498
  applyAiSectionsToDom(nextState);
@@ -19654,7 +18512,6 @@ function OhhwellsBridge() {
19654
18512
  if (!sectionId || sectionId === "navbar" || sectionId === "footer") return;
19655
18513
  const exists = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
19656
18514
  if (!exists) return;
19657
- clearInteractionChrome();
19658
18515
  const previous = aiSectionsRef.current;
19659
18516
  const nextState = deleteSectionFromState(parseAiSectionsState(previous), sectionId);
19660
18517
  const nextValue = serializeAiSectionsState(nextState);
@@ -19670,58 +18527,14 @@ function OhhwellsBridge() {
19670
18527
  const handleAiSetSections = (e) => {
19671
18528
  if (e.data?.type !== "ow:ai-set-sections") return;
19672
18529
  const value = typeof e.data.value === "string" ? e.data.value : "";
19673
- clearInteractionChrome();
19674
18530
  aiSectionsRef.current = value;
19675
18531
  applyAiSectionsToDom(parseAiSectionsState(value));
19676
- applyStylesToDom(parseStyleStore(stylesRef.current));
19677
18532
  const restoredHeight = document.documentElement.scrollHeight;
19678
18533
  if (restoredHeight > 50) postToParentRef.current({ type: "ow:height", height: restoredHeight });
19679
18534
  postToParentRef.current({ type: "ow:change", nodes: [{ key: AI_SECTIONS_KEY, text: value }] });
19680
18535
  postAiSectionsChanged();
19681
18536
  };
19682
18537
  window.addEventListener("message", handleAiSetSections);
19683
- const handleMoveSection = (e) => {
19684
- if (e.data?.type !== "ow:move-section") return;
19685
- const instanceId = typeof e.data.instanceId === "string" ? e.data.instanceId : "";
19686
- const direction = e.data.direction === "up" || e.data.direction === "down" ? e.data.direction : null;
19687
- if (!instanceId || !direction) return;
19688
- const entries = moveSectionInstance(instanceId, direction, window.location.pathname);
19689
- if (!entries) return;
19690
- const orderJson = JSON.stringify(entries);
19691
- editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
19692
- postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
19693
- window.dispatchEvent(new Event("resize"));
19694
- };
19695
- window.addEventListener("message", handleMoveSection);
19696
- const handleAiSetBrand = (e) => {
19697
- if (e.data?.type !== "ow:ai-set-brand") return;
19698
- const value = typeof e.data.value === "string" ? e.data.value : "";
19699
- const previous = brandKitRef.current;
19700
- brandKitRef.current = value;
19701
- applyBrandToDom(parseBrandKit(value));
19702
- if (aiSectionsRef.current) applyAiSectionsToDom(parseAiSectionsState(aiSectionsRef.current));
19703
- applyStylesToDom(parseStyleStore(stylesRef.current));
19704
- postToParentRef.current({ type: "ow:change", nodes: [{ key: BRAND_KIT_KEY, text: value }] });
19705
- postToParentRef.current({ type: "ow:ai-brand-applied", previous, value });
19706
- };
19707
- window.addEventListener("message", handleAiSetBrand);
19708
- const handleAiSetStyles = (e) => {
19709
- if (e.data?.type !== "ow:ai-set-styles") return;
19710
- const value = typeof e.data.value === "string" ? e.data.value : "";
19711
- const previous = stylesRef.current;
19712
- stylesRef.current = value;
19713
- applyStylesToDom(parseStyleStore(value));
19714
- postToParentRef.current({ type: "ow:change", nodes: [{ key: STYLE_STORE_KEY, text: value }] });
19715
- postToParentRef.current({ type: "ow:ai-styles-applied", previous, value });
19716
- };
19717
- window.addEventListener("message", handleAiSetStyles);
19718
- const handleGetBrand = (e) => {
19719
- if (e.data?.type !== "ow:get-brand") return;
19720
- const template = deriveTemplateBrand();
19721
- const value = brandKitRef.current || (template ? JSON.stringify(template) : "");
19722
- postToParentRef.current({ type: "ow:brand-value", value });
19723
- };
19724
- window.addEventListener("message", handleGetBrand);
19725
18538
  const handlePanelDragging = (e) => {
19726
18539
  if (e.data?.type !== "ow:panel-dragging") return;
19727
18540
  if (e.data.dragging) document.documentElement.setAttribute("data-ohw-panel-dragging", "");
@@ -19737,15 +18550,8 @@ function OhhwellsBridge() {
19737
18550
  closeLinkPopoverRef.current();
19738
18551
  return;
19739
18552
  }
19740
- if (floatingPanelOpenRef.current) {
19741
- setFloatingPanelRef.current(null);
19742
- deselectRef.current();
19743
- deactivateRef.current();
19744
- return;
19745
- }
19746
18553
  deselectRef.current();
19747
18554
  deactivateRef.current();
19748
- clearMediaSelectionRef.current();
19749
18555
  };
19750
18556
  window.addEventListener("message", handleDeactivate);
19751
18557
  const handleToastAction = (e) => {
@@ -19831,10 +18637,6 @@ function OhhwellsBridge() {
19831
18637
  const handleKeyDown = (e) => {
19832
18638
  if (e.key === "Escape" && document.querySelector("[data-ohw-section-picker]")) return;
19833
18639
  if (e.key === "Escape" && document.querySelector("[data-ohw-more-menu]")) return;
19834
- if (e.key === "Escape" && selectedMediaElRef.current) {
19835
- clearMediaSelectionRef.current();
19836
- return;
19837
- }
19838
18640
  if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "a" && activeElRef.current) {
19839
18641
  e.preventDefault();
19840
18642
  selectAllTextInEditable(activeElRef.current);
@@ -19994,12 +18796,6 @@ function OhhwellsBridge() {
19994
18796
  if (aiSectionsRef.current) {
19995
18797
  nodes.push({ key: AI_SECTIONS_KEY, type: "sections", text: aiSectionsRef.current });
19996
18798
  }
19997
- if (stylesRef.current) {
19998
- nodes.push({ key: STYLE_STORE_KEY, type: "styles", text: stylesRef.current });
19999
- }
20000
- if (brandKitRef.current) {
20001
- nodes.push({ key: BRAND_KIT_KEY, type: "brand", text: brandKitRef.current });
20002
- }
20003
18799
  document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
20004
18800
  const formKey = formKeyOf(form);
20005
18801
  if (!formKey) return;
@@ -20017,12 +18813,8 @@ function OhhwellsBridge() {
20017
18813
  if (inserted) {
20018
18814
  const tracker = getSectionsTracker();
20019
18815
  postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
20020
- const reportHeight = () => {
20021
- const h = document.body.scrollHeight;
20022
- if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
20023
- };
20024
- reportHeight();
20025
- setTimeout(reportHeight, 500);
18816
+ const h = document.documentElement.scrollHeight;
18817
+ if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
20026
18818
  }
20027
18819
  };
20028
18820
  const handleSwitchSchedule = (e) => {
@@ -20409,23 +19201,19 @@ function OhhwellsBridge() {
20409
19201
  window.removeEventListener("message", handleAiApplyTree);
20410
19202
  window.removeEventListener("message", handleAiDeleteSection);
20411
19203
  window.removeEventListener("message", handleAiSetSections);
20412
- window.removeEventListener("message", handleMoveSection);
20413
- window.removeEventListener("message", handleAiSetBrand);
20414
- window.removeEventListener("message", handleAiSetStyles);
20415
- window.removeEventListener("message", handleGetBrand);
20416
19204
  window.removeEventListener("message", handlePanelDragging);
20417
19205
  window.removeEventListener("message", handleDeactivate);
19206
+ document.documentElement.removeAttribute("data-ohw-panel-dragging");
20418
19207
  window.removeEventListener("message", handleToastAction);
20419
19208
  window.removeEventListener("message", handleFormCount);
20420
19209
  window.removeEventListener("message", handleUiEscape);
20421
- document.documentElement.removeAttribute("data-ohw-panel-dragging");
20422
19210
  autoSaveTimers.current.forEach(clearTimeout);
20423
19211
  autoSaveTimers.current.clear();
20424
19212
  if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
20425
19213
  if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
20426
19214
  };
20427
19215
  }, [isEditMode, refreshStateRules]);
20428
- useEffect13(() => {
19216
+ useEffect12(() => {
20429
19217
  if (!isEditMode) return;
20430
19218
  const THRESHOLD = 10;
20431
19219
  const resolveWasSelected = (el) => {
@@ -20581,7 +19369,7 @@ function OhhwellsBridge() {
20581
19369
  unlockFooterDragInteraction();
20582
19370
  };
20583
19371
  }, [isEditMode]);
20584
- useEffect13(() => {
19372
+ useEffect12(() => {
20585
19373
  const handler = (e) => {
20586
19374
  if (e.data?.type !== "ow:request-schedule-config") return;
20587
19375
  const insertAfterVal = e.data.insertAfter;
@@ -20597,7 +19385,7 @@ function OhhwellsBridge() {
20597
19385
  window.addEventListener("message", handler);
20598
19386
  return () => window.removeEventListener("message", handler);
20599
19387
  }, [processConfigRequest]);
20600
- useEffect13(() => {
19388
+ useEffect12(() => {
20601
19389
  if (!isEditMode) return;
20602
19390
  document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
20603
19391
  el.removeAttribute("data-ohw-active-state");
@@ -20621,7 +19409,7 @@ function OhhwellsBridge() {
20621
19409
  postToParent2({
20622
19410
  type: "ow:ready",
20623
19411
  version: "1",
20624
- bridgeVersion: "0.1.70",
19412
+ bridgeVersion: "0.1.69",
20625
19413
  path: pathname,
20626
19414
  nodes: collectEditableNodes(editContentRef.current),
20627
19415
  sections
@@ -20633,13 +19421,13 @@ function OhhwellsBridge() {
20633
19421
  clearTimeout(timer);
20634
19422
  };
20635
19423
  }, [pathname, isEditMode, refreshStateRules, postToParent2]);
20636
- useEffect13(() => {
19424
+ useEffect12(() => {
20637
19425
  scrollToHashSectionWhenReady();
20638
19426
  const onHashChange = () => scrollToHashSectionWhenReady();
20639
19427
  window.addEventListener("hashchange", onHashChange);
20640
19428
  return () => window.removeEventListener("hashchange", onHashChange);
20641
19429
  }, [pathname]);
20642
- const handleCommand = useCallback8((cmd) => {
19430
+ const handleCommand = useCallback7((cmd) => {
20643
19431
  const el = activeElRef.current;
20644
19432
  const selBefore = window.getSelection();
20645
19433
  let savedOffsets = null;
@@ -20675,7 +19463,7 @@ function OhhwellsBridge() {
20675
19463
  if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
20676
19464
  refreshActiveCommandsRef.current();
20677
19465
  }, []);
20678
- useEffect13(() => {
19466
+ useEffect12(() => {
20679
19467
  const session = floatingPanel && floatingPanel.kind === "logo-size" ? floatingPanel : null;
20680
19468
  if (!session || !logoSizeDraft) return;
20681
19469
  const onPanelAction = (e) => {
@@ -20713,7 +19501,7 @@ function OhhwellsBridge() {
20713
19501
  window.addEventListener("message", onPanelAction);
20714
19502
  return () => window.removeEventListener("message", onPanelAction);
20715
19503
  }, [floatingPanel, logoSizeDraft, editorViewport, persistLogoSizeDraft, closeFloatingPanelAndDeselect]);
20716
- const handleStateChange = useCallback8((state) => {
19504
+ const handleStateChange = useCallback7((state) => {
20717
19505
  if (!activeStateElRef.current) return;
20718
19506
  const el = activeStateElRef.current;
20719
19507
  if (state === "Default") {
@@ -20726,7 +19514,7 @@ function OhhwellsBridge() {
20726
19514
  }
20727
19515
  setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
20728
19516
  }, [deactivate]);
20729
- const reselectAfterLinkPopover = useCallback8(
19517
+ const reselectAfterLinkPopover = useCallback7(
20730
19518
  (hrefKey) => {
20731
19519
  requestAnimationFrame(() => {
20732
19520
  const el = resolveHrefKeyElement(hrefKey);
@@ -20735,7 +19523,7 @@ function OhhwellsBridge() {
20735
19523
  },
20736
19524
  [resolveHrefKeyElement]
20737
19525
  );
20738
- const closeLinkPopover = useCallback8(() => {
19526
+ const closeLinkPopover = useCallback7(() => {
20739
19527
  const session = linkPopoverSessionRef.current;
20740
19528
  addNavAfterAnchorRef.current = null;
20741
19529
  setLinkPopover(null);
@@ -20743,9 +19531,9 @@ function OhhwellsBridge() {
20743
19531
  reselectAfterLinkPopover(session.key);
20744
19532
  }
20745
19533
  }, [reselectAfterLinkPopover]);
20746
- const closeLinkPopoverRef = useRef10(closeLinkPopover);
19534
+ const closeLinkPopoverRef = useRef9(closeLinkPopover);
20747
19535
  closeLinkPopoverRef.current = closeLinkPopover;
20748
- const openLinkPopoverForActive = useCallback8(() => {
19536
+ const openLinkPopoverForActive = useCallback7(() => {
20749
19537
  const hrefCtx = getHrefKeyFromElement(activeElRef.current);
20750
19538
  if (!hrefCtx) return;
20751
19539
  bumpLinkPopoverGrace();
@@ -20756,7 +19544,7 @@ function OhhwellsBridge() {
20756
19544
  });
20757
19545
  deactivate();
20758
19546
  }, [deactivate]);
20759
- const openLinkPopoverForSelected = useCallback8(() => {
19547
+ const openLinkPopoverForSelected = useCallback7(() => {
20760
19548
  const anchor = selectedElRef.current;
20761
19549
  if (!anchor) return;
20762
19550
  const key = anchor.getAttribute("data-ohw-href-key");
@@ -20773,7 +19561,7 @@ function OhhwellsBridge() {
20773
19561
  });
20774
19562
  deselect();
20775
19563
  }, [deselect]);
20776
- const handleSelectParent = useCallback8(() => {
19564
+ const handleSelectParent = useCallback7(() => {
20777
19565
  const selected = selectedElRef.current;
20778
19566
  if (!selected) return;
20779
19567
  if (toolbarVariantRef.current === "select-frame") {
@@ -20800,7 +19588,7 @@ function OhhwellsBridge() {
20800
19588
  }
20801
19589
  deselectRef.current();
20802
19590
  }, []);
20803
- const handleDuplicateSelected = useCallback8(() => {
19591
+ const handleDuplicateSelected = useCallback7(() => {
20804
19592
  const selected = selectedElRef.current;
20805
19593
  if (!selected || !isNavigationItem2(selected)) return;
20806
19594
  const hrefKey = selected.getAttribute("data-ohw-href-key");
@@ -20933,7 +19721,7 @@ function OhhwellsBridge() {
20933
19721
  });
20934
19722
  }
20935
19723
  }, [postToParent2]);
20936
- const runPendingDeleteUndo = useCallback8(() => {
19724
+ const runPendingDeleteUndo = useCallback7(() => {
20937
19725
  const pending = pendingDeleteUndoRef.current;
20938
19726
  if (!pending) return false;
20939
19727
  pendingDeleteUndoRef.current = null;
@@ -20941,7 +19729,7 @@ function OhhwellsBridge() {
20941
19729
  enforceLinkHrefs();
20942
19730
  return true;
20943
19731
  }, []);
20944
- const handleDeleteSelected = useCallback8(() => {
19732
+ const handleDeleteSelected = useCallback7(() => {
20945
19733
  const selected = selectedElRef.current;
20946
19734
  if (!selected) return false;
20947
19735
  return deleteSelectedNavFooterItem({
@@ -20962,7 +19750,7 @@ function OhhwellsBridge() {
20962
19750
  }, [postToParent2]);
20963
19751
  handleDeleteSelectedRef.current = handleDeleteSelected;
20964
19752
  runPendingDeleteUndoRef.current = runPendingDeleteUndo;
20965
- const handleLinkPopoverSubmit = useCallback8(
19753
+ const handleLinkPopoverSubmit = useCallback7(
20966
19754
  (target) => {
20967
19755
  const session = linkPopoverSessionRef.current;
20968
19756
  if (!session) return;
@@ -21028,30 +19816,19 @@ function OhhwellsBridge() {
21028
19816
  const showEditLink = toolbarShowEditLink;
21029
19817
  const currentSections = sectionsByPath[pathname] ?? [];
21030
19818
  linkPopoverOpenRef.current = linkPopover !== null;
21031
- const handleMediaSelect = useCallback8((key) => {
21032
- const el = hoveredImageRef.current?.dataset.ohwKey === key ? hoveredImageRef.current : Array.from(document.querySelectorAll(MEDIA_SELECTOR)).find(
21033
- (m) => (m.dataset.ohwKey ?? "") === key
21034
- ) ?? null;
21035
- if (!el) return;
21036
- selectMediaElementRef.current(el);
21037
- }, []);
21038
- const handleMediaReplace = useCallback8(
19819
+ const handleMediaReplace = useCallback7(
21039
19820
  (key) => {
21040
- postToParent2({
21041
- type: "ow:image-pick",
21042
- key,
21043
- elementType: mediaHover?.elementType ?? selectedMedia?.elementType ?? "image"
21044
- });
19821
+ postToParent2({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
21045
19822
  },
21046
- [postToParent2, mediaHover?.elementType, selectedMedia?.elementType]
19823
+ [postToParent2, mediaHover?.elementType]
21047
19824
  );
21048
- const handleEditCarousel = useCallback8(
19825
+ const handleEditCarousel = useCallback7(
21049
19826
  (key) => {
21050
19827
  postToParent2({ type: "ow:carousel-open", key, images: readCarouselValue(key) });
21051
19828
  },
21052
19829
  [postToParent2]
21053
19830
  );
21054
- const handleMediaFadeOutComplete = useCallback8((key) => {
19831
+ const handleMediaFadeOutComplete = useCallback7((key) => {
21055
19832
  setUploadingRects((prev) => {
21056
19833
  if (!(key in prev)) return prev;
21057
19834
  const next = { ...prev };
@@ -21059,7 +19836,7 @@ function OhhwellsBridge() {
21059
19836
  return next;
21060
19837
  });
21061
19838
  }, []);
21062
- const handleVideoSettingsChange = useCallback8(
19839
+ const handleVideoSettingsChange = useCallback7(
21063
19840
  (key, settings) => {
21064
19841
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
21065
19842
  const video = getVideoEl2(el);
@@ -21081,516 +19858,430 @@ function OhhwellsBridge() {
21081
19858
  },
21082
19859
  [postToParent2]
21083
19860
  );
21084
- return /* @__PURE__ */ jsxs20(Fragment8, { children: [
21085
- /* @__PURE__ */ jsx33("div", { id: "ohw-loader", suppressHydrationWarning: true, style: { ...OHW_LOADER_STYLE, display: "none" }, children: /* @__PURE__ */ jsx33(OhwLoaderSpinner, {}) }),
21086
- /* @__PURE__ */ jsx33("script", { suppressHydrationWarning: true, dangerouslySetInnerHTML: { __html: OHW_LOADER_PREHYDRATE_SCRIPT } }),
21087
- bridgeRoot ? createPortal2(
21088
- /* @__PURE__ */ jsxs20(Fragment8, { children: [
21089
- /* @__PURE__ */ jsx33("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
21090
- isEditMode && /* @__PURE__ */ jsx33(AiSectionOverlay, { postToParent: postToParent2, apiRef: aiSectionApiRef }),
21091
- isSectionDragging && sectionDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
21092
- "div",
21093
- {
21094
- className: "pointer-events-none fixed z-2147483646",
21095
- style: { left: slot.left, top: slot.y, width: slot.width, height: 3, transform: "translateY(-50%)" },
21096
- children: /* @__PURE__ */ jsx33(
21097
- DropIndicator,
21098
- {
21099
- direction: "horizontal",
21100
- state: activeSectionDropIndex === i ? "dragActive" : "dragIdle",
21101
- className: "!h-full !w-full"
21102
- }
21103
- )
21104
- },
21105
- `section-drop-${slot.insertIndex}-${i}`
21106
- )),
21107
- Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ jsx33(
21108
- MediaOverlay,
21109
- {
21110
- hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
21111
- isUploading: true,
21112
- fadingOut,
21113
- onFadeOutComplete: handleMediaFadeOutComplete,
21114
- onReplace: handleMediaReplace
21115
- },
21116
- `uploading-${key}`
21117
- )),
21118
- mediaHover && !(mediaHover.key in uploadingRects) && mediaHover.key !== selectedMedia?.key && /* @__PURE__ */ jsx33(
21119
- MediaOverlay,
21120
- {
21121
- hover: mediaHover,
21122
- isUploading: false,
21123
- onReplace: handleMediaReplace,
21124
- onSelect: handleMediaSelect,
21125
- onVideoSettingsChange: handleVideoSettingsChange
21126
- }
21127
- ),
21128
- selectedMedia && !(selectedMedia.key in uploadingRects) && !linkPopover && /* @__PURE__ */ jsx33(
21129
- MediaOverlay,
21130
- {
21131
- hover: selectedMedia,
21132
- selected: true,
21133
- hovered: mediaHover?.key === selectedMedia.key,
21134
- isUploading: false,
21135
- onReplace: handleMediaReplace,
21136
- onSelect: handleMediaSelect,
21137
- onVideoSettingsChange: handleVideoSettingsChange
21138
- }
21139
- ),
21140
- carouselHover && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx33(CarouselOverlay, { hover: carouselHover, onEdit: handleEditCarousel }),
21141
- siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
21142
- siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
21143
- isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
21144
- isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
21145
- "div",
21146
- {
21147
- className: "pointer-events-none fixed z-2147483646",
21148
- style: {
21149
- left: slot.left,
21150
- top: slot.top,
21151
- width: slot.width,
21152
- height: slot.height
21153
- },
21154
- children: /* @__PURE__ */ jsx33(
21155
- DropIndicator,
21156
- {
21157
- direction: slot.direction,
21158
- state: activeFooterDropIndex === i ? "dragActive" : "dragIdle",
21159
- className: "!h-full !w-full"
21160
- }
21161
- )
21162
- },
21163
- `footer-drop-${slot.direction}-${slot.columnIndex}-${slot.insertIndex}-${i}`
21164
- )),
21165
- isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
21166
- "div",
21167
- {
21168
- className: "pointer-events-none fixed z-2147483646",
21169
- style: {
21170
- left: slot.left,
21171
- top: slot.top,
21172
- width: slot.width,
21173
- height: slot.height
21174
- },
21175
- children: /* @__PURE__ */ jsx33(
21176
- DropIndicator,
21177
- {
21178
- direction: slot.direction,
21179
- state: activeNavDropIndex === i ? "dragActive" : "dragIdle",
21180
- className: "!h-full !w-full"
21181
- }
21182
- )
19861
+ return bridgeRoot ? createPortal2(
19862
+ /* @__PURE__ */ jsxs20(Fragment8, { children: [
19863
+ /* @__PURE__ */ jsx33("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
19864
+ isEditMode && /* @__PURE__ */ jsx33(AiSectionOverlay, { postToParent: postToParent2, apiRef: aiSectionApiRef }),
19865
+ Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ jsx33(
19866
+ MediaOverlay,
19867
+ {
19868
+ hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
19869
+ isUploading: true,
19870
+ fadingOut,
19871
+ onFadeOutComplete: handleMediaFadeOutComplete,
19872
+ onReplace: handleMediaReplace
19873
+ },
19874
+ `uploading-${key}`
19875
+ )),
19876
+ mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ jsx33(
19877
+ MediaOverlay,
19878
+ {
19879
+ hover: mediaHover,
19880
+ isUploading: false,
19881
+ onReplace: handleMediaReplace,
19882
+ onVideoSettingsChange: handleVideoSettingsChange
19883
+ }
19884
+ ),
19885
+ carouselHover && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx33(CarouselOverlay, { hover: carouselHover, onEdit: handleEditCarousel }),
19886
+ siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
19887
+ siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
19888
+ isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
19889
+ isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
19890
+ "div",
19891
+ {
19892
+ className: "pointer-events-none fixed z-2147483646",
19893
+ style: {
19894
+ left: slot.left,
19895
+ top: slot.top,
19896
+ width: slot.width,
19897
+ height: slot.height
21183
19898
  },
21184
- `nav-drop-${slot.direction}-${slot.parentId ?? "root"}-${slot.insertIndex}-${i}`
21185
- )),
21186
- hoveredNavContainerRect && (toolbarVariant !== "select-frame" || isFooterFrameSelection) && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
21187
- hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
21188
- hoveredTextRect && !hoveredNavContainerRect && !hoveredItemRect && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredTextRect, state: "hover" }),
21189
- formPickRect && !isItemDragging && /* @__PURE__ */ jsx33(
21190
- ItemInteractionLayer,
21191
- {
21192
- rect: formPickRect,
21193
- state: "active-top",
21194
- itemDragSurface: false,
21195
- toolbarAlign: "left",
21196
- chromeGap: 24,
21197
- toolbar: fieldPickRect ? void 0 : /* @__PURE__ */ jsxs20(
21198
- "div",
21199
- {
21200
- "data-ohw-form-toolbar": "",
21201
- className: "pointer-events-auto flex items-center gap-0.5 whitespace-nowrap rounded-lg border border-border bg-background p-1 shadow-md",
21202
- children: [
21203
- /* @__PURE__ */ jsx33(
21204
- "button",
21205
- {
21206
- type: "button",
21207
- "aria-label": "Add field",
21208
- className: "flex h-7 w-7 items-center justify-center rounded-md text-foreground transition-colors hover:bg-muted/80",
21209
- onClick: () => setFieldTypePickerOpen((open) => !open),
21210
- "data-ohw-add-field": "",
21211
- children: /* @__PURE__ */ jsx33(Plus4, { size: 15, "aria-hidden": true })
21212
- }
21213
- ),
21214
- /* @__PURE__ */ jsx33("div", { className: "mx-0.5 h-5 w-px bg-border" }),
21215
- /* @__PURE__ */ jsxs20(
21216
- "button",
21217
- {
21218
- type: "button",
21219
- 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",
21220
- onClick: () => {
21221
- setFieldTypePickerOpen(false);
21222
- const form = formPickElRef.current;
21223
- if (!form) return;
21224
- postToParent2({
21225
- type: "ow:form-pick",
21226
- formKey: formKeyOf(form),
21227
- hasLongText: formHasLongText(form)
21228
- });
21229
- },
21230
- children: [
21231
- /* @__PURE__ */ jsx33(Settings, { size: 14, "aria-hidden": true }),
21232
- "Form settings",
21233
- formPickCount ? (
21234
- // Counter pill, per the design — not a text suffix.
21235
- /* @__PURE__ */ jsx33(
21236
- "span",
21237
- {
21238
- "data-ohw-form-count": "",
21239
- 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",
21240
- children: formPickCount
21241
- }
21242
- )
21243
- ) : null
21244
- ]
21245
- }
21246
- ),
21247
- /* @__PURE__ */ jsx33("div", { className: "mx-0.5 h-5 w-px bg-border" }),
21248
- /* @__PURE__ */ jsx33("div", { className: "flex items-center rounded-md bg-muted/70 p-0.5", children: ["default", "success"].map((state) => /* @__PURE__ */ jsx33(
21249
- "button",
21250
- {
21251
- type: "button",
21252
- "aria-pressed": formViewState === state,
21253
- 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"),
21254
- onClick: () => {
21255
- setFieldTypePickerOpen(false);
21256
- const form = formPickElRef.current;
21257
- const key = form ? formKeyOf(form) : null;
21258
- if (!form || !key) return;
21259
- const initial = successInitialFor(form, key, editContentRef.current);
21260
- setFormViewState(form, key, state, initial);
21261
- setFormViewStateUi(state);
21262
- setFormPickRect(form.getBoundingClientRect());
21263
- if (state === "success") {
21264
- const successEl = form.querySelector(`[${SUCCESS_TEXT_ATTR}]`);
21265
- if (successEl) requestAnimationFrame(() => activateRef.current(successEl));
21266
- } else {
21267
- deactivateRef.current();
21268
- }
21269
- },
21270
- children: state
21271
- },
21272
- state
21273
- )) })
21274
- ]
21275
- }
21276
- )
21277
- }
21278
- ),
21279
- formHoverRect && !isItemDragging && /* @__PURE__ */ jsx33(
21280
- ItemInteractionLayer,
21281
- {
21282
- rect: formHoverRect,
21283
- state: "hover",
21284
- chromeGap: formHoverElRef.current && getFieldWrapper(formHoverElRef.current) ? 8 : 24
21285
- }
21286
- ),
21287
- fieldPickRect && fieldPickState && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ jsx33(
21288
- ItemInteractionLayer,
21289
- {
21290
- rect: fieldPickRect,
21291
- state: fieldDragging ? "dragging" : "active-top",
21292
- itemDragSurface: false,
21293
- toolbarAlign: "left",
21294
- chromeGap: 10,
21295
- showHandle: true,
21296
- dragHandleLabel: "Reorder field",
21297
- onDragHandleDragStart: handleFieldDragStart,
21298
- onDragHandleDragEnd: handleFieldDragEnd,
21299
- toolbar: /* @__PURE__ */ jsx33(
21300
- FormFieldToolbar,
21301
- {
21302
- type: fieldPickState.type,
21303
- required: fieldPickState.required,
21304
- onTypeChange: handleFieldTypeChange,
21305
- onRequiredToggle: handleFieldRequiredToggle,
21306
- onDuplicate: handleFieldDuplicate,
21307
- onDelete: handleFieldDelete
21308
- }
21309
- )
21310
- }
21311
- ),
21312
- fieldDragging ? fieldDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
21313
- "div",
21314
- {
21315
- className: "pointer-events-none fixed z-[2147483644]",
21316
- style: { top: slot.top, left: slot.left, width: slot.width },
21317
- children: /* @__PURE__ */ jsx33(
21318
- DropIndicator,
21319
- {
21320
- direction: "horizontal",
21321
- state: fieldDropIndex === i ? "dragActive" : "dragIdle",
21322
- className: "!w-full"
21323
- }
21324
- )
19899
+ children: /* @__PURE__ */ jsx33(
19900
+ DropIndicator,
19901
+ {
19902
+ direction: slot.direction,
19903
+ state: activeFooterDropIndex === i ? "dragActive" : "dragIdle",
19904
+ className: "!h-full !w-full"
19905
+ }
19906
+ )
19907
+ },
19908
+ `footer-drop-${slot.direction}-${slot.columnIndex}-${slot.insertIndex}-${i}`
19909
+ )),
19910
+ isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
19911
+ "div",
19912
+ {
19913
+ className: "pointer-events-none fixed z-2147483646",
19914
+ style: {
19915
+ left: slot.left,
19916
+ top: slot.top,
19917
+ width: slot.width,
19918
+ height: slot.height
21325
19919
  },
21326
- `field-drop-${i}`
21327
- )) : null,
21328
- fieldTypePickerOpen && formPickRect ? (() => {
21329
- const toolbar = document.querySelector("[data-ohw-form-toolbar]")?.getBoundingClientRect();
21330
- return /* @__PURE__ */ jsx33(
21331
- "div",
19920
+ children: /* @__PURE__ */ jsx33(
19921
+ DropIndicator,
21332
19922
  {
21333
- className: "pointer-events-none fixed z-[2147483645]",
21334
- style: {
21335
- top: toolbar ? toolbar.bottom + 6 : formPickRect.top + 16,
21336
- left: toolbar ? toolbar.left : formPickRect.left + 24
21337
- },
21338
- children: /* @__PURE__ */ jsx33(FieldTypePicker, { onPick: handleAddField })
19923
+ direction: slot.direction,
19924
+ state: activeNavDropIndex === i ? "dragActive" : "dragIdle",
19925
+ className: "!h-full !w-full"
21339
19926
  }
21340
- );
21341
- })() : null,
21342
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer2(selectedElRef.current) && /* @__PURE__ */ jsx33(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
21343
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isFooterLinksContainer(selectedElRef.current) && /* @__PURE__ */ jsx33(
21344
- FooterContainerChrome,
21345
- {
21346
- rect: toolbarRect,
21347
- onAdd: handleAddFooterColumn,
21348
- addDisabled: !canAddFooterColumn()
21349
- }
21350
- ),
21351
- toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame" || toolbarVariant === "logo") && /* @__PURE__ */ jsx33(
21352
- ItemInteractionLayer,
21353
- {
21354
- rect: isItemDragging && draggedItemRect && (footerDragRef.current?.wasSelected || navDragRef.current?.wasSelected) ? draggedItemRect : toolbarRect,
21355
- toolbarBelowRect: selectedElRef.current && isNavigationItem2(selectedElRef.current) ? getOpenNavDropdownPanelRect(selectedElRef.current) : null,
21356
- elRef: glowElRef,
21357
- state: isItemDragging ? "dragging" : selectedElRef.current && isNavigationItem2(selectedElRef.current) && getOpenNavDropdownPanelRect(selectedElRef.current) ? "active-bottom" : resolveItemInteractionState(toolbarRect, parentScrollRef.current),
21358
- showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey) || toolbarVariant === "select-frame" && isFooterFrameSelection,
21359
- dragDisabled: reorderDragDisabled,
21360
- dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
21361
- onDragHandleDragStart: handleItemDragStart,
21362
- onDragHandleDragEnd: handleItemDragEnd,
21363
- onItemPointerDown: toolbarVariant === "logo" ? void 0 : handleItemChromePointerDown,
21364
- onItemClick: toolbarVariant === "logo" ? void 0 : handleItemChromeClick,
21365
- itemDragSurface: toolbarVariant !== "logo" && !isFooterFrameSelection && !selectedIsSocialsRow,
21366
- toolbar: toolbarVariant === "link-action" && !isItemDragging || toolbarVariant === "select-frame" && (isFooterFrameSelection || selectedIsSocialsRow) && !isItemDragging ? /* @__PURE__ */ jsx33(
21367
- ItemActionToolbar,
21368
- {
21369
- onEditLink: openLinkPopoverForSelected,
21370
- onStyle: () => {
21371
- const row = selectedElRef.current;
21372
- if (!row) return;
21373
- if (floatingPanel?.kind === "socials-display") closeFloatingPanelOnly();
21374
- else openSocialsDisplayPanel(row);
21375
- },
21376
- showStyle: selectedIsSocialsRow,
21377
- styleActive: floatingPanel?.kind === "socials-display",
21378
- onAddItem: handleAddChildItem,
21379
- onSelectParent: handleSelectParent,
21380
- onDuplicate: handleDuplicateSelected,
21381
- onDelete: handleDeleteSelected,
21382
- addItemDisabled: isFooterFrameSelection && isFooterAddItemDisabled(selectedElRef.current) || selectedElRef.current !== null && (() => {
21383
- const row = isSocialsRow(selectedElRef.current) ? selectedElRef.current : findSocialsRow(selectedElRef.current);
21384
- return row ? !canAddSocialItem(row) : false;
21385
- })(),
21386
- editLinkDisabled: false,
21387
- moreDisabled: false,
21388
- deleteDisabled: selectedElRef.current !== null && (() => {
21389
- const social = getSocialItem(selectedElRef.current);
21390
- return social ? !canRemoveSocialItem(social) : false;
21391
- })(),
21392
- duplicateDisabled: isFooterFrameSelection || selectedIsSocialsRow || selectedElRef.current !== null && (() => {
21393
- const social = getSocialItem(selectedElRef.current);
21394
- const row = social ? findSocialsRow(social) : null;
21395
- return row ? !canAddSocialItem(row) : false;
21396
- })(),
21397
- showEditLink: !isFooterFrameSelection && !selectedIsSocialsRow && navDropdownPreviewOpen === null,
21398
- showAddItem: isFooterFrameSelection || selectedIsSocialsRow || !selectedIsCta && Boolean(
21399
- selectedElRef.current && isNavbarHrefKey(selectedElRef.current.getAttribute("data-ohw-href-key")) && !isNestedNavChild(selectedElRef.current)
19927
+ )
19928
+ },
19929
+ `nav-drop-${slot.direction}-${slot.parentId ?? "root"}-${slot.insertIndex}-${i}`
19930
+ )),
19931
+ hoveredNavContainerRect && (toolbarVariant !== "select-frame" || isFooterFrameSelection) && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
19932
+ hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
19933
+ hoveredTextRect && !hoveredNavContainerRect && !hoveredItemRect && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredTextRect, state: "hover" }),
19934
+ formPickRect && !isItemDragging && /* @__PURE__ */ jsx33(
19935
+ ItemInteractionLayer,
19936
+ {
19937
+ rect: formPickRect,
19938
+ state: "active-top",
19939
+ itemDragSurface: false,
19940
+ toolbarAlign: "left",
19941
+ chromeGap: 24,
19942
+ toolbar: fieldPickRect ? void 0 : /* @__PURE__ */ jsxs20(
19943
+ "div",
19944
+ {
19945
+ "data-ohw-form-toolbar": "",
19946
+ className: "pointer-events-auto flex items-center gap-0.5 whitespace-nowrap rounded-lg border border-border bg-background p-1 shadow-md",
19947
+ children: [
19948
+ /* @__PURE__ */ jsx33(
19949
+ "button",
19950
+ {
19951
+ type: "button",
19952
+ "aria-label": "Add field",
19953
+ className: "flex h-7 w-7 items-center justify-center rounded-md text-foreground transition-colors hover:bg-muted/80",
19954
+ onClick: () => setFieldTypePickerOpen((open) => !open),
19955
+ "data-ohw-add-field": "",
19956
+ children: /* @__PURE__ */ jsx33(Plus4, { size: 15, "aria-hidden": true })
19957
+ }
21400
19958
  ),
21401
- showMore: (!selectedIsCta || isFooterFrameSelection) && !selectedIsSocialsRow,
21402
- dropdownOpen: navDropdownPreviewOpen,
21403
- onDropdownOpenChange: handleNavDropdownOpenChange,
21404
- headingVisible: footerHeadingVisible,
21405
- onHeadingVisibleChange: handleFooterHeadingVisibleChange
21406
- }
21407
- ) : void 0
21408
- }
21409
- ),
21410
- toolbarRect && toolbarVariant === "rich-text" && !linkPopover && /* @__PURE__ */ jsxs20(Fragment8, { children: [
21411
- /* @__PURE__ */ jsx33(
21412
- EditGlowChrome,
19959
+ /* @__PURE__ */ jsx33("div", { className: "mx-0.5 h-5 w-px bg-border" }),
19960
+ /* @__PURE__ */ jsxs20(
19961
+ "button",
19962
+ {
19963
+ type: "button",
19964
+ 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",
19965
+ onClick: () => {
19966
+ setFieldTypePickerOpen(false);
19967
+ const form = formPickElRef.current;
19968
+ if (!form) return;
19969
+ postToParent2({
19970
+ type: "ow:form-pick",
19971
+ formKey: formKeyOf(form),
19972
+ hasLongText: formHasLongText(form)
19973
+ });
19974
+ },
19975
+ children: [
19976
+ /* @__PURE__ */ jsx33(Settings, { size: 14, "aria-hidden": true }),
19977
+ "Form settings",
19978
+ formPickCount ? (
19979
+ // Counter pill, per the design — not a text suffix.
19980
+ /* @__PURE__ */ jsx33(
19981
+ "span",
19982
+ {
19983
+ "data-ohw-form-count": "",
19984
+ 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",
19985
+ children: formPickCount
19986
+ }
19987
+ )
19988
+ ) : null
19989
+ ]
19990
+ }
19991
+ ),
19992
+ /* @__PURE__ */ jsx33("div", { className: "mx-0.5 h-5 w-px bg-border" }),
19993
+ /* @__PURE__ */ jsx33("div", { className: "flex items-center rounded-md bg-muted/70 p-0.5", children: ["default", "success"].map((state) => /* @__PURE__ */ jsx33(
19994
+ "button",
19995
+ {
19996
+ type: "button",
19997
+ "aria-pressed": formViewState === state,
19998
+ 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"),
19999
+ onClick: () => {
20000
+ setFieldTypePickerOpen(false);
20001
+ const form = formPickElRef.current;
20002
+ const key = form ? formKeyOf(form) : null;
20003
+ if (!form || !key) return;
20004
+ const initial = successInitialFor(form, key, editContentRef.current);
20005
+ setFormViewState(form, key, state, initial);
20006
+ setFormViewStateUi(state);
20007
+ setFormPickRect(form.getBoundingClientRect());
20008
+ if (state === "success") {
20009
+ const successEl = form.querySelector(`[${SUCCESS_TEXT_ATTR}]`);
20010
+ if (successEl) requestAnimationFrame(() => activateRef.current(successEl));
20011
+ } else {
20012
+ deactivateRef.current();
20013
+ }
20014
+ },
20015
+ children: state
20016
+ },
20017
+ state
20018
+ )) })
20019
+ ]
20020
+ }
20021
+ )
20022
+ }
20023
+ ),
20024
+ formHoverRect && !isItemDragging && /* @__PURE__ */ jsx33(
20025
+ ItemInteractionLayer,
20026
+ {
20027
+ rect: formHoverRect,
20028
+ state: "hover",
20029
+ chromeGap: formHoverElRef.current && getFieldWrapper(formHoverElRef.current) ? 8 : 24
20030
+ }
20031
+ ),
20032
+ fieldPickRect && fieldPickState && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ jsx33(
20033
+ ItemInteractionLayer,
20034
+ {
20035
+ rect: fieldPickRect,
20036
+ state: fieldDragging ? "dragging" : "active-top",
20037
+ itemDragSurface: false,
20038
+ toolbarAlign: "left",
20039
+ chromeGap: 10,
20040
+ showHandle: true,
20041
+ dragHandleLabel: "Reorder field",
20042
+ onDragHandleDragStart: handleFieldDragStart,
20043
+ onDragHandleDragEnd: handleFieldDragEnd,
20044
+ toolbar: /* @__PURE__ */ jsx33(
20045
+ FormFieldToolbar,
21413
20046
  {
21414
- rect: toolbarRect,
21415
- elRef: glowElRef,
21416
- reorderHrefKey,
21417
- dragDisabled: reorderDragDisabled,
21418
- hideHandle: isItemDragging
20047
+ type: fieldPickState.type,
20048
+ required: fieldPickState.required,
20049
+ onTypeChange: handleFieldTypeChange,
20050
+ onRequiredToggle: handleFieldRequiredToggle,
20051
+ onDuplicate: handleFieldDuplicate,
20052
+ onDelete: handleFieldDelete
21419
20053
  }
21420
- ),
21421
- /* @__PURE__ */ jsx33(
21422
- FloatingToolbar,
20054
+ )
20055
+ }
20056
+ ),
20057
+ fieldDragging ? fieldDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
20058
+ "div",
20059
+ {
20060
+ className: "pointer-events-none fixed z-[2147483644]",
20061
+ style: { top: slot.top, left: slot.left, width: slot.width },
20062
+ children: /* @__PURE__ */ jsx33(
20063
+ DropIndicator,
21423
20064
  {
21424
- rect: toolbarRect,
21425
- parentScroll: parentScrollRef.current,
21426
- elRef: toolbarElRef,
21427
- onCommand: handleCommand,
21428
- activeCommands,
21429
- showEditLink,
21430
- onEditLink: openLinkPopoverForActive
20065
+ direction: "horizontal",
20066
+ state: fieldDropIndex === i ? "dragActive" : "dragIdle",
20067
+ className: "!w-full"
21431
20068
  }
21432
20069
  )
21433
- ] }),
21434
- maxBadge && /* @__PURE__ */ jsxs20(
20070
+ },
20071
+ `field-drop-${i}`
20072
+ )) : null,
20073
+ fieldTypePickerOpen && formPickRect ? (() => {
20074
+ const toolbar = document.querySelector("[data-ohw-form-toolbar]")?.getBoundingClientRect();
20075
+ return /* @__PURE__ */ jsx33(
21435
20076
  "div",
21436
20077
  {
21437
- "data-ohw-max-badge": "",
20078
+ className: "pointer-events-none fixed z-[2147483645]",
21438
20079
  style: {
21439
- position: "fixed",
21440
- top: maxBadge.rect.bottom + 4,
21441
- left: maxBadge.rect.right,
21442
- transform: "translateX(-100%)",
21443
- zIndex: 2147483647,
21444
- background: maxBadge.current > maxBadge.max ? "#FEF2F2" : "#F5F5F4",
21445
- color: maxBadge.current > maxBadge.max ? "#DC2626" : "#78716C",
21446
- border: `1px solid ${maxBadge.current > maxBadge.max ? "#FECACA" : "#E7E5E4"}`,
21447
- borderRadius: 4,
21448
- padding: "2px 6px",
21449
- fontSize: 11,
21450
- fontWeight: 500,
21451
- pointerEvents: "none"
20080
+ top: toolbar ? toolbar.bottom + 6 : formPickRect.top + 16,
20081
+ left: toolbar ? toolbar.left : formPickRect.left + 24
21452
20082
  },
21453
- children: [
21454
- maxBadge.current,
21455
- "/",
21456
- maxBadge.max
21457
- ]
20083
+ children: /* @__PURE__ */ jsx33(FieldTypePicker, { onPick: handleAddField })
21458
20084
  }
21459
- ),
21460
- toggleState && !linkPopover && /* @__PURE__ */ jsx33(
21461
- StateToggle,
20085
+ );
20086
+ })() : null,
20087
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer2(selectedElRef.current) && /* @__PURE__ */ jsx33(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
20088
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isFooterLinksContainer(selectedElRef.current) && /* @__PURE__ */ jsx33(
20089
+ FooterContainerChrome,
20090
+ {
20091
+ rect: toolbarRect,
20092
+ onAdd: handleAddFooterColumn,
20093
+ addDisabled: !canAddFooterColumn()
20094
+ }
20095
+ ),
20096
+ toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame" || toolbarVariant === "logo") && /* @__PURE__ */ jsx33(
20097
+ ItemInteractionLayer,
20098
+ {
20099
+ rect: isItemDragging && draggedItemRect && (footerDragRef.current?.wasSelected || navDragRef.current?.wasSelected) ? draggedItemRect : toolbarRect,
20100
+ toolbarBelowRect: selectedElRef.current && isNavigationItem2(selectedElRef.current) ? getOpenNavDropdownPanelRect(selectedElRef.current) : null,
20101
+ elRef: glowElRef,
20102
+ state: isItemDragging ? "dragging" : selectedElRef.current && isNavigationItem2(selectedElRef.current) && getOpenNavDropdownPanelRect(selectedElRef.current) ? "active-bottom" : resolveItemInteractionState(toolbarRect, parentScrollRef.current),
20103
+ showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey) || toolbarVariant === "select-frame" && isFooterFrameSelection,
20104
+ dragDisabled: reorderDragDisabled,
20105
+ dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
20106
+ onDragHandleDragStart: handleItemDragStart,
20107
+ onDragHandleDragEnd: handleItemDragEnd,
20108
+ onItemPointerDown: toolbarVariant === "logo" ? void 0 : handleItemChromePointerDown,
20109
+ onItemClick: toolbarVariant === "logo" ? void 0 : handleItemChromeClick,
20110
+ itemDragSurface: toolbarVariant !== "logo" && !isFooterFrameSelection && !selectedIsSocialsRow,
20111
+ toolbar: toolbarVariant === "link-action" && !isItemDragging || toolbarVariant === "select-frame" && (isFooterFrameSelection || selectedIsSocialsRow) && !isItemDragging ? /* @__PURE__ */ jsx33(
20112
+ ItemActionToolbar,
20113
+ {
20114
+ onEditLink: openLinkPopoverForSelected,
20115
+ onStyle: () => {
20116
+ const row = selectedElRef.current;
20117
+ if (!row) return;
20118
+ if (floatingPanel?.kind === "socials-display") closeFloatingPanelOnly();
20119
+ else openSocialsDisplayPanel(row);
20120
+ },
20121
+ showStyle: selectedIsSocialsRow,
20122
+ styleActive: floatingPanel?.kind === "socials-display",
20123
+ onAddItem: handleAddChildItem,
20124
+ onSelectParent: handleSelectParent,
20125
+ onDuplicate: handleDuplicateSelected,
20126
+ onDelete: handleDeleteSelected,
20127
+ addItemDisabled: isFooterFrameSelection && isFooterAddItemDisabled(selectedElRef.current) || selectedElRef.current !== null && (() => {
20128
+ const row = isSocialsRow(selectedElRef.current) ? selectedElRef.current : findSocialsRow(selectedElRef.current);
20129
+ return row ? !canAddSocialItem(row) : false;
20130
+ })(),
20131
+ editLinkDisabled: false,
20132
+ moreDisabled: false,
20133
+ deleteDisabled: selectedElRef.current !== null && (() => {
20134
+ const social = getSocialItem(selectedElRef.current);
20135
+ return social ? !canRemoveSocialItem(social) : false;
20136
+ })(),
20137
+ duplicateDisabled: isFooterFrameSelection || selectedIsSocialsRow || selectedElRef.current !== null && (() => {
20138
+ const social = getSocialItem(selectedElRef.current);
20139
+ const row = social ? findSocialsRow(social) : null;
20140
+ return row ? !canAddSocialItem(row) : false;
20141
+ })(),
20142
+ showEditLink: !isFooterFrameSelection && !selectedIsSocialsRow && navDropdownPreviewOpen === null,
20143
+ showAddItem: isFooterFrameSelection || selectedIsSocialsRow || !selectedIsCta && Boolean(
20144
+ selectedElRef.current && isNavbarHrefKey(selectedElRef.current.getAttribute("data-ohw-href-key")) && !isNestedNavChild(selectedElRef.current)
20145
+ ),
20146
+ showMore: (!selectedIsCta || isFooterFrameSelection) && !selectedIsSocialsRow,
20147
+ dropdownOpen: navDropdownPreviewOpen,
20148
+ onDropdownOpenChange: handleNavDropdownOpenChange,
20149
+ headingVisible: footerHeadingVisible,
20150
+ onHeadingVisibleChange: handleFooterHeadingVisibleChange
20151
+ }
20152
+ ) : void 0
20153
+ }
20154
+ ),
20155
+ toolbarRect && toolbarVariant === "rich-text" && !linkPopover && /* @__PURE__ */ jsxs20(Fragment8, { children: [
20156
+ /* @__PURE__ */ jsx33(
20157
+ EditGlowChrome,
21462
20158
  {
21463
- rect: toggleState.rect,
21464
- activeState: toggleState.activeState,
21465
- states: toggleState.states,
21466
- onStateChange: handleStateChange
20159
+ rect: toolbarRect,
20160
+ elRef: glowElRef,
20161
+ reorderHrefKey,
20162
+ dragDisabled: reorderDragDisabled,
20163
+ hideHandle: isItemDragging
21467
20164
  }
21468
20165
  ),
21469
- sectionGap && !linkPopover && /* @__PURE__ */ jsxs20(
21470
- "div",
20166
+ /* @__PURE__ */ jsx33(
20167
+ FloatingToolbar,
21471
20168
  {
21472
- "data-ohw-section-insert-line": "",
21473
- className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
21474
- style: { top: sectionGap.y, transform: "translateY(-50%)" },
21475
- children: [
21476
- /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
21477
- /* @__PURE__ */ jsx33(
21478
- Badge,
21479
- {
21480
- 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",
21481
- onClick: () => {
21482
- window.parent.postMessage(
21483
- {
21484
- type: "ow:add-section",
21485
- insertAfter: sectionGap.insertAfter,
21486
- insertBefore: sectionGap.insertBefore
21487
- },
21488
- "*"
21489
- );
21490
- },
21491
- children: "Add Section"
21492
- }
21493
- ),
21494
- /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-primary", style: { height: 3 } })
21495
- ]
20169
+ rect: toolbarRect,
20170
+ parentScroll: parentScrollRef.current,
20171
+ elRef: toolbarElRef,
20172
+ onCommand: handleCommand,
20173
+ activeCommands,
20174
+ showEditLink,
20175
+ onEditLink: openLinkPopoverForActive
21496
20176
  }
21497
- ),
21498
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx33(
21499
- LinkPopover,
21500
- {
21501
- panelRef: linkPopoverPanelRef,
21502
- portalContainer: dialogPortalContainer,
21503
- open: true,
21504
- mode: linkPopover.mode ?? "edit",
21505
- pages: sitePages,
21506
- sections: currentSections,
21507
- sectionsByPath,
21508
- initialTarget: linkPopover.target,
21509
- existingTargets: linkPopover.intent === "add-nav" ? getNavbarExistingTargets() : [],
21510
- onClose: closeLinkPopover,
21511
- onSubmit: handleLinkPopoverSubmit
20177
+ )
20178
+ ] }),
20179
+ maxBadge && /* @__PURE__ */ jsxs20(
20180
+ "div",
20181
+ {
20182
+ "data-ohw-max-badge": "",
20183
+ style: {
20184
+ position: "fixed",
20185
+ top: maxBadge.rect.bottom + 4,
20186
+ left: maxBadge.rect.right,
20187
+ transform: "translateX(-100%)",
20188
+ zIndex: 2147483647,
20189
+ background: maxBadge.current > maxBadge.max ? "#FEF2F2" : "#F5F5F4",
20190
+ color: maxBadge.current > maxBadge.max ? "#DC2626" : "#78716C",
20191
+ border: `1px solid ${maxBadge.current > maxBadge.max ? "#FECACA" : "#E7E5E4"}`,
20192
+ borderRadius: 4,
20193
+ padding: "2px 6px",
20194
+ fontSize: 11,
20195
+ fontWeight: 500,
20196
+ pointerEvents: "none"
21512
20197
  },
21513
- linkPopover.key
21514
- ) : null,
21515
- floatingPanel && floatingPanel.kind === "socials-display" ? /* @__PURE__ */ jsx33(
21516
- FloatingPanel,
21517
- {
21518
- open: true,
21519
- title: floatingPanel.title,
21520
- context: floatingPanel.context,
21521
- position: floatingPanelPos,
21522
- onPositionChange: setFloatingPanelPos,
21523
- parentScroll: parentScrollSnap ?? parentScrollRef.current,
21524
- onClose: closeFloatingPanelOnly,
21525
- children: /* @__PURE__ */ jsx33(
21526
- SocialsDisplayPanel,
20198
+ children: [
20199
+ maxBadge.current,
20200
+ "/",
20201
+ maxBadge.max
20202
+ ]
20203
+ }
20204
+ ),
20205
+ toggleState && !linkPopover && /* @__PURE__ */ jsx33(
20206
+ StateToggle,
20207
+ {
20208
+ rect: toggleState.rect,
20209
+ activeState: toggleState.activeState,
20210
+ states: toggleState.states,
20211
+ onStateChange: handleStateChange
20212
+ }
20213
+ ),
20214
+ sectionGap && !linkPopover && /* @__PURE__ */ jsxs20(
20215
+ "div",
20216
+ {
20217
+ "data-ohw-section-insert-line": "",
20218
+ className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
20219
+ style: { top: sectionGap.y, transform: "translateY(-50%)" },
20220
+ children: [
20221
+ /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
20222
+ /* @__PURE__ */ jsx33(
20223
+ Badge,
21527
20224
  {
21528
- display: socialsDisplayFor(floatingPanel.row, editContentRef.current),
21529
- onChange: (next) => {
21530
- changeSocialsDisplay(floatingPanel.row, next);
21531
- setFloatingPanel({ ...floatingPanel });
21532
- }
20225
+ 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",
20226
+ onClick: () => {
20227
+ window.parent.postMessage(
20228
+ {
20229
+ type: "ow:add-section",
20230
+ insertAfter: sectionGap.insertAfter,
20231
+ insertBefore: sectionGap.insertBefore
20232
+ },
20233
+ "*"
20234
+ );
20235
+ },
20236
+ children: "Add Section"
21533
20237
  }
21534
- )
21535
- }
21536
- ) : null
21537
- ] }),
21538
- bridgeRoot
21539
- ) : null
21540
- ] });
21541
- }
21542
-
21543
- // src/ui/EmptySection.tsx
21544
- import Link3 from "next/link";
21545
- import { Fragment as Fragment9, jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
21546
- function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey }) {
21547
- return /* @__PURE__ */ jsxs21(Fragment9, { children: [
21548
- /* @__PURE__ */ jsx34(
21549
- "p",
21550
- {
21551
- style: {
21552
- fontFamily: "var(--brand-font-body)",
21553
- fontSize: "0.75rem",
21554
- fontWeight: 500,
21555
- letterSpacing: "0.15em",
21556
- textTransform: "uppercase",
21557
- color: "var(--brand-accent)",
21558
- marginBottom: "1.5rem"
21559
- },
21560
- children: /* @__PURE__ */ jsx34(Link3, { href: homeHref, style: { color: "inherit" }, children: /* @__PURE__ */ jsx34("span", { ...eyebrowKey ? { "data-ohw-editable": "text", "data-ohw-key": eyebrowKey } : {}, children: "Home" }) })
21561
- }
21562
- ),
21563
- /* @__PURE__ */ jsx34(
21564
- "h1",
21565
- {
21566
- style: {
21567
- fontFamily: "var(--brand-font-heading)",
21568
- fontSize: "clamp(2rem, 3.5vw, 2.75rem)",
21569
- lineHeight: 1.1,
21570
- letterSpacing: "-0.025em",
21571
- color: "var(--brand-text)",
21572
- marginBottom: "1rem"
21573
- },
21574
- ...titleKey ? { "data-ohw-editable": "text", "data-ohw-key": titleKey, "data-ohw-max-length": 80 } : {},
21575
- children: title
21576
- }
21577
- ),
21578
- /* @__PURE__ */ jsx34(
21579
- "p",
21580
- {
21581
- style: {
21582
- fontFamily: "var(--brand-font-body)",
21583
- fontSize: "1rem",
21584
- lineHeight: 1.7,
21585
- fontWeight: 300,
21586
- color: "var(--brand-text-muted)",
21587
- maxWidth: "340px"
20238
+ ),
20239
+ /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-primary", style: { height: 3 } })
20240
+ ]
20241
+ }
20242
+ ),
20243
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx33(
20244
+ LinkPopover,
20245
+ {
20246
+ panelRef: linkPopoverPanelRef,
20247
+ portalContainer: dialogPortalContainer,
20248
+ open: true,
20249
+ mode: linkPopover.mode ?? "edit",
20250
+ pages: sitePages,
20251
+ sections: currentSections,
20252
+ sectionsByPath,
20253
+ initialTarget: linkPopover.target,
20254
+ existingTargets: linkPopover.intent === "add-nav" ? getNavbarExistingTargets() : [],
20255
+ onClose: closeLinkPopover,
20256
+ onSubmit: handleLinkPopoverSubmit
21588
20257
  },
21589
- ...subtitleKey ? { "data-ohw-editable": "text", "data-ohw-key": subtitleKey, "data-ohw-max-length": 160 } : {},
21590
- children: "This page doesn't have any content yet."
21591
- }
21592
- )
21593
- ] });
20258
+ linkPopover.key
20259
+ ) : null,
20260
+ floatingPanel && floatingPanel.kind === "socials-display" ? /* @__PURE__ */ jsx33(
20261
+ FloatingPanel,
20262
+ {
20263
+ open: true,
20264
+ title: floatingPanel.title,
20265
+ context: floatingPanel.context,
20266
+ position: floatingPanelPos,
20267
+ onPositionChange: setFloatingPanelPos,
20268
+ parentScroll: parentScrollSnap ?? parentScrollRef.current,
20269
+ onClose: closeFloatingPanelOnly,
20270
+ children: /* @__PURE__ */ jsx33(
20271
+ SocialsDisplayPanel,
20272
+ {
20273
+ display: socialsDisplayFor(floatingPanel.row, editContentRef.current),
20274
+ onChange: (next) => {
20275
+ changeSocialsDisplay(floatingPanel.row, next);
20276
+ setFloatingPanel({ ...floatingPanel });
20277
+ }
20278
+ }
20279
+ )
20280
+ }
20281
+ ) : null
20282
+ ] }),
20283
+ bridgeRoot
20284
+ ) : null;
21594
20285
  }
21595
20286
  export {
21596
20287
  AI_DEFAULT_BRAND,
@@ -21608,7 +20299,6 @@ export {
21608
20299
  DropdownMenuItem,
21609
20300
  DropdownMenuSeparator,
21610
20301
  DropdownMenuTrigger,
21611
- EmptySection,
21612
20302
  ItemActionToolbar,
21613
20303
  ItemInteractionLayer,
21614
20304
  LinkEditorPanel,