@ohhwells/bridge 0.1.68 → 0.1.69-next.207

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  // src/OhhwellsBridge.tsx
4
- import React11, { useCallback as useCallback7, useEffect as useEffect12, useLayoutEffect as useLayoutEffect4, useRef as useRef9, useState as useState11 } from "react";
4
+ import React11, { useCallback as useCallback8, useEffect as useEffect13, useLayoutEffect as useLayoutEffect4, useRef as useRef10, useState as useState12 } from "react";
5
5
  import { createRoot as createRoot2 } from "react-dom/client";
6
6
  import { flushSync as flushSync2 } from "react-dom";
7
7
 
@@ -83,7 +83,12 @@ 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 { v: 1, sections, ...removed.length ? { removed } : {} };
86
+ return {
87
+ v: 1,
88
+ sections,
89
+ ...removed.length ? { removed } : {},
90
+ ...parsed.hideTemplate === true ? { hideTemplate: true } : {}
91
+ };
87
92
  } catch {
88
93
  return EMPTY_AI_SECTIONS;
89
94
  }
@@ -96,6 +101,7 @@ function applyTreeToState(state, payload) {
96
101
  const entry = {
97
102
  id: payload.id,
98
103
  label: payload.label ?? "Generated section",
104
+ ...typeof payload.path === "string" && payload.path ? { path: payload.path } : {},
99
105
  afterSection: payload.mode === "insert" && !insertBefore ? payload.targetSectionId ?? null : null,
100
106
  ...insertBefore && payload.targetSectionId ? { beforeSection: payload.targetSectionId } : {},
101
107
  ...payload.mode === "replace" && payload.targetSectionId ? { replaces: payload.targetSectionId } : {},
@@ -118,6 +124,317 @@ function deleteSectionFromState(state, sectionId) {
118
124
  return { ...state, v: 1, sections: state.sections, removed: [...removed, sectionId] };
119
125
  }
120
126
 
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
+
121
438
  // src/ui/ai-tree/aiSectionsManager.tsx
122
439
  import { flushSync } from "react-dom";
123
440
  import { createRoot } from "react-dom/client";
@@ -132,7 +449,8 @@ function lucideByName(name) {
132
449
  }
133
450
  var typeStyle = (spec, font) => ({
134
451
  fontFamily: font,
135
- fontSize: spec.size,
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,
136
454
  lineHeight: spec.line,
137
455
  fontWeight: spec.weight
138
456
  });
@@ -147,6 +465,36 @@ var FEATURE_LINE_CSS = [
147
465
  `background-color:currentColor;-webkit-mask:url("${CHECK_MASK}") no-repeat 0 0/24px 24px;`,
148
466
  `mask:url("${CHECK_MASK}") no-repeat 0 0/24px 24px}`
149
467
  ].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
+ }
150
498
  function textAttrs(ctx, path) {
151
499
  return ctx.keyFor ? { "data-ohw-key": ctx.keyFor(path), "data-ohw-editable": "text" } : {};
152
500
  }
@@ -158,7 +506,12 @@ var AI_RESPONSIVE_CSS = [
158
506
  "@media (max-width: 640px) {",
159
507
  " [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
160
508
  " [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; }",
161
512
  " [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%; }",
162
515
  "}"
163
516
  ].join("\n");
164
517
  var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
@@ -236,7 +589,7 @@ function ButtonEl({
236
589
  }) {
237
590
  const secondary = slots.variant === "secondary";
238
591
  const href = str(slots.href);
239
- const linkAttrs = ctx.keyFor && path ? { "data-ohw-href-key": ctx.keyFor(`${path}.href`) } : {};
592
+ const linkAttrs = ctx.keyFor && path ? { "data-ohw-href-key": ctx.keyFor(`${path}.href`), "data-ohw-role": "button" } : {};
240
593
  return /* @__PURE__ */ jsx(
241
594
  "a",
242
595
  {
@@ -252,7 +605,7 @@ function ButtonEl({
252
605
  textDecoration: "none",
253
606
  cursor: "pointer",
254
607
  ...typeStyle(AI_TREE_TOKENS.type.button, ctx.brand.fonts.body),
255
- ...secondary ? { border: `1px solid ${ctx.brand.palette.dark}`, color: ctx.brand.palette.dark } : { background: ctx.brand.palette.primary, color: AI_TREE_TOKENS.textPrimaryForeground }
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 }
256
609
  },
257
610
  children: /* @__PURE__ */ jsx("span", { ...path ? textAttrs(ctx, `${path}.label`) : {}, children: str(slots.label) })
258
611
  }
@@ -758,7 +1111,24 @@ function CardBlock({ node, ctx, path }) {
758
1111
  minWidth: 0
759
1112
  },
760
1113
  children: [
761
- media && (horizontal ? /* @__PURE__ */ jsx("div", { style: { flex: 1, minWidth: 0, display: "flex", alignItems: "center", padding: mediaInset }, children: media }) : /* @__PURE__ */ jsx(
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(
762
1132
  "div",
763
1133
  {
764
1134
  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" },
@@ -1022,6 +1392,49 @@ function renderNode(node, ctx, path) {
1022
1392
  switch (node.type) {
1023
1393
  case "text":
1024
1394
  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
+ }
1025
1438
  case "button":
1026
1439
  return /* @__PURE__ */ jsx(ButtonEl, { slots, ctx, path });
1027
1440
  case "button-row":
@@ -1154,11 +1567,14 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1154
1567
  return null;
1155
1568
  }
1156
1569
  const resolvedBrand = brand ?? AI_DEFAULT_BRAND;
1570
+ const band = (tree.settings ?? {}).sectionBackground === "accent" ? accentBandContext(resolvedBrand) : null;
1571
+ const blockBrand = band?.brand ?? resolvedBrand;
1157
1572
  const ctx = {
1158
- brand: resolvedBrand,
1573
+ brand: blockBrand,
1159
1574
  resolveMedia: resolveMedia ?? (() => null),
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
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 } : {}
1162
1578
  };
1163
1579
  const settings = tree.settings ?? {};
1164
1580
  const pad = AI_TREE_TOKENS.sectionPadding[settings.spacing ?? "balanced"];
@@ -1166,6 +1582,20 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1166
1582
  const overlayAlpha = { low: 0.25, medium: 0.45, high: 0.65 }[settings.overlayIntensity ?? "medium"];
1167
1583
  const backgroundUrl = isOverlay && settings.backgroundMedia ? ctx.resolveMedia(settings.backgroundMedia) : null;
1168
1584
  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;
1169
1599
  return /* @__PURE__ */ jsxs(
1170
1600
  "section",
1171
1601
  {
@@ -1175,10 +1605,11 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1175
1605
  style: {
1176
1606
  position: "relative",
1177
1607
  padding: `${pad}px 0`,
1178
- background: isOverlay && !backgroundUrl ? "#DBEAFE" : void 0,
1608
+ background: toneBackground ?? (isOverlay && !backgroundUrl ? "#DBEAFE" : void 0),
1179
1609
  backgroundImage: backgroundUrl ? `url(${backgroundUrl})` : void 0,
1180
1610
  backgroundSize: "cover",
1181
- backgroundPosition: "center"
1611
+ backgroundPosition: "center",
1612
+ color: settings.sectionBackground === "accent" ? blockBrand.palette.dark : void 0
1182
1613
  },
1183
1614
  children: [
1184
1615
  /* @__PURE__ */ jsx("style", { children: AI_RESPONSIVE_CSS }),
@@ -1202,10 +1633,24 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1202
1633
  display: "grid",
1203
1634
  gridTemplateColumns: "repeat(12, 1fr)",
1204
1635
  gap: AI_TREE_TOKENS.spacing6,
1205
- alignItems: settings.verticalPosition === "top" ? "start" : "center",
1636
+ alignItems: distributed === "space-between" ? "stretch" : (distributed ?? settings.verticalPosition) === "top" ? "start" : "center",
1206
1637
  marginTop: r2 > 0 ? AI_TREE_TOKENS.spacing8 : 0
1207
1638
  },
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))
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
+ ))
1209
1654
  },
1210
1655
  r2
1211
1656
  ))
@@ -1221,17 +1666,36 @@ import { jsx as jsx2 } from "react/jsx-runtime";
1221
1666
  var CONTAINER_ATTR = "data-ohw-ai-generated";
1222
1667
  var REPLACED_ATTR = "data-ohw-ai-replaced-by";
1223
1668
  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
+ }
1224
1691
  function deriveTemplateBrand() {
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");
1692
+ const dark = readRootVar("--color-dark");
1693
+ const primary = readRootVar("--color-primary");
1694
+ const light = readRootVar("--color-light");
1231
1695
  if (!dark || !primary || !light) return null;
1232
- const accent = read("--color-accent");
1233
- const heading = read("--font-heading") || read("--font-display");
1234
- const body = read("--font-body");
1696
+ const accent = readRootVar("--color-accent");
1697
+ const heading = readRootVar("--font-heading") || readRootVar("--font-display");
1698
+ const body = readRootVar("--font-body");
1235
1699
  return {
1236
1700
  palette: { dark, primary, accent: accent || dark, light },
1237
1701
  fonts: {
@@ -1303,6 +1767,24 @@ function syncRemovedSections(state) {
1303
1767
  }
1304
1768
  }
1305
1769
  }
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
+ }
1306
1788
  function syncReplacedOriginals(state) {
1307
1789
  for (const el of document.querySelectorAll(`[${REPLACED_ATTR}]`)) {
1308
1790
  const byId = el.getAttribute(REPLACED_ATTR) ?? "";
@@ -1323,8 +1805,12 @@ function syncReplacedOriginals(state) {
1323
1805
  }
1324
1806
  function applyAiSectionsToDom(state, options) {
1325
1807
  if (typeof document === "undefined") return;
1808
+ const brandOverride = deriveBrandOverride();
1326
1809
  const templateBrand = deriveTemplateBrand();
1327
- const activeIds = new Set(state.sections.map((entry) => entry.id));
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));
1328
1814
  for (const [id, section] of mounted) {
1329
1815
  if (!activeIds.has(id)) {
1330
1816
  section.root.unmount();
@@ -1332,8 +1818,8 @@ function applyAiSectionsToDom(state, options) {
1332
1818
  mounted.delete(id);
1333
1819
  }
1334
1820
  }
1335
- for (const entry of state.sections) {
1336
- const serialized = JSON.stringify(entry);
1821
+ for (const entry of pageSections) {
1822
+ const serialized = JSON.stringify(entry) + brandKey;
1337
1823
  const existing = mounted.get(entry.id);
1338
1824
  if (existing && existing.serialized === serialized && existing.container.isConnected) {
1339
1825
  continue;
@@ -1347,6 +1833,7 @@ function applyAiSectionsToDom(state, options) {
1347
1833
  mounted.delete(entry.id);
1348
1834
  }
1349
1835
  container.setAttribute("data-ohw-section", entry.id);
1836
+ container.setAttribute("data-ohw-instance", entry.id);
1350
1837
  container.setAttribute("data-ohw-section-label", entry.label);
1351
1838
  placeContainer(container, entry);
1352
1839
  const root = mounted.get(entry.id)?.root ?? createRoot(container);
@@ -1357,7 +1844,7 @@ function applyAiSectionsToDom(state, options) {
1357
1844
  AiTreeRenderer,
1358
1845
  {
1359
1846
  tree: entry.tree,
1360
- brand: entry.brand ?? options?.brand ?? templateBrand ?? void 0,
1847
+ brand: brandOverride ?? entry.brand ?? options?.brand ?? templateBrand ?? void 0,
1361
1848
  resolveMedia,
1362
1849
  editKeyPrefix: `ai.${entry.id}`
1363
1850
  }
@@ -1368,6 +1855,7 @@ function applyAiSectionsToDom(state, options) {
1368
1855
  }
1369
1856
  syncReplacedOriginals(state);
1370
1857
  syncRemovedSections(state);
1858
+ syncTemplateHidden(state, pageSections.length > 0);
1371
1859
  }
1372
1860
 
1373
1861
  // src/useLinkHrefGuardian.ts
@@ -1974,7 +2462,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
1974
2462
  const autoId = useId();
1975
2463
  const insertAfter = insertAfterProp ?? autoId;
1976
2464
  const [schedule, setSchedule] = useState2(null);
1977
- const [loading, setLoading] = useState2(true);
2465
+ const [loading, setLoading] = useState2(initialScheduleId !== null);
1978
2466
  const [inEditor, setInEditor] = useState2(false);
1979
2467
  const [isHovered, setIsHovered] = useState2(false);
1980
2468
  const [modalState, setModalState] = useState2(null);
@@ -2148,8 +2636,10 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
2148
2636
  "*"
2149
2637
  );
2150
2638
  };
2151
- if (!inEditor && !loading && !schedule) return null;
2152
2639
  const sectionId = `scheduling-${insertAfter}`;
2640
+ if (!inEditor && !loading && !schedule) {
2641
+ return /* @__PURE__ */ jsx4("section", { "data-ohw-section": sectionId, "data-ohw-scheduling-anchor": insertAfter, style: { display: "none" } });
2642
+ }
2153
2643
  return /* @__PURE__ */ jsxs3(
2154
2644
  "section",
2155
2645
  {
@@ -7057,13 +7547,17 @@ function MediaOverlay({
7057
7547
  hover,
7058
7548
  isUploading,
7059
7549
  fadingOut = false,
7550
+ selected = false,
7551
+ hovered = false,
7060
7552
  onFadeOutComplete,
7061
7553
  onReplace,
7554
+ onSelect,
7062
7555
  onVideoSettingsChange
7063
7556
  }) {
7064
7557
  const { rect } = hover;
7065
7558
  const skeletonRef = React7.useRef(null);
7066
7559
  const isVideo = hover.elementType === "video";
7560
+ const showChrome = !selected || hovered;
7067
7561
  const autoplay = hover.videoAutoplay ?? true;
7068
7562
  const muted = hover.videoMuted ?? true;
7069
7563
  const box = {
@@ -7098,7 +7592,7 @@ function MediaOverlay({
7098
7592
  }
7099
7593
  );
7100
7594
  }
7101
- const settingsBar = isVideo && !hover.isDragOver ? /* @__PURE__ */ jsxs7(
7595
+ const settingsBar = isVideo && !hover.isDragOver && showChrome ? /* @__PURE__ */ jsxs7(
7102
7596
  "div",
7103
7597
  {
7104
7598
  "data-ohw-bridge": "",
@@ -7168,11 +7662,13 @@ function MediaOverlay({
7168
7662
  // in-document, pointer-events does it natively. The button below opts back in, so
7169
7663
  // Replace still works.
7170
7664
  pointerEvents: hover.hasTextOverlap ? "none" : "auto",
7171
- boxShadow: "inset 0 0 0 1.5px var(--color-primary)",
7172
- background: "color-mix(in srgb, var(--color-primary) 20%, transparent)"
7665
+ // Selected: a firm component ring with no wash, so the image reads as chosen rather
7666
+ // than hovered. Hover keeps the existing tinted preview.
7667
+ boxShadow: selected ? "inset 0 0 0 2px var(--color-primary)" : "inset 0 0 0 1.5px var(--color-primary)",
7668
+ background: selected ? "transparent" : "color-mix(in srgb, var(--color-primary) 20%, transparent)"
7173
7669
  },
7174
- onClick: () => onReplace(hover.key),
7175
- children: /* @__PURE__ */ jsxs7(
7670
+ onClick: () => onSelect && !selected ? onSelect(hover.key) : onReplace(hover.key),
7671
+ children: showChrome && /* @__PURE__ */ jsxs7(
7176
7672
  Button,
7177
7673
  {
7178
7674
  "data-ohw-media-overlay": "",
@@ -7191,7 +7687,7 @@ function MediaOverlay({
7191
7687
  },
7192
7688
  children: [
7193
7689
  isVideo ? /* @__PURE__ */ jsx15(Film, { size: 14 }) : /* @__PURE__ */ jsx15(ImageIcon, { size: 14 }),
7194
- isVideo ? "Replace video" : "Replace image"
7690
+ isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image"
7195
7691
  ]
7196
7692
  }
7197
7693
  )
@@ -7261,6 +7757,9 @@ import { Check, X } from "lucide-react";
7261
7757
 
7262
7758
  // src/lib/sections.ts
7263
7759
  var LINK_PICKER_EXCLUDED_IDS = /* @__PURE__ */ new Set(["navbar", "footer"]);
7760
+ function isChromeSection(el) {
7761
+ return el.matches("header, nav, footer, aside");
7762
+ }
7264
7763
  function titleCaseSectionId(id) {
7265
7764
  return id.split("-").filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
7266
7765
  }
@@ -7271,6 +7770,8 @@ function parseSectionsFromRoot(root) {
7271
7770
  const id = el.getAttribute("data-ohw-section") ?? "";
7272
7771
  if (!id || seen.has(id) || LINK_PICKER_EXCLUDED_IDS.has(id)) continue;
7273
7772
  if (el.parentElement?.closest("[data-ohw-section]")) continue;
7773
+ if (el.hasAttribute("data-ohw-ai-template-hidden") || el.hasAttribute("data-ohw-ai-removed") || el.hasAttribute("data-ohw-ai-replaced-by"))
7774
+ continue;
7274
7775
  seen.add(id);
7275
7776
  const label = el.getAttribute("data-ohw-section-label") ?? titleCaseSectionId(id);
7276
7777
  sections.push({ id, label });
@@ -7288,8 +7789,12 @@ function parseSectionsFromHtml(html) {
7288
7789
 
7289
7790
  // src/ui/ai-section/AiSectionOverlay.tsx
7290
7791
  import { Fragment as Fragment5, jsx as jsx17, jsxs as jsxs9 } from "react/jsx-runtime";
7291
- function readRect(sectionId) {
7292
- const el = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
7792
+ function findSectionElement(instanceId) {
7793
+ const escaped = CSS.escape(instanceId);
7794
+ return document.querySelector(`[data-ohw-instance="${escaped}"]`) ?? document.querySelector(`[data-ohw-section="${escaped}"]:not([data-ohw-instance])`);
7795
+ }
7796
+ function readRect(instanceId) {
7797
+ const el = findSectionElement(instanceId);
7293
7798
  if (!el) return null;
7294
7799
  const r2 = el.getBoundingClientRect();
7295
7800
  if (r2.width <= 0 || r2.height <= 0) return null;
@@ -7312,7 +7817,7 @@ function useLiveSectionRect(sectionId) {
7312
7817
  const opts = { capture: true, passive: true };
7313
7818
  window.addEventListener("scroll", update, opts);
7314
7819
  window.addEventListener("resize", update);
7315
- const el = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
7820
+ const el = findSectionElement(sectionId);
7316
7821
  const ro = el ? new ResizeObserver(update) : null;
7317
7822
  if (el && ro) ro.observe(el);
7318
7823
  const interval = setInterval(update, 500);
@@ -7325,6 +7830,14 @@ function useLiveSectionRect(sectionId) {
7325
7830
  }, [sectionId]);
7326
7831
  return rect;
7327
7832
  }
7833
+ function computeSectionBoundaryFlags(instanceId) {
7834
+ const topLevel = Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
7835
+ (el) => !el.parentElement?.closest("[data-ohw-section]") && !isChromeSection(el)
7836
+ );
7837
+ const index = topLevel.findIndex((el) => (el.dataset.ohwInstance ?? el.dataset.ohwSection) === instanceId);
7838
+ if (index === -1) return { isFirst: true, isLast: true };
7839
+ return { isFirst: index === 0, isLast: index === topLevel.length - 1 };
7840
+ }
7328
7841
  var PRIMARY2 = "#0885FE";
7329
7842
  function edgeAwareRadius(rect) {
7330
7843
  const container = window.innerWidth <= 480 ? 16 : 24;
@@ -7398,6 +7911,7 @@ function AiSectionOverlay({
7398
7911
  }) {
7399
7912
  const [selectedId, setSelectedId] = useState4(null);
7400
7913
  const [reviewId, setReviewId] = useState4(null);
7914
+ const [reviewButtonsHidden, setReviewButtonsHidden] = useState4(false);
7401
7915
  const reviewIdRef = useRef4(null);
7402
7916
  reviewIdRef.current = reviewId;
7403
7917
  const selectedIdRef = useRef4(null);
@@ -7406,7 +7920,7 @@ function AiSectionOverlay({
7406
7920
  (el) => {
7407
7921
  postToParent2({
7408
7922
  type: "ow:section-selected",
7409
- sectionId: el?.dataset.ohwSection ?? null,
7923
+ sectionId: el ? el.dataset.ohwInstance ?? el.dataset.ohwSection ?? null : null,
7410
7924
  sectionLabel: el ? el.dataset.ohwSectionLabel ?? titleCaseSectionId(el.dataset.ohwSection ?? "") : null
7411
7925
  });
7412
7926
  },
@@ -7415,7 +7929,7 @@ function AiSectionOverlay({
7415
7929
  const selectFromElement = useCallback2(
7416
7930
  (el, options) => {
7417
7931
  const sectionEl = el?.closest("[data-ohw-section]") ?? null;
7418
- const id = sectionEl?.dataset.ohwSection ?? null;
7932
+ const id = sectionEl ? sectionEl.dataset.ohwInstance ?? sectionEl.dataset.ohwSection ?? null : null;
7419
7933
  if (id === selectedIdRef.current) return;
7420
7934
  setSelectedId(id);
7421
7935
  if (options?.report !== false) report(sectionEl);
@@ -7456,9 +7970,10 @@ function AiSectionOverlay({
7456
7970
  }
7457
7971
  const found = readRect(sectionId) != null;
7458
7972
  setReviewId(found ? sectionId : null);
7973
+ setReviewButtonsHidden(e.data.hideButtons === true);
7459
7974
  postToParent2({ type: "ow:ai-review-started", sectionId, found });
7460
7975
  if (found) {
7461
- document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`)?.scrollIntoView({ behavior: "smooth", block: "nearest" });
7976
+ document.querySelector(`[data-ohw-instance="${CSS.escape(sectionId)}"]`)?.scrollIntoView({ behavior: "smooth", block: "nearest" });
7462
7977
  }
7463
7978
  }
7464
7979
  };
@@ -7477,7 +7992,7 @@ function AiSectionOverlay({
7477
7992
  return;
7478
7993
  }
7479
7994
  const sec = t.closest("[data-ohw-section]");
7480
- setHoveredId(sec?.dataset.ohwSection ?? null);
7995
+ setHoveredId(sec ? sec.dataset.ohwInstance ?? sec.dataset.ohwSection ?? null : null);
7481
7996
  };
7482
7997
  const onLeave = () => setHoveredId(null);
7483
7998
  document.addEventListener("mousemove", onMove, { passive: true });
@@ -7509,9 +8024,30 @@ function AiSectionOverlay({
7509
8024
  },
7510
8025
  [postToParent2]
7511
8026
  );
7512
- const selectionRect = useLiveSectionRect(reviewId ? null : selectedId);
8027
+ const activeSelectionId = reviewId ? null : selectedId;
8028
+ const selectionRect = useLiveSectionRect(activeSelectionId);
7513
8029
  const reviewRect = useLiveSectionRect(reviewId);
7514
8030
  const hoverRect = useLiveSectionRect(reviewId || hoveredId === selectedId ? null : hoveredId);
8031
+ useEffect4(() => {
8032
+ const selectedEl = activeSelectionId ? findSectionElement(activeSelectionId) : null;
8033
+ if (!activeSelectionId || !selectionRect || selectedEl && isChromeSection(selectedEl)) {
8034
+ postToParent2({ type: "ow:section-rect", instanceId: null, rect: null });
8035
+ return;
8036
+ }
8037
+ const { isFirst, isLast } = computeSectionBoundaryFlags(activeSelectionId);
8038
+ postToParent2({
8039
+ type: "ow:section-rect",
8040
+ instanceId: activeSelectionId,
8041
+ rect: {
8042
+ top: selectionRect.top + window.scrollY,
8043
+ left: selectionRect.left + window.scrollX,
8044
+ width: selectionRect.width,
8045
+ height: selectionRect.height
8046
+ },
8047
+ isFirst,
8048
+ isLast
8049
+ });
8050
+ }, [activeSelectionId, selectionRect, postToParent2]);
7515
8051
  return /* @__PURE__ */ jsxs9(Fragment5, { children: [
7516
8052
  hoverRect && /* @__PURE__ */ jsx17(
7517
8053
  "div",
@@ -7562,13 +8098,16 @@ function AiSectionOverlay({
7562
8098
  border: `2px solid ${PRIMARY2}`,
7563
8099
  borderRadius: edgeAwareRadius(reviewRect),
7564
8100
  zIndex: 2147483200,
7565
- // The veil itself: swallows clicks so the section stays locked until decided.
8101
+ // The veil itself: swallows clicks so the section stays locked until decided. This
8102
+ // stopPropagation only guards the bubble phase; the bridge's capture-phase click
8103
+ // handlers additionally bail on `[data-ohw-ai-review]`, without which a click on
8104
+ // Accept/Discard resolves to the media beneath and opens the file picker.
7566
8105
  background: "rgba(8, 133, 254, 0.04)",
7567
8106
  pointerEvents: "auto",
7568
8107
  cursor: "default"
7569
8108
  },
7570
8109
  onClick: (e) => e.stopPropagation(),
7571
- children: /* @__PURE__ */ jsxs9(
8110
+ children: !reviewButtonsHidden && /* @__PURE__ */ jsxs9(
7572
8111
  "div",
7573
8112
  {
7574
8113
  style: {
@@ -7593,6 +8132,58 @@ function AiSectionOverlay({
7593
8132
 
7594
8133
  // src/lib/section-instances.ts
7595
8134
  var SECTION_ORDER_KEY = "__ohw_section_order";
8135
+ function topLevelSections() {
8136
+ return Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
8137
+ (el) => !el.parentElement?.closest("[data-ohw-section]") && !isChromeSection(el)
8138
+ );
8139
+ }
8140
+ function instanceIdOf(el) {
8141
+ return el.dataset.ohwInstance ?? el.dataset.ohwSection ?? "";
8142
+ }
8143
+ function planSectionMove(instanceId, targetIndex, currentPath) {
8144
+ const sections = topLevelSections();
8145
+ const index = sections.findIndex((el) => instanceIdOf(el) === instanceId);
8146
+ if (index === -1) return null;
8147
+ const dragged = sections[index];
8148
+ const others = sections.filter((_, i) => i !== index);
8149
+ const clamped = Math.max(0, Math.min(targetIndex, others.length));
8150
+ const reordered = [...others.slice(0, clamped), dragged, ...others.slice(clamped)];
8151
+ return reordered.map((el, order) => ({
8152
+ instanceId: instanceIdOf(el),
8153
+ type: el.getAttribute("data-ohw-section") ?? "",
8154
+ order,
8155
+ pagePath: currentPath
8156
+ }));
8157
+ }
8158
+ function moveSectionInstance(instanceId, direction, currentPath) {
8159
+ const sections = topLevelSections();
8160
+ const index = sections.findIndex((el) => instanceIdOf(el) === instanceId);
8161
+ if (index === -1) return null;
8162
+ const siblingIndex = direction === "up" ? index - 1 : index + 1;
8163
+ if (siblingIndex < 0 || siblingIndex >= sections.length) return null;
8164
+ const entries = planSectionMove(instanceId, siblingIndex, currentPath);
8165
+ if (!entries) return null;
8166
+ applyPersistedOrder(entries);
8167
+ return entries;
8168
+ }
8169
+ function applyPersistedOrder(entries) {
8170
+ if (entries.length === 0) return;
8171
+ const sections = topLevelSections();
8172
+ if (sections.length === 0) return;
8173
+ const orderIndex = new Map(entries.map((e) => [e.instanceId, e.order]));
8174
+ const ordered = [...sections].sort((a, b) => {
8175
+ const aOrder = orderIndex.get(instanceIdOf(a));
8176
+ const bOrder = orderIndex.get(instanceIdOf(b));
8177
+ if (aOrder === void 0 && bOrder === void 0) return 0;
8178
+ if (aOrder === void 0) return 1;
8179
+ if (bOrder === void 0) return -1;
8180
+ return aOrder - bOrder;
8181
+ });
8182
+ const parent = sections[0].parentElement;
8183
+ if (!parent) return;
8184
+ const anchor = sections[sections.length - 1].nextSibling;
8185
+ ordered.forEach((el) => parent.insertBefore(el, anchor));
8186
+ }
7596
8187
  function getPageSectionOrderEntries(raw, currentPath) {
7597
8188
  if (!raw) return [];
7598
8189
  try {
@@ -7630,6 +8221,7 @@ function initSectionInstancesFromContent(content, currentPath) {
7630
8221
  rekeySectionSubtree(clone, entry.instanceId);
7631
8222
  original.insertAdjacentElement("afterend", clone);
7632
8223
  }
8224
+ applyPersistedOrder(entries);
7633
8225
  }
7634
8226
 
7635
8227
  // src/OhhwellsBridge.tsx
@@ -10174,8 +10766,13 @@ var GLYPH_SELECTOR = "svg, img";
10174
10766
  function referenceBox(slot) {
10175
10767
  const row = slot.closest("[data-ohw-socials-row]") ?? slot.closest("a")?.parentElement ?? null;
10176
10768
  const neighbour = row ? Array.from(row.querySelectorAll('[data-ohw-editable="icon"]')).find((el) => el !== slot) : null;
10177
- const source = (neighbour ?? slot).querySelector(GLYPH_SELECTOR);
10178
- const box = source?.getBoundingClientRect() ?? null;
10769
+ if (neighbour) {
10770
+ const box2 = neighbour.querySelector(GLYPH_SELECTOR)?.getBoundingClientRect();
10771
+ if (box2?.width && box2.height) return box2;
10772
+ }
10773
+ const own = slot.getBoundingClientRect();
10774
+ if (own.width && own.height) return own;
10775
+ const box = slot.querySelector(GLYPH_SELECTOR)?.getBoundingClientRect() ?? null;
10179
10776
  return box?.width && box.height ? box : null;
10180
10777
  }
10181
10778
  function iconMarkupSizedFor(slot, markup) {
@@ -10254,7 +10851,8 @@ function findSocialsRow(el) {
10254
10851
  }
10255
10852
  function isSocialsRow(el) {
10256
10853
  const anchors = Array.from(el.querySelectorAll("a"));
10257
- return anchors.length > 0 && anchors.every((anchor) => isSocialItem(anchor));
10854
+ if (!anchors.length || !anchors.some((anchor) => isSocialItem(anchor))) return false;
10855
+ return findSocialsRow(anchors[0]) === el;
10258
10856
  }
10259
10857
  var MAX_SOCIAL_ITEMS_PER_ROW = 7;
10260
10858
  function canAddSocialItem(row) {
@@ -10292,6 +10890,7 @@ function markSocialsRows(root = document) {
10292
10890
  });
10293
10891
  listSocialsRows(root).forEach((row) => {
10294
10892
  row.setAttribute(SOCIALS_ROW_ATTR, "");
10893
+ allowRowToWrap(row);
10295
10894
  const items = listSocialItems(row);
10296
10895
  const firstUnit = items[0] ? socialRowUnit(items[0], row) : null;
10297
10896
  if (firstUnit) rowTemplates.set(rowKeyOf(row), firstUnit.outerHTML);
@@ -10586,7 +11185,29 @@ function applySocialsDisplayToRow(row, display) {
10586
11185
  const icon = item.querySelector(ICON_SELECTOR);
10587
11186
  if (label) label.style.display = display.text ? "" : "none";
10588
11187
  if (icon) icon.style.display = display.icon ? "" : "none";
11188
+ layOutIconAndLabel(item, Boolean(display.text && display.icon));
10589
11189
  });
11190
+ allowRowToWrap(row);
11191
+ }
11192
+ function layOutIconAndLabel(item, on) {
11193
+ const hasBoth = Boolean(item.querySelector(ICON_SELECTOR)) && Boolean(socialLabelElement(item));
11194
+ if (!hasBoth) {
11195
+ item.style.display = "";
11196
+ item.style.alignItems = "";
11197
+ item.style.gap = "";
11198
+ item.style.whiteSpace = "";
11199
+ item.style.flex = "";
11200
+ return;
11201
+ }
11202
+ item.style.display = on ? "inline-flex" : "";
11203
+ item.style.alignItems = on ? "center" : "";
11204
+ item.style.gap = on ? "8px" : "";
11205
+ item.style.whiteSpace = on ? "nowrap" : "";
11206
+ item.style.flex = on ? "0 0 auto" : "";
11207
+ }
11208
+ function allowRowToWrap(row) {
11209
+ const display = row.ownerDocument.defaultView?.getComputedStyle(row).display ?? "";
11210
+ if (display === "flex" || display === "inline-flex") row.style.flexWrap = "wrap";
10590
11211
  }
10591
11212
  function applySocialsDisplayFromContent(content, root = document) {
10592
11213
  const stored = parseSocialsDisplay(content[SOCIALS_DISPLAY_KEY]);
@@ -11796,6 +12417,7 @@ function readLogoSizeState(content, placement) {
11796
12417
  function getLogoElement(el) {
11797
12418
  const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
11798
12419
  if (marked) return marked;
12420
+ if (el.closest('[data-ohw-editable="icon"]')) return null;
11799
12421
  const root = el.closest("nav, [data-ohw-nav-root], footer");
11800
12422
  if (!root) return null;
11801
12423
  const anchor = el.closest("a");
@@ -12675,101 +13297,428 @@ function useNavItemDrag({
12675
13297
  };
12676
13298
  }
12677
13299
 
12678
- // src/ui/footer-container-chrome.tsx
12679
- import { Plus as Plus2 } from "lucide-react";
12680
- import { jsx as jsx29, jsxs as jsxs19 } from "react/jsx-runtime";
12681
- function FooterContainerChrome({
12682
- rect,
12683
- onAdd,
12684
- addDisabled = false
12685
- }) {
12686
- const chromeGap = 6;
12687
- const buttonMargin = 7;
12688
- return /* @__PURE__ */ jsx29(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsx29(
12689
- "div",
12690
- {
12691
- "data-ohw-footer-container-chrome": "",
12692
- "data-ohw-bridge": "",
12693
- className: "pointer-events-none fixed z-[2147483647]",
12694
- style: {
12695
- top: rect.top - chromeGap,
12696
- left: rect.left - chromeGap,
12697
- width: rect.width + chromeGap * 2,
12698
- height: rect.height + chromeGap * 2
12699
- },
12700
- children: /* @__PURE__ */ jsxs19(Tooltip, { children: [
12701
- /* @__PURE__ */ jsx29(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx29(
12702
- "button",
12703
- {
12704
- type: "button",
12705
- "data-ohw-footer-add-button": "",
12706
- disabled: addDisabled,
12707
- 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",
12708
- style: { top: chromeGap - buttonMargin },
12709
- "aria-label": "Add item",
12710
- onMouseDown: (e) => {
12711
- e.preventDefault();
12712
- e.stopPropagation();
12713
- },
12714
- onClick: (e) => {
12715
- e.preventDefault();
12716
- e.stopPropagation();
12717
- if (addDisabled) return;
12718
- onAdd();
12719
- },
12720
- children: /* @__PURE__ */ jsx29(Plus2, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
12721
- }
12722
- ) }),
12723
- /* @__PURE__ */ jsx29(TooltipContent, { side: "bottom", sideOffset: 9, children: addDisabled ? "Maximum columns reached" : "Add item" })
12724
- ] })
12725
- }
12726
- ) });
12727
- }
13300
+ // src/useSectionDrag.ts
13301
+ import { useCallback as useCallback7, useEffect as useEffect11, useRef as useRef9, useState as useState10 } from "react";
12728
13302
 
12729
- // src/lib/carousel.ts
12730
- import { useEffect as useEffect11, useState as useState10 } from "react";
12731
- var CAROUSEL_ATTR = "data-ohw-carousel";
12732
- var CAROUSEL_VALUE_ATTR = "data-ohw-carousel-value";
12733
- var CAROUSEL_SLIDE_ATTR = "data-ohw-carousel-slide";
12734
- var CAROUSEL_EVENT = "ohw:carousel-change";
12735
- function listCarouselKeys() {
12736
- const keys = /* @__PURE__ */ new Set();
12737
- document.querySelectorAll(`[${CAROUSEL_ATTR}]`).forEach((el) => {
12738
- const key = el.getAttribute("data-ohw-key");
12739
- if (key) keys.add(key);
12740
- });
12741
- return [...keys];
13303
+ // src/lib/section-dnd.ts
13304
+ function isFooterSection(el) {
13305
+ return el.dataset.ohwSection === "footer";
12742
13306
  }
12743
- function containersForKey(key) {
12744
- return Array.from(
12745
- document.querySelectorAll(`[${CAROUSEL_ATTR}][data-ohw-key="${CSS.escape(key)}"]`)
13307
+ function buildSectionDropSlots(draggedInstanceId) {
13308
+ const sections = topLevelSections().filter(
13309
+ (el) => instanceIdOf(el) !== draggedInstanceId && !isFooterSection(el)
12746
13310
  );
13311
+ const slots = [];
13312
+ if (sections.length === 0) return slots;
13313
+ const left = 0;
13314
+ const width = document.documentElement.clientWidth;
13315
+ for (let i = 0; i <= sections.length; i++) {
13316
+ let y;
13317
+ if (i === 0) {
13318
+ y = sections[0].getBoundingClientRect().top;
13319
+ } else if (i === sections.length) {
13320
+ y = sections[sections.length - 1].getBoundingClientRect().bottom;
13321
+ } else {
13322
+ const prev = sections[i - 1].getBoundingClientRect();
13323
+ const next = sections[i].getBoundingClientRect();
13324
+ y = (prev.bottom + next.top) / 2;
13325
+ }
13326
+ slots.push({ insertIndex: i, y, left, width });
13327
+ }
13328
+ return slots;
12747
13329
  }
12748
- function isCarouselKey(key) {
12749
- return containersForKey(key).length > 0;
12750
- }
12751
- function parseSlides(raw) {
12752
- if (!raw) return [];
12753
- try {
12754
- const parsed = JSON.parse(raw);
12755
- if (!Array.isArray(parsed)) return [];
12756
- return parsed.filter((s) => Boolean(s) && typeof s === "object").map((s) => ({ src: String(s.src ?? ""), alt: String(s.alt ?? "") }));
12757
- } catch {
12758
- return [];
13330
+ function hitTestSectionDropSlot(y, slots) {
13331
+ let best = null;
13332
+ for (const slot of slots) {
13333
+ const dist = Math.abs(y - slot.y);
13334
+ if (!best || dist < best.dist) best = { slot, dist };
12759
13335
  }
13336
+ return best?.slot ?? null;
12760
13337
  }
12761
- function readCarouselValue(key) {
12762
- const container = containersForKey(key)[0];
12763
- if (!container) return [];
12764
- const fromAttr = parseSlides(container.getAttribute(CAROUSEL_VALUE_ATTR));
12765
- if (fromAttr.length > 0) return fromAttr;
12766
- return Array.from(container.querySelectorAll(`[${CAROUSEL_SLIDE_ATTR}]`)).filter((slide) => slide.closest(`[${CAROUSEL_ATTR}]`) === container).sort((a, b) => slideIndex(a) - slideIndex(b)).map((slide) => {
12767
- const img = slide instanceof HTMLImageElement ? slide : slide.querySelector("img");
12768
- return { src: img?.src ?? "", alt: img?.alt ?? "" };
12769
- });
13338
+
13339
+ // src/useSectionDrag.ts
13340
+ var PRESS_THRESHOLD = 10;
13341
+ var EDGE_ZONE = 60;
13342
+ var MAX_AUTO_SCROLL_SPEED = 18;
13343
+ var SECTION_DRAG_EXCLUDED_SELECTOR = [
13344
+ "[data-ohw-toolbar]",
13345
+ "[data-ohw-edit-chrome]",
13346
+ "[data-ohw-item-interaction]",
13347
+ "[data-ohw-drag-handle-container]",
13348
+ '[data-slot="drag-handle"]',
13349
+ "[data-ohw-item-toolbar-anchor]",
13350
+ "[data-ohw-item-drag-surface]",
13351
+ "[data-ohw-more-menu]",
13352
+ '[data-slot="dropdown-menu-content"]',
13353
+ '[data-slot="dropdown-menu-item"]',
13354
+ "[data-ohw-state-toggle]",
13355
+ "[data-ohw-max-badge]",
13356
+ "[data-ohw-floating-panel]",
13357
+ "[data-ohw-section-picker]",
13358
+ "[data-ohw-link-popover-root]",
13359
+ "[data-ohw-link-modal-root]",
13360
+ "[data-ohw-link-page-dropdown]",
13361
+ '[data-slot="popover-content"]',
13362
+ '[data-slot="dialog-content"]',
13363
+ '[data-slot="dialog-overlay"]',
13364
+ "[data-ohw-ai-review]",
13365
+ "[data-ohw-editable]",
13366
+ "[data-ohw-editable-state]",
13367
+ "[contenteditable]",
13368
+ "[data-ohw-href-key]",
13369
+ "[data-ohw-footer-col]",
13370
+ "[data-ohw-social-label]",
13371
+ "a",
13372
+ "button",
13373
+ '[role="button"]',
13374
+ '[data-ohw-role="navbar-button"]',
13375
+ '[data-ohw-role="button"]',
13376
+ "[data-ohw-carousel]",
13377
+ "[data-ohw-carousel-value]",
13378
+ "[data-ohw-carousel-slide]",
13379
+ "[data-ohw-carousel-overlay]",
13380
+ "[data-ohw-media-chrome]",
13381
+ "[data-ohw-media-overlay]",
13382
+ "[data-ohw-media-skeleton]"
13383
+ ].join(", ");
13384
+ function visibleClip(ps) {
13385
+ if (!ps) return null;
13386
+ const top = Math.max(0, ps.headerH - ps.iframeOffsetTop);
13387
+ const bottom = Math.min(window.innerHeight, ps.headerH + ps.canvasH - ps.iframeOffsetTop);
13388
+ return { top, bottom: Math.max(top, bottom) };
12770
13389
  }
12771
- function slideIndex(el) {
12772
- const raw = el.getAttribute(CAROUSEL_SLIDE_ATTR);
13390
+ function useSectionDrag({
13391
+ isEditMode,
13392
+ editContentRef,
13393
+ postToParentRef,
13394
+ parentScrollRef,
13395
+ navDragRef,
13396
+ footerDragRef,
13397
+ suppressNextClickRef,
13398
+ suppressClickUntilRef
13399
+ }) {
13400
+ const sectionDragRef = useRef9(null);
13401
+ const [sectionDropSlots, setSectionDropSlots] = useState10([]);
13402
+ const [activeSectionDropIndex, setActiveSectionDropIndex] = useState10(null);
13403
+ const [isSectionDragging, setIsSectionDragging] = useState10(false);
13404
+ const sectionPointerDragRef = useRef9(null);
13405
+ const autoScrollRafRef = useRef9(null);
13406
+ const autoScrollDeltaRef = useRef9(0);
13407
+ const stopAutoScroll = useCallback7(() => {
13408
+ if (autoScrollRafRef.current != null) {
13409
+ cancelAnimationFrame(autoScrollRafRef.current);
13410
+ autoScrollRafRef.current = null;
13411
+ }
13412
+ autoScrollDeltaRef.current = 0;
13413
+ }, []);
13414
+ const tickAutoScroll = useCallback7(() => {
13415
+ if (!sectionDragRef.current) {
13416
+ stopAutoScroll();
13417
+ return;
13418
+ }
13419
+ if (autoScrollDeltaRef.current !== 0) {
13420
+ postToParentRef.current({ type: "ow:request-scroll", deltaY: autoScrollDeltaRef.current });
13421
+ }
13422
+ autoScrollRafRef.current = requestAnimationFrame(tickAutoScroll);
13423
+ }, [postToParentRef, stopAutoScroll]);
13424
+ const updateAutoScroll = useCallback7(
13425
+ (clientY) => {
13426
+ const clip = visibleClip(parentScrollRef.current);
13427
+ let delta = 0;
13428
+ if (clip) {
13429
+ const distTop = clientY - clip.top;
13430
+ const distBottom = clip.bottom - clientY;
13431
+ if (distTop >= 0 && distTop < EDGE_ZONE) {
13432
+ delta = -MAX_AUTO_SCROLL_SPEED * (1 - distTop / EDGE_ZONE);
13433
+ } else if (distBottom >= 0 && distBottom < EDGE_ZONE) {
13434
+ delta = MAX_AUTO_SCROLL_SPEED * (1 - distBottom / EDGE_ZONE);
13435
+ }
13436
+ }
13437
+ autoScrollDeltaRef.current = delta;
13438
+ if (delta !== 0 && autoScrollRafRef.current == null) {
13439
+ autoScrollRafRef.current = requestAnimationFrame(tickAutoScroll);
13440
+ } else if (delta === 0) {
13441
+ stopAutoScroll();
13442
+ }
13443
+ },
13444
+ [parentScrollRef, stopAutoScroll, tickAutoScroll]
13445
+ );
13446
+ const clearSectionDragVisuals = useCallback7(() => {
13447
+ sectionDragRef.current?.draggedEl.removeAttribute("data-ohw-section-dragging");
13448
+ sectionDragRef.current = null;
13449
+ setSectionDropSlots([]);
13450
+ setActiveSectionDropIndex(null);
13451
+ setIsSectionDragging(false);
13452
+ stopAutoScroll();
13453
+ document.documentElement.removeAttribute("data-ohw-section-dragging-root");
13454
+ unlockItemDragInteraction();
13455
+ }, [stopAutoScroll]);
13456
+ const refreshSectionDragVisuals = useCallback7(
13457
+ (session, clientX, clientY) => {
13458
+ session.lastClientX = clientX;
13459
+ session.lastClientY = clientY;
13460
+ const slots = buildSectionDropSlots(session.instanceId);
13461
+ const activeSlot = hitTestSectionDropSlot(clientY, slots);
13462
+ session.activeSlot = activeSlot;
13463
+ setSectionDropSlots(slots);
13464
+ const activeIdx = activeSlot ? slots.findIndex((s) => s.insertIndex === activeSlot.insertIndex) : -1;
13465
+ setActiveSectionDropIndex(activeIdx >= 0 ? activeIdx : null);
13466
+ updateAutoScroll(clientY);
13467
+ },
13468
+ [updateAutoScroll]
13469
+ );
13470
+ const beginSectionDrag = useCallback7(
13471
+ (session) => {
13472
+ sectionDragRef.current = session;
13473
+ setIsSectionDragging(true);
13474
+ lockItemDuringDrag();
13475
+ document.documentElement.setAttribute("data-ohw-section-dragging-root", "");
13476
+ session.draggedEl.setAttribute("data-ohw-section-dragging", "");
13477
+ refreshSectionDragVisuals(session, session.lastClientX, session.lastClientY);
13478
+ },
13479
+ [refreshSectionDragVisuals]
13480
+ );
13481
+ const commitSectionDrag = useCallback7(() => {
13482
+ const session = sectionDragRef.current;
13483
+ if (!session) {
13484
+ clearSectionDragVisuals();
13485
+ return;
13486
+ }
13487
+ const slot = session.activeSlot ?? hitTestSectionDropSlot(session.lastClientY, buildSectionDropSlots(session.instanceId));
13488
+ const entries = slot ? planSectionMove(session.instanceId, slot.insertIndex, window.location.pathname) : null;
13489
+ if (!entries) {
13490
+ clearSectionDragVisuals();
13491
+ return;
13492
+ }
13493
+ const orderJson = JSON.stringify(entries);
13494
+ editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
13495
+ postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
13496
+ applyPersistedOrder(entries);
13497
+ clearSectionDragVisuals();
13498
+ requestAnimationFrame(() => {
13499
+ if (editContentRef.current[SECTION_ORDER_KEY] === orderJson) {
13500
+ applyPersistedOrder(entries);
13501
+ }
13502
+ requestAnimationFrame(() => {
13503
+ window.dispatchEvent(new Event("resize"));
13504
+ });
13505
+ });
13506
+ }, [clearSectionDragVisuals, editContentRef, postToParentRef]);
13507
+ const startSectionPressDrag = useCallback7(
13508
+ (el, clientX, clientY, pointerId) => {
13509
+ if (navDragRef.current || footerDragRef.current || sectionDragRef.current) return false;
13510
+ const instanceId = instanceIdOf(el);
13511
+ if (!instanceId) return false;
13512
+ sectionPointerDragRef.current = {
13513
+ el,
13514
+ instanceId,
13515
+ startX: clientX,
13516
+ startY: clientY,
13517
+ pointerId,
13518
+ started: false
13519
+ };
13520
+ return true;
13521
+ },
13522
+ [footerDragRef, navDragRef]
13523
+ );
13524
+ useEffect11(() => {
13525
+ if (!isEditMode) return;
13526
+ const onPointerDown = (e) => {
13527
+ if (e.button !== 0) return;
13528
+ if (navDragRef.current || footerDragRef.current || sectionDragRef.current) return;
13529
+ if (sectionPointerDragRef.current) return;
13530
+ const target = e.target;
13531
+ if (!(target instanceof HTMLElement)) return;
13532
+ if (target.closest(SECTION_DRAG_EXCLUDED_SELECTOR)) return;
13533
+ const sectionEl = target.closest("[data-ohw-section]");
13534
+ if (!sectionEl || isChromeSection(sectionEl) || sectionEl.dataset.ohwSection === "footer") return;
13535
+ if (!topLevelSections().includes(sectionEl)) return;
13536
+ startSectionPressDrag(sectionEl, e.clientX, e.clientY, e.pointerId);
13537
+ };
13538
+ const onPointerMove = (e) => {
13539
+ const pending = sectionPointerDragRef.current;
13540
+ if (!pending) return;
13541
+ if (pending.started) {
13542
+ e.preventDefault();
13543
+ clearTextSelection();
13544
+ const session = sectionDragRef.current;
13545
+ if (!session) return;
13546
+ refreshSectionDragVisuals(session, e.clientX, e.clientY);
13547
+ return;
13548
+ }
13549
+ const dx = e.clientX - pending.startX;
13550
+ const dy = e.clientY - pending.startY;
13551
+ if (dx * dx + dy * dy < PRESS_THRESHOLD * PRESS_THRESHOLD) return;
13552
+ e.preventDefault();
13553
+ pending.started = true;
13554
+ armItemPressDrag();
13555
+ clearTextSelection();
13556
+ try {
13557
+ document.body.setPointerCapture(pending.pointerId);
13558
+ } catch {
13559
+ }
13560
+ beginSectionDrag({
13561
+ instanceId: pending.instanceId,
13562
+ draggedEl: pending.el,
13563
+ lastClientX: e.clientX,
13564
+ lastClientY: e.clientY,
13565
+ activeSlot: null
13566
+ });
13567
+ };
13568
+ const endPointerDrag = (e) => {
13569
+ const pending = sectionPointerDragRef.current;
13570
+ sectionPointerDragRef.current = null;
13571
+ try {
13572
+ if (document.body.hasPointerCapture(e.pointerId)) {
13573
+ document.body.releasePointerCapture(e.pointerId);
13574
+ }
13575
+ } catch {
13576
+ }
13577
+ if (!pending) return;
13578
+ if (!pending.started) {
13579
+ unlockItemDragInteraction();
13580
+ return;
13581
+ }
13582
+ suppressNextClickRef.current = true;
13583
+ suppressClickUntilRef.current = Date.now() + 500;
13584
+ commitSectionDrag();
13585
+ };
13586
+ const onKeyDown = (e) => {
13587
+ if (e.key !== "Escape") return;
13588
+ if (!sectionDragRef.current && !sectionPointerDragRef.current) return;
13589
+ sectionPointerDragRef.current = null;
13590
+ clearSectionDragVisuals();
13591
+ };
13592
+ document.addEventListener("pointerdown", onPointerDown, true);
13593
+ document.addEventListener("pointermove", onPointerMove, true);
13594
+ document.addEventListener("pointerup", endPointerDrag, true);
13595
+ document.addEventListener("pointercancel", endPointerDrag, true);
13596
+ document.addEventListener("keydown", onKeyDown, true);
13597
+ return () => {
13598
+ document.removeEventListener("pointerdown", onPointerDown, true);
13599
+ document.removeEventListener("pointermove", onPointerMove, true);
13600
+ document.removeEventListener("pointerup", endPointerDrag, true);
13601
+ document.removeEventListener("pointercancel", endPointerDrag, true);
13602
+ document.removeEventListener("keydown", onKeyDown, true);
13603
+ unlockItemDragInteraction();
13604
+ stopAutoScroll();
13605
+ };
13606
+ }, [
13607
+ beginSectionDrag,
13608
+ clearSectionDragVisuals,
13609
+ commitSectionDrag,
13610
+ footerDragRef,
13611
+ isEditMode,
13612
+ navDragRef,
13613
+ refreshSectionDragVisuals,
13614
+ startSectionPressDrag,
13615
+ stopAutoScroll,
13616
+ suppressClickUntilRef,
13617
+ suppressNextClickRef
13618
+ ]);
13619
+ return {
13620
+ sectionDragRef,
13621
+ sectionDropSlots,
13622
+ activeSectionDropIndex,
13623
+ isSectionDragging
13624
+ };
13625
+ }
13626
+
13627
+ // src/ui/footer-container-chrome.tsx
13628
+ import { Plus as Plus2 } from "lucide-react";
13629
+ import { jsx as jsx29, jsxs as jsxs19 } from "react/jsx-runtime";
13630
+ function FooterContainerChrome({
13631
+ rect,
13632
+ onAdd,
13633
+ addDisabled = false
13634
+ }) {
13635
+ const chromeGap = 6;
13636
+ const buttonMargin = 7;
13637
+ return /* @__PURE__ */ jsx29(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsx29(
13638
+ "div",
13639
+ {
13640
+ "data-ohw-footer-container-chrome": "",
13641
+ "data-ohw-bridge": "",
13642
+ className: "pointer-events-none fixed z-[2147483647]",
13643
+ style: {
13644
+ top: rect.top - chromeGap,
13645
+ left: rect.left - chromeGap,
13646
+ width: rect.width + chromeGap * 2,
13647
+ height: rect.height + chromeGap * 2
13648
+ },
13649
+ children: /* @__PURE__ */ jsxs19(Tooltip, { children: [
13650
+ /* @__PURE__ */ jsx29(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx29(
13651
+ "button",
13652
+ {
13653
+ type: "button",
13654
+ "data-ohw-footer-add-button": "",
13655
+ disabled: addDisabled,
13656
+ 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",
13657
+ style: { top: chromeGap - buttonMargin },
13658
+ "aria-label": "Add item",
13659
+ onMouseDown: (e) => {
13660
+ e.preventDefault();
13661
+ e.stopPropagation();
13662
+ },
13663
+ onClick: (e) => {
13664
+ e.preventDefault();
13665
+ e.stopPropagation();
13666
+ if (addDisabled) return;
13667
+ onAdd();
13668
+ },
13669
+ children: /* @__PURE__ */ jsx29(Plus2, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
13670
+ }
13671
+ ) }),
13672
+ /* @__PURE__ */ jsx29(TooltipContent, { side: "bottom", sideOffset: 9, children: addDisabled ? "Maximum columns reached" : "Add item" })
13673
+ ] })
13674
+ }
13675
+ ) });
13676
+ }
13677
+
13678
+ // src/lib/carousel.ts
13679
+ import { useEffect as useEffect12, useState as useState11 } from "react";
13680
+ var CAROUSEL_ATTR = "data-ohw-carousel";
13681
+ var CAROUSEL_VALUE_ATTR = "data-ohw-carousel-value";
13682
+ var CAROUSEL_SLIDE_ATTR = "data-ohw-carousel-slide";
13683
+ var CAROUSEL_EVENT = "ohw:carousel-change";
13684
+ function listCarouselKeys() {
13685
+ const keys = /* @__PURE__ */ new Set();
13686
+ document.querySelectorAll(`[${CAROUSEL_ATTR}]`).forEach((el) => {
13687
+ const key = el.getAttribute("data-ohw-key");
13688
+ if (key) keys.add(key);
13689
+ });
13690
+ return [...keys];
13691
+ }
13692
+ function containersForKey(key) {
13693
+ return Array.from(
13694
+ document.querySelectorAll(`[${CAROUSEL_ATTR}][data-ohw-key="${CSS.escape(key)}"]`)
13695
+ );
13696
+ }
13697
+ function isCarouselKey(key) {
13698
+ return containersForKey(key).length > 0;
13699
+ }
13700
+ function parseSlides(raw) {
13701
+ if (!raw) return [];
13702
+ try {
13703
+ const parsed = JSON.parse(raw);
13704
+ if (!Array.isArray(parsed)) return [];
13705
+ return parsed.filter((s) => Boolean(s) && typeof s === "object").map((s) => ({ src: String(s.src ?? ""), alt: String(s.alt ?? "") }));
13706
+ } catch {
13707
+ return [];
13708
+ }
13709
+ }
13710
+ function readCarouselValue(key) {
13711
+ const container = containersForKey(key)[0];
13712
+ if (!container) return [];
13713
+ const fromAttr = parseSlides(container.getAttribute(CAROUSEL_VALUE_ATTR));
13714
+ if (fromAttr.length > 0) return fromAttr;
13715
+ return Array.from(container.querySelectorAll(`[${CAROUSEL_SLIDE_ATTR}]`)).filter((slide) => slide.closest(`[${CAROUSEL_ATTR}]`) === container).sort((a, b) => slideIndex(a) - slideIndex(b)).map((slide) => {
13716
+ const img = slide instanceof HTMLImageElement ? slide : slide.querySelector("img");
13717
+ return { src: img?.src ?? "", alt: img?.alt ?? "" };
13718
+ });
13719
+ }
13720
+ function slideIndex(el) {
13721
+ const raw = el.getAttribute(CAROUSEL_SLIDE_ATTR);
12773
13722
  const n = raw ? parseInt(raw, 10) : NaN;
12774
13723
  return Number.isFinite(n) ? n : 0;
12775
13724
  }
@@ -12789,8 +13738,8 @@ function applyCarouselNode(key, val) {
12789
13738
  return true;
12790
13739
  }
12791
13740
  function useOhwCarousel(key, initial) {
12792
- const [images, setImages] = useState10(initial);
12793
- useEffect11(() => {
13741
+ const [images, setImages] = useState11(initial);
13742
+ useEffect12(() => {
12794
13743
  const el = document.querySelector(
12795
13744
  `[${CAROUSEL_ATTR}][data-ohw-key="${CSS.escape(key)}"]`
12796
13745
  );
@@ -12868,7 +13817,7 @@ function collectEditableNodes(extraContent, root = document) {
12868
13817
  nodes.push({ key, type: "link", text: href });
12869
13818
  }
12870
13819
  if (extraContent) {
12871
- for (const key of [NAV_ORDER_KEY, FOOTER_ORDER_KEY, NAV_COUNT_KEY]) {
13820
+ for (const key of [NAV_ORDER_KEY, FOOTER_ORDER_KEY, NAV_COUNT_KEY, SECTION_ORDER_KEY]) {
12872
13821
  const text = extraContent[key];
12873
13822
  if (typeof text === "string" && text.length > 0) {
12874
13823
  nodes.push({ key, type: "meta", text });
@@ -12905,6 +13854,18 @@ function collectEditableNodes(extraContent, root = document) {
12905
13854
  }
12906
13855
  if (extraContent && !isScoped) {
12907
13856
  applyNavFooterDeleteOverrides(byKey, extraContent);
13857
+ for (const key of LOGO_IMAGE_KEYS) {
13858
+ if (!(key in extraContent)) continue;
13859
+ byKey.set(key, { key, type: "image", text: extraContent[key] ?? "" });
13860
+ }
13861
+ for (const key of [LOGO_PLACEHOLDER_KEY, LOGO_ALT_KEY]) {
13862
+ if (!(key in extraContent)) continue;
13863
+ byKey.set(key, { key, type: "meta", text: extraContent[key] ?? "" });
13864
+ }
13865
+ for (const key of LOGO_SIZE_KEYS) {
13866
+ if (!(key in extraContent)) continue;
13867
+ byKey.set(key, { key, type: "meta", text: extraContent[key] ?? "" });
13868
+ }
12908
13869
  }
12909
13870
  return Array.from(byKey.values());
12910
13871
  }
@@ -13309,6 +14270,7 @@ function fadeInImageElement(img, onReady) {
13309
14270
  function applyEditableImageSrc(img, url) {
13310
14271
  img.removeAttribute("srcset");
13311
14272
  img.removeAttribute("sizes");
14273
+ if (img.loading === "lazy") img.loading = "eager";
13312
14274
  img.src = url;
13313
14275
  }
13314
14276
  function fadeInBgImage(el, url, onReady) {
@@ -13373,21 +14335,10 @@ function parseSchedulingInsertAfter(insertAfter) {
13373
14335
  insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
13374
14336
  };
13375
14337
  }
13376
- function resolveSchedulingInsert(insertAfter, explicitInsertBefore) {
13377
- const parsed = parseSchedulingInsertAfter(insertAfter);
13378
- const insertBefore = explicitInsertBefore ?? parsed.insertBefore;
13379
- const effectiveInsertAfter = insertBefore && parsed.insertBefore === null ? `${insertAfter}${INSERT_BEFORE_MARKER}${insertBefore}` : insertAfter;
13380
- return { effectiveInsertAfter, insertBefore };
13381
- }
13382
- function getSchedulingMountPoint(insertAfter) {
13383
- const { anchor } = parseSchedulingInsertAfter(insertAfter);
13384
- let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
13385
- if (!anchorEl && anchor === "scheduling") {
13386
- const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
13387
- anchorEl = widgets.at(-1) ?? null;
13388
- }
13389
- if (!anchorEl) return null;
13390
- return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
14338
+ function resolveEntryAnchor(entry) {
14339
+ if (entry.anchorId !== void 0) return { anchorId: entry.anchorId, beforeId: entry.beforeId ?? null };
14340
+ const parsed = parseSchedulingInsertAfter(entry.insertAfter);
14341
+ return { anchorId: parsed.anchor, beforeId: parsed.insertBefore };
13391
14342
  }
13392
14343
  function schedulingMountDepth(insertAfter) {
13393
14344
  if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
@@ -13404,8 +14355,7 @@ function getPageSchedulingEntries(raw) {
13404
14355
  }
13405
14356
  }
13406
14357
  function isSchedulingWidgetMissing(entry) {
13407
- const { effectiveInsertAfter } = resolveSchedulingInsert(entry.insertAfter);
13408
- return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
14358
+ return !document.querySelector(`[data-ohw-section="${schedulingSectionId(entry.insertAfter)}"]`);
13409
14359
  }
13410
14360
  function hasMissingSchedulingWidgets(entries) {
13411
14361
  return entries.some(isSchedulingWidgetMissing);
@@ -13435,16 +14385,17 @@ function initSectionsFromContent(content, removeExisting = false) {
13435
14385
  } catch {
13436
14386
  }
13437
14387
  }
13438
- function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId, explicitInsertBefore) {
13439
- const { effectiveInsertAfter, insertBefore } = resolveSchedulingInsert(insertAfter, explicitInsertBefore);
13440
- const sectionId = schedulingSectionId(effectiveInsertAfter);
14388
+ function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, beforeId, existingWidgetId) {
14389
+ const widgetId = existingWidgetId ?? (beforeId ? `${anchorId}${INSERT_BEFORE_MARKER}${beforeId}` : anchorId);
14390
+ const sectionId = schedulingSectionId(widgetId);
13441
14391
  if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
13442
- const mountPoint = getSchedulingMountPoint(effectiveInsertAfter);
13443
- if (!mountPoint) return false;
14392
+ const anchorEl = document.querySelector(`[data-ohw-section="${anchorId}"]`);
14393
+ if (!anchorEl) return false;
14394
+ const mountPoint = anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
13444
14395
  const container = document.createElement("div");
13445
14396
  container.dataset.ohwSectionContainer = "scheduling";
13446
- if (insertBefore) {
13447
- const beforeAnchor = document.querySelector(`[data-ohw-section="${insertBefore}"]`);
14397
+ if (beforeId) {
14398
+ const beforeAnchor = document.querySelector(`[data-ohw-section="${beforeId}"]`);
13448
14399
  const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
13449
14400
  if (!beforePoint) return false;
13450
14401
  beforePoint.insertAdjacentElement("beforebegin", container);
@@ -13455,19 +14406,25 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
13455
14406
  }
13456
14407
  tail.insertAdjacentElement("afterend", container);
13457
14408
  }
13458
- const root = createRoot2(container);
13459
- flushSync2(() => {
13460
- root.render(
13461
- /* @__PURE__ */ jsx33(
13462
- SchedulingWidget,
13463
- {
13464
- notifyOnConnect,
13465
- initialScheduleId: scheduleId,
13466
- insertAfter: effectiveInsertAfter
13467
- }
13468
- )
13469
- );
13470
- });
14409
+ try {
14410
+ const root = createRoot2(container);
14411
+ flushSync2(() => {
14412
+ root.render(
14413
+ /* @__PURE__ */ jsx33(
14414
+ SchedulingWidget,
14415
+ {
14416
+ notifyOnConnect,
14417
+ initialScheduleId: scheduleId,
14418
+ insertAfter: widgetId
14419
+ }
14420
+ )
14421
+ );
14422
+ });
14423
+ } catch (err) {
14424
+ console.error("[ow:scheduling] render threw", err);
14425
+ container.remove();
14426
+ return false;
14427
+ }
13471
14428
  const tracker = getSectionsTracker();
13472
14429
  let sections = [];
13473
14430
  try {
@@ -13475,10 +14432,12 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
13475
14432
  } catch {
13476
14433
  }
13477
14434
  const inEditor = typeof window !== "undefined" && window.self !== window.top;
13478
- if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === effectiveInsertAfter)) {
14435
+ if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === widgetId)) {
13479
14436
  sections.push({
13480
14437
  type: "scheduling",
13481
- insertAfter: effectiveInsertAfter,
14438
+ insertAfter: widgetId,
14439
+ anchorId,
14440
+ beforeId: beforeId ?? null,
13482
14441
  pagePath: window.location.pathname,
13483
14442
  ...scheduleId ? { scheduleId } : {}
13484
14443
  });
@@ -13492,7 +14451,8 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
13492
14451
  for (let i = pending.length - 1; i >= 0; i--) {
13493
14452
  const entry = pending[i];
13494
14453
  const shouldNotify = typeof notifyOnConnect === "function" ? notifyOnConnect(entry) : notifyOnConnect;
13495
- if (mountSchedulingWidget(entry.insertAfter, shouldNotify, entry.scheduleId)) {
14454
+ const { anchorId, beforeId } = resolveEntryAnchor(entry);
14455
+ if (mountSchedulingWidget(anchorId, shouldNotify, entry.scheduleId ?? null, beforeId, entry.insertAfter)) {
13496
14456
  pending.splice(i, 1);
13497
14457
  }
13498
14458
  }
@@ -13640,6 +14600,13 @@ function isInsideLinkEditor(target) {
13640
14600
  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"]')
13641
14601
  );
13642
14602
  }
14603
+ function isInsideFloatingPanel(target) {
14604
+ return Boolean(target.closest("[data-ohw-floating-panel]"));
14605
+ }
14606
+ function isPointOverFloatingPanel(clientX, clientY) {
14607
+ const el = document.elementFromPoint(clientX, clientY);
14608
+ return Boolean(el instanceof Element && el.closest("[data-ohw-floating-panel]"));
14609
+ }
13643
14610
  function getHrefKeyFromElement(el) {
13644
14611
  if (!el) return null;
13645
14612
  const anchor = el.closest("[data-ohw-href-key]");
@@ -13885,7 +14852,7 @@ function getNavigationSelectionParent(el) {
13885
14852
  if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
13886
14853
  return getFooterLinksContainer();
13887
14854
  }
13888
- if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
14855
+ 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)) {
13889
14856
  return getNavigationRoot(el);
13890
14857
  }
13891
14858
  return null;
@@ -14478,6 +15445,45 @@ function StateToggle({
14478
15445
  );
14479
15446
  }
14480
15447
  var contentCache = /* @__PURE__ */ new Map();
15448
+ var fetchedContentPaths = /* @__PURE__ */ new Set();
15449
+ var OHW_LOADER_STYLE = {
15450
+ position: "fixed",
15451
+ inset: 0,
15452
+ background: "#fff",
15453
+ zIndex: 2147483646,
15454
+ display: "flex",
15455
+ alignItems: "center",
15456
+ justifyContent: "center"
15457
+ };
15458
+ function OhwLoaderSpinner() {
15459
+ return /* @__PURE__ */ jsxs20("svg", { width: "28", height: "28", viewBox: "0 0 28 28", fill: "none", "aria-hidden": true, children: [
15460
+ /* @__PURE__ */ jsx33("circle", { cx: "14", cy: "14", r: "11", stroke: "#E7E5E4", strokeWidth: "3" }),
15461
+ /* @__PURE__ */ jsx33(
15462
+ "circle",
15463
+ {
15464
+ cx: "14",
15465
+ cy: "14",
15466
+ r: "11",
15467
+ stroke: "#1C1917",
15468
+ strokeWidth: "3",
15469
+ strokeDasharray: "17 52",
15470
+ strokeLinecap: "round",
15471
+ children: /* @__PURE__ */ jsx33(
15472
+ "animateTransform",
15473
+ {
15474
+ attributeName: "transform",
15475
+ type: "rotate",
15476
+ from: "0 14 14",
15477
+ to: "360 14 14",
15478
+ dur: "0.7s",
15479
+ repeatCount: "indefinite"
15480
+ }
15481
+ )
15482
+ }
15483
+ )
15484
+ ] });
15485
+ }
15486
+ 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){}})();`;
14481
15487
  function resolveSubdomain(subdomainFromQuery) {
14482
15488
  if (subdomainFromQuery) return subdomainFromQuery;
14483
15489
  if (typeof window !== "undefined") {
@@ -14500,8 +15506,8 @@ function OhhwellsBridge() {
14500
15506
  const router = useRouter3();
14501
15507
  const searchParams = useSearchParams();
14502
15508
  const isEditMode = isEditSessionActive();
14503
- const [bridgeRoot, setBridgeRoot] = useState11(null);
14504
- useEffect12(() => {
15509
+ const [bridgeRoot, setBridgeRoot] = useState12(null);
15510
+ useEffect13(() => {
14505
15511
  const figtreeFontId = "ohw-figtree-font";
14506
15512
  if (!document.getElementById(figtreeFontId)) {
14507
15513
  const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
@@ -14530,86 +15536,150 @@ function OhhwellsBridge() {
14530
15536
  const subdomain = resolveSubdomain(subdomainFromQuery);
14531
15537
  useLinkHrefGuardian(pathname, subdomain, isEditMode);
14532
15538
  useSavedLinkNavigation(isEditMode);
14533
- const postToParent2 = useCallback7((data) => {
15539
+ const postToParent2 = useCallback8((data) => {
14534
15540
  if (typeof window !== "undefined" && window.parent !== window) {
14535
15541
  window.parent.postMessage(data, "*");
14536
15542
  }
14537
15543
  }, []);
14538
- const [fetchState, setFetchState] = useState11("idle");
14539
- const autoSaveTimers = useRef9(/* @__PURE__ */ new Map());
14540
- const activeElRef = useRef9(null);
14541
- const pointerHeldRef = useRef9(false);
14542
- const selectedElRef = useRef9(null);
14543
- const selectedHrefKeyRef = useRef9(null);
14544
- const selectedFooterColAttrRef = useRef9(null);
14545
- const originalContentRef = useRef9(null);
14546
- const activeStateElRef = useRef9(null);
14547
- const parentScrollRef = useRef9(null);
14548
- const visibleViewportRef = useRef9(null);
14549
- const [dialogPortalContainer, setDialogPortalContainer] = useState11(null);
14550
- const attachVisibleViewport = useCallback7((node) => {
15544
+ const [fetchState, setFetchState] = useState12("idle");
15545
+ const autoSaveTimers = useRef10(/* @__PURE__ */ new Map());
15546
+ const activeElRef = useRef10(null);
15547
+ const pointerHeldRef = useRef10(false);
15548
+ const selectedElRef = useRef10(null);
15549
+ const selectedHrefKeyRef = useRef10(null);
15550
+ const selectedFooterColAttrRef = useRef10(null);
15551
+ const originalContentRef = useRef10(null);
15552
+ const activeStateElRef = useRef10(null);
15553
+ const parentScrollRef = useRef10(null);
15554
+ const visibleViewportRef = useRef10(null);
15555
+ const [dialogPortalContainer, setDialogPortalContainer] = useState12(null);
15556
+ const attachVisibleViewport = useCallback8((node) => {
14551
15557
  visibleViewportRef.current = node;
14552
15558
  setDialogPortalContainer(node);
14553
15559
  if (node) applyVisibleViewport(node, parentScrollRef.current);
14554
15560
  }, []);
14555
- const toolbarElRef = useRef9(null);
14556
- const glowElRef = useRef9(null);
14557
- const hoveredImageRef = useRef9(null);
14558
- const hoveredImageHasTextOverlapRef = useRef9(false);
14559
- const dragOverElRef = useRef9(null);
14560
- const [mediaHover, setMediaHover] = useState11(null);
14561
- const [carouselHover, setCarouselHover] = useState11(null);
14562
- const [uploadingRects, setUploadingRects] = useState11({});
14563
- const hoveredGapRef = useRef9(null);
14564
- const imageUnhoverTimerRef = useRef9(null);
14565
- const imageShowTimerRef = useRef9(null);
14566
- const editStylesRef = useRef9(null);
14567
- const activateRef = useRef9(() => {
14568
- });
14569
- const deactivateRef = useRef9(() => {
14570
- });
14571
- const selectRef = useRef9(() => {
14572
- });
14573
- const selectFrameRef = useRef9(() => {
14574
- });
14575
- const selectLogoRef = useRef9(() => {
14576
- });
14577
- const openLogoSizePanelRef = useRef9(() => {
14578
- });
14579
- const deselectRef = useRef9(() => {
14580
- });
14581
- const closeFloatingPanelOnlyRef = useRef9(() => {
14582
- });
14583
- const reselectNavigationItemRef = useRef9(() => {
14584
- });
14585
- const commitNavigationTextEditRef = useRef9(() => {
14586
- });
14587
- const handleDeleteSelectedRef = useRef9(() => false);
14588
- const runPendingDeleteUndoRef = useRef9(() => false);
14589
- const isFooterFrameSelectionRef = useRef9(false);
14590
- const refreshActiveCommandsRef = useRef9(() => {
14591
- });
14592
- const postToParentRef = useRef9(postToParent2);
14593
- postToParentRef.current = postToParent2;
14594
- const aiSectionApiRef = useRef9(null);
14595
- const sectionsLoadedRef = useRef9(false);
14596
- const pendingScheduleConfigRequests = useRef9([]);
14597
- const [toolbarRect, setToolbarRect] = useState11(null);
14598
- const [formPickRect, setFormPickRect] = useState11(null);
14599
- const formPickElRef = useRef9(null);
14600
- const [formViewState, setFormViewStateUi] = useState11("default");
14601
- const [formPickCount, setFormPickCount] = useState11(null);
14602
- const [formHoverRect, setFormHoverRect] = useState11(null);
14603
- const formHoverElRef = useRef9(null);
14604
- const [fieldPickRect, setFieldPickRect] = useState11(null);
14605
- const fieldPickElRef = useRef9(null);
14606
- const [fieldPickState, setFieldPickState] = useState11(null);
14607
- const [fieldTypePickerOpen, setFieldTypePickerOpen] = useState11(false);
14608
- const clearFormPick = useCallback7(() => {
14609
- const form = formPickElRef.current;
14610
- const editing = fieldPickElRef.current;
14611
- if (commitPlaceholderEdit(editing) && editing) {
14612
- const owner = editing.closest('[data-ohw-editable="form"]');
15561
+ const toolbarElRef = useRef10(null);
15562
+ const glowElRef = useRef10(null);
15563
+ const hoveredImageRef = useRef10(null);
15564
+ const hoveredImageHasTextOverlapRef = useRef10(false);
15565
+ const dragOverElRef = useRef10(null);
15566
+ const [mediaHover, setMediaHover] = useState12(null);
15567
+ const [selectedMedia, setSelectedMedia] = useState12(null);
15568
+ const selectedMediaElRef = useRef10(null);
15569
+ const clearMediaSelection = useCallback8(() => {
15570
+ const prev = selectedMediaElRef.current;
15571
+ selectedMediaElRef.current = null;
15572
+ setSelectedMedia(null);
15573
+ const sectionEl = prev?.closest("[data-ohw-section]") ?? null;
15574
+ if (sectionEl) {
15575
+ postToParentRef.current({
15576
+ type: "ow:section-selected",
15577
+ sectionId: sectionEl.dataset.ohwSection ?? null,
15578
+ sectionLabel: sectionEl.dataset.ohwSectionLabel ?? titleCaseSectionId(sectionEl.dataset.ohwSection ?? ""),
15579
+ key: null
15580
+ });
15581
+ }
15582
+ }, []);
15583
+ const clearMediaSelectionRef = useRef10(clearMediaSelection);
15584
+ clearMediaSelectionRef.current = clearMediaSelection;
15585
+ const selectMediaElement = useCallback8((el) => {
15586
+ const r2 = el.getBoundingClientRect();
15587
+ const video = el.dataset.ohwEditable === "video" ? el.querySelector("video") : null;
15588
+ selectedMediaElRef.current = el;
15589
+ setSelectedMedia({
15590
+ key: el.dataset.ohwKey ?? "",
15591
+ rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height },
15592
+ elementType: el.dataset.ohwEditable ?? "image",
15593
+ hasTextOverlap: false,
15594
+ isDragOver: false,
15595
+ ...video ? { videoAutoplay: video.autoplay, videoMuted: video.muted } : {}
15596
+ });
15597
+ const sectionEl = el.closest("[data-ohw-section]");
15598
+ aiSectionApiRef.current?.selectFromElement(el, { report: false });
15599
+ postToParentRef.current({
15600
+ type: "ow:section-selected",
15601
+ sectionId: sectionEl?.dataset.ohwSection ?? null,
15602
+ sectionLabel: sectionEl ? sectionEl.dataset.ohwSectionLabel ?? titleCaseSectionId(sectionEl.dataset.ohwSection ?? "") : null,
15603
+ key: el.dataset.ohwKey ?? null,
15604
+ // Display name for the pill — the raw key prettifies into fragments ("Img"); the
15605
+ // bridge knows what the node IS, so it names it.
15606
+ keyLabel: el.dataset.ohwEditable === "video" ? "Video" : el.dataset.ohwEditable === "bg-image" ? "Background" : "Image"
15607
+ });
15608
+ }, []);
15609
+ const selectMediaElementRef = useRef10(selectMediaElement);
15610
+ selectMediaElementRef.current = selectMediaElement;
15611
+ useEffect13(() => {
15612
+ if (!selectedMedia) return;
15613
+ const update = () => {
15614
+ const el = selectedMediaElRef.current;
15615
+ if (!el || !el.isConnected) {
15616
+ clearMediaSelection();
15617
+ return;
15618
+ }
15619
+ const r2 = el.getBoundingClientRect();
15620
+ setSelectedMedia(
15621
+ (prev) => prev ? { ...prev, rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height } } : prev
15622
+ );
15623
+ };
15624
+ window.addEventListener("scroll", update, true);
15625
+ window.addEventListener("resize", update);
15626
+ return () => {
15627
+ window.removeEventListener("scroll", update, true);
15628
+ window.removeEventListener("resize", update);
15629
+ };
15630
+ }, [selectedMedia !== null]);
15631
+ const [carouselHover, setCarouselHover] = useState12(null);
15632
+ const [uploadingRects, setUploadingRects] = useState12({});
15633
+ const hoveredGapRef = useRef10(null);
15634
+ const imageUnhoverTimerRef = useRef10(null);
15635
+ const imageShowTimerRef = useRef10(null);
15636
+ const editStylesRef = useRef10(null);
15637
+ const activateRef = useRef10(() => {
15638
+ });
15639
+ const deactivateRef = useRef10(() => {
15640
+ });
15641
+ const selectRef = useRef10(() => {
15642
+ });
15643
+ const selectFrameRef = useRef10(() => {
15644
+ });
15645
+ const selectLogoRef = useRef10(() => {
15646
+ });
15647
+ const openLogoSizePanelRef = useRef10(() => {
15648
+ });
15649
+ const deselectRef = useRef10(() => {
15650
+ });
15651
+ const closeFloatingPanelOnlyRef = useRef10(() => {
15652
+ });
15653
+ const reselectNavigationItemRef = useRef10(() => {
15654
+ });
15655
+ const commitNavigationTextEditRef = useRef10(() => {
15656
+ });
15657
+ const handleDeleteSelectedRef = useRef10(() => false);
15658
+ const runPendingDeleteUndoRef = useRef10(() => false);
15659
+ const isFooterFrameSelectionRef = useRef10(false);
15660
+ const refreshActiveCommandsRef = useRef10(() => {
15661
+ });
15662
+ const postToParentRef = useRef10(postToParent2);
15663
+ postToParentRef.current = postToParent2;
15664
+ const aiSectionApiRef = useRef10(null);
15665
+ const sectionsLoadedRef = useRef10(false);
15666
+ const pendingScheduleConfigRequests = useRef10([]);
15667
+ const [toolbarRect, setToolbarRect] = useState12(null);
15668
+ const [formPickRect, setFormPickRect] = useState12(null);
15669
+ const formPickElRef = useRef10(null);
15670
+ const [formViewState, setFormViewStateUi] = useState12("default");
15671
+ const [formPickCount, setFormPickCount] = useState12(null);
15672
+ const [formHoverRect, setFormHoverRect] = useState12(null);
15673
+ const formHoverElRef = useRef10(null);
15674
+ const [fieldPickRect, setFieldPickRect] = useState12(null);
15675
+ const fieldPickElRef = useRef10(null);
15676
+ const [fieldPickState, setFieldPickState] = useState12(null);
15677
+ const [fieldTypePickerOpen, setFieldTypePickerOpen] = useState12(false);
15678
+ const clearFormPick = useCallback8(() => {
15679
+ const form = formPickElRef.current;
15680
+ const editing = fieldPickElRef.current;
15681
+ if (commitPlaceholderEdit(editing) && editing) {
15682
+ const owner = editing.closest('[data-ohw-editable="form"]');
14613
15683
  if (owner) persistFieldsRef.current(owner);
14614
15684
  }
14615
15685
  if (form) {
@@ -14625,7 +15695,7 @@ function OhhwellsBridge() {
14625
15695
  formPickElRef.current = null;
14626
15696
  setFormPickRect(null);
14627
15697
  }, []);
14628
- const clearFieldPick = useCallback7(() => {
15698
+ const clearFieldPick = useCallback8(() => {
14629
15699
  const wrapper = fieldPickElRef.current;
14630
15700
  if (commitPlaceholderEdit(wrapper) && wrapper) {
14631
15701
  const form = wrapper.closest('[data-ohw-editable="form"]');
@@ -14635,9 +15705,9 @@ function OhhwellsBridge() {
14635
15705
  setFieldPickRect(null);
14636
15706
  setFieldPickState(null);
14637
15707
  }, []);
14638
- const persistFieldsRef = useRef9(() => {
15708
+ const persistFieldsRef = useRef10(() => {
14639
15709
  });
14640
- const persistFields = useCallback7(
15710
+ const persistFields = useCallback8(
14641
15711
  (form) => {
14642
15712
  const key = formKeyOf(form);
14643
15713
  if (!key) return;
@@ -14648,7 +15718,7 @@ function OhhwellsBridge() {
14648
15718
  []
14649
15719
  );
14650
15720
  persistFieldsRef.current = persistFields;
14651
- const selectField = useCallback7((wrapper) => {
15721
+ const selectField = useCallback8((wrapper) => {
14652
15722
  if (fieldPickElRef.current && fieldPickElRef.current !== wrapper) {
14653
15723
  commitPlaceholderEdit(fieldPickElRef.current);
14654
15724
  }
@@ -14661,7 +15731,7 @@ function OhhwellsBridge() {
14661
15731
  setFieldPickState({ type: fieldTypeOf(wrapper), required: isFieldRequired(wrapper) });
14662
15732
  setFieldTypePickerOpen(false);
14663
15733
  }, []);
14664
- const withSelectedField = useCallback7(
15734
+ const withSelectedField = useCallback8(
14665
15735
  (run) => {
14666
15736
  const wrapper = fieldPickElRef.current;
14667
15737
  const form = formPickElRef.current;
@@ -14674,28 +15744,28 @@ function OhhwellsBridge() {
14674
15744
  },
14675
15745
  [persistFields]
14676
15746
  );
14677
- const handleFieldTypeChange = useCallback7(
15747
+ const handleFieldTypeChange = useCallback8(
14678
15748
  (type) => withSelectedField((_form, wrapper) => {
14679
15749
  applyFieldType(wrapper, type);
14680
15750
  selectField(wrapper);
14681
15751
  }),
14682
15752
  [selectField, withSelectedField]
14683
15753
  );
14684
- const handleFieldRequiredToggle = useCallback7(
15754
+ const handleFieldRequiredToggle = useCallback8(
14685
15755
  () => withSelectedField((_form, wrapper) => {
14686
15756
  setFieldRequired(wrapper, !isFieldRequired(wrapper));
14687
15757
  selectField(wrapper);
14688
15758
  }),
14689
15759
  [selectField, withSelectedField]
14690
15760
  );
14691
- const handleFieldDuplicate = useCallback7(
15761
+ const handleFieldDuplicate = useCallback8(
14692
15762
  () => withSelectedField((form, wrapper) => {
14693
15763
  const copy = duplicateField(form, wrapper);
14694
15764
  selectField(copy);
14695
15765
  }),
14696
15766
  [selectField, withSelectedField]
14697
15767
  );
14698
- const handleFieldDelete = useCallback7(
15768
+ const handleFieldDelete = useCallback8(
14699
15769
  () => withSelectedField((_form, wrapper) => {
14700
15770
  removeField(wrapper);
14701
15771
  clearFieldPick();
@@ -14703,7 +15773,7 @@ function OhhwellsBridge() {
14703
15773
  }),
14704
15774
  [clearFieldPick, withSelectedField]
14705
15775
  );
14706
- const handleAddField = useCallback7(
15776
+ const handleAddField = useCallback8(
14707
15777
  (type) => {
14708
15778
  const form = formPickElRef.current;
14709
15779
  if (!form) return;
@@ -14719,8 +15789,8 @@ function OhhwellsBridge() {
14719
15789
  },
14720
15790
  [persistFields, selectField]
14721
15791
  );
14722
- const fieldDragRef = useRef9(null);
14723
- const buildFieldDropSlots = useCallback7((form, draggedKey) => {
15792
+ const fieldDragRef = useRef10(null);
15793
+ const buildFieldDropSlots = useCallback8((form, draggedKey) => {
14724
15794
  const others = listFieldWrappers(form).filter((el) => fieldKeyOf(el) !== draggedKey);
14725
15795
  const slots = others.map((el) => {
14726
15796
  const rect = el.getBoundingClientRect();
@@ -14733,7 +15803,7 @@ function OhhwellsBridge() {
14733
15803
  }
14734
15804
  return slots;
14735
15805
  }, []);
14736
- const handleFieldDragStart = useCallback7(() => {
15806
+ const handleFieldDragStart = useCallback8(() => {
14737
15807
  const wrapper = fieldPickElRef.current;
14738
15808
  const form = formPickElRef.current;
14739
15809
  if (!wrapper || !form) return;
@@ -14742,18 +15812,18 @@ function OhhwellsBridge() {
14742
15812
  setFieldDragging(true);
14743
15813
  setFieldDropSlots(buildFieldDropSlots(form, key));
14744
15814
  }, [buildFieldDropSlots]);
14745
- const handleFieldDragEnd = useCallback7(() => {
15815
+ const handleFieldDragEnd = useCallback8(() => {
14746
15816
  fieldDragRef.current = null;
14747
15817
  setFieldDropIndex(null);
14748
15818
  setFieldDropSlots([]);
14749
15819
  setFieldDragging(false);
14750
15820
  }, []);
14751
- const [fieldDropIndex, setFieldDropIndex] = useState11(null);
14752
- const [fieldDropSlots, setFieldDropSlots] = useState11([]);
14753
- const [fieldDragging, setFieldDragging] = useState11(false);
14754
- const clearFormPickRef = useRef9(clearFormPick);
15821
+ const [fieldDropIndex, setFieldDropIndex] = useState12(null);
15822
+ const [fieldDropSlots, setFieldDropSlots] = useState12([]);
15823
+ const [fieldDragging, setFieldDragging] = useState12(false);
15824
+ const clearFormPickRef = useRef10(clearFormPick);
14755
15825
  clearFormPickRef.current = clearFormPick;
14756
- useEffect12(() => {
15826
+ useEffect13(() => {
14757
15827
  const el = fieldPickElRef.current;
14758
15828
  if (!el || fieldPickRect === null) return;
14759
15829
  const observer = new ResizeObserver(() => {
@@ -14762,7 +15832,7 @@ function OhhwellsBridge() {
14762
15832
  observer.observe(el);
14763
15833
  return () => observer.disconnect();
14764
15834
  }, [fieldPickRect !== null, fieldPickState]);
14765
- useEffect12(() => {
15835
+ useEffect13(() => {
14766
15836
  const el = formPickElRef.current;
14767
15837
  if (!el || formPickRect === null) return;
14768
15838
  const observer = new ResizeObserver(() => {
@@ -14771,25 +15841,25 @@ function OhhwellsBridge() {
14771
15841
  observer.observe(el);
14772
15842
  return () => observer.disconnect();
14773
15843
  }, [formPickRect !== null, formViewState]);
14774
- const [toolbarVariant, setToolbarVariant] = useState11("none");
14775
- const toolbarVariantRef = useRef9("none");
15844
+ const [toolbarVariant, setToolbarVariant] = useState12("none");
15845
+ const toolbarVariantRef = useRef10("none");
14776
15846
  toolbarVariantRef.current = toolbarVariant;
14777
- const [selectedIsCta, setSelectedIsCta] = useState11(false);
14778
- const [selectedIsSocial, setSelectedIsSocial] = useState11(false);
14779
- const [selectedIsSocialsRow, setSelectedIsSocialsRow] = useState11(false);
14780
- const [reorderHrefKey, setReorderHrefKey] = useState11(null);
14781
- const [reorderDragDisabled, setReorderDragDisabled] = useState11(false);
14782
- const [toggleState, setToggleState] = useState11(null);
14783
- const [maxBadge, setMaxBadge] = useState11(null);
14784
- const [activeCommands, setActiveCommands] = useState11(/* @__PURE__ */ new Set());
14785
- const [sectionGap, setSectionGap] = useState11(null);
14786
- const [toolbarShowEditLink, setToolbarShowEditLink] = useState11(false);
14787
- const hoveredNavContainerRef = useRef9(null);
14788
- const [hoveredNavContainerRect, setHoveredNavContainerRect] = useState11(null);
14789
- const hoveredItemElRef = useRef9(null);
14790
- const [hoveredItemRect, setHoveredItemRect] = useState11(null);
14791
- const [hoveredTextRect, setHoveredTextRect] = useState11(null);
14792
- useEffect12(() => {
15847
+ const [selectedIsCta, setSelectedIsCta] = useState12(false);
15848
+ const [selectedIsSocial, setSelectedIsSocial] = useState12(false);
15849
+ const [selectedIsSocialsRow, setSelectedIsSocialsRow] = useState12(false);
15850
+ const [reorderHrefKey, setReorderHrefKey] = useState12(null);
15851
+ const [reorderDragDisabled, setReorderDragDisabled] = useState12(false);
15852
+ const [toggleState, setToggleState] = useState12(null);
15853
+ const [maxBadge, setMaxBadge] = useState12(null);
15854
+ const [activeCommands, setActiveCommands] = useState12(/* @__PURE__ */ new Set());
15855
+ const [sectionGap, setSectionGap] = useState12(null);
15856
+ const [toolbarShowEditLink, setToolbarShowEditLink] = useState12(false);
15857
+ const hoveredNavContainerRef = useRef10(null);
15858
+ const [hoveredNavContainerRect, setHoveredNavContainerRect] = useState12(null);
15859
+ const hoveredItemElRef = useRef10(null);
15860
+ const [hoveredItemRect, setHoveredItemRect] = useState12(null);
15861
+ const [hoveredTextRect, setHoveredTextRect] = useState12(null);
15862
+ useEffect13(() => {
14793
15863
  const sync = () => {
14794
15864
  const el = document.querySelector(
14795
15865
  '[data-ohw-hovered]:not([contenteditable]):not([data-ohw-href-key]):not([data-ohw-editable="form"] *)'
@@ -14810,43 +15880,56 @@ function OhhwellsBridge() {
14810
15880
  });
14811
15881
  return () => observer.disconnect();
14812
15882
  }, []);
14813
- const siblingHintElRef = useRef9(null);
14814
- const [siblingHintRect, setSiblingHintRect] = useState11(null);
14815
- const [siblingHintRects, setSiblingHintRects] = useState11([]);
14816
- const [isItemDragging, setIsItemDragging] = useState11(false);
14817
- const [isFooterFrameSelection, setIsFooterFrameSelection] = useState11(false);
15883
+ const siblingHintElRef = useRef10(null);
15884
+ const [siblingHintRect, setSiblingHintRect] = useState12(null);
15885
+ const [siblingHintRects, setSiblingHintRects] = useState12([]);
15886
+ const [isItemDragging, setIsItemDragging] = useState12(false);
15887
+ const [isFooterFrameSelection, setIsFooterFrameSelection] = useState12(false);
14818
15888
  isFooterFrameSelectionRef.current = isFooterFrameSelection;
14819
- const [floatingPanel, setFloatingPanel] = useState11(null);
14820
- const floatingPanelOpenRef = useRef9(false);
14821
- floatingPanelOpenRef.current = floatingPanel !== null;
14822
- const [floatingPanelPos, setFloatingPanelPos] = useState11(null);
14823
- const [logoSizeDraft, setLogoSizeDraft] = useState11(null);
14824
- const [editorViewport, setEditorViewport] = useState11("desktop");
14825
- const [parentScrollSnap, setParentScrollSnap] = useState11(null);
14826
- const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = useState11(null);
14827
- const [footerHeadingVisible, setFooterHeadingVisible] = useState11(null);
14828
- const footerDragRef = useRef9(null);
14829
- const [footerDropSlots, setFooterDropSlots] = useState11([]);
14830
- const [activeFooterDropIndex, setActiveFooterDropIndex] = useState11(null);
14831
- const [draggedItemRect, setDraggedItemRect] = useState11(null);
14832
- const footerPointerDragRef = useRef9(null);
14833
- const suppressNextClickRef = useRef9(false);
14834
- const suppressClickUntilRef = useRef9(0);
14835
- const [linkPopover, setLinkPopover] = useState11(null);
14836
- const linkPopoverSessionRef = useRef9(null);
14837
- const addNavAfterAnchorRef = useRef9(null);
14838
- const editContentRef = useRef9({});
14839
- const aiSectionsRef = useRef9("");
14840
- const pendingDeleteUndoRef = useRef9(null);
14841
- const [sitePages, setSitePages] = useState11([]);
14842
- const [sectionsByPath, setSectionsByPath] = useState11({});
14843
- const sectionsPrefetchGenRef = useRef9(0);
14844
- const setLinkPopoverRef = useRef9(setLinkPopover);
14845
- const linkPopoverPanelRef = useRef9(null);
14846
- const linkPopoverOpenRef = useRef9(false);
14847
- const linkPopoverGraceUntilRef = useRef9(0);
15889
+ const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = useState12(null);
15890
+ const [footerHeadingVisible, setFooterHeadingVisible] = useState12(null);
15891
+ const footerDragRef = useRef10(null);
15892
+ const [footerDropSlots, setFooterDropSlots] = useState12([]);
15893
+ const [activeFooterDropIndex, setActiveFooterDropIndex] = useState12(null);
15894
+ const [draggedItemRect, setDraggedItemRect] = useState12(null);
15895
+ const footerPointerDragRef = useRef10(null);
15896
+ const suppressNextClickRef = useRef10(false);
15897
+ const suppressClickUntilRef = useRef10(0);
15898
+ const [linkPopover, setLinkPopover] = useState12(null);
15899
+ const linkPopoverSessionRef = useRef10(null);
15900
+ const addNavAfterAnchorRef = useRef10(null);
15901
+ const editContentRef = useRef10({});
15902
+ const aiSectionsRef = useRef10("");
15903
+ const brandKitRef = useRef10("");
15904
+ const stylesRef = useRef10("");
15905
+ const pendingDeleteUndoRef = useRef10(null);
15906
+ const [floatingPanel, setFloatingPanel] = useState12(null);
15907
+ const floatingPanelOpenRef = useRef10(false);
15908
+ const setFloatingPanelRef = useRef10(setFloatingPanel);
15909
+ const [floatingPanelPos, setFloatingPanelPos] = useState12(null);
15910
+ const [logoSizeDraft, setLogoSizeDraft] = useState12(null);
15911
+ const [editorViewport, setEditorViewport] = useState12("desktop");
15912
+ const [parentScrollSnap, setParentScrollSnap] = useState12(null);
15913
+ const [sitePages, setSitePages] = useState12([]);
15914
+ const [sectionsByPath, setSectionsByPath] = useState12({});
15915
+ const sectionsPrefetchGenRef = useRef10(0);
15916
+ const setLinkPopoverRef = useRef10(setLinkPopover);
15917
+ const linkPopoverPanelRef = useRef10(null);
15918
+ const linkPopoverOpenRef = useRef10(false);
15919
+ const linkPopoverGraceUntilRef = useRef10(0);
14848
15920
  setLinkPopoverRef.current = setLinkPopover;
15921
+ setFloatingPanelRef.current = setFloatingPanel;
14849
15922
  linkPopoverSessionRef.current = linkPopover;
15923
+ floatingPanelOpenRef.current = Boolean(floatingPanel);
15924
+ useEffect13(() => {
15925
+ const syncViewport = () => {
15926
+ const next = window.innerWidth <= 480 ? "mobile" : "desktop";
15927
+ setEditorViewport((prev) => prev === next ? prev : next);
15928
+ };
15929
+ syncViewport();
15930
+ window.addEventListener("resize", syncViewport);
15931
+ return () => window.removeEventListener("resize", syncViewport);
15932
+ }, []);
14850
15933
  const {
14851
15934
  navDragRef,
14852
15935
  navDropSlots,
@@ -14879,10 +15962,20 @@ function OhhwellsBridge() {
14879
15962
  getNavigationItemAnchor,
14880
15963
  isDragHandleDisabled
14881
15964
  });
15965
+ const { sectionDropSlots, activeSectionDropIndex, isSectionDragging } = useSectionDrag({
15966
+ isEditMode,
15967
+ editContentRef,
15968
+ postToParentRef,
15969
+ parentScrollRef,
15970
+ navDragRef,
15971
+ footerDragRef,
15972
+ suppressNextClickRef,
15973
+ suppressClickUntilRef
15974
+ });
14882
15975
  const bumpLinkPopoverGrace = () => {
14883
15976
  linkPopoverGraceUntilRef.current = Date.now() + 350;
14884
15977
  };
14885
- const runSectionsPrefetch = useCallback7((pages) => {
15978
+ const runSectionsPrefetch = useCallback8((pages) => {
14886
15979
  if (!isEditMode || shouldUseDevFixtures() || pages.length === 0) return;
14887
15980
  const gen = ++sectionsPrefetchGenRef.current;
14888
15981
  const paths = pages.map((p) => p.path);
@@ -14901,9 +15994,9 @@ function OhhwellsBridge() {
14901
15994
  );
14902
15995
  });
14903
15996
  }, [isEditMode, pathname]);
14904
- const runSectionsPrefetchRef = useRef9(runSectionsPrefetch);
15997
+ const runSectionsPrefetchRef = useRef10(runSectionsPrefetch);
14905
15998
  runSectionsPrefetchRef.current = runSectionsPrefetch;
14906
- useEffect12(() => {
15999
+ useEffect13(() => {
14907
16000
  if (!linkPopover) {
14908
16001
  document.documentElement.removeAttribute("data-ohw-link-popover-open");
14909
16002
  return;
@@ -14931,7 +16024,7 @@ function OhhwellsBridge() {
14931
16024
  document.documentElement.removeAttribute("data-ohw-link-popover-open");
14932
16025
  };
14933
16026
  }, [linkPopover, postToParent2]);
14934
- useEffect12(() => {
16027
+ useEffect13(() => {
14935
16028
  if (!isEditMode) return;
14936
16029
  const useFixtures = shouldUseDevFixtures();
14937
16030
  if (useFixtures) {
@@ -14955,14 +16048,14 @@ function OhhwellsBridge() {
14955
16048
  if (!useFixtures) postToParent2({ type: "ow:request-site-pages" });
14956
16049
  return () => window.removeEventListener("message", onSitePages);
14957
16050
  }, [isEditMode, postToParent2]);
14958
- useEffect12(() => {
16051
+ useEffect13(() => {
14959
16052
  if (!isEditMode || shouldUseDevFixtures()) return;
14960
16053
  void loadAllSectionsManifest().then((manifest) => {
14961
16054
  if (Object.keys(manifest).length === 0) return;
14962
16055
  setSectionsByPath((prev) => ({ ...manifest, ...prev }));
14963
16056
  });
14964
16057
  }, [isEditMode]);
14965
- useEffect12(() => {
16058
+ useEffect13(() => {
14966
16059
  const update = () => {
14967
16060
  const el = activeElRef.current ?? selectedElRef.current;
14968
16061
  if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
@@ -14986,10 +16079,10 @@ function OhhwellsBridge() {
14986
16079
  vvp.removeEventListener("resize", update);
14987
16080
  };
14988
16081
  }, []);
14989
- const refreshStateRules = useCallback7(() => {
16082
+ const refreshStateRules = useCallback8(() => {
14990
16083
  editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
14991
16084
  }, []);
14992
- const processConfigRequest = useCallback7((insertAfterVal) => {
16085
+ const processConfigRequest = useCallback8((insertAfterVal) => {
14993
16086
  const tracker = getSectionsTracker();
14994
16087
  let entries = [];
14995
16088
  try {
@@ -15012,7 +16105,7 @@ function OhhwellsBridge() {
15012
16105
  }
15013
16106
  window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: null }, "*");
15014
16107
  }, [isEditMode]);
15015
- const deactivate = useCallback7(() => {
16108
+ const deactivate = useCallback8(() => {
15016
16109
  const el = activeElRef.current;
15017
16110
  if (!el) return;
15018
16111
  const isFormBlock = el.dataset.ohwEditable === "form";
@@ -15053,12 +16146,12 @@ function OhhwellsBridge() {
15053
16146
  setToolbarShowEditLink(false);
15054
16147
  postToParent2({ type: "ow:exit-edit" });
15055
16148
  }, [postToParent2]);
15056
- const clearSelectedAttr = useCallback7(() => {
16149
+ const clearSelectedAttr = useCallback8(() => {
15057
16150
  document.querySelectorAll("[data-ohw-selected]").forEach((el) => {
15058
16151
  el.removeAttribute("data-ohw-selected");
15059
16152
  });
15060
16153
  }, []);
15061
- const deselect = useCallback7(() => {
16154
+ const deselect = useCallback8(() => {
15062
16155
  clearSelectedAttr();
15063
16156
  selectedElRef.current = null;
15064
16157
  selectedHrefKeyRef.current = null;
@@ -15087,11 +16180,12 @@ function OhhwellsBridge() {
15087
16180
  setToolbarVariant("none");
15088
16181
  }
15089
16182
  }, [clearSelectedAttr]);
15090
- const markSelected = useCallback7((el) => {
16183
+ const markSelected = useCallback8((el) => {
15091
16184
  clearSelectedAttr();
16185
+ el.removeAttribute("data-ohw-hovered");
15092
16186
  el.setAttribute("data-ohw-selected", "");
15093
16187
  }, [clearSelectedAttr]);
15094
- const resolveHrefKeyElement = useCallback7((hrefKey) => {
16188
+ const resolveHrefKeyElement = useCallback8((hrefKey) => {
15095
16189
  if (isFooterHrefKey(hrefKey)) {
15096
16190
  return document.querySelector(
15097
16191
  `footer [data-ohw-href-key="${CSS.escape(hrefKey)}"]`
@@ -15106,7 +16200,7 @@ function OhhwellsBridge() {
15106
16200
  `[data-ohw-href-key="${CSS.escape(hrefKey)}"]`
15107
16201
  );
15108
16202
  }, []);
15109
- const resyncSelectedNavigationItem = useCallback7(() => {
16203
+ const resyncSelectedNavigationItem = useCallback8(() => {
15110
16204
  const hrefKey = selectedHrefKeyRef.current;
15111
16205
  if (hrefKey) {
15112
16206
  const link = resolveHrefKeyElement(hrefKey);
@@ -15144,7 +16238,7 @@ function OhhwellsBridge() {
15144
16238
  );
15145
16239
  }
15146
16240
  }, [resolveHrefKeyElement]);
15147
- const reselectNavigationItem = useCallback7((navAnchor) => {
16241
+ const reselectNavigationItem = useCallback8((navAnchor) => {
15148
16242
  selectedElRef.current = navAnchor;
15149
16243
  selectedHrefKeyRef.current = navAnchor.getAttribute("data-ohw-href-key");
15150
16244
  selectedFooterColAttrRef.current = null;
@@ -15175,7 +16269,7 @@ function OhhwellsBridge() {
15175
16269
  setToolbarShowEditLink(false);
15176
16270
  setActiveCommands(/* @__PURE__ */ new Set());
15177
16271
  }, [markSelected]);
15178
- const commitNavigationTextEdit = useCallback7((navAnchor) => {
16272
+ const commitNavigationTextEdit = useCallback8((navAnchor) => {
15179
16273
  const el = activeElRef.current;
15180
16274
  if (!el) return;
15181
16275
  const key = el.dataset.ohwKey;
@@ -15208,7 +16302,7 @@ function OhhwellsBridge() {
15208
16302
  postToParent2({ type: "ow:exit-edit" });
15209
16303
  reselectNavigationItem(navAnchor);
15210
16304
  }, [postToParent2, reselectNavigationItem]);
15211
- const handleAddTopLevelNavItem = useCallback7(() => {
16305
+ const handleAddTopLevelNavItem = useCallback8(() => {
15212
16306
  const items = listNavbarRootItems();
15213
16307
  addNavAfterAnchorRef.current = items[items.length - 1] ?? null;
15214
16308
  deselectRef.current();
@@ -15220,7 +16314,7 @@ function OhhwellsBridge() {
15220
16314
  intent: "add-nav"
15221
16315
  });
15222
16316
  }, []);
15223
- const maybeWarnNavLinkDropdownConflict = useCallback7(
16317
+ const maybeWarnNavLinkDropdownConflict = useCallback8(
15224
16318
  (anchor) => {
15225
16319
  if (!isNavbarHrefKey(anchor.getAttribute("data-ohw-href-key"))) return;
15226
16320
  if (!navDropdownsOpenOnClick()) return;
@@ -15233,7 +16327,7 @@ function OhhwellsBridge() {
15233
16327
  },
15234
16328
  [postToParent2]
15235
16329
  );
15236
- const handleNavDropdownOpenChange = useCallback7((open) => {
16330
+ const handleNavDropdownOpenChange = useCallback8((open) => {
15237
16331
  const selected = selectedElRef.current;
15238
16332
  if (!selected || !isNavigationItem2(selected)) return;
15239
16333
  setNavGroupForceOpen(selected, open);
@@ -15245,7 +16339,7 @@ function OhhwellsBridge() {
15245
16339
  }
15246
16340
  });
15247
16341
  }, []);
15248
- const handleFooterHeadingVisibleChange = useCallback7(
16342
+ const handleFooterHeadingVisibleChange = useCallback8(
15249
16343
  (visible) => {
15250
16344
  const selected = selectedElRef.current;
15251
16345
  if (!selected || !isFooterFrameSelectionRef.current) return;
@@ -15269,7 +16363,7 @@ function OhhwellsBridge() {
15269
16363
  },
15270
16364
  [postToParent2]
15271
16365
  );
15272
- const enterEditOnNewItem = useCallback7((anchor) => {
16366
+ const enterEditOnNewItem = useCallback8((anchor) => {
15273
16367
  const label = anchor.querySelector('[data-ohw-editable="text"]');
15274
16368
  if (!label) {
15275
16369
  selectRef.current(anchor);
@@ -15278,7 +16372,7 @@ function OhhwellsBridge() {
15278
16372
  setNavGroupForceOpen(anchor, true);
15279
16373
  activateRef.current(label);
15280
16374
  }, []);
15281
- const handleAddChildItem = useCallback7(() => {
16375
+ const handleAddChildItem = useCallback8(() => {
15282
16376
  const selected = selectedElRef.current;
15283
16377
  if (!selected) return;
15284
16378
  const socialsRow = isSocialsRow(selected) ? selected : findSocialsRow(selected);
@@ -15390,7 +16484,7 @@ function OhhwellsBridge() {
15390
16484
  enterEditOnNewItem(result.anchor);
15391
16485
  });
15392
16486
  }, [enterEditOnNewItem, isFooterFrameSelection, maybeWarnNavLinkDropdownConflict, postToParent2]);
15393
- const handleAddFooterColumn = useCallback7(() => {
16487
+ const handleAddFooterColumn = useCallback8(() => {
15394
16488
  if (!canAddFooterColumn()) {
15395
16489
  postToParent2({
15396
16490
  type: "ow:toast",
@@ -15411,7 +16505,7 @@ function OhhwellsBridge() {
15411
16505
  selectRef.current(result.firstLink);
15412
16506
  });
15413
16507
  }, [postToParent2]);
15414
- const clearFooterDragVisuals = useCallback7(() => {
16508
+ const clearFooterDragVisuals = useCallback8(() => {
15415
16509
  footerDragRef.current = null;
15416
16510
  setSiblingHintRects([]);
15417
16511
  setFooterDropSlots([]);
@@ -15420,7 +16514,7 @@ function OhhwellsBridge() {
15420
16514
  setIsItemDragging(false);
15421
16515
  unlockFooterDragInteraction();
15422
16516
  }, []);
15423
- const refreshFooterDragVisuals = useCallback7((session, activeSlot, clientX, clientY) => {
16517
+ const refreshFooterDragVisuals = useCallback8((session, activeSlot, clientX, clientY) => {
15424
16518
  const dragged = session.draggedEl;
15425
16519
  setDraggedItemRect(dragged.getBoundingClientRect());
15426
16520
  if (typeof clientX === "number" && typeof clientY === "number") {
@@ -15452,13 +16546,13 @@ function OhhwellsBridge() {
15452
16546
  const activeIdx = activeSlot ? slots.findIndex((s) => s.insertIndex === activeSlot.insertIndex) : -1;
15453
16547
  setActiveFooterDropIndex(activeIdx >= 0 ? activeIdx : null);
15454
16548
  }, []);
15455
- const refreshFooterDragVisualsRef = useRef9(refreshFooterDragVisuals);
16549
+ const refreshFooterDragVisualsRef = useRef10(refreshFooterDragVisuals);
15456
16550
  refreshFooterDragVisualsRef.current = refreshFooterDragVisuals;
15457
- const commitFooterDragRef = useRef9(() => {
16551
+ const commitFooterDragRef = useRef10(() => {
15458
16552
  });
15459
- const beginFooterDragRef = useRef9(() => {
16553
+ const beginFooterDragRef = useRef10(() => {
15460
16554
  });
15461
- const beginFooterDrag = useCallback7(
16555
+ const beginFooterDrag = useCallback8(
15462
16556
  (session) => {
15463
16557
  const rect = session.draggedEl.getBoundingClientRect();
15464
16558
  session.lastClientX = session.lastClientX || rect.left + rect.width / 2;
@@ -15478,7 +16572,7 @@ function OhhwellsBridge() {
15478
16572
  [refreshFooterDragVisuals]
15479
16573
  );
15480
16574
  beginFooterDragRef.current = beginFooterDrag;
15481
- const commitFooterDrag = useCallback7(
16575
+ const commitFooterDrag = useCallback8(
15482
16576
  (clientX, clientY) => {
15483
16577
  const session = footerDragRef.current;
15484
16578
  if (!session) {
@@ -15606,7 +16700,7 @@ function OhhwellsBridge() {
15606
16700
  [clearFooterDragVisuals, resolveHrefKeyElement, resyncSelectedNavigationItem]
15607
16701
  );
15608
16702
  commitFooterDragRef.current = commitFooterDrag;
15609
- const startFooterLinkDrag = useCallback7(
16703
+ const startFooterLinkDrag = useCallback8(
15610
16704
  (anchor, clientX, clientY, wasSelected) => {
15611
16705
  const hrefKey = anchor.getAttribute("data-ohw-href-key");
15612
16706
  if (!hrefKey) return false;
@@ -15642,7 +16736,7 @@ function OhhwellsBridge() {
15642
16736
  },
15643
16737
  [beginFooterDrag]
15644
16738
  );
15645
- const startFooterColumnDrag = useCallback7(
16739
+ const startFooterColumnDrag = useCallback8(
15646
16740
  (columnEl, clientX, clientY, wasSelected) => {
15647
16741
  const columns = listFooterColumns();
15648
16742
  const idx = columns.indexOf(columnEl);
@@ -15662,7 +16756,7 @@ function OhhwellsBridge() {
15662
16756
  },
15663
16757
  [beginFooterDrag]
15664
16758
  );
15665
- const handleItemDragStart = useCallback7(
16759
+ const handleItemDragStart = useCallback8(
15666
16760
  (e) => {
15667
16761
  const selected = selectedElRef.current;
15668
16762
  if (!selected) {
@@ -15682,7 +16776,7 @@ function OhhwellsBridge() {
15682
16776
  },
15683
16777
  [startFooterColumnDrag, startFooterLinkDrag, startNavLinkDrag]
15684
16778
  );
15685
- const handleItemDragEnd = useCallback7(
16779
+ const handleItemDragEnd = useCallback8(
15686
16780
  (e) => {
15687
16781
  if (footerDragRef.current) {
15688
16782
  const x = e?.clientX;
@@ -15708,7 +16802,7 @@ function OhhwellsBridge() {
15708
16802
  },
15709
16803
  [commitFooterDrag, commitNavDrag, navDragRef]
15710
16804
  );
15711
- const handleItemChromePointerDown = useCallback7((e) => {
16805
+ const handleItemChromePointerDown = useCallback8((e) => {
15712
16806
  if (e.button !== 0) return;
15713
16807
  const selected = selectedElRef.current;
15714
16808
  if (!selected) return;
@@ -15739,7 +16833,7 @@ function OhhwellsBridge() {
15739
16833
  }
15740
16834
  if (armNavPressFromChrome(selected, e.clientX, e.clientY, e.pointerId)) return;
15741
16835
  }, [armNavPressFromChrome]);
15742
- const handleItemChromeClick = useCallback7((clientX, clientY) => {
16836
+ const handleItemChromeClick = useCallback8((clientX, clientY) => {
15743
16837
  if (suppressNextClickRef.current || Date.now() < suppressClickUntilRef.current) {
15744
16838
  suppressNextClickRef.current = false;
15745
16839
  return;
@@ -15752,7 +16846,7 @@ function OhhwellsBridge() {
15752
16846
  }, []);
15753
16847
  reselectNavigationItemRef.current = reselectNavigationItem;
15754
16848
  commitNavigationTextEditRef.current = commitNavigationTextEdit;
15755
- const select = useCallback7((anchor) => {
16849
+ const select = useCallback8((anchor) => {
15756
16850
  if (!isNavigationItem2(anchor)) return;
15757
16851
  if (activeElRef.current) deactivate();
15758
16852
  aiSectionApiRef.current?.selectFromElement(anchor);
@@ -15795,7 +16889,7 @@ function OhhwellsBridge() {
15795
16889
  setFloatingPanel(null);
15796
16890
  setLogoSizeDraft(null);
15797
16891
  }, [deactivate, markSelected]);
15798
- const selectFrame = useCallback7((el) => {
16892
+ const selectFrame = useCallback8((el) => {
15799
16893
  if (!isNavigationContainer(el)) return;
15800
16894
  if (activeElRef.current) deactivate();
15801
16895
  aiSectionApiRef.current?.selectFromElement(el);
@@ -15846,7 +16940,7 @@ function OhhwellsBridge() {
15846
16940
  setFloatingPanel(null);
15847
16941
  setLogoSizeDraft(null);
15848
16942
  }, [deactivate, markSelected, postToParent2]);
15849
- const selectLogo = useCallback7(
16943
+ const selectLogo = useCallback8(
15850
16944
  (logoEl) => {
15851
16945
  if (activeElRef.current) deactivate();
15852
16946
  selectedElRef.current = logoEl;
@@ -15875,7 +16969,7 @@ function OhhwellsBridge() {
15875
16969
  },
15876
16970
  [deactivate, markSelected]
15877
16971
  );
15878
- const openLogoSizePanel = useCallback7((logoEl) => {
16972
+ const openLogoSizePanel = useCallback8((logoEl) => {
15879
16973
  const placement = getLogoPlacement(logoEl);
15880
16974
  const draft = readLogoSizeState(editContentRef.current, placement);
15881
16975
  setLogoSizeDraft(draft);
@@ -15888,7 +16982,7 @@ function OhhwellsBridge() {
15888
16982
  placement
15889
16983
  });
15890
16984
  }, []);
15891
- const openSocialsDisplayPanel = useCallback7((row) => {
16985
+ const openSocialsDisplayPanel = useCallback8((row) => {
15892
16986
  setParentScrollSnap(parentScrollRef.current);
15893
16987
  setFloatingPanel({
15894
16988
  key: "socials-display",
@@ -15898,7 +16992,7 @@ function OhhwellsBridge() {
15898
16992
  row
15899
16993
  });
15900
16994
  }, []);
15901
- const changeSocialsDisplay = useCallback7(
16995
+ const changeSocialsDisplay = useCallback8(
15902
16996
  (row, next) => {
15903
16997
  if (next.icon) {
15904
16998
  const missing = socialsMissingIcons(row);
@@ -15921,17 +17015,17 @@ function OhhwellsBridge() {
15921
17015
  },
15922
17016
  []
15923
17017
  );
15924
- const closeFloatingPanelOnly = useCallback7(() => {
17018
+ const closeFloatingPanelOnly = useCallback8(() => {
15925
17019
  setFloatingPanel(null);
15926
17020
  setLogoSizeDraft(null);
15927
17021
  }, []);
15928
17022
  closeFloatingPanelOnlyRef.current = closeFloatingPanelOnly;
15929
- const closeFloatingPanelAndDeselect = useCallback7(() => {
17023
+ const closeFloatingPanelAndDeselect = useCallback8(() => {
15930
17024
  setFloatingPanel(null);
15931
17025
  setLogoSizeDraft(null);
15932
17026
  deselectRef.current();
15933
17027
  }, []);
15934
- useEffect12(() => {
17028
+ useEffect13(() => {
15935
17029
  const session = floatingPanel && floatingPanel.kind === "logo-size" ? floatingPanel : null;
15936
17030
  if (!session || !logoSizeDraft) {
15937
17031
  postToParentRef.current({ type: "ow:logo-size-panel", open: false });
@@ -15950,7 +17044,7 @@ function OhhwellsBridge() {
15950
17044
  max: LOGO_SIZE_MAX
15951
17045
  });
15952
17046
  }, [floatingPanel, logoSizeDraft, editorViewport]);
15953
- const persistLogoSizeDraft = useCallback7(
17047
+ const persistLogoSizeDraft = useCallback8(
15954
17048
  (placement, draft) => {
15955
17049
  const desktopKey = LOGO_SIZE_DESKTOP_KEYS[placement];
15956
17050
  const mobileKey = LOGO_SIZE_MOBILE_KEYS[placement];
@@ -15990,7 +17084,7 @@ function OhhwellsBridge() {
15990
17084
  },
15991
17085
  [postToParent2]
15992
17086
  );
15993
- const activate = useCallback7((el, options) => {
17087
+ const activate = useCallback8((el, options) => {
15994
17088
  if (activeElRef.current === el) return;
15995
17089
  if (isIconEditable(el)) return;
15996
17090
  if (el.hasAttribute("data-ohw-social-label")) return;
@@ -16074,8 +17168,8 @@ function OhhwellsBridge() {
16074
17168
  openLogoSizePanelRef.current = openLogoSizePanel;
16075
17169
  deselectRef.current = deselect;
16076
17170
  closeFloatingPanelOnlyRef.current = closeFloatingPanelOnly;
16077
- const lastSiteWideScopeRef = useRef9(null);
16078
- useEffect12(() => {
17171
+ const lastSiteWideScopeRef = useRef10(null);
17172
+ useEffect13(() => {
16079
17173
  if (!isEditMode) {
16080
17174
  if (lastSiteWideScopeRef.current !== false) {
16081
17175
  lastSiteWideScopeRef.current = false;
@@ -16108,15 +17202,27 @@ function OhhwellsBridge() {
16108
17202
  }
16109
17203
  const applyContent = (content) => {
16110
17204
  const imageLoads = [];
17205
+ if (typeof content[BRAND_KIT_KEY] === "string") {
17206
+ brandKitRef.current = content[BRAND_KIT_KEY];
17207
+ applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
17208
+ }
16111
17209
  if (typeof content[AI_SECTIONS_KEY] === "string") {
16112
17210
  aiSectionsRef.current = content[AI_SECTIONS_KEY];
16113
17211
  applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
16114
17212
  }
17213
+ if (typeof content[STYLE_STORE_KEY] === "string") {
17214
+ stylesRef.current = content[STYLE_STORE_KEY];
17215
+ applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
17216
+ }
17217
+ applyBrandChrome(content);
16115
17218
  for (const [key, val] of Object.entries(content)) {
16116
17219
  if (key === "__ohw_sections") continue;
16117
17220
  if (key === AI_SECTIONS_KEY) continue;
16118
17221
  if (key === LOGO_PLACEHOLDER_KEY) continue;
16119
17222
  if (LOGO_IMAGE_KEYS.includes(key)) continue;
17223
+ if (key === BRAND_KIT_KEY) continue;
17224
+ if (key === STYLE_STORE_KEY) continue;
17225
+ if (BRAND_CHROME_KEYS.has(key)) continue;
16120
17226
  if (applyVideoSettingNode(key, val)) continue;
16121
17227
  if (applyCarouselNode(key, val)) continue;
16122
17228
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -16174,7 +17280,9 @@ function OhhwellsBridge() {
16174
17280
  let cancelled = false;
16175
17281
  setFetchState("loading");
16176
17282
  const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
16177
- fetch(`${apiUrl}/api/public/sites/${subdomain}/content`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
17283
+ const initialPath = pathname;
17284
+ fetchedContentPaths.add(`${subdomain}::${initialPath}`);
17285
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
16178
17286
  if (cancelled) return;
16179
17287
  const content = data?.content ?? {};
16180
17288
  contentCache.set(subdomain, content);
@@ -16187,7 +17295,7 @@ function OhhwellsBridge() {
16187
17295
  cancelled = true;
16188
17296
  };
16189
17297
  }, [subdomain, isEditMode]);
16190
- useEffect12(() => {
17298
+ useEffect13(() => {
16191
17299
  if (!isEditMode) return;
16192
17300
  const resolveIndex = (form, clientY) => {
16193
17301
  const wrappers = listFieldWrappers(form).filter((el) => fieldKeyOf(el) !== fieldDragRef.current?.key);
@@ -16228,7 +17336,7 @@ function OhhwellsBridge() {
16228
17336
  window.removeEventListener("drop", onDrop, true);
16229
17337
  };
16230
17338
  }, [buildFieldDropSlots, isEditMode, persistFields, selectField]);
16231
- useEffect12(() => {
17339
+ useEffect13(() => {
16232
17340
  if (!isEditMode) return;
16233
17341
  const mark = () => document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
16234
17342
  markFormFields(form);
@@ -16240,7 +17348,7 @@ function OhhwellsBridge() {
16240
17348
  });
16241
17349
  return () => observer.disconnect();
16242
17350
  }, [isEditMode, fetchState, pathname]);
16243
- useEffect12(() => {
17351
+ useEffect13(() => {
16244
17352
  if (!isEditMode) return;
16245
17353
  let saveTimer = null;
16246
17354
  const onInput = (e) => {
@@ -16262,14 +17370,14 @@ function OhhwellsBridge() {
16262
17370
  document.addEventListener("input", onInput, true);
16263
17371
  return () => document.removeEventListener("input", onInput, true);
16264
17372
  }, [isEditMode, persistFields]);
16265
- useEffect12(() => {
17373
+ useEffect13(() => {
16266
17374
  if (isEditMode || fetchState !== "done") return;
16267
17375
  const content = contentCache.get(subdomain) ?? {};
16268
17376
  document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
16269
17377
  reconcileFieldsFromContent(form, content);
16270
17378
  });
16271
17379
  }, [isEditMode, fetchState, subdomain]);
16272
- useEffect12(() => {
17380
+ useEffect13(() => {
16273
17381
  if (!isEditMode) return;
16274
17382
  const swallow = (e) => {
16275
17383
  const target = e.target;
@@ -16278,12 +17386,12 @@ function OhhwellsBridge() {
16278
17386
  document.addEventListener("submit", swallow, true);
16279
17387
  return () => document.removeEventListener("submit", swallow, true);
16280
17388
  }, [isEditMode]);
16281
- useEffect12(() => {
17389
+ useEffect13(() => {
16282
17390
  if (isEditMode || fetchState !== "done") return;
16283
17391
  const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
16284
17392
  bindPublishedForms(apiUrl, subdomain, contentCache.get(subdomain) ?? {});
16285
17393
  }, [isEditMode, fetchState, subdomain]);
16286
- useEffect12(() => {
17394
+ useEffect13(() => {
16287
17395
  if (!subdomain || isEditMode) return;
16288
17396
  let debounceTimer = null;
16289
17397
  let observer = null;
@@ -16294,10 +17402,21 @@ function OhhwellsBridge() {
16294
17402
  initSectionInstancesFromContent(content, window.location.pathname);
16295
17403
  observer?.disconnect();
16296
17404
  try {
17405
+ applyBrandChrome(content);
17406
+ if (typeof content[BRAND_KIT_KEY] === "string") {
17407
+ applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
17408
+ }
17409
+ if (typeof content[STYLE_STORE_KEY] === "string") {
17410
+ applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
17411
+ }
16297
17412
  for (const [key, val] of Object.entries(content)) {
16298
17413
  if (key === "__ohw_sections") continue;
16299
17414
  if (key === LOGO_PLACEHOLDER_KEY) continue;
16300
17415
  if (LOGO_IMAGE_KEYS.includes(key)) continue;
17416
+ if (key === BRAND_KIT_KEY) continue;
17417
+ if (key === STYLE_STORE_KEY) continue;
17418
+ if (key === STYLE_STORE_KEY) continue;
17419
+ if (BRAND_CHROME_KEYS.has(key)) continue;
16301
17420
  if (applyVideoSettingNode(key, val)) continue;
16302
17421
  if (applyCarouselNode(key, val)) continue;
16303
17422
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -16340,6 +17459,17 @@ function OhhwellsBridge() {
16340
17459
  debounceTimer = setTimeout(applyFromCache, 150);
16341
17460
  };
16342
17461
  applyFromCache();
17462
+ const pathCacheKey = `${subdomain}::${pathname}`;
17463
+ if (!fetchedContentPaths.has(pathCacheKey)) {
17464
+ fetchedContentPaths.add(pathCacheKey);
17465
+ const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
17466
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(pathname)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
17467
+ if (!data?.content) return;
17468
+ contentCache.set(subdomain, data.content);
17469
+ applyFromCache();
17470
+ }).catch(() => {
17471
+ });
17472
+ }
16343
17473
  observer = new MutationObserver(scheduleApply);
16344
17474
  observer.observe(document.body, { childList: true, subtree: true });
16345
17475
  return () => {
@@ -16353,10 +17483,10 @@ function OhhwellsBridge() {
16353
17483
  const visible = Boolean(subdomain) && fetchState !== "done";
16354
17484
  el.style.display = visible ? "flex" : "none";
16355
17485
  }, [subdomain, fetchState]);
16356
- useEffect12(() => {
17486
+ useEffect13(() => {
16357
17487
  postToParent2({ type: "ow:navigation", path: pathname });
16358
17488
  }, [pathname, postToParent2]);
16359
- useEffect12(() => {
17489
+ useEffect13(() => {
16360
17490
  if (!isEditMode) return;
16361
17491
  if (linkPopoverSessionRef.current?.intent === "add-nav") return;
16362
17492
  if (document.querySelector("[data-ohw-section-picker]")) return;
@@ -16364,7 +17494,7 @@ function OhhwellsBridge() {
16364
17494
  deselectRef.current();
16365
17495
  deactivateRef.current();
16366
17496
  }, [pathname, isEditMode]);
16367
- useEffect12(() => {
17497
+ useEffect13(() => {
16368
17498
  const contentForNav = () => {
16369
17499
  if (isEditMode) return editContentRef.current;
16370
17500
  if (!subdomain) return {};
@@ -16431,31 +17561,36 @@ function OhhwellsBridge() {
16431
17561
  observer?.disconnect();
16432
17562
  };
16433
17563
  }, [isEditMode, pathname, subdomain, fetchState, resyncSelectedNavigationItem]);
16434
- useEffect12(() => {
17564
+ useEffect13(() => {
16435
17565
  if (!isEditMode) return;
17566
+ let lastPosted = 0;
16436
17567
  const measure = () => {
16437
17568
  const h = document.body.scrollHeight;
16438
- if (h > 50) postToParent2({ type: "ow:height", height: h });
17569
+ if (h > 50 && Math.abs(h - lastPosted) > 1) {
17570
+ lastPosted = h;
17571
+ postToParent2({ type: "ow:height", height: h });
17572
+ }
17573
+ };
17574
+ let raf = null;
17575
+ const schedule = () => {
17576
+ if (raf != null) return;
17577
+ raf = requestAnimationFrame(() => {
17578
+ raf = null;
17579
+ measure();
17580
+ });
16439
17581
  };
16440
17582
  const t1 = setTimeout(measure, 50);
16441
17583
  const t2 = setTimeout(measure, 500);
16442
- let lastWidth = window.innerWidth;
16443
- let resizeTimer = null;
16444
- const handleResize = () => {
16445
- if (window.innerWidth === lastWidth) return;
16446
- lastWidth = window.innerWidth;
16447
- if (resizeTimer) clearTimeout(resizeTimer);
16448
- resizeTimer = setTimeout(measure, 150);
16449
- };
16450
- window.addEventListener("resize", handleResize);
17584
+ const ro = new ResizeObserver(schedule);
17585
+ ro.observe(document.body);
16451
17586
  return () => {
16452
17587
  clearTimeout(t1);
16453
17588
  clearTimeout(t2);
16454
- if (resizeTimer) clearTimeout(resizeTimer);
16455
- window.removeEventListener("resize", handleResize);
17589
+ if (raf != null) cancelAnimationFrame(raf);
17590
+ ro.disconnect();
16456
17591
  };
16457
17592
  }, [pathname, isEditMode, postToParent2]);
16458
- useEffect12(() => {
17593
+ useEffect13(() => {
16459
17594
  if (!subdomainFromQuery || isEditMode) return;
16460
17595
  const handleClick = (e) => {
16461
17596
  const anchor = e.target.closest("a");
@@ -16471,7 +17606,7 @@ function OhhwellsBridge() {
16471
17606
  document.addEventListener("click", handleClick, true);
16472
17607
  return () => document.removeEventListener("click", handleClick, true);
16473
17608
  }, [subdomainFromQuery, isEditMode, router]);
16474
- useEffect12(() => {
17609
+ useEffect13(() => {
16475
17610
  if (!isEditMode) {
16476
17611
  editStylesRef.current?.base.remove();
16477
17612
  editStylesRef.current?.forceHover.remove();
@@ -16633,16 +17768,21 @@ function OhhwellsBridge() {
16633
17768
  return;
16634
17769
  }
16635
17770
  const target = e.target;
17771
+ if (target.closest("[data-ohw-ai-review]")) return;
16636
17772
  if (target.closest("[data-ohw-toolbar]")) return;
16637
17773
  if (target.closest("[data-ohw-state-toggle]")) return;
16638
17774
  if (target.closest("[data-ohw-max-badge]")) return;
16639
17775
  if (isInsideLinkEditor(target)) return;
17776
+ if (isInsideFloatingPanel(target)) return;
16640
17777
  if (target.closest("[data-ohw-form-toolbar]")) return;
16641
17778
  if (target.closest(
16642
17779
  '[data-ohw-field-toolbar], [data-ohw-field-type-picker], [data-radix-popper-content-wrapper], [role="menu"], [data-slot="dropdown-menu-content"]'
16643
17780
  )) {
16644
17781
  return;
16645
17782
  }
17783
+ if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
17784
+ clearMediaSelectionRef.current();
17785
+ }
16646
17786
  {
16647
17787
  const formEl = getFormElement(target);
16648
17788
  const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
@@ -16790,8 +17930,11 @@ function OhhwellsBridge() {
16790
17930
  if (isMediaEditable(editable) && !buttonOnMedia) {
16791
17931
  e.preventDefault();
16792
17932
  e.stopPropagation();
16793
- aiSectionApiRef.current?.selectFromElement(editable);
16794
- postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "", elementType: editable.dataset.ohwEditable ?? "image" });
17933
+ if (selectedMediaElRef.current === editable) {
17934
+ postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "", elementType: editable.dataset.ohwEditable ?? "image" });
17935
+ } else {
17936
+ selectMediaElementRef.current(editable);
17937
+ }
16795
17938
  return;
16796
17939
  }
16797
17940
  const hrefLookupTarget = buttonOnMedia ? clickedButton : editable;
@@ -16910,10 +18053,12 @@ function OhhwellsBridge() {
16910
18053
  };
16911
18054
  const handleDblClick = (e) => {
16912
18055
  const target = e.target;
18056
+ if (target.closest("[data-ohw-ai-review]")) return;
16913
18057
  if (target.closest("[data-ohw-toolbar]")) return;
16914
18058
  if (target.closest("[data-ohw-state-toggle]")) return;
16915
18059
  if (target.closest("[data-ohw-max-badge]")) return;
16916
18060
  if (isInsideLinkEditor(target)) return;
18061
+ if (isInsideFloatingPanel(target)) return;
16917
18062
  if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
16918
18063
  return;
16919
18064
  }
@@ -16954,11 +18099,14 @@ function OhhwellsBridge() {
16954
18099
  setSiblingHintRects([]);
16955
18100
  return;
16956
18101
  }
16957
- if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || target.closest("[data-ohw-floating-panel]")) {
18102
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || isInsideFloatingPanel(target) || isPointOverFloatingPanel(e.clientX, e.clientY)) {
16958
18103
  hoveredItemElRef.current = null;
16959
18104
  setHoveredItemRect(null);
16960
18105
  hoveredNavContainerRef.current = null;
16961
18106
  setHoveredNavContainerRect(null);
18107
+ siblingHintElRef.current = null;
18108
+ setSiblingHintRect(null);
18109
+ setSiblingHintRects([]);
16962
18110
  return;
16963
18111
  }
16964
18112
  {
@@ -16976,6 +18124,16 @@ function OhhwellsBridge() {
16976
18124
  return;
16977
18125
  }
16978
18126
  }
18127
+ if (allowNavContainerHover) {
18128
+ const socialsRow = isSocialsRow(target) ? target : null;
18129
+ if (socialsRow && !getSocialItem(target) && selected2 !== socialsRow) {
18130
+ hoveredNavContainerRef.current = socialsRow;
18131
+ setHoveredNavContainerRect(socialsRow.getBoundingClientRect());
18132
+ hoveredItemElRef.current = null;
18133
+ setHoveredItemRect(null);
18134
+ return;
18135
+ }
18136
+ }
16979
18137
  if (allowFooterLinksHover) {
16980
18138
  const navContainer = target.closest("[data-ohw-nav-container]");
16981
18139
  if (!navContainer) {
@@ -17061,13 +18219,12 @@ function OhhwellsBridge() {
17061
18219
  clearHrefKeyHover(hoverTarget);
17062
18220
  hoveredItemElRef.current = hoverTarget;
17063
18221
  setHoveredItemRect(hoverTarget.getBoundingClientRect());
17064
- } else if (!isInsideNavigationItem(editable)) {
18222
+ } else if (!isInsideNavigationItem(editable) && hoverTarget !== selectedElRef.current) {
17065
18223
  hoverTarget.setAttribute("data-ohw-hovered", "");
17066
18224
  if (editable.closest("footer") || editable.closest('[data-ohw-section="footer"]')) {
17067
18225
  hoveredNavContainerRef.current = null;
17068
18226
  setHoveredNavContainerRect(null);
17069
18227
  hoveredItemElRef.current = editable;
17070
- setHoveredItemRect(editable.getBoundingClientRect());
17071
18228
  }
17072
18229
  }
17073
18230
  }
@@ -17364,7 +18521,7 @@ function OhhwellsBridge() {
17364
18521
  }
17365
18522
  };
17366
18523
  const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
17367
- if (linkPopoverOpenRef.current) {
18524
+ if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
17368
18525
  if (hoveredImageRef.current) {
17369
18526
  hoveredImageRef.current = null;
17370
18527
  hoveredImageHasTextOverlapRef.current = false;
@@ -17623,7 +18780,7 @@ function OhhwellsBridge() {
17623
18780
  }
17624
18781
  };
17625
18782
  const probeHoverCardsAt = (clientX, clientY, fromParentViewport = false) => {
17626
- if (linkPopoverOpenRef.current || document.documentElement.hasAttribute("data-ohw-section-picking")) {
18783
+ if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY) || document.documentElement.hasAttribute("data-ohw-section-picking")) {
17627
18784
  if (activeStateElRef.current) {
17628
18785
  activeStateElRef.current.removeAttribute("data-ohw-state-hovered");
17629
18786
  activeStateElRef.current = null;
@@ -17660,6 +18817,35 @@ function OhhwellsBridge() {
17660
18817
  }
17661
18818
  }
17662
18819
  };
18820
+ const probeSocialsRowAt = (clientX, clientY, fromParentViewport = false) => {
18821
+ const wasSocialsRow = Boolean(hoveredNavContainerRef.current?.hasAttribute(SOCIALS_ROW_ATTR));
18822
+ const clear = () => {
18823
+ if (!wasSocialsRow) return false;
18824
+ hoveredNavContainerRef.current = null;
18825
+ setHoveredNavContainerRect(null);
18826
+ return false;
18827
+ };
18828
+ if (linkPopoverOpenRef.current || toolbarVariantRef.current === "select-frame") return clear();
18829
+ const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
18830
+ const SLACK = 6;
18831
+ for (const row of Array.from(document.querySelectorAll(`[${SOCIALS_ROW_ATTR}]`))) {
18832
+ const rect = row.getBoundingClientRect();
18833
+ const inside = x >= rect.left - SLACK && x <= rect.right + SLACK && y >= rect.top - SLACK && y <= rect.bottom + SLACK;
18834
+ if (!inside) continue;
18835
+ if (selectedElRef.current === row) return clear();
18836
+ const overItem = listSocialItems(row).some((item) => {
18837
+ const box = item.getBoundingClientRect();
18838
+ return x >= box.left && x <= box.right && y >= box.top && y <= box.bottom;
18839
+ });
18840
+ if (overItem) return clear();
18841
+ hoveredNavContainerRef.current = row;
18842
+ setHoveredNavContainerRect(rect);
18843
+ hoveredItemElRef.current = null;
18844
+ setHoveredItemRect(null);
18845
+ return true;
18846
+ }
18847
+ return clear();
18848
+ };
17663
18849
  const probeSectionGapAt = (clientX, clientY, fromParentViewport = false) => {
17664
18850
  if (linkPopoverOpenRef.current) {
17665
18851
  if (hoveredGapRef.current) {
@@ -17669,7 +18855,9 @@ function OhhwellsBridge() {
17669
18855
  return;
17670
18856
  }
17671
18857
  const { y } = toProbeCoords(clientX, clientY, fromParentViewport);
17672
- const sections = Array.from(document.querySelectorAll("[data-ohw-section]")).sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);
18858
+ const sections = Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
18859
+ (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
18860
+ ).sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);
17673
18861
  const ZONE = 20;
17674
18862
  for (let i = 0; i < sections.length; i++) {
17675
18863
  const a = sections[i];
@@ -17698,8 +18886,7 @@ function OhhwellsBridge() {
17698
18886
  };
17699
18887
  const handleMouseMove = (e) => {
17700
18888
  const { clientX, clientY } = e;
17701
- if (pointOwnedByFloatingPanel(clientX, clientY)) return;
17702
- if (isOverEditorChrome(clientX, clientY)) {
18889
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY) || isOverEditorChrome(clientX, clientY)) {
17703
18890
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
17704
18891
  formHoverElRef.current = null;
17705
18892
  setFormHoverRect(null);
@@ -17707,8 +18894,15 @@ function OhhwellsBridge() {
17707
18894
  setHoveredItemRect(null);
17708
18895
  hoveredNavContainerRef.current = null;
17709
18896
  setHoveredNavContainerRect(null);
18897
+ siblingHintElRef.current = null;
18898
+ setSiblingHintRect(null);
18899
+ setSiblingHintRects([]);
18900
+ dismissImageHover();
18901
+ clearImageHover();
18902
+ setSectionGap(null);
17710
18903
  return;
17711
18904
  }
18905
+ if (probeSocialsRowAt(clientX, clientY)) return;
17712
18906
  probeSectionGapAt(clientX, clientY);
17713
18907
  probeImageAt(clientX, clientY);
17714
18908
  probeHoverCardsAt(clientX, clientY);
@@ -17717,7 +18911,12 @@ function OhhwellsBridge() {
17717
18911
  if (e.data?.type !== "ow:pointer-sync") return;
17718
18912
  const { clientX, clientY } = e.data;
17719
18913
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
17720
- if (pointOwnedByFloatingPanel(clientX, clientY)) return;
18914
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
18915
+ dismissImageHover();
18916
+ clearImageHover();
18917
+ return;
18918
+ }
18919
+ if (probeSocialsRowAt(clientX, clientY)) return;
17721
18920
  probeSectionGapAt(clientX, clientY);
17722
18921
  probeImageAt(clientX, clientY);
17723
18922
  probeHoverCardsAt(clientX, clientY);
@@ -17963,10 +19162,19 @@ function OhhwellsBridge() {
17963
19162
  if (e.data?.type !== "ow:hydrate") return;
17964
19163
  const content = e.data.content;
17965
19164
  if (!content) return;
19165
+ if (typeof content[BRAND_KIT_KEY] === "string") {
19166
+ brandKitRef.current = content[BRAND_KIT_KEY];
19167
+ applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
19168
+ }
17966
19169
  if (typeof content[AI_SECTIONS_KEY] === "string") {
17967
19170
  aiSectionsRef.current = content[AI_SECTIONS_KEY];
17968
19171
  applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
17969
19172
  }
19173
+ if (typeof content[STYLE_STORE_KEY] === "string") {
19174
+ stylesRef.current = content[STYLE_STORE_KEY];
19175
+ applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
19176
+ }
19177
+ applyBrandChrome(content);
17970
19178
  let sectionsJson = null;
17971
19179
  for (const [key, val] of Object.entries(content)) {
17972
19180
  if (key === "__ohw_sections") {
@@ -17976,6 +19184,9 @@ function OhhwellsBridge() {
17976
19184
  if (key === AI_SECTIONS_KEY) continue;
17977
19185
  if (key === LOGO_PLACEHOLDER_KEY) continue;
17978
19186
  if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
19187
+ if (key === BRAND_KIT_KEY) continue;
19188
+ if (key === STYLE_STORE_KEY) continue;
19189
+ if (BRAND_CHROME_KEYS.has(key)) continue;
17979
19190
  if (applyVideoSettingNode(key, val)) continue;
17980
19191
  if (applyCarouselNode(key, val)) continue;
17981
19192
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -17989,6 +19200,8 @@ function OhhwellsBridge() {
17989
19200
  if (video && video.src !== val) applyVideoSrc(video, val);
17990
19201
  } else if (el.dataset.ohwEditable === "link") {
17991
19202
  applyLinkHref(el, val);
19203
+ } else if (el.dataset.ohwEditable === "icon") {
19204
+ applyIconMarkup(el, val);
17992
19205
  } else {
17993
19206
  el.innerHTML = val;
17994
19207
  }
@@ -18072,12 +19285,21 @@ function OhhwellsBridge() {
18072
19285
  nodes: collectEditableNodes(editContentRef.current)
18073
19286
  });
18074
19287
  };
19288
+ const clearInteractionChrome = () => {
19289
+ deactivateRef.current();
19290
+ deselectRef.current();
19291
+ clearMediaSelectionRef.current();
19292
+ };
18075
19293
  const handleAiApplyTree = (e) => {
18076
19294
  if (e.data?.type !== "ow:ai-apply-tree") return;
18077
19295
  const payload = e.data.payload;
18078
19296
  if (!payload || typeof payload.id !== "string" || !isRenderableTree(payload.tree)) return;
19297
+ clearInteractionChrome();
18079
19298
  const previous = aiSectionsRef.current;
18080
- const nextState = applyTreeToState(parseAiSectionsState(previous), payload);
19299
+ const nextState = applyTreeToState(parseAiSectionsState(previous), {
19300
+ ...payload,
19301
+ path: payload.path ?? window.location.pathname
19302
+ });
18081
19303
  const nextValue = serializeAiSectionsState(nextState);
18082
19304
  aiSectionsRef.current = nextValue;
18083
19305
  applyAiSectionsToDom(nextState);
@@ -18097,6 +19319,7 @@ function OhhwellsBridge() {
18097
19319
  if (!sectionId || sectionId === "navbar" || sectionId === "footer") return;
18098
19320
  const exists = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
18099
19321
  if (!exists) return;
19322
+ clearInteractionChrome();
18100
19323
  const previous = aiSectionsRef.current;
18101
19324
  const nextState = deleteSectionFromState(parseAiSectionsState(previous), sectionId);
18102
19325
  const nextValue = serializeAiSectionsState(nextState);
@@ -18112,14 +19335,58 @@ function OhhwellsBridge() {
18112
19335
  const handleAiSetSections = (e) => {
18113
19336
  if (e.data?.type !== "ow:ai-set-sections") return;
18114
19337
  const value = typeof e.data.value === "string" ? e.data.value : "";
19338
+ clearInteractionChrome();
18115
19339
  aiSectionsRef.current = value;
18116
19340
  applyAiSectionsToDom(parseAiSectionsState(value));
19341
+ applyStylesToDom(parseStyleStore(stylesRef.current));
18117
19342
  const restoredHeight = document.documentElement.scrollHeight;
18118
19343
  if (restoredHeight > 50) postToParentRef.current({ type: "ow:height", height: restoredHeight });
18119
19344
  postToParentRef.current({ type: "ow:change", nodes: [{ key: AI_SECTIONS_KEY, text: value }] });
18120
19345
  postAiSectionsChanged();
18121
19346
  };
18122
19347
  window.addEventListener("message", handleAiSetSections);
19348
+ const handleMoveSection = (e) => {
19349
+ if (e.data?.type !== "ow:move-section") return;
19350
+ const instanceId = typeof e.data.instanceId === "string" ? e.data.instanceId : "";
19351
+ const direction = e.data.direction === "up" || e.data.direction === "down" ? e.data.direction : null;
19352
+ if (!instanceId || !direction) return;
19353
+ const entries = moveSectionInstance(instanceId, direction, window.location.pathname);
19354
+ if (!entries) return;
19355
+ const orderJson = JSON.stringify(entries);
19356
+ editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
19357
+ postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
19358
+ window.dispatchEvent(new Event("resize"));
19359
+ };
19360
+ window.addEventListener("message", handleMoveSection);
19361
+ const handleAiSetBrand = (e) => {
19362
+ if (e.data?.type !== "ow:ai-set-brand") return;
19363
+ const value = typeof e.data.value === "string" ? e.data.value : "";
19364
+ const previous = brandKitRef.current;
19365
+ brandKitRef.current = value;
19366
+ applyBrandToDom(parseBrandKit(value));
19367
+ if (aiSectionsRef.current) applyAiSectionsToDom(parseAiSectionsState(aiSectionsRef.current));
19368
+ applyStylesToDom(parseStyleStore(stylesRef.current));
19369
+ postToParentRef.current({ type: "ow:change", nodes: [{ key: BRAND_KIT_KEY, text: value }] });
19370
+ postToParentRef.current({ type: "ow:ai-brand-applied", previous, value });
19371
+ };
19372
+ window.addEventListener("message", handleAiSetBrand);
19373
+ const handleAiSetStyles = (e) => {
19374
+ if (e.data?.type !== "ow:ai-set-styles") return;
19375
+ const value = typeof e.data.value === "string" ? e.data.value : "";
19376
+ const previous = stylesRef.current;
19377
+ stylesRef.current = value;
19378
+ applyStylesToDom(parseStyleStore(value));
19379
+ postToParentRef.current({ type: "ow:change", nodes: [{ key: STYLE_STORE_KEY, text: value }] });
19380
+ postToParentRef.current({ type: "ow:ai-styles-applied", previous, value });
19381
+ };
19382
+ window.addEventListener("message", handleAiSetStyles);
19383
+ const handleGetBrand = (e) => {
19384
+ if (e.data?.type !== "ow:get-brand") return;
19385
+ const template = deriveTemplateBrand();
19386
+ const value = brandKitRef.current || (template ? JSON.stringify(template) : "");
19387
+ postToParentRef.current({ type: "ow:brand-value", value });
19388
+ };
19389
+ window.addEventListener("message", handleGetBrand);
18123
19390
  const handlePanelDragging = (e) => {
18124
19391
  if (e.data?.type !== "ow:panel-dragging") return;
18125
19392
  if (e.data.dragging) document.documentElement.setAttribute("data-ohw-panel-dragging", "");
@@ -18135,8 +19402,15 @@ function OhhwellsBridge() {
18135
19402
  closeLinkPopoverRef.current();
18136
19403
  return;
18137
19404
  }
19405
+ if (floatingPanelOpenRef.current) {
19406
+ setFloatingPanelRef.current(null);
19407
+ deselectRef.current();
19408
+ deactivateRef.current();
19409
+ return;
19410
+ }
18138
19411
  deselectRef.current();
18139
19412
  deactivateRef.current();
19413
+ clearMediaSelectionRef.current();
18140
19414
  };
18141
19415
  window.addEventListener("message", handleDeactivate);
18142
19416
  const handleToastAction = (e) => {
@@ -18222,6 +19496,10 @@ function OhhwellsBridge() {
18222
19496
  const handleKeyDown = (e) => {
18223
19497
  if (e.key === "Escape" && document.querySelector("[data-ohw-section-picker]")) return;
18224
19498
  if (e.key === "Escape" && document.querySelector("[data-ohw-more-menu]")) return;
19499
+ if (e.key === "Escape" && selectedMediaElRef.current) {
19500
+ clearMediaSelectionRef.current();
19501
+ return;
19502
+ }
18225
19503
  if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "a" && activeElRef.current) {
18226
19504
  e.preventDefault();
18227
19505
  selectAllTextInEditable(activeElRef.current);
@@ -18381,6 +19659,12 @@ function OhhwellsBridge() {
18381
19659
  if (aiSectionsRef.current) {
18382
19660
  nodes.push({ key: AI_SECTIONS_KEY, type: "sections", text: aiSectionsRef.current });
18383
19661
  }
19662
+ if (stylesRef.current) {
19663
+ nodes.push({ key: STYLE_STORE_KEY, type: "styles", text: stylesRef.current });
19664
+ }
19665
+ if (brandKitRef.current) {
19666
+ nodes.push({ key: BRAND_KIT_KEY, type: "brand", text: brandKitRef.current });
19667
+ }
18384
19668
  document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
18385
19669
  const formKey = formKeyOf(form);
18386
19670
  if (!formKey) return;
@@ -18398,8 +19682,12 @@ function OhhwellsBridge() {
18398
19682
  if (inserted) {
18399
19683
  const tracker = getSectionsTracker();
18400
19684
  postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
18401
- const h = document.documentElement.scrollHeight;
18402
- if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
19685
+ const reportHeight = () => {
19686
+ const h = document.body.scrollHeight;
19687
+ if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
19688
+ };
19689
+ reportHeight();
19690
+ setTimeout(reportHeight, 500);
18403
19691
  }
18404
19692
  };
18405
19693
  const handleSwitchSchedule = (e) => {
@@ -18786,19 +20074,23 @@ function OhhwellsBridge() {
18786
20074
  window.removeEventListener("message", handleAiApplyTree);
18787
20075
  window.removeEventListener("message", handleAiDeleteSection);
18788
20076
  window.removeEventListener("message", handleAiSetSections);
20077
+ window.removeEventListener("message", handleMoveSection);
20078
+ window.removeEventListener("message", handleAiSetBrand);
20079
+ window.removeEventListener("message", handleAiSetStyles);
20080
+ window.removeEventListener("message", handleGetBrand);
18789
20081
  window.removeEventListener("message", handlePanelDragging);
18790
20082
  window.removeEventListener("message", handleDeactivate);
18791
- document.documentElement.removeAttribute("data-ohw-panel-dragging");
18792
20083
  window.removeEventListener("message", handleToastAction);
18793
20084
  window.removeEventListener("message", handleFormCount);
18794
20085
  window.removeEventListener("message", handleUiEscape);
20086
+ document.documentElement.removeAttribute("data-ohw-panel-dragging");
18795
20087
  autoSaveTimers.current.forEach(clearTimeout);
18796
20088
  autoSaveTimers.current.clear();
18797
20089
  if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
18798
20090
  if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
18799
20091
  };
18800
20092
  }, [isEditMode, refreshStateRules]);
18801
- useEffect12(() => {
20093
+ useEffect13(() => {
18802
20094
  if (!isEditMode) return;
18803
20095
  const THRESHOLD = 10;
18804
20096
  const resolveWasSelected = (el) => {
@@ -18954,7 +20246,7 @@ function OhhwellsBridge() {
18954
20246
  unlockFooterDragInteraction();
18955
20247
  };
18956
20248
  }, [isEditMode]);
18957
- useEffect12(() => {
20249
+ useEffect13(() => {
18958
20250
  const handler = (e) => {
18959
20251
  if (e.data?.type !== "ow:request-schedule-config") return;
18960
20252
  const insertAfterVal = e.data.insertAfter;
@@ -18970,7 +20262,7 @@ function OhhwellsBridge() {
18970
20262
  window.addEventListener("message", handler);
18971
20263
  return () => window.removeEventListener("message", handler);
18972
20264
  }, [processConfigRequest]);
18973
- useEffect12(() => {
20265
+ useEffect13(() => {
18974
20266
  if (!isEditMode) return;
18975
20267
  document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
18976
20268
  el.removeAttribute("data-ohw-active-state");
@@ -18994,7 +20286,7 @@ function OhhwellsBridge() {
18994
20286
  postToParent2({
18995
20287
  type: "ow:ready",
18996
20288
  version: "1",
18997
- bridgeVersion: "0.1.67",
20289
+ bridgeVersion: "0.1.69",
18998
20290
  path: pathname,
18999
20291
  nodes: collectEditableNodes(editContentRef.current),
19000
20292
  sections
@@ -19006,13 +20298,13 @@ function OhhwellsBridge() {
19006
20298
  clearTimeout(timer);
19007
20299
  };
19008
20300
  }, [pathname, isEditMode, refreshStateRules, postToParent2]);
19009
- useEffect12(() => {
20301
+ useEffect13(() => {
19010
20302
  scrollToHashSectionWhenReady();
19011
20303
  const onHashChange = () => scrollToHashSectionWhenReady();
19012
20304
  window.addEventListener("hashchange", onHashChange);
19013
20305
  return () => window.removeEventListener("hashchange", onHashChange);
19014
20306
  }, [pathname]);
19015
- const handleCommand = useCallback7((cmd) => {
20307
+ const handleCommand = useCallback8((cmd) => {
19016
20308
  const el = activeElRef.current;
19017
20309
  const selBefore = window.getSelection();
19018
20310
  let savedOffsets = null;
@@ -19048,7 +20340,7 @@ function OhhwellsBridge() {
19048
20340
  if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
19049
20341
  refreshActiveCommandsRef.current();
19050
20342
  }, []);
19051
- useEffect12(() => {
20343
+ useEffect13(() => {
19052
20344
  const session = floatingPanel && floatingPanel.kind === "logo-size" ? floatingPanel : null;
19053
20345
  if (!session || !logoSizeDraft) return;
19054
20346
  const onPanelAction = (e) => {
@@ -19086,7 +20378,7 @@ function OhhwellsBridge() {
19086
20378
  window.addEventListener("message", onPanelAction);
19087
20379
  return () => window.removeEventListener("message", onPanelAction);
19088
20380
  }, [floatingPanel, logoSizeDraft, editorViewport, persistLogoSizeDraft, closeFloatingPanelAndDeselect]);
19089
- const handleStateChange = useCallback7((state) => {
20381
+ const handleStateChange = useCallback8((state) => {
19090
20382
  if (!activeStateElRef.current) return;
19091
20383
  const el = activeStateElRef.current;
19092
20384
  if (state === "Default") {
@@ -19099,7 +20391,7 @@ function OhhwellsBridge() {
19099
20391
  }
19100
20392
  setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
19101
20393
  }, [deactivate]);
19102
- const reselectAfterLinkPopover = useCallback7(
20394
+ const reselectAfterLinkPopover = useCallback8(
19103
20395
  (hrefKey) => {
19104
20396
  requestAnimationFrame(() => {
19105
20397
  const el = resolveHrefKeyElement(hrefKey);
@@ -19108,7 +20400,7 @@ function OhhwellsBridge() {
19108
20400
  },
19109
20401
  [resolveHrefKeyElement]
19110
20402
  );
19111
- const closeLinkPopover = useCallback7(() => {
20403
+ const closeLinkPopover = useCallback8(() => {
19112
20404
  const session = linkPopoverSessionRef.current;
19113
20405
  addNavAfterAnchorRef.current = null;
19114
20406
  setLinkPopover(null);
@@ -19116,9 +20408,9 @@ function OhhwellsBridge() {
19116
20408
  reselectAfterLinkPopover(session.key);
19117
20409
  }
19118
20410
  }, [reselectAfterLinkPopover]);
19119
- const closeLinkPopoverRef = useRef9(closeLinkPopover);
20411
+ const closeLinkPopoverRef = useRef10(closeLinkPopover);
19120
20412
  closeLinkPopoverRef.current = closeLinkPopover;
19121
- const openLinkPopoverForActive = useCallback7(() => {
20413
+ const openLinkPopoverForActive = useCallback8(() => {
19122
20414
  const hrefCtx = getHrefKeyFromElement(activeElRef.current);
19123
20415
  if (!hrefCtx) return;
19124
20416
  bumpLinkPopoverGrace();
@@ -19129,7 +20421,7 @@ function OhhwellsBridge() {
19129
20421
  });
19130
20422
  deactivate();
19131
20423
  }, [deactivate]);
19132
- const openLinkPopoverForSelected = useCallback7(() => {
20424
+ const openLinkPopoverForSelected = useCallback8(() => {
19133
20425
  const anchor = selectedElRef.current;
19134
20426
  if (!anchor) return;
19135
20427
  const key = anchor.getAttribute("data-ohw-href-key");
@@ -19146,7 +20438,7 @@ function OhhwellsBridge() {
19146
20438
  });
19147
20439
  deselect();
19148
20440
  }, [deselect]);
19149
- const handleSelectParent = useCallback7(() => {
20441
+ const handleSelectParent = useCallback8(() => {
19150
20442
  const selected = selectedElRef.current;
19151
20443
  if (!selected) return;
19152
20444
  if (toolbarVariantRef.current === "select-frame") {
@@ -19173,7 +20465,7 @@ function OhhwellsBridge() {
19173
20465
  }
19174
20466
  deselectRef.current();
19175
20467
  }, []);
19176
- const handleDuplicateSelected = useCallback7(() => {
20468
+ const handleDuplicateSelected = useCallback8(() => {
19177
20469
  const selected = selectedElRef.current;
19178
20470
  if (!selected || !isNavigationItem2(selected)) return;
19179
20471
  const hrefKey = selected.getAttribute("data-ohw-href-key");
@@ -19289,7 +20581,7 @@ function OhhwellsBridge() {
19289
20581
  });
19290
20582
  }
19291
20583
  }, [postToParent2]);
19292
- const runPendingDeleteUndo = useCallback7(() => {
20584
+ const runPendingDeleteUndo = useCallback8(() => {
19293
20585
  const pending = pendingDeleteUndoRef.current;
19294
20586
  if (!pending) return false;
19295
20587
  pendingDeleteUndoRef.current = null;
@@ -19297,7 +20589,7 @@ function OhhwellsBridge() {
19297
20589
  enforceLinkHrefs();
19298
20590
  return true;
19299
20591
  }, []);
19300
- const handleDeleteSelected = useCallback7(() => {
20592
+ const handleDeleteSelected = useCallback8(() => {
19301
20593
  const selected = selectedElRef.current;
19302
20594
  if (!selected) return false;
19303
20595
  return deleteSelectedNavFooterItem({
@@ -19318,7 +20610,7 @@ function OhhwellsBridge() {
19318
20610
  }, [postToParent2]);
19319
20611
  handleDeleteSelectedRef.current = handleDeleteSelected;
19320
20612
  runPendingDeleteUndoRef.current = runPendingDeleteUndo;
19321
- const handleLinkPopoverSubmit = useCallback7(
20613
+ const handleLinkPopoverSubmit = useCallback8(
19322
20614
  (target) => {
19323
20615
  const session = linkPopoverSessionRef.current;
19324
20616
  if (!session) return;
@@ -19384,19 +20676,30 @@ function OhhwellsBridge() {
19384
20676
  const showEditLink = toolbarShowEditLink;
19385
20677
  const currentSections = sectionsByPath[pathname] ?? [];
19386
20678
  linkPopoverOpenRef.current = linkPopover !== null;
19387
- const handleMediaReplace = useCallback7(
20679
+ const handleMediaSelect = useCallback8((key) => {
20680
+ const el = hoveredImageRef.current?.dataset.ohwKey === key ? hoveredImageRef.current : Array.from(document.querySelectorAll(MEDIA_SELECTOR)).find(
20681
+ (m) => (m.dataset.ohwKey ?? "") === key
20682
+ ) ?? null;
20683
+ if (!el) return;
20684
+ selectMediaElementRef.current(el);
20685
+ }, []);
20686
+ const handleMediaReplace = useCallback8(
19388
20687
  (key) => {
19389
- postToParent2({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
20688
+ postToParent2({
20689
+ type: "ow:image-pick",
20690
+ key,
20691
+ elementType: mediaHover?.elementType ?? selectedMedia?.elementType ?? "image"
20692
+ });
19390
20693
  },
19391
- [postToParent2, mediaHover?.elementType]
20694
+ [postToParent2, mediaHover?.elementType, selectedMedia?.elementType]
19392
20695
  );
19393
- const handleEditCarousel = useCallback7(
20696
+ const handleEditCarousel = useCallback8(
19394
20697
  (key) => {
19395
20698
  postToParent2({ type: "ow:carousel-open", key, images: readCarouselValue(key) });
19396
20699
  },
19397
20700
  [postToParent2]
19398
20701
  );
19399
- const handleMediaFadeOutComplete = useCallback7((key) => {
20702
+ const handleMediaFadeOutComplete = useCallback8((key) => {
19400
20703
  setUploadingRects((prev) => {
19401
20704
  if (!(key in prev)) return prev;
19402
20705
  const next = { ...prev };
@@ -19404,7 +20707,7 @@ function OhhwellsBridge() {
19404
20707
  return next;
19405
20708
  });
19406
20709
  }, []);
19407
- const handleVideoSettingsChange = useCallback7(
20710
+ const handleVideoSettingsChange = useCallback8(
19408
20711
  (key, settings) => {
19409
20712
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
19410
20713
  const video = getVideoEl2(el);
@@ -19426,420 +20729,506 @@ function OhhwellsBridge() {
19426
20729
  },
19427
20730
  [postToParent2]
19428
20731
  );
19429
- return bridgeRoot ? createPortal2(
19430
- /* @__PURE__ */ jsxs20(Fragment8, { children: [
19431
- /* @__PURE__ */ jsx33("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
19432
- isEditMode && /* @__PURE__ */ jsx33(AiSectionOverlay, { postToParent: postToParent2, apiRef: aiSectionApiRef }),
19433
- Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ jsx33(
19434
- MediaOverlay,
19435
- {
19436
- hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
19437
- isUploading: true,
19438
- fadingOut,
19439
- onFadeOutComplete: handleMediaFadeOutComplete,
19440
- onReplace: handleMediaReplace
19441
- },
19442
- `uploading-${key}`
19443
- )),
19444
- mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ jsx33(
19445
- MediaOverlay,
19446
- {
19447
- hover: mediaHover,
19448
- isUploading: false,
19449
- onReplace: handleMediaReplace,
19450
- onVideoSettingsChange: handleVideoSettingsChange
19451
- }
19452
- ),
19453
- carouselHover && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx33(CarouselOverlay, { hover: carouselHover, onEdit: handleEditCarousel }),
19454
- siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
19455
- siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
19456
- isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
19457
- isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
19458
- "div",
19459
- {
19460
- className: "pointer-events-none fixed z-2147483646",
19461
- style: {
19462
- left: slot.left,
19463
- top: slot.top,
19464
- width: slot.width,
19465
- height: slot.height
20732
+ return /* @__PURE__ */ jsxs20(Fragment8, { children: [
20733
+ /* @__PURE__ */ jsx33("div", { id: "ohw-loader", suppressHydrationWarning: true, style: { ...OHW_LOADER_STYLE, display: "none" }, children: /* @__PURE__ */ jsx33(OhwLoaderSpinner, {}) }),
20734
+ /* @__PURE__ */ jsx33("script", { suppressHydrationWarning: true, dangerouslySetInnerHTML: { __html: OHW_LOADER_PREHYDRATE_SCRIPT } }),
20735
+ bridgeRoot ? createPortal2(
20736
+ /* @__PURE__ */ jsxs20(Fragment8, { children: [
20737
+ /* @__PURE__ */ jsx33("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
20738
+ isEditMode && /* @__PURE__ */ jsx33(AiSectionOverlay, { postToParent: postToParent2, apiRef: aiSectionApiRef }),
20739
+ isSectionDragging && sectionDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
20740
+ "div",
20741
+ {
20742
+ className: "pointer-events-none fixed z-2147483646",
20743
+ style: { left: slot.left, top: slot.y, width: slot.width, height: 3, transform: "translateY(-50%)" },
20744
+ children: /* @__PURE__ */ jsx33(
20745
+ DropIndicator,
20746
+ {
20747
+ direction: "horizontal",
20748
+ state: activeSectionDropIndex === i ? "dragActive" : "dragIdle",
20749
+ className: "!h-full !w-full"
20750
+ }
20751
+ )
19466
20752
  },
19467
- children: /* @__PURE__ */ jsx33(
19468
- DropIndicator,
19469
- {
19470
- direction: slot.direction,
19471
- state: activeFooterDropIndex === i ? "dragActive" : "dragIdle",
19472
- className: "!h-full !w-full"
19473
- }
19474
- )
19475
- },
19476
- `footer-drop-${slot.direction}-${slot.columnIndex}-${slot.insertIndex}-${i}`
19477
- )),
19478
- isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
19479
- "div",
19480
- {
19481
- className: "pointer-events-none fixed z-2147483646",
19482
- style: {
19483
- left: slot.left,
19484
- top: slot.top,
19485
- width: slot.width,
19486
- height: slot.height
20753
+ `section-drop-${slot.insertIndex}-${i}`
20754
+ )),
20755
+ Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ jsx33(
20756
+ MediaOverlay,
20757
+ {
20758
+ hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
20759
+ isUploading: true,
20760
+ fadingOut,
20761
+ onFadeOutComplete: handleMediaFadeOutComplete,
20762
+ onReplace: handleMediaReplace
19487
20763
  },
19488
- children: /* @__PURE__ */ jsx33(
19489
- DropIndicator,
19490
- {
19491
- direction: slot.direction,
19492
- state: activeNavDropIndex === i ? "dragActive" : "dragIdle",
19493
- className: "!h-full !w-full"
19494
- }
19495
- )
19496
- },
19497
- `nav-drop-${slot.direction}-${slot.parentId ?? "root"}-${slot.insertIndex}-${i}`
19498
- )),
19499
- hoveredNavContainerRect && (toolbarVariant !== "select-frame" || isFooterFrameSelection) && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
19500
- hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
19501
- hoveredTextRect && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredTextRect, state: "hover" }),
19502
- formPickRect && !isItemDragging && /* @__PURE__ */ jsx33(
19503
- ItemInteractionLayer,
19504
- {
19505
- rect: formPickRect,
19506
- state: "active-top",
19507
- itemDragSurface: false,
19508
- toolbarAlign: "left",
19509
- chromeGap: 24,
19510
- toolbar: fieldPickRect ? void 0 : /* @__PURE__ */ jsxs20(
20764
+ `uploading-${key}`
20765
+ )),
20766
+ mediaHover && !(mediaHover.key in uploadingRects) && mediaHover.key !== selectedMedia?.key && /* @__PURE__ */ jsx33(
20767
+ MediaOverlay,
20768
+ {
20769
+ hover: mediaHover,
20770
+ isUploading: false,
20771
+ onReplace: handleMediaReplace,
20772
+ onSelect: handleMediaSelect,
20773
+ onVideoSettingsChange: handleVideoSettingsChange
20774
+ }
20775
+ ),
20776
+ selectedMedia && !(selectedMedia.key in uploadingRects) && !linkPopover && /* @__PURE__ */ jsx33(
20777
+ MediaOverlay,
20778
+ {
20779
+ hover: selectedMedia,
20780
+ selected: true,
20781
+ hovered: mediaHover?.key === selectedMedia.key,
20782
+ isUploading: false,
20783
+ onReplace: handleMediaReplace,
20784
+ onSelect: handleMediaSelect,
20785
+ onVideoSettingsChange: handleVideoSettingsChange
20786
+ }
20787
+ ),
20788
+ carouselHover && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx33(CarouselOverlay, { hover: carouselHover, onEdit: handleEditCarousel }),
20789
+ siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
20790
+ siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
20791
+ isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
20792
+ isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
20793
+ "div",
20794
+ {
20795
+ className: "pointer-events-none fixed z-2147483646",
20796
+ style: {
20797
+ left: slot.left,
20798
+ top: slot.top,
20799
+ width: slot.width,
20800
+ height: slot.height
20801
+ },
20802
+ children: /* @__PURE__ */ jsx33(
20803
+ DropIndicator,
20804
+ {
20805
+ direction: slot.direction,
20806
+ state: activeFooterDropIndex === i ? "dragActive" : "dragIdle",
20807
+ className: "!h-full !w-full"
20808
+ }
20809
+ )
20810
+ },
20811
+ `footer-drop-${slot.direction}-${slot.columnIndex}-${slot.insertIndex}-${i}`
20812
+ )),
20813
+ isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
20814
+ "div",
20815
+ {
20816
+ className: "pointer-events-none fixed z-2147483646",
20817
+ style: {
20818
+ left: slot.left,
20819
+ top: slot.top,
20820
+ width: slot.width,
20821
+ height: slot.height
20822
+ },
20823
+ children: /* @__PURE__ */ jsx33(
20824
+ DropIndicator,
20825
+ {
20826
+ direction: slot.direction,
20827
+ state: activeNavDropIndex === i ? "dragActive" : "dragIdle",
20828
+ className: "!h-full !w-full"
20829
+ }
20830
+ )
20831
+ },
20832
+ `nav-drop-${slot.direction}-${slot.parentId ?? "root"}-${slot.insertIndex}-${i}`
20833
+ )),
20834
+ hoveredNavContainerRect && (toolbarVariant !== "select-frame" || isFooterFrameSelection) && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
20835
+ hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
20836
+ hoveredTextRect && !hoveredNavContainerRect && !hoveredItemRect && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredTextRect, state: "hover" }),
20837
+ formPickRect && !isItemDragging && /* @__PURE__ */ jsx33(
20838
+ ItemInteractionLayer,
20839
+ {
20840
+ rect: formPickRect,
20841
+ state: "active-top",
20842
+ itemDragSurface: false,
20843
+ toolbarAlign: "left",
20844
+ chromeGap: 24,
20845
+ toolbar: fieldPickRect ? void 0 : /* @__PURE__ */ jsxs20(
20846
+ "div",
20847
+ {
20848
+ "data-ohw-form-toolbar": "",
20849
+ className: "pointer-events-auto flex items-center gap-0.5 whitespace-nowrap rounded-lg border border-border bg-background p-1 shadow-md",
20850
+ children: [
20851
+ /* @__PURE__ */ jsx33(
20852
+ "button",
20853
+ {
20854
+ type: "button",
20855
+ "aria-label": "Add field",
20856
+ className: "flex h-7 w-7 items-center justify-center rounded-md text-foreground transition-colors hover:bg-muted/80",
20857
+ onClick: () => setFieldTypePickerOpen((open) => !open),
20858
+ "data-ohw-add-field": "",
20859
+ children: /* @__PURE__ */ jsx33(Plus4, { size: 15, "aria-hidden": true })
20860
+ }
20861
+ ),
20862
+ /* @__PURE__ */ jsx33("div", { className: "mx-0.5 h-5 w-px bg-border" }),
20863
+ /* @__PURE__ */ jsxs20(
20864
+ "button",
20865
+ {
20866
+ type: "button",
20867
+ 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",
20868
+ onClick: () => {
20869
+ const form = formPickElRef.current;
20870
+ if (!form) return;
20871
+ postToParent2({
20872
+ type: "ow:form-pick",
20873
+ formKey: formKeyOf(form),
20874
+ hasLongText: formHasLongText(form)
20875
+ });
20876
+ },
20877
+ children: [
20878
+ /* @__PURE__ */ jsx33(Settings, { size: 14, "aria-hidden": true }),
20879
+ "Form settings",
20880
+ formPickCount ? (
20881
+ // Counter pill, per the design — not a text suffix.
20882
+ /* @__PURE__ */ jsx33(
20883
+ "span",
20884
+ {
20885
+ "data-ohw-form-count": "",
20886
+ 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",
20887
+ children: formPickCount
20888
+ }
20889
+ )
20890
+ ) : null
20891
+ ]
20892
+ }
20893
+ ),
20894
+ /* @__PURE__ */ jsx33("div", { className: "mx-0.5 h-5 w-px bg-border" }),
20895
+ /* @__PURE__ */ jsx33("div", { className: "flex items-center rounded-md bg-muted/70 p-0.5", children: ["default", "success"].map((state) => /* @__PURE__ */ jsx33(
20896
+ "button",
20897
+ {
20898
+ type: "button",
20899
+ "aria-pressed": formViewState === state,
20900
+ 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"),
20901
+ onClick: () => {
20902
+ const form = formPickElRef.current;
20903
+ const key = form ? formKeyOf(form) : null;
20904
+ if (!form || !key) return;
20905
+ const initial = successInitialFor(form, key, editContentRef.current);
20906
+ setFormViewState(form, key, state, initial);
20907
+ setFormViewStateUi(state);
20908
+ setFormPickRect(form.getBoundingClientRect());
20909
+ if (state === "success") {
20910
+ const successEl = form.querySelector(`[${SUCCESS_TEXT_ATTR}]`);
20911
+ if (successEl) requestAnimationFrame(() => activateRef.current(successEl));
20912
+ } else {
20913
+ deactivateRef.current();
20914
+ }
20915
+ },
20916
+ children: state
20917
+ },
20918
+ state
20919
+ )) })
20920
+ ]
20921
+ }
20922
+ )
20923
+ }
20924
+ ),
20925
+ formHoverRect && !isItemDragging && /* @__PURE__ */ jsx33(
20926
+ ItemInteractionLayer,
20927
+ {
20928
+ rect: formHoverRect,
20929
+ state: "hover",
20930
+ chromeGap: formHoverElRef.current && getFieldWrapper(formHoverElRef.current) ? 8 : 24
20931
+ }
20932
+ ),
20933
+ fieldPickRect && fieldPickState && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ jsx33(
20934
+ ItemInteractionLayer,
20935
+ {
20936
+ rect: fieldPickRect,
20937
+ state: fieldDragging ? "dragging" : "active-top",
20938
+ itemDragSurface: false,
20939
+ toolbarAlign: "left",
20940
+ chromeGap: 10,
20941
+ showHandle: true,
20942
+ dragHandleLabel: "Reorder field",
20943
+ onDragHandleDragStart: handleFieldDragStart,
20944
+ onDragHandleDragEnd: handleFieldDragEnd,
20945
+ toolbar: /* @__PURE__ */ jsx33(
20946
+ FormFieldToolbar,
20947
+ {
20948
+ type: fieldPickState.type,
20949
+ required: fieldPickState.required,
20950
+ onTypeChange: handleFieldTypeChange,
20951
+ onRequiredToggle: handleFieldRequiredToggle,
20952
+ onDuplicate: handleFieldDuplicate,
20953
+ onDelete: handleFieldDelete
20954
+ }
20955
+ )
20956
+ }
20957
+ ),
20958
+ fieldDragging ? fieldDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
20959
+ "div",
20960
+ {
20961
+ className: "pointer-events-none fixed z-[2147483644]",
20962
+ style: { top: slot.top, left: slot.left, width: slot.width },
20963
+ children: /* @__PURE__ */ jsx33(
20964
+ DropIndicator,
20965
+ {
20966
+ direction: "horizontal",
20967
+ state: fieldDropIndex === i ? "dragActive" : "dragIdle",
20968
+ className: "!w-full"
20969
+ }
20970
+ )
20971
+ },
20972
+ `field-drop-${i}`
20973
+ )) : null,
20974
+ fieldTypePickerOpen && formPickRect ? (() => {
20975
+ const toolbar = document.querySelector("[data-ohw-form-toolbar]")?.getBoundingClientRect();
20976
+ return /* @__PURE__ */ jsx33(
19511
20977
  "div",
19512
20978
  {
19513
- "data-ohw-form-toolbar": "",
19514
- className: "pointer-events-auto flex items-center gap-0.5 whitespace-nowrap rounded-lg border border-border bg-background p-1 shadow-md",
19515
- children: [
19516
- /* @__PURE__ */ jsx33(
19517
- "button",
19518
- {
19519
- type: "button",
19520
- "aria-label": "Add field",
19521
- className: "flex h-7 w-7 items-center justify-center rounded-md text-foreground transition-colors hover:bg-muted/80",
19522
- onClick: () => setFieldTypePickerOpen((open) => !open),
19523
- "data-ohw-add-field": "",
19524
- children: /* @__PURE__ */ jsx33(Plus4, { size: 15, "aria-hidden": true })
19525
- }
19526
- ),
19527
- /* @__PURE__ */ jsx33("div", { className: "mx-0.5 h-5 w-px bg-border" }),
19528
- /* @__PURE__ */ jsxs20(
19529
- "button",
19530
- {
19531
- type: "button",
19532
- 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",
19533
- onClick: () => {
19534
- const form = formPickElRef.current;
19535
- if (!form) return;
19536
- postToParent2({
19537
- type: "ow:form-pick",
19538
- formKey: formKeyOf(form),
19539
- hasLongText: formHasLongText(form)
19540
- });
19541
- },
19542
- children: [
19543
- /* @__PURE__ */ jsx33(Settings, { size: 14, "aria-hidden": true }),
19544
- "Form settings",
19545
- formPickCount ? (
19546
- // Counter pill, per the design — not a text suffix.
19547
- /* @__PURE__ */ jsx33(
19548
- "span",
19549
- {
19550
- "data-ohw-form-count": "",
19551
- 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",
19552
- children: formPickCount
19553
- }
19554
- )
19555
- ) : null
19556
- ]
19557
- }
19558
- ),
19559
- /* @__PURE__ */ jsx33("div", { className: "mx-0.5 h-5 w-px bg-border" }),
19560
- /* @__PURE__ */ jsx33("div", { className: "flex items-center rounded-md bg-muted/70 p-0.5", children: ["default", "success"].map((state) => /* @__PURE__ */ jsx33(
19561
- "button",
19562
- {
19563
- type: "button",
19564
- "aria-pressed": formViewState === state,
19565
- 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"),
19566
- onClick: () => {
19567
- const form = formPickElRef.current;
19568
- const key = form ? formKeyOf(form) : null;
19569
- if (!form || !key) return;
19570
- const initial = successInitialFor(form, key, editContentRef.current);
19571
- setFormViewState(form, key, state, initial);
19572
- setFormViewStateUi(state);
19573
- setFormPickRect(form.getBoundingClientRect());
19574
- if (state === "success") {
19575
- const successEl = form.querySelector(`[${SUCCESS_TEXT_ATTR}]`);
19576
- if (successEl) requestAnimationFrame(() => activateRef.current(successEl));
19577
- } else {
19578
- deactivateRef.current();
19579
- }
19580
- },
19581
- children: state
19582
- },
19583
- state
19584
- )) })
19585
- ]
20979
+ className: "pointer-events-none fixed z-[2147483645]",
20980
+ style: {
20981
+ top: toolbar ? toolbar.bottom + 6 : formPickRect.top + 16,
20982
+ left: toolbar ? toolbar.left : formPickRect.left + 24
20983
+ },
20984
+ children: /* @__PURE__ */ jsx33(FieldTypePicker, { onPick: handleAddField })
19586
20985
  }
19587
- )
19588
- }
19589
- ),
19590
- formHoverRect && !isItemDragging && /* @__PURE__ */ jsx33(
19591
- ItemInteractionLayer,
19592
- {
19593
- rect: formHoverRect,
19594
- state: "hover",
19595
- chromeGap: formHoverElRef.current && getFieldWrapper(formHoverElRef.current) ? 8 : 24
19596
- }
19597
- ),
19598
- fieldPickRect && fieldPickState && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ jsx33(
19599
- ItemInteractionLayer,
19600
- {
19601
- rect: fieldPickRect,
19602
- state: fieldDragging ? "dragging" : "active-top",
19603
- itemDragSurface: false,
19604
- toolbarAlign: "left",
19605
- chromeGap: 10,
19606
- showHandle: true,
19607
- dragHandleLabel: "Reorder field",
19608
- onDragHandleDragStart: handleFieldDragStart,
19609
- onDragHandleDragEnd: handleFieldDragEnd,
19610
- toolbar: /* @__PURE__ */ jsx33(
19611
- FormFieldToolbar,
20986
+ );
20987
+ })() : null,
20988
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer2(selectedElRef.current) && /* @__PURE__ */ jsx33(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
20989
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isFooterLinksContainer(selectedElRef.current) && /* @__PURE__ */ jsx33(
20990
+ FooterContainerChrome,
20991
+ {
20992
+ rect: toolbarRect,
20993
+ onAdd: handleAddFooterColumn,
20994
+ addDisabled: !canAddFooterColumn()
20995
+ }
20996
+ ),
20997
+ toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame" || toolbarVariant === "logo") && /* @__PURE__ */ jsx33(
20998
+ ItemInteractionLayer,
20999
+ {
21000
+ rect: isItemDragging && draggedItemRect && (footerDragRef.current?.wasSelected || navDragRef.current?.wasSelected) ? draggedItemRect : toolbarRect,
21001
+ toolbarBelowRect: selectedElRef.current && isNavigationItem2(selectedElRef.current) ? getOpenNavDropdownPanelRect(selectedElRef.current) : null,
21002
+ elRef: glowElRef,
21003
+ state: isItemDragging ? "dragging" : selectedElRef.current && isNavigationItem2(selectedElRef.current) && getOpenNavDropdownPanelRect(selectedElRef.current) ? "active-bottom" : resolveItemInteractionState(toolbarRect, parentScrollRef.current),
21004
+ showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey) || toolbarVariant === "select-frame" && isFooterFrameSelection,
21005
+ dragDisabled: reorderDragDisabled,
21006
+ dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
21007
+ onDragHandleDragStart: handleItemDragStart,
21008
+ onDragHandleDragEnd: handleItemDragEnd,
21009
+ onItemPointerDown: toolbarVariant === "logo" ? void 0 : handleItemChromePointerDown,
21010
+ onItemClick: toolbarVariant === "logo" ? void 0 : handleItemChromeClick,
21011
+ itemDragSurface: toolbarVariant !== "logo" && !isFooterFrameSelection,
21012
+ toolbar: toolbarVariant === "link-action" && !isItemDragging || toolbarVariant === "select-frame" && (isFooterFrameSelection || selectedIsSocialsRow) && !isItemDragging ? /* @__PURE__ */ jsx33(
21013
+ ItemActionToolbar,
21014
+ {
21015
+ onEditLink: openLinkPopoverForSelected,
21016
+ onStyle: () => {
21017
+ const row = selectedElRef.current;
21018
+ if (!row) return;
21019
+ if (floatingPanel?.kind === "socials-display") closeFloatingPanelOnly();
21020
+ else openSocialsDisplayPanel(row);
21021
+ },
21022
+ showStyle: selectedIsSocialsRow,
21023
+ styleActive: floatingPanel?.kind === "socials-display",
21024
+ onAddItem: handleAddChildItem,
21025
+ onSelectParent: handleSelectParent,
21026
+ onDuplicate: handleDuplicateSelected,
21027
+ onDelete: handleDeleteSelected,
21028
+ addItemDisabled: isFooterFrameSelection && isFooterAddItemDisabled(selectedElRef.current) || selectedElRef.current !== null && (() => {
21029
+ const row = isSocialsRow(selectedElRef.current) ? selectedElRef.current : findSocialsRow(selectedElRef.current);
21030
+ return row ? !canAddSocialItem(row) : false;
21031
+ })(),
21032
+ editLinkDisabled: false,
21033
+ moreDisabled: false,
21034
+ duplicateDisabled: isFooterFrameSelection || selectedIsSocialsRow,
21035
+ showEditLink: !isFooterFrameSelection && !selectedIsSocialsRow && navDropdownPreviewOpen === null,
21036
+ showAddItem: isFooterFrameSelection || selectedIsSocialsRow || !selectedIsCta && Boolean(
21037
+ selectedElRef.current && isNavbarHrefKey(selectedElRef.current.getAttribute("data-ohw-href-key")) && !isNestedNavChild(selectedElRef.current)
21038
+ ),
21039
+ showMore: (!selectedIsCta || isFooterFrameSelection) && !selectedIsSocialsRow,
21040
+ dropdownOpen: navDropdownPreviewOpen,
21041
+ onDropdownOpenChange: handleNavDropdownOpenChange,
21042
+ headingVisible: footerHeadingVisible,
21043
+ onHeadingVisibleChange: handleFooterHeadingVisibleChange
21044
+ }
21045
+ ) : void 0
21046
+ }
21047
+ ),
21048
+ toolbarRect && toolbarVariant === "rich-text" && !linkPopover && /* @__PURE__ */ jsxs20(Fragment8, { children: [
21049
+ /* @__PURE__ */ jsx33(
21050
+ EditGlowChrome,
19612
21051
  {
19613
- type: fieldPickState.type,
19614
- required: fieldPickState.required,
19615
- onTypeChange: handleFieldTypeChange,
19616
- onRequiredToggle: handleFieldRequiredToggle,
19617
- onDuplicate: handleFieldDuplicate,
19618
- onDelete: handleFieldDelete
21052
+ rect: toolbarRect,
21053
+ elRef: glowElRef,
21054
+ reorderHrefKey,
21055
+ dragDisabled: reorderDragDisabled,
21056
+ hideHandle: isItemDragging
19619
21057
  }
19620
- )
19621
- }
19622
- ),
19623
- fieldDragging ? fieldDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
19624
- "div",
19625
- {
19626
- className: "pointer-events-none fixed z-[2147483644]",
19627
- style: { top: slot.top, left: slot.left, width: slot.width },
19628
- children: /* @__PURE__ */ jsx33(
19629
- DropIndicator,
21058
+ ),
21059
+ /* @__PURE__ */ jsx33(
21060
+ FloatingToolbar,
19630
21061
  {
19631
- direction: "horizontal",
19632
- state: fieldDropIndex === i ? "dragActive" : "dragIdle",
19633
- className: "!w-full"
21062
+ rect: toolbarRect,
21063
+ parentScroll: parentScrollRef.current,
21064
+ elRef: toolbarElRef,
21065
+ onCommand: handleCommand,
21066
+ activeCommands,
21067
+ showEditLink,
21068
+ onEditLink: openLinkPopoverForActive
19634
21069
  }
19635
21070
  )
19636
- },
19637
- `field-drop-${i}`
19638
- )) : null,
19639
- fieldTypePickerOpen && formPickRect ? (() => {
19640
- const toolbar = document.querySelector("[data-ohw-form-toolbar]")?.getBoundingClientRect();
19641
- return /* @__PURE__ */ jsx33(
21071
+ ] }),
21072
+ maxBadge && /* @__PURE__ */ jsxs20(
19642
21073
  "div",
19643
21074
  {
19644
- className: "pointer-events-none fixed z-[2147483645]",
21075
+ "data-ohw-max-badge": "",
19645
21076
  style: {
19646
- top: toolbar ? toolbar.bottom + 6 : formPickRect.top + 16,
19647
- left: toolbar ? toolbar.left : formPickRect.left + 24
21077
+ position: "fixed",
21078
+ top: maxBadge.rect.bottom + 4,
21079
+ left: maxBadge.rect.right,
21080
+ transform: "translateX(-100%)",
21081
+ zIndex: 2147483647,
21082
+ background: maxBadge.current > maxBadge.max ? "#FEF2F2" : "#F5F5F4",
21083
+ color: maxBadge.current > maxBadge.max ? "#DC2626" : "#78716C",
21084
+ border: `1px solid ${maxBadge.current > maxBadge.max ? "#FECACA" : "#E7E5E4"}`,
21085
+ borderRadius: 4,
21086
+ padding: "2px 6px",
21087
+ fontSize: 11,
21088
+ fontWeight: 500,
21089
+ pointerEvents: "none"
19648
21090
  },
19649
- children: /* @__PURE__ */ jsx33(FieldTypePicker, { onPick: handleAddField })
21091
+ children: [
21092
+ maxBadge.current,
21093
+ "/",
21094
+ maxBadge.max
21095
+ ]
19650
21096
  }
19651
- );
19652
- })() : null,
19653
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer2(selectedElRef.current) && /* @__PURE__ */ jsx33(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
19654
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isFooterLinksContainer(selectedElRef.current) && /* @__PURE__ */ jsx33(
19655
- FooterContainerChrome,
19656
- {
19657
- rect: toolbarRect,
19658
- onAdd: handleAddFooterColumn,
19659
- addDisabled: !canAddFooterColumn()
19660
- }
19661
- ),
19662
- toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame" || toolbarVariant === "logo") && /* @__PURE__ */ jsx33(
19663
- ItemInteractionLayer,
19664
- {
19665
- rect: isItemDragging && draggedItemRect && (footerDragRef.current?.wasSelected || navDragRef.current?.wasSelected) ? draggedItemRect : toolbarRect,
19666
- toolbarBelowRect: selectedElRef.current && isNavigationItem2(selectedElRef.current) ? getOpenNavDropdownPanelRect(selectedElRef.current) : null,
19667
- elRef: glowElRef,
19668
- state: isItemDragging ? "dragging" : selectedElRef.current && isNavigationItem2(selectedElRef.current) && getOpenNavDropdownPanelRect(selectedElRef.current) ? "active-bottom" : resolveItemInteractionState(toolbarRect, parentScrollRef.current),
19669
- showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey) || toolbarVariant === "select-frame" && isFooterFrameSelection,
19670
- dragDisabled: reorderDragDisabled,
19671
- dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
19672
- onDragHandleDragStart: handleItemDragStart,
19673
- onDragHandleDragEnd: handleItemDragEnd,
19674
- onItemPointerDown: toolbarVariant === "logo" ? void 0 : handleItemChromePointerDown,
19675
- onItemClick: toolbarVariant === "logo" ? void 0 : handleItemChromeClick,
19676
- itemDragSurface: toolbarVariant !== "logo" && !isFooterFrameSelection,
19677
- toolbar: toolbarVariant === "link-action" && !isItemDragging || toolbarVariant === "select-frame" && (isFooterFrameSelection || selectedIsSocialsRow) && !isItemDragging ? /* @__PURE__ */ jsx33(
19678
- ItemActionToolbar,
19679
- {
19680
- onEditLink: openLinkPopoverForSelected,
19681
- onStyle: () => {
19682
- const row = selectedElRef.current;
19683
- if (!row) return;
19684
- if (floatingPanel?.kind === "socials-display") closeFloatingPanelOnly();
19685
- else openSocialsDisplayPanel(row);
19686
- },
19687
- showStyle: selectedIsSocialsRow,
19688
- styleActive: floatingPanel?.kind === "socials-display",
19689
- onAddItem: handleAddChildItem,
19690
- onSelectParent: handleSelectParent,
19691
- onDuplicate: handleDuplicateSelected,
19692
- onDelete: handleDeleteSelected,
19693
- addItemDisabled: isFooterFrameSelection && isFooterAddItemDisabled(selectedElRef.current) || selectedElRef.current !== null && (() => {
19694
- const row = isSocialsRow(selectedElRef.current) ? selectedElRef.current : findSocialsRow(selectedElRef.current);
19695
- return row ? !canAddSocialItem(row) : false;
19696
- })(),
19697
- editLinkDisabled: false,
19698
- moreDisabled: false,
19699
- duplicateDisabled: isFooterFrameSelection || selectedIsSocialsRow,
19700
- showEditLink: !isFooterFrameSelection && !selectedIsSocialsRow && navDropdownPreviewOpen === null,
19701
- showAddItem: isFooterFrameSelection || selectedIsSocialsRow || !selectedIsCta && Boolean(
19702
- selectedElRef.current && isNavbarHrefKey(selectedElRef.current.getAttribute("data-ohw-href-key")) && !isNestedNavChild(selectedElRef.current)
19703
- ),
19704
- showMore: (!selectedIsCta || isFooterFrameSelection) && !selectedIsSocialsRow,
19705
- dropdownOpen: navDropdownPreviewOpen,
19706
- onDropdownOpenChange: handleNavDropdownOpenChange,
19707
- headingVisible: footerHeadingVisible,
19708
- onHeadingVisibleChange: handleFooterHeadingVisibleChange
19709
- }
19710
- ) : void 0
19711
- }
19712
- ),
19713
- toolbarRect && toolbarVariant === "rich-text" && !linkPopover && /* @__PURE__ */ jsxs20(Fragment8, { children: [
19714
- /* @__PURE__ */ jsx33(
19715
- EditGlowChrome,
21097
+ ),
21098
+ toggleState && !linkPopover && /* @__PURE__ */ jsx33(
21099
+ StateToggle,
19716
21100
  {
19717
- rect: toolbarRect,
19718
- elRef: glowElRef,
19719
- reorderHrefKey,
19720
- dragDisabled: reorderDragDisabled,
19721
- hideHandle: isItemDragging
21101
+ rect: toggleState.rect,
21102
+ activeState: toggleState.activeState,
21103
+ states: toggleState.states,
21104
+ onStateChange: handleStateChange
19722
21105
  }
19723
21106
  ),
19724
- /* @__PURE__ */ jsx33(
19725
- FloatingToolbar,
21107
+ sectionGap && !linkPopover && /* @__PURE__ */ jsxs20(
21108
+ "div",
19726
21109
  {
19727
- rect: toolbarRect,
19728
- parentScroll: parentScrollRef.current,
19729
- elRef: toolbarElRef,
19730
- onCommand: handleCommand,
19731
- activeCommands,
19732
- showEditLink,
19733
- onEditLink: openLinkPopoverForActive
21110
+ "data-ohw-section-insert-line": "",
21111
+ className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
21112
+ style: { top: sectionGap.y, transform: "translateY(-50%)" },
21113
+ children: [
21114
+ /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
21115
+ /* @__PURE__ */ jsx33(
21116
+ Badge,
21117
+ {
21118
+ 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",
21119
+ onClick: () => {
21120
+ window.parent.postMessage(
21121
+ {
21122
+ type: "ow:add-section",
21123
+ insertAfter: sectionGap.insertAfter,
21124
+ insertBefore: sectionGap.insertBefore
21125
+ },
21126
+ "*"
21127
+ );
21128
+ },
21129
+ children: "Add Section"
21130
+ }
21131
+ ),
21132
+ /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-primary", style: { height: 3 } })
21133
+ ]
19734
21134
  }
19735
- )
19736
- ] }),
19737
- maxBadge && /* @__PURE__ */ jsxs20(
19738
- "div",
19739
- {
19740
- "data-ohw-max-badge": "",
19741
- style: {
19742
- position: "fixed",
19743
- top: maxBadge.rect.bottom + 4,
19744
- left: maxBadge.rect.right,
19745
- transform: "translateX(-100%)",
19746
- zIndex: 2147483647,
19747
- background: maxBadge.current > maxBadge.max ? "#FEF2F2" : "#F5F5F4",
19748
- color: maxBadge.current > maxBadge.max ? "#DC2626" : "#78716C",
19749
- border: `1px solid ${maxBadge.current > maxBadge.max ? "#FECACA" : "#E7E5E4"}`,
19750
- borderRadius: 4,
19751
- padding: "2px 6px",
19752
- fontSize: 11,
19753
- fontWeight: 500,
19754
- pointerEvents: "none"
21135
+ ),
21136
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx33(
21137
+ LinkPopover,
21138
+ {
21139
+ panelRef: linkPopoverPanelRef,
21140
+ portalContainer: dialogPortalContainer,
21141
+ open: true,
21142
+ mode: linkPopover.mode ?? "edit",
21143
+ pages: sitePages,
21144
+ sections: currentSections,
21145
+ sectionsByPath,
21146
+ initialTarget: linkPopover.target,
21147
+ existingTargets: linkPopover.intent === "add-nav" ? getNavbarExistingTargets() : [],
21148
+ onClose: closeLinkPopover,
21149
+ onSubmit: handleLinkPopoverSubmit
19755
21150
  },
19756
- children: [
19757
- maxBadge.current,
19758
- "/",
19759
- maxBadge.max
19760
- ]
19761
- }
19762
- ),
19763
- toggleState && !linkPopover && /* @__PURE__ */ jsx33(
19764
- StateToggle,
19765
- {
19766
- rect: toggleState.rect,
19767
- activeState: toggleState.activeState,
19768
- states: toggleState.states,
19769
- onStateChange: handleStateChange
19770
- }
19771
- ),
19772
- sectionGap && !linkPopover && /* @__PURE__ */ jsxs20(
19773
- "div",
19774
- {
19775
- "data-ohw-section-insert-line": "",
19776
- className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
19777
- style: { top: sectionGap.y, transform: "translateY(-50%)" },
19778
- children: [
19779
- /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
19780
- /* @__PURE__ */ jsx33(
19781
- Badge,
21151
+ linkPopover.key
21152
+ ) : null,
21153
+ floatingPanel && floatingPanel.kind === "socials-display" ? /* @__PURE__ */ jsx33(
21154
+ FloatingPanel,
21155
+ {
21156
+ open: true,
21157
+ title: floatingPanel.title,
21158
+ context: floatingPanel.context,
21159
+ position: floatingPanelPos,
21160
+ onPositionChange: setFloatingPanelPos,
21161
+ parentScroll: parentScrollSnap ?? parentScrollRef.current,
21162
+ onClose: closeFloatingPanelOnly,
21163
+ children: /* @__PURE__ */ jsx33(
21164
+ SocialsDisplayPanel,
19782
21165
  {
19783
- 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",
19784
- onClick: () => {
19785
- window.parent.postMessage(
19786
- {
19787
- type: "ow:add-section",
19788
- insertAfter: sectionGap.insertAfter,
19789
- insertBefore: sectionGap.insertBefore
19790
- },
19791
- "*"
19792
- );
19793
- },
19794
- children: "Add Section"
21166
+ display: socialsDisplayFor(floatingPanel.row, editContentRef.current),
21167
+ onChange: (next) => {
21168
+ changeSocialsDisplay(floatingPanel.row, next);
21169
+ setFloatingPanel({ ...floatingPanel });
21170
+ }
19795
21171
  }
19796
- ),
19797
- /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-primary", style: { height: 3 } })
19798
- ]
19799
- }
19800
- ),
19801
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx33(
19802
- LinkPopover,
19803
- {
19804
- panelRef: linkPopoverPanelRef,
19805
- portalContainer: dialogPortalContainer,
19806
- open: true,
19807
- mode: linkPopover.mode ?? "edit",
19808
- pages: sitePages,
19809
- sections: currentSections,
19810
- sectionsByPath,
19811
- initialTarget: linkPopover.target,
19812
- existingTargets: linkPopover.intent === "add-nav" ? getNavbarExistingTargets() : [],
19813
- onClose: closeLinkPopover,
19814
- onSubmit: handleLinkPopoverSubmit
21172
+ )
21173
+ }
21174
+ ) : null
21175
+ ] }),
21176
+ bridgeRoot
21177
+ ) : null
21178
+ ] });
21179
+ }
21180
+
21181
+ // src/ui/EmptySection.tsx
21182
+ import Link3 from "next/link";
21183
+ import { Fragment as Fragment9, jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
21184
+ function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey }) {
21185
+ return /* @__PURE__ */ jsxs21(Fragment9, { children: [
21186
+ /* @__PURE__ */ jsx34(
21187
+ "p",
21188
+ {
21189
+ style: {
21190
+ fontFamily: "var(--brand-font-body)",
21191
+ fontSize: "0.75rem",
21192
+ fontWeight: 500,
21193
+ letterSpacing: "0.15em",
21194
+ textTransform: "uppercase",
21195
+ color: "var(--brand-accent)",
21196
+ marginBottom: "1.5rem"
19815
21197
  },
19816
- linkPopover.key
19817
- ) : null,
19818
- floatingPanel && floatingPanel.kind === "socials-display" ? /* @__PURE__ */ jsx33(
19819
- FloatingPanel,
19820
- {
19821
- open: true,
19822
- title: floatingPanel.title,
19823
- context: floatingPanel.context,
19824
- position: floatingPanelPos,
19825
- onPositionChange: setFloatingPanelPos,
19826
- parentScroll: parentScrollSnap ?? parentScrollRef.current,
19827
- onClose: closeFloatingPanelOnly,
19828
- children: /* @__PURE__ */ jsx33(
19829
- SocialsDisplayPanel,
19830
- {
19831
- display: socialsDisplayFor(floatingPanel.row, editContentRef.current),
19832
- onChange: (next) => {
19833
- changeSocialsDisplay(floatingPanel.row, next);
19834
- setFloatingPanel({ ...floatingPanel });
19835
- }
19836
- }
19837
- )
19838
- }
19839
- ) : null
19840
- ] }),
19841
- bridgeRoot
19842
- ) : null;
21198
+ children: /* @__PURE__ */ jsx34(Link3, { href: homeHref, style: { color: "inherit" }, children: /* @__PURE__ */ jsx34("span", { ...eyebrowKey ? { "data-ohw-editable": "text", "data-ohw-key": eyebrowKey } : {}, children: "Home" }) })
21199
+ }
21200
+ ),
21201
+ /* @__PURE__ */ jsx34(
21202
+ "h1",
21203
+ {
21204
+ style: {
21205
+ fontFamily: "var(--brand-font-heading)",
21206
+ fontSize: "clamp(2rem, 3.5vw, 2.75rem)",
21207
+ lineHeight: 1.1,
21208
+ letterSpacing: "-0.025em",
21209
+ color: "var(--brand-text)",
21210
+ marginBottom: "1rem"
21211
+ },
21212
+ ...titleKey ? { "data-ohw-editable": "text", "data-ohw-key": titleKey, "data-ohw-max-length": 80 } : {},
21213
+ children: title
21214
+ }
21215
+ ),
21216
+ /* @__PURE__ */ jsx34(
21217
+ "p",
21218
+ {
21219
+ style: {
21220
+ fontFamily: "var(--brand-font-body)",
21221
+ fontSize: "1rem",
21222
+ lineHeight: 1.7,
21223
+ fontWeight: 300,
21224
+ color: "var(--brand-text-muted)",
21225
+ maxWidth: "340px"
21226
+ },
21227
+ ...subtitleKey ? { "data-ohw-editable": "text", "data-ohw-key": subtitleKey, "data-ohw-max-length": 160 } : {},
21228
+ children: "This page doesn't have any content yet."
21229
+ }
21230
+ )
21231
+ ] });
19843
21232
  }
19844
21233
  export {
19845
21234
  AI_DEFAULT_BRAND,
@@ -19857,6 +21246,7 @@ export {
19857
21246
  DropdownMenuItem,
19858
21247
  DropdownMenuSeparator,
19859
21248
  DropdownMenuTrigger,
21249
+ EmptySection,
19860
21250
  ItemActionToolbar,
19861
21251
  ItemInteractionLayer,
19862
21252
  LinkEditorPanel,