@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.cjs CHANGED
@@ -46,6 +46,7 @@ __export(index_exports, {
46
46
  DropdownMenuItem: () => DropdownMenuItem,
47
47
  DropdownMenuSeparator: () => DropdownMenuSeparator,
48
48
  DropdownMenuTrigger: () => DropdownMenuTrigger,
49
+ EmptySection: () => EmptySection,
49
50
  ItemActionToolbar: () => ItemActionToolbar,
50
51
  ItemInteractionLayer: () => ItemInteractionLayer,
51
52
  LinkEditorPanel: () => LinkEditorPanel,
@@ -74,7 +75,7 @@ __export(index_exports, {
74
75
  module.exports = __toCommonJS(index_exports);
75
76
 
76
77
  // src/OhhwellsBridge.tsx
77
- var import_react16 = __toESM(require("react"), 1);
78
+ var import_react17 = __toESM(require("react"), 1);
78
79
  var import_client2 = require("react-dom/client");
79
80
  var import_react_dom3 = require("react-dom");
80
81
 
@@ -156,7 +157,12 @@ function parseAiSectionsState(raw) {
156
157
  media: entry.media && typeof entry.media === "object" ? entry.media : {}
157
158
  }));
158
159
  const removed = Array.isArray(parsed.removed) ? parsed.removed.filter((id) => typeof id === "string" && id.length > 0) : [];
159
- return { v: 1, sections, ...removed.length ? { removed } : {} };
160
+ return {
161
+ v: 1,
162
+ sections,
163
+ ...removed.length ? { removed } : {},
164
+ ...parsed.hideTemplate === true ? { hideTemplate: true } : {}
165
+ };
160
166
  } catch {
161
167
  return EMPTY_AI_SECTIONS;
162
168
  }
@@ -169,6 +175,7 @@ function applyTreeToState(state, payload) {
169
175
  const entry = {
170
176
  id: payload.id,
171
177
  label: payload.label ?? "Generated section",
178
+ ...typeof payload.path === "string" && payload.path ? { path: payload.path } : {},
172
179
  afterSection: payload.mode === "insert" && !insertBefore ? payload.targetSectionId ?? null : null,
173
180
  ...insertBefore && payload.targetSectionId ? { beforeSection: payload.targetSectionId } : {},
174
181
  ...payload.mode === "replace" && payload.targetSectionId ? { replaces: payload.targetSectionId } : {},
@@ -191,6 +198,317 @@ function deleteSectionFromState(state, sectionId) {
191
198
  return { ...state, v: 1, sections: state.sections, removed: [...removed, sectionId] };
192
199
  }
193
200
 
201
+ // src/lib/brand-chrome.ts
202
+ var BRAND_NAME_KEY = "__ohw_brand_name";
203
+ var BRAND_TITLE_KEY = "__ohw_site_title";
204
+ var BRAND_FAVICON_LETTER_KEY = "__ohw_favicon_letter";
205
+ var BRAND_CHROME_KEYS = /* @__PURE__ */ new Set([
206
+ BRAND_NAME_KEY,
207
+ BRAND_TITLE_KEY,
208
+ BRAND_FAVICON_LETTER_KEY
209
+ ]);
210
+ function upsertMeta(selector, attr, token, value) {
211
+ let el = document.head.querySelector(selector);
212
+ if (!el) {
213
+ el = document.createElement("meta");
214
+ el.setAttribute(attr, token);
215
+ document.head.appendChild(el);
216
+ }
217
+ if (el.getAttribute("content") !== value) el.setAttribute("content", value);
218
+ }
219
+ function escapeXml(value) {
220
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
221
+ }
222
+ function applyLetterFavicon(letter) {
223
+ 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>`;
224
+ const href = `data:image/svg+xml,${encodeURIComponent(svg)}`;
225
+ let link = document.head.querySelector('link[rel="icon"]');
226
+ if (!link) {
227
+ link = document.createElement("link");
228
+ link.rel = "icon";
229
+ document.head.appendChild(link);
230
+ }
231
+ link.type = "image/svg+xml";
232
+ if (link.href !== href) link.href = href;
233
+ }
234
+ function applyBrandChrome(content) {
235
+ const name = content[BRAND_NAME_KEY];
236
+ if (typeof name === "string" && name.length > 0) {
237
+ document.querySelectorAll("[data-ohw-wordmark]").forEach((el) => {
238
+ if (el.textContent !== name) el.textContent = name;
239
+ if (el.getAttribute("title") !== name) el.setAttribute("title", name);
240
+ });
241
+ }
242
+ const title = content[BRAND_TITLE_KEY];
243
+ if (typeof title === "string" && title.length > 0) {
244
+ if (document.title !== title) document.title = title;
245
+ upsertMeta('meta[property="og:title"]', "property", "og:title", title);
246
+ upsertMeta('meta[name="twitter:title"]', "name", "twitter:title", title);
247
+ }
248
+ const letter = content[BRAND_FAVICON_LETTER_KEY];
249
+ if (typeof letter === "string" && letter.length > 0) {
250
+ applyLetterFavicon(letter);
251
+ }
252
+ }
253
+
254
+ // src/lib/brand-kit.ts
255
+ var BRAND_KIT_KEY = "__ohw_brand";
256
+ var BRAND_VAR_PREFIX = "--ohw-brand-";
257
+ var BRAND_VAR_NAMES = ["primary", "accent", "light", "dark", "surface", "border", "muted"].map(
258
+ (role) => `${BRAND_VAR_PREFIX}${role}`
259
+ );
260
+ var FONT_VARS = {
261
+ heading: ["--font-heading", "--font-display", "--brand-font-heading"],
262
+ body: ["--font-body", "--brand-font-body"]
263
+ };
264
+ var BRAND_FONT_LINK_ID = "ohw-brand-fonts";
265
+ function brandColorVars(kit) {
266
+ const { dark, primary, accent, light } = kit.palette;
267
+ const mix = (a, aPct, b) => `color-mix(in srgb, ${a} ${aPct}%, ${b})`;
268
+ return {
269
+ [`${BRAND_VAR_PREFIX}primary`]: primary,
270
+ [`${BRAND_VAR_PREFIX}accent`]: accent,
271
+ [`${BRAND_VAR_PREFIX}light`]: light,
272
+ [`${BRAND_VAR_PREFIX}dark`]: dark,
273
+ [`${BRAND_VAR_PREFIX}surface`]: mix(light, 95, dark),
274
+ [`${BRAND_VAR_PREFIX}border`]: mix(light, 85, dark),
275
+ [`${BRAND_VAR_PREFIX}muted`]: mix(dark, 62, light)
276
+ };
277
+ }
278
+ function parseBrandKit(raw) {
279
+ if (!raw) return null;
280
+ try {
281
+ const parsed = JSON.parse(raw);
282
+ const p = parsed?.palette;
283
+ const f = parsed?.fonts;
284
+ 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") {
285
+ return null;
286
+ }
287
+ return {
288
+ palette: { dark: p.dark, primary: p.primary, accent: p.accent, light: p.light },
289
+ fonts: { heading: f.heading, body: f.body }
290
+ };
291
+ } catch {
292
+ return null;
293
+ }
294
+ }
295
+ function familyOf(stack) {
296
+ const first = stack.split(",")[0]?.trim() ?? "";
297
+ return first.replace(/^['"]|['"]$/g, "");
298
+ }
299
+ function loadBrandFonts(families) {
300
+ const unique = [...new Set(families.map((f) => f.trim()).filter(Boolean))];
301
+ if (unique.length === 0) return;
302
+ const spec = unique.map((f) => `${f.replace(/ /g, "+")}:400,500,600,700`).join("|");
303
+ const href = `https://fonts.googleapis.com/css?family=${spec}&display=swap`;
304
+ let link = document.getElementById(BRAND_FONT_LINK_ID);
305
+ if (!link) {
306
+ link = document.createElement("link");
307
+ link.id = BRAND_FONT_LINK_ID;
308
+ link.rel = "stylesheet";
309
+ document.head.appendChild(link);
310
+ }
311
+ if (link.href !== href) link.href = href;
312
+ }
313
+ function applyBrandToDom(kit) {
314
+ const root = document.documentElement;
315
+ if (!kit) {
316
+ for (const name of BRAND_VAR_NAMES) root.style.removeProperty(name);
317
+ for (const name of [...FONT_VARS.heading, ...FONT_VARS.body]) root.style.removeProperty(name);
318
+ document.getElementById(BRAND_FONT_LINK_ID)?.remove();
319
+ return;
320
+ }
321
+ for (const [name, value] of Object.entries(brandColorVars(kit))) root.style.setProperty(name, value);
322
+ for (const name of FONT_VARS.heading) root.style.setProperty(name, kit.fonts.heading);
323
+ for (const name of FONT_VARS.body) root.style.setProperty(name, kit.fonts.body);
324
+ loadBrandFonts([familyOf(kit.fonts.heading), familyOf(kit.fonts.body)]);
325
+ }
326
+
327
+ // src/lib/section-styles.ts
328
+ var STYLE_STORE_KEY = "__ohw_styles";
329
+ var STYLE_SHEET_ID = "ohw-section-styles";
330
+ function parseStyleStore(raw) {
331
+ if (!raw) return null;
332
+ try {
333
+ const parsed = JSON.parse(raw);
334
+ if (parsed?.v !== 1) return null;
335
+ return {
336
+ v: 1,
337
+ sections: typeof parsed.sections === "object" && parsed.sections ? parsed.sections : {},
338
+ nodes: typeof parsed.nodes === "object" && parsed.nodes ? parsed.nodes : {}
339
+ };
340
+ } catch {
341
+ return null;
342
+ }
343
+ }
344
+ var BG_VALUES = {
345
+ surface: "color-mix(in srgb, var(--ohw-brand-light, var(--color-light, #ECECEB)) 94%, var(--ohw-brand-dark, var(--color-dark, #0C0A09)))",
346
+ accent: "var(--ohw-brand-primary, var(--color-primary, #0078E5))",
347
+ "accent-soft": "color-mix(in srgb, var(--ohw-brand-primary, var(--color-primary, #0078E5)) 12%, var(--ohw-brand-light, var(--color-light, #FFFFFF)))"
348
+ };
349
+ var HEADLINE_SIZES = { md: 24, lg: 32, xl: 40, display: 56 };
350
+ function styleSheetCss() {
351
+ const rules = [];
352
+ for (const [tone, value] of Object.entries(BG_VALUES)) {
353
+ rules.push(`[data-ohw-style-bg="${tone}"] { background: ${value} !important; }`);
354
+ }
355
+ rules.push(
356
+ `[data-ohw-style-bg="accent"] { color: var(--ohw-brand-light, var(--color-light, #FFFFFF)) !important; }`
357
+ );
358
+ rules.push(
359
+ `[data-ohw-style-distribution="space-between"] { display: flex !important; flex-direction: column; justify-content: space-between; }`,
360
+ `[data-ohw-style-distribution="center"] { display: flex !important; flex-direction: column; justify-content: center; }`
361
+ );
362
+ for (const [scale, size] of Object.entries(HEADLINE_SIZES)) {
363
+ rules.push(
364
+ `[data-ohw-style-headline="${scale}"] :is(h1, h2, h3) { font-size: ${size}px !important; line-height: 1.15 !important; }`
365
+ );
366
+ }
367
+ rules.push(
368
+ `[data-ohw-style-aspect="fill-height"] img { height: 100% !important; aspect-ratio: auto !important; object-fit: cover; }`
369
+ );
370
+ for (const aspect of ["1:1", "4:5", "3:4", "3:2", "16:9", "2:1", "3:1"]) {
371
+ rules.push(
372
+ `[data-ohw-style-aspect="${aspect.replace(":", "-")}"] img { aspect-ratio: ${aspect.replace(":", " / ")} !important; height: auto !important; object-fit: cover; }`
373
+ );
374
+ }
375
+ const pad = { tight: 40, balanced: 64, airy: 96 };
376
+ for (const [spacing, px] of Object.entries(pad)) {
377
+ rules.push(
378
+ `[data-ohw-style-spacing="${spacing}"] { padding-top: ${px}px !important; padding-bottom: ${px}px !important; }`
379
+ );
380
+ }
381
+ return rules.join("\n");
382
+ }
383
+ var STYLE_FONT_LINK_ID = "ohw-style-fonts";
384
+ function loadStyleFonts(families) {
385
+ const unique = [...new Set(families.map((f) => f.trim()).filter(Boolean))];
386
+ const existing = document.getElementById(STYLE_FONT_LINK_ID);
387
+ if (unique.length === 0) {
388
+ existing?.remove();
389
+ return;
390
+ }
391
+ const spec = unique.map((f) => `${f.replace(/ /g, "+")}:400,500,600,700`).join("|");
392
+ const href = `https://fonts.googleapis.com/css?family=${spec}&display=swap`;
393
+ let link = existing;
394
+ if (!link) {
395
+ link = document.createElement("link");
396
+ link.id = STYLE_FONT_LINK_ID;
397
+ link.rel = "stylesheet";
398
+ document.head.appendChild(link);
399
+ }
400
+ if (link.href !== href) link.href = href;
401
+ }
402
+ var SECTION_ATTRS = {
403
+ sectionBackground: "data-ohw-style-bg",
404
+ textDistribution: "data-ohw-style-distribution",
405
+ headlineScale: "data-ohw-style-headline",
406
+ imageAspect: "data-ohw-style-aspect",
407
+ spacing: "data-ohw-style-spacing"
408
+ };
409
+ var NODE_WROTE_ATTR = "data-ohw-style-node";
410
+ var NODE_PROPS = ["color", "font-family", "font-size", "background"];
411
+ function saveInline(el, prop) {
412
+ const attr = `data-ohw-style-prev-${prop}`;
413
+ if (!el.hasAttribute(attr)) el.setAttribute(attr, el.style.getPropertyValue(prop));
414
+ }
415
+ function restoreInline(el, prop) {
416
+ const attr = `data-ohw-style-prev-${prop}`;
417
+ if (!el.hasAttribute(attr)) return;
418
+ const prev = el.getAttribute(attr) ?? "";
419
+ if (prev) el.style.setProperty(prop, prev);
420
+ else el.style.removeProperty(prop);
421
+ el.removeAttribute(attr);
422
+ }
423
+ function ensureStyleSheet() {
424
+ let el = document.getElementById(STYLE_SHEET_ID);
425
+ if (!el) {
426
+ el = document.createElement("style");
427
+ el.id = STYLE_SHEET_ID;
428
+ document.head.appendChild(el);
429
+ }
430
+ const css = styleSheetCss();
431
+ if (el.textContent !== css) el.textContent = css;
432
+ }
433
+ function clearSectionAttrs(root) {
434
+ for (const attr of Object.values(SECTION_ATTRS)) {
435
+ for (const el of Array.from(root.querySelectorAll(`[${attr}]`))) el.removeAttribute(attr);
436
+ }
437
+ for (const el of Array.from(root.querySelectorAll("[data-ohw-style-bgcolor]"))) {
438
+ restoreInline(el, "background");
439
+ el.removeAttribute("data-ohw-style-bgcolor");
440
+ }
441
+ }
442
+ function clearNodeProps(root) {
443
+ for (const el of Array.from(root.querySelectorAll(`[${NODE_WROTE_ATTR}]`))) {
444
+ const h = el;
445
+ for (const prop of NODE_PROPS) restoreInline(h, prop);
446
+ h.removeAttribute(NODE_WROTE_ATTR);
447
+ }
448
+ }
449
+ function buttonSurfaceOf(el) {
450
+ return el.closest("a, button") ?? el;
451
+ }
452
+ function applyStylesToDom(store) {
453
+ ensureStyleSheet();
454
+ clearSectionAttrs(document);
455
+ clearNodeProps(document);
456
+ loadStyleFonts(
457
+ store ? Object.values(store.nodes).flatMap((n) => n.fontFamily ? [n.fontFamily] : []) : []
458
+ );
459
+ if (!store) return;
460
+ for (const [sectionId, override] of Object.entries(store.sections)) {
461
+ const sections = document.querySelectorAll(
462
+ `[data-ohw-section="${CSS.escape(sectionId)}"]`
463
+ );
464
+ for (const section of Array.from(sections)) {
465
+ for (const [prop, attr] of Object.entries(SECTION_ATTRS)) {
466
+ const value = override[prop];
467
+ if (value === void 0) continue;
468
+ if (prop === "sectionBackground" && override.sectionBackgroundColor !== void 0) continue;
469
+ section.setAttribute(attr, String(value).replace(":", "-"));
470
+ }
471
+ if (override.sectionBackgroundColor !== void 0) {
472
+ saveInline(section, "background");
473
+ section.style.setProperty("background", override.sectionBackgroundColor, "important");
474
+ section.setAttribute("data-ohw-style-bgcolor", "");
475
+ }
476
+ }
477
+ }
478
+ for (const [key, override] of Object.entries(store.nodes)) {
479
+ const nodes = document.querySelectorAll(`[data-ohw-key="${CSS.escape(key)}"]`);
480
+ for (const el of Array.from(nodes)) {
481
+ if (override.color !== void 0) {
482
+ saveInline(el, "color");
483
+ el.style.setProperty("color", override.color, "important");
484
+ el.setAttribute(NODE_WROTE_ATTR, "");
485
+ }
486
+ if (override.fontFamily !== void 0) {
487
+ saveInline(el, "font-family");
488
+ el.style.setProperty("font-family", `'${override.fontFamily}'`, "important");
489
+ el.setAttribute(NODE_WROTE_ATTR, "");
490
+ }
491
+ if (override.fontSize !== void 0) {
492
+ saveInline(el, "font-size");
493
+ el.style.setProperty("font-size", `${override.fontSize}px`, "important");
494
+ el.setAttribute(NODE_WROTE_ATTR, "");
495
+ }
496
+ if (override.buttonBackground !== void 0 || override.buttonText !== void 0) {
497
+ const surface = buttonSurfaceOf(el);
498
+ if (override.buttonBackground !== void 0) {
499
+ saveInline(surface, "background");
500
+ surface.style.setProperty("background", override.buttonBackground, "important");
501
+ }
502
+ if (override.buttonText !== void 0) {
503
+ saveInline(surface, "color");
504
+ surface.style.setProperty("color", override.buttonText, "important");
505
+ }
506
+ surface.setAttribute(NODE_WROTE_ATTR, "");
507
+ }
508
+ }
509
+ }
510
+ }
511
+
194
512
  // src/ui/ai-tree/aiSectionsManager.tsx
195
513
  var import_react_dom = require("react-dom");
196
514
  var import_client = require("react-dom/client");
@@ -205,7 +523,8 @@ function lucideByName(name) {
205
523
  }
206
524
  var typeStyle = (spec, font) => ({
207
525
  fontFamily: font,
208
- fontSize: spec.size,
526
+ // Headings shrink with the viewport (reaching full size around ~900px wide); body copy stays put.
527
+ 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,
209
528
  lineHeight: spec.line,
210
529
  fontWeight: spec.weight
211
530
  });
@@ -220,6 +539,36 @@ var FEATURE_LINE_CSS = [
220
539
  `background-color:currentColor;-webkit-mask:url("${CHECK_MASK}") no-repeat 0 0/24px 24px;`,
221
540
  `mask:url("${CHECK_MASK}") no-repeat 0 0/24px 24px}`
222
541
  ].join("");
542
+ function hexLuminance(color) {
543
+ const m = /^#([0-9a-f]{6})$/i.exec(color.trim());
544
+ if (!m) return null;
545
+ const [r2, g, b] = [0, 2, 4].map((i) => {
546
+ const c = parseInt(m[1].slice(i, i + 2), 16) / 255;
547
+ return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
548
+ });
549
+ return 0.2126 * r2 + 0.7152 * g + 0.0722 * b;
550
+ }
551
+ function hexContrast(a, b) {
552
+ const la = hexLuminance(a);
553
+ const lb = hexLuminance(b);
554
+ if (la === null || lb === null) return null;
555
+ const [hi, lo] = la > lb ? [la, lb] : [lb, la];
556
+ return (hi + 0.05) / (lo + 0.05);
557
+ }
558
+ function accentBandContext(brand) {
559
+ const p = brand.palette;
560
+ const lightWins = (hexContrast(p.light, p.primary) ?? 99) >= (hexContrast(p.dark, p.primary) ?? 0);
561
+ if (lightWins) {
562
+ return {
563
+ brand: { ...brand, palette: { dark: p.light, primary: p.light, accent: p.light, light: p.primary } },
564
+ buttonLabel: p.primary
565
+ };
566
+ }
567
+ return {
568
+ brand: { ...brand, palette: { dark: p.dark, primary: p.dark, accent: p.dark, light: p.light } },
569
+ buttonLabel: p.light
570
+ };
571
+ }
223
572
  function textAttrs(ctx, path) {
224
573
  return ctx.keyFor ? { "data-ohw-key": ctx.keyFor(path), "data-ohw-editable": "text" } : {};
225
574
  }
@@ -231,7 +580,12 @@ var AI_RESPONSIVE_CSS = [
231
580
  "@media (max-width: 640px) {",
232
581
  " [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
233
582
  " [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
583
+ // Group containers flatten to a column on phones; span placements come along for free.
584
+ " [data-ai-responsive] [data-ai-group] { display: flex !important; flex-direction: column !important; }",
585
+ " [data-ai-responsive] [data-ai-group] > * { grid-column: auto !important; }",
234
586
  " [data-ai-responsive] [data-ai-section-inner] { padding-left: 20px !important; padding-right: 20px !important; }",
587
+ " [data-ai-responsive] { overflow-x: hidden; }",
588
+ " [data-ai-responsive] img { max-width: 100%; }",
235
589
  "}"
236
590
  ].join("\n");
237
591
  var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
@@ -309,7 +663,7 @@ function ButtonEl({
309
663
  }) {
310
664
  const secondary = slots.variant === "secondary";
311
665
  const href = str(slots.href);
312
- const linkAttrs = ctx.keyFor && path ? { "data-ohw-href-key": ctx.keyFor(`${path}.href`) } : {};
666
+ const linkAttrs = ctx.keyFor && path ? { "data-ohw-href-key": ctx.keyFor(`${path}.href`), "data-ohw-role": "button" } : {};
313
667
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
314
668
  "a",
315
669
  {
@@ -325,7 +679,7 @@ function ButtonEl({
325
679
  textDecoration: "none",
326
680
  cursor: "pointer",
327
681
  ...typeStyle(AI_TREE_TOKENS.type.button, ctx.brand.fonts.body),
328
- ...secondary ? { border: `1px solid ${ctx.brand.palette.dark}`, color: ctx.brand.palette.dark } : { background: ctx.brand.palette.primary, color: AI_TREE_TOKENS.textPrimaryForeground }
682
+ ...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 }
329
683
  },
330
684
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ...path ? textAttrs(ctx, `${path}.label`) : {}, children: str(slots.label) })
331
685
  }
@@ -831,7 +1185,24 @@ function CardBlock({ node, ctx, path }) {
831
1185
  minWidth: 0
832
1186
  },
833
1187
  children: [
834
- media && (horizontal ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { flex: 1, minWidth: 0, display: "flex", alignItems: "center", padding: mediaInset }, children: media }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1188
+ media && (horizontal ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1189
+ "div",
1190
+ {
1191
+ style: (
1192
+ // An icon hugs its glyph — flex:1 gave a 48px icon half the card and pushed the
1193
+ // text to the far side. Photos keep the half-and-half split. The inset has no
1194
+ // inner padding (the photo split absorbed that), so the icon carries its own gap.
1195
+ /^(lucide|simple):/.test(mediaRef) ? {
1196
+ flexShrink: 0,
1197
+ display: "flex",
1198
+ alignItems: "center",
1199
+ padding: mediaInset,
1200
+ [mediaPosition === "right" ? "marginLeft" : "marginRight"]: 20
1201
+ } : { flex: 1, minWidth: 0, display: "flex", alignItems: "center", padding: mediaInset }
1202
+ ),
1203
+ children: media
1204
+ }
1205
+ ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
835
1206
  "div",
836
1207
  {
837
1208
  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" },
@@ -1095,6 +1466,49 @@ function renderNode(node, ctx, path) {
1095
1466
  switch (node.type) {
1096
1467
  case "text":
1097
1468
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TextBlock, { slots, ctx, path });
1469
+ // Layout container: arranges child blocks, contributes no content of its own. `grid` is a
1470
+ // nested 12-column grid the children span (a collage is 3–5 media on it, bottom-aligned so
1471
+ // mixed aspects read as a composition); `split` is exactly two children at a ratio; `stack`
1472
+ // is a column. Children render through this same dispatcher, so edit markers, media
1473
+ // resolution, and copy paths all work unchanged inside a group.
1474
+ case "group": {
1475
+ const layout = str(slots.layout);
1476
+ const gap = slots.spacing === "tight" ? AI_TREE_TOKENS.spacing3 : slots.spacing === "airy" ? AI_TREE_TOKENS.spacing8 : AI_TREE_TOKENS.spacing6;
1477
+ const kids = (node.children ?? []).map((child, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1478
+ "div",
1479
+ {
1480
+ style: layout === "grid" ? {
1481
+ gridColumn: `span ${typeof child.span === "number" ? Math.min(12, Math.max(1, child.span)) : 12}`,
1482
+ minWidth: 0
1483
+ } : { minWidth: 0 },
1484
+ children: renderNode(child, ctx, `${path}.c${i}`)
1485
+ },
1486
+ i
1487
+ ));
1488
+ if (layout === "grid") {
1489
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1490
+ "div",
1491
+ {
1492
+ "data-ai-group": "grid",
1493
+ style: { display: "grid", gridTemplateColumns: "repeat(12, 1fr)", gap, alignItems: "end" },
1494
+ children: kids
1495
+ }
1496
+ );
1497
+ }
1498
+ if (layout === "split") {
1499
+ const ratio = str(slots.ratio);
1500
+ const cols = ratio === "3:5" ? "3fr 5fr" : ratio === "5:3" ? "5fr 3fr" : "1fr 1fr";
1501
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1502
+ "div",
1503
+ {
1504
+ "data-ai-group": "split",
1505
+ style: { display: "grid", gridTemplateColumns: cols, gap, alignItems: "center" },
1506
+ children: kids
1507
+ }
1508
+ );
1509
+ }
1510
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { "data-ai-group": "stack", style: { display: "flex", flexDirection: "column", gap }, children: kids });
1511
+ }
1098
1512
  case "button":
1099
1513
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ButtonEl, { slots, ctx, path });
1100
1514
  case "button-row":
@@ -1227,11 +1641,14 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1227
1641
  return null;
1228
1642
  }
1229
1643
  const resolvedBrand = brand ?? AI_DEFAULT_BRAND;
1644
+ const band = (tree.settings ?? {}).sectionBackground === "accent" ? accentBandContext(resolvedBrand) : null;
1645
+ const blockBrand = band?.brand ?? resolvedBrand;
1230
1646
  const ctx = {
1231
- brand: resolvedBrand,
1647
+ brand: blockBrand,
1232
1648
  resolveMedia: resolveMedia ?? (() => null),
1233
- cardSurface: resolvedBrand.palette.light.toUpperCase() === AI_DEFAULT_BRAND.palette.light.toUpperCase() ? "#ECECEB" : `color-mix(in srgb, ${resolvedBrand.palette.light} 90%, ${resolvedBrand.palette.dark})`,
1234
- keyFor: editKeyPrefix ? (path) => `${editKeyPrefix}.${path}` : null
1649
+ cardSurface: blockBrand.palette.light.toUpperCase() === AI_DEFAULT_BRAND.palette.light.toUpperCase() ? "#ECECEB" : `color-mix(in srgb, ${blockBrand.palette.light} 90%, ${blockBrand.palette.dark})`,
1650
+ keyFor: editKeyPrefix ? (path) => `${editKeyPrefix}.${path}` : null,
1651
+ ...band ? { buttonLabel: band.buttonLabel } : {}
1235
1652
  };
1236
1653
  const settings = tree.settings ?? {};
1237
1654
  const pad = AI_TREE_TOKENS.sectionPadding[settings.spacing ?? "balanced"];
@@ -1239,6 +1656,20 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1239
1656
  const overlayAlpha = { low: 0.25, medium: 0.45, high: 0.65 }[settings.overlayIntensity ?? "medium"];
1240
1657
  const backgroundUrl = isOverlay && settings.backgroundMedia ? ctx.resolveMedia(settings.backgroundMedia) : null;
1241
1658
  const bgAttrs = ctx.keyFor && isOverlay && settings.backgroundMedia ? { "data-ohw-key": ctx.keyFor("settings.backgroundMedia"), "data-ohw-editable": "bg-image" } : {};
1659
+ const toneBackground = (() => {
1660
+ const { dark, primary, light } = resolvedBrand.palette;
1661
+ switch (settings.sectionBackground) {
1662
+ case "surface":
1663
+ return `color-mix(in srgb, ${light} 94%, ${dark})`;
1664
+ case "accent":
1665
+ return primary;
1666
+ case "accent-soft":
1667
+ return `color-mix(in srgb, ${primary} 12%, ${light})`;
1668
+ default:
1669
+ return void 0;
1670
+ }
1671
+ })();
1672
+ const distributed = !isOverlay && settings.textDistribution;
1242
1673
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1243
1674
  "section",
1244
1675
  {
@@ -1248,10 +1679,11 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1248
1679
  style: {
1249
1680
  position: "relative",
1250
1681
  padding: `${pad}px 0`,
1251
- background: isOverlay && !backgroundUrl ? "#DBEAFE" : void 0,
1682
+ background: toneBackground ?? (isOverlay && !backgroundUrl ? "#DBEAFE" : void 0),
1252
1683
  backgroundImage: backgroundUrl ? `url(${backgroundUrl})` : void 0,
1253
1684
  backgroundSize: "cover",
1254
- backgroundPosition: "center"
1685
+ backgroundPosition: "center",
1686
+ color: settings.sectionBackground === "accent" ? blockBrand.palette.dark : void 0
1255
1687
  },
1256
1688
  children: [
1257
1689
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_RESPONSIVE_CSS }),
@@ -1275,10 +1707,24 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1275
1707
  display: "grid",
1276
1708
  gridTemplateColumns: "repeat(12, 1fr)",
1277
1709
  gap: AI_TREE_TOKENS.spacing6,
1278
- alignItems: settings.verticalPosition === "top" ? "start" : "center",
1710
+ alignItems: distributed === "space-between" ? "stretch" : (distributed ?? settings.verticalPosition) === "top" ? "start" : "center",
1279
1711
  marginTop: r2 > 0 ? AI_TREE_TOKENS.spacing8 : 0
1280
1712
  },
1281
- children: (row.blocks ?? []).map((block, b) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { gridColumn: `span ${Math.min(12, Math.max(1, block.span))}`, minWidth: 0 }, children: renderNode(block, ctx, `r${r2}.b${b}`) }, b))
1713
+ children: (row.blocks ?? []).map((block, b) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1714
+ "div",
1715
+ {
1716
+ "data-ai-cell": "",
1717
+ style: {
1718
+ gridColumn: `span ${Math.min(12, Math.max(1, block.span))}`,
1719
+ minWidth: 0,
1720
+ // space-between: each column becomes a flex column whose content spreads over
1721
+ // the full row height instead of clumping at the top.
1722
+ ...distributed === "space-between" ? { display: "flex", flexDirection: "column", justifyContent: "space-between" } : {}
1723
+ },
1724
+ children: renderNode(block, ctx, `r${r2}.b${b}`)
1725
+ },
1726
+ b
1727
+ ))
1282
1728
  },
1283
1729
  r2
1284
1730
  ))
@@ -1294,17 +1740,36 @@ var import_jsx_runtime2 = require("react/jsx-runtime");
1294
1740
  var CONTAINER_ATTR = "data-ohw-ai-generated";
1295
1741
  var REPLACED_ATTR = "data-ohw-ai-replaced-by";
1296
1742
  var REMOVED_ATTR = "data-ohw-ai-removed";
1743
+ var TEMPLATE_HIDDEN_ATTR = "data-ohw-ai-template-hidden";
1744
+ var CHROME_SECTION_IDS = /* @__PURE__ */ new Set(["navbar", "footer"]);
1745
+ function readRootVar(name) {
1746
+ if (typeof document === "undefined") return "";
1747
+ return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
1748
+ }
1749
+ function deriveBrandOverride() {
1750
+ const dark = readRootVar("--ohw-brand-dark");
1751
+ const primary = readRootVar("--ohw-brand-primary");
1752
+ const light = readRootVar("--ohw-brand-light");
1753
+ if (!dark || !primary || !light) return null;
1754
+ const accent = readRootVar("--ohw-brand-accent");
1755
+ const heading = readRootVar("--font-heading") || readRootVar("--font-display");
1756
+ const body = readRootVar("--font-body");
1757
+ return {
1758
+ palette: { dark, primary, accent: accent || dark, light },
1759
+ fonts: {
1760
+ heading: heading || AI_DEFAULT_BRAND.fonts.heading,
1761
+ body: body || AI_DEFAULT_BRAND.fonts.body
1762
+ }
1763
+ };
1764
+ }
1297
1765
  function deriveTemplateBrand() {
1298
- if (typeof document === "undefined") return null;
1299
- const cs = getComputedStyle(document.documentElement);
1300
- const read = (name) => cs.getPropertyValue(name).trim();
1301
- const dark = read("--color-dark");
1302
- const primary = read("--color-primary");
1303
- const light = read("--color-light");
1766
+ const dark = readRootVar("--color-dark");
1767
+ const primary = readRootVar("--color-primary");
1768
+ const light = readRootVar("--color-light");
1304
1769
  if (!dark || !primary || !light) return null;
1305
- const accent = read("--color-accent");
1306
- const heading = read("--font-heading") || read("--font-display");
1307
- const body = read("--font-body");
1770
+ const accent = readRootVar("--color-accent");
1771
+ const heading = readRootVar("--font-heading") || readRootVar("--font-display");
1772
+ const body = readRootVar("--font-body");
1308
1773
  return {
1309
1774
  palette: { dark, primary, accent: accent || dark, light },
1310
1775
  fonts: {
@@ -1376,6 +1841,24 @@ function syncRemovedSections(state) {
1376
1841
  }
1377
1842
  }
1378
1843
  }
1844
+ function syncTemplateHidden(state, pageHasSections) {
1845
+ const hide = state.hideTemplate === true && pageHasSections;
1846
+ for (const el of Array.from(document.querySelectorAll(`[${TEMPLATE_HIDDEN_ATTR}]`))) {
1847
+ if (!hide) {
1848
+ el.style.removeProperty("display");
1849
+ el.removeAttribute(TEMPLATE_HIDDEN_ATTR);
1850
+ }
1851
+ }
1852
+ if (!hide) return;
1853
+ for (const el of Array.from(document.querySelectorAll("[data-ohw-section]"))) {
1854
+ if (el.hasAttribute(CONTAINER_ATTR)) continue;
1855
+ if (CHROME_SECTION_IDS.has(el.getAttribute("data-ohw-section") ?? "")) continue;
1856
+ if (el.parentElement?.closest("[data-ohw-section]")) continue;
1857
+ if (el.hasAttribute(REPLACED_ATTR) || el.hasAttribute(REMOVED_ATTR)) continue;
1858
+ el.style.display = "none";
1859
+ el.setAttribute(TEMPLATE_HIDDEN_ATTR, "");
1860
+ }
1861
+ }
1379
1862
  function syncReplacedOriginals(state) {
1380
1863
  for (const el of document.querySelectorAll(`[${REPLACED_ATTR}]`)) {
1381
1864
  const byId = el.getAttribute(REPLACED_ATTR) ?? "";
@@ -1396,8 +1879,12 @@ function syncReplacedOriginals(state) {
1396
1879
  }
1397
1880
  function applyAiSectionsToDom(state, options) {
1398
1881
  if (typeof document === "undefined") return;
1882
+ const brandOverride = deriveBrandOverride();
1399
1883
  const templateBrand = deriveTemplateBrand();
1400
- const activeIds = new Set(state.sections.map((entry) => entry.id));
1884
+ const brandKey = brandOverride ? JSON.stringify(brandOverride) : "";
1885
+ const pagePath = window.location.pathname;
1886
+ const pageSections = state.sections.filter((entry) => (entry.path ?? "/") === pagePath);
1887
+ const activeIds = new Set(pageSections.map((entry) => entry.id));
1401
1888
  for (const [id, section] of mounted) {
1402
1889
  if (!activeIds.has(id)) {
1403
1890
  section.root.unmount();
@@ -1405,8 +1892,8 @@ function applyAiSectionsToDom(state, options) {
1405
1892
  mounted.delete(id);
1406
1893
  }
1407
1894
  }
1408
- for (const entry of state.sections) {
1409
- const serialized = JSON.stringify(entry);
1895
+ for (const entry of pageSections) {
1896
+ const serialized = JSON.stringify(entry) + brandKey;
1410
1897
  const existing = mounted.get(entry.id);
1411
1898
  if (existing && existing.serialized === serialized && existing.container.isConnected) {
1412
1899
  continue;
@@ -1420,6 +1907,7 @@ function applyAiSectionsToDom(state, options) {
1420
1907
  mounted.delete(entry.id);
1421
1908
  }
1422
1909
  container.setAttribute("data-ohw-section", entry.id);
1910
+ container.setAttribute("data-ohw-instance", entry.id);
1423
1911
  container.setAttribute("data-ohw-section-label", entry.label);
1424
1912
  placeContainer(container, entry);
1425
1913
  const root = mounted.get(entry.id)?.root ?? (0, import_client.createRoot)(container);
@@ -1430,7 +1918,7 @@ function applyAiSectionsToDom(state, options) {
1430
1918
  AiTreeRenderer,
1431
1919
  {
1432
1920
  tree: entry.tree,
1433
- brand: entry.brand ?? options?.brand ?? templateBrand ?? void 0,
1921
+ brand: brandOverride ?? entry.brand ?? options?.brand ?? templateBrand ?? void 0,
1434
1922
  resolveMedia,
1435
1923
  editKeyPrefix: `ai.${entry.id}`
1436
1924
  }
@@ -1441,6 +1929,7 @@ function applyAiSectionsToDom(state, options) {
1441
1929
  }
1442
1930
  syncReplacedOriginals(state);
1443
1931
  syncRemovedSections(state);
1932
+ syncTemplateHidden(state, pageSections.length > 0);
1444
1933
  }
1445
1934
 
1446
1935
  // src/useLinkHrefGuardian.ts
@@ -2047,7 +2536,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
2047
2536
  const autoId = (0, import_react5.useId)();
2048
2537
  const insertAfter = insertAfterProp ?? autoId;
2049
2538
  const [schedule, setSchedule] = (0, import_react5.useState)(null);
2050
- const [loading, setLoading] = (0, import_react5.useState)(true);
2539
+ const [loading, setLoading] = (0, import_react5.useState)(initialScheduleId !== null);
2051
2540
  const [inEditor, setInEditor] = (0, import_react5.useState)(false);
2052
2541
  const [isHovered, setIsHovered] = (0, import_react5.useState)(false);
2053
2542
  const [modalState, setModalState] = (0, import_react5.useState)(null);
@@ -2221,8 +2710,10 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
2221
2710
  "*"
2222
2711
  );
2223
2712
  };
2224
- if (!inEditor && !loading && !schedule) return null;
2225
2713
  const sectionId = `scheduling-${insertAfter}`;
2714
+ if (!inEditor && !loading && !schedule) {
2715
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("section", { "data-ohw-section": sectionId, "data-ohw-scheduling-anchor": insertAfter, style: { display: "none" } });
2716
+ }
2226
2717
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2227
2718
  "section",
2228
2719
  {
@@ -7130,13 +7621,17 @@ function MediaOverlay({
7130
7621
  hover,
7131
7622
  isUploading,
7132
7623
  fadingOut = false,
7624
+ selected = false,
7625
+ hovered = false,
7133
7626
  onFadeOutComplete,
7134
7627
  onReplace,
7628
+ onSelect,
7135
7629
  onVideoSettingsChange
7136
7630
  }) {
7137
7631
  const { rect } = hover;
7138
7632
  const skeletonRef = React7.useRef(null);
7139
7633
  const isVideo = hover.elementType === "video";
7634
+ const showChrome = !selected || hovered;
7140
7635
  const autoplay = hover.videoAutoplay ?? true;
7141
7636
  const muted = hover.videoMuted ?? true;
7142
7637
  const box = {
@@ -7171,7 +7666,7 @@ function MediaOverlay({
7171
7666
  }
7172
7667
  );
7173
7668
  }
7174
- const settingsBar = isVideo && !hover.isDragOver ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7669
+ const settingsBar = isVideo && !hover.isDragOver && showChrome ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7175
7670
  "div",
7176
7671
  {
7177
7672
  "data-ohw-bridge": "",
@@ -7241,11 +7736,13 @@ function MediaOverlay({
7241
7736
  // in-document, pointer-events does it natively. The button below opts back in, so
7242
7737
  // Replace still works.
7243
7738
  pointerEvents: hover.hasTextOverlap ? "none" : "auto",
7244
- boxShadow: "inset 0 0 0 1.5px var(--color-primary)",
7245
- background: "color-mix(in srgb, var(--color-primary) 20%, transparent)"
7739
+ // Selected: a firm component ring with no wash, so the image reads as chosen rather
7740
+ // than hovered. Hover keeps the existing tinted preview.
7741
+ boxShadow: selected ? "inset 0 0 0 2px var(--color-primary)" : "inset 0 0 0 1.5px var(--color-primary)",
7742
+ background: selected ? "transparent" : "color-mix(in srgb, var(--color-primary) 20%, transparent)"
7246
7743
  },
7247
- onClick: () => onReplace(hover.key),
7248
- children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7744
+ onClick: () => onSelect && !selected ? onSelect(hover.key) : onReplace(hover.key),
7745
+ children: showChrome && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7249
7746
  Button,
7250
7747
  {
7251
7748
  "data-ohw-media-overlay": "",
@@ -7264,7 +7761,7 @@ function MediaOverlay({
7264
7761
  },
7265
7762
  children: [
7266
7763
  isVideo ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.Film, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.ImageIcon, { size: 14 }),
7267
- isVideo ? "Replace video" : "Replace image"
7764
+ isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image"
7268
7765
  ]
7269
7766
  }
7270
7767
  )
@@ -7334,6 +7831,9 @@ var import_lucide_react7 = require("lucide-react");
7334
7831
 
7335
7832
  // src/lib/sections.ts
7336
7833
  var LINK_PICKER_EXCLUDED_IDS = /* @__PURE__ */ new Set(["navbar", "footer"]);
7834
+ function isChromeSection(el) {
7835
+ return el.matches("header, nav, footer, aside");
7836
+ }
7337
7837
  function titleCaseSectionId(id) {
7338
7838
  return id.split("-").filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
7339
7839
  }
@@ -7344,6 +7844,8 @@ function parseSectionsFromRoot(root) {
7344
7844
  const id = el.getAttribute("data-ohw-section") ?? "";
7345
7845
  if (!id || seen.has(id) || LINK_PICKER_EXCLUDED_IDS.has(id)) continue;
7346
7846
  if (el.parentElement?.closest("[data-ohw-section]")) continue;
7847
+ if (el.hasAttribute("data-ohw-ai-template-hidden") || el.hasAttribute("data-ohw-ai-removed") || el.hasAttribute("data-ohw-ai-replaced-by"))
7848
+ continue;
7347
7849
  seen.add(id);
7348
7850
  const label = el.getAttribute("data-ohw-section-label") ?? titleCaseSectionId(id);
7349
7851
  sections.push({ id, label });
@@ -7361,8 +7863,12 @@ function parseSectionsFromHtml(html) {
7361
7863
 
7362
7864
  // src/ui/ai-section/AiSectionOverlay.tsx
7363
7865
  var import_jsx_runtime17 = require("react/jsx-runtime");
7364
- function readRect(sectionId) {
7365
- const el = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
7866
+ function findSectionElement(instanceId) {
7867
+ const escaped = CSS.escape(instanceId);
7868
+ return document.querySelector(`[data-ohw-instance="${escaped}"]`) ?? document.querySelector(`[data-ohw-section="${escaped}"]:not([data-ohw-instance])`);
7869
+ }
7870
+ function readRect(instanceId) {
7871
+ const el = findSectionElement(instanceId);
7366
7872
  if (!el) return null;
7367
7873
  const r2 = el.getBoundingClientRect();
7368
7874
  if (r2.width <= 0 || r2.height <= 0) return null;
@@ -7385,7 +7891,7 @@ function useLiveSectionRect(sectionId) {
7385
7891
  const opts = { capture: true, passive: true };
7386
7892
  window.addEventListener("scroll", update, opts);
7387
7893
  window.addEventListener("resize", update);
7388
- const el = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
7894
+ const el = findSectionElement(sectionId);
7389
7895
  const ro = el ? new ResizeObserver(update) : null;
7390
7896
  if (el && ro) ro.observe(el);
7391
7897
  const interval = setInterval(update, 500);
@@ -7398,6 +7904,14 @@ function useLiveSectionRect(sectionId) {
7398
7904
  }, [sectionId]);
7399
7905
  return rect;
7400
7906
  }
7907
+ function computeSectionBoundaryFlags(instanceId) {
7908
+ const topLevel = Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
7909
+ (el) => !el.parentElement?.closest("[data-ohw-section]") && !isChromeSection(el)
7910
+ );
7911
+ const index = topLevel.findIndex((el) => (el.dataset.ohwInstance ?? el.dataset.ohwSection) === instanceId);
7912
+ if (index === -1) return { isFirst: true, isLast: true };
7913
+ return { isFirst: index === 0, isLast: index === topLevel.length - 1 };
7914
+ }
7401
7915
  var PRIMARY2 = "#0885FE";
7402
7916
  function edgeAwareRadius(rect) {
7403
7917
  const container = window.innerWidth <= 480 ? 16 : 24;
@@ -7471,6 +7985,7 @@ function AiSectionOverlay({
7471
7985
  }) {
7472
7986
  const [selectedId, setSelectedId] = (0, import_react8.useState)(null);
7473
7987
  const [reviewId, setReviewId] = (0, import_react8.useState)(null);
7988
+ const [reviewButtonsHidden, setReviewButtonsHidden] = (0, import_react8.useState)(false);
7474
7989
  const reviewIdRef = (0, import_react8.useRef)(null);
7475
7990
  reviewIdRef.current = reviewId;
7476
7991
  const selectedIdRef = (0, import_react8.useRef)(null);
@@ -7479,7 +7994,7 @@ function AiSectionOverlay({
7479
7994
  (el) => {
7480
7995
  postToParent2({
7481
7996
  type: "ow:section-selected",
7482
- sectionId: el?.dataset.ohwSection ?? null,
7997
+ sectionId: el ? el.dataset.ohwInstance ?? el.dataset.ohwSection ?? null : null,
7483
7998
  sectionLabel: el ? el.dataset.ohwSectionLabel ?? titleCaseSectionId(el.dataset.ohwSection ?? "") : null
7484
7999
  });
7485
8000
  },
@@ -7488,7 +8003,7 @@ function AiSectionOverlay({
7488
8003
  const selectFromElement = (0, import_react8.useCallback)(
7489
8004
  (el, options) => {
7490
8005
  const sectionEl = el?.closest("[data-ohw-section]") ?? null;
7491
- const id = sectionEl?.dataset.ohwSection ?? null;
8006
+ const id = sectionEl ? sectionEl.dataset.ohwInstance ?? sectionEl.dataset.ohwSection ?? null : null;
7492
8007
  if (id === selectedIdRef.current) return;
7493
8008
  setSelectedId(id);
7494
8009
  if (options?.report !== false) report(sectionEl);
@@ -7529,9 +8044,10 @@ function AiSectionOverlay({
7529
8044
  }
7530
8045
  const found = readRect(sectionId) != null;
7531
8046
  setReviewId(found ? sectionId : null);
8047
+ setReviewButtonsHidden(e.data.hideButtons === true);
7532
8048
  postToParent2({ type: "ow:ai-review-started", sectionId, found });
7533
8049
  if (found) {
7534
- document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`)?.scrollIntoView({ behavior: "smooth", block: "nearest" });
8050
+ document.querySelector(`[data-ohw-instance="${CSS.escape(sectionId)}"]`)?.scrollIntoView({ behavior: "smooth", block: "nearest" });
7535
8051
  }
7536
8052
  }
7537
8053
  };
@@ -7550,7 +8066,7 @@ function AiSectionOverlay({
7550
8066
  return;
7551
8067
  }
7552
8068
  const sec = t.closest("[data-ohw-section]");
7553
- setHoveredId(sec?.dataset.ohwSection ?? null);
8069
+ setHoveredId(sec ? sec.dataset.ohwInstance ?? sec.dataset.ohwSection ?? null : null);
7554
8070
  };
7555
8071
  const onLeave = () => setHoveredId(null);
7556
8072
  document.addEventListener("mousemove", onMove, { passive: true });
@@ -7582,9 +8098,30 @@ function AiSectionOverlay({
7582
8098
  },
7583
8099
  [postToParent2]
7584
8100
  );
7585
- const selectionRect = useLiveSectionRect(reviewId ? null : selectedId);
8101
+ const activeSelectionId = reviewId ? null : selectedId;
8102
+ const selectionRect = useLiveSectionRect(activeSelectionId);
7586
8103
  const reviewRect = useLiveSectionRect(reviewId);
7587
8104
  const hoverRect = useLiveSectionRect(reviewId || hoveredId === selectedId ? null : hoveredId);
8105
+ (0, import_react8.useEffect)(() => {
8106
+ const selectedEl = activeSelectionId ? findSectionElement(activeSelectionId) : null;
8107
+ if (!activeSelectionId || !selectionRect || selectedEl && isChromeSection(selectedEl)) {
8108
+ postToParent2({ type: "ow:section-rect", instanceId: null, rect: null });
8109
+ return;
8110
+ }
8111
+ const { isFirst, isLast } = computeSectionBoundaryFlags(activeSelectionId);
8112
+ postToParent2({
8113
+ type: "ow:section-rect",
8114
+ instanceId: activeSelectionId,
8115
+ rect: {
8116
+ top: selectionRect.top + window.scrollY,
8117
+ left: selectionRect.left + window.scrollX,
8118
+ width: selectionRect.width,
8119
+ height: selectionRect.height
8120
+ },
8121
+ isFirst,
8122
+ isLast
8123
+ });
8124
+ }, [activeSelectionId, selectionRect, postToParent2]);
7588
8125
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
7589
8126
  hoverRect && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
7590
8127
  "div",
@@ -7635,13 +8172,16 @@ function AiSectionOverlay({
7635
8172
  border: `2px solid ${PRIMARY2}`,
7636
8173
  borderRadius: edgeAwareRadius(reviewRect),
7637
8174
  zIndex: 2147483200,
7638
- // The veil itself: swallows clicks so the section stays locked until decided.
8175
+ // The veil itself: swallows clicks so the section stays locked until decided. This
8176
+ // stopPropagation only guards the bubble phase; the bridge's capture-phase click
8177
+ // handlers additionally bail on `[data-ohw-ai-review]`, without which a click on
8178
+ // Accept/Discard resolves to the media beneath and opens the file picker.
7639
8179
  background: "rgba(8, 133, 254, 0.04)",
7640
8180
  pointerEvents: "auto",
7641
8181
  cursor: "default"
7642
8182
  },
7643
8183
  onClick: (e) => e.stopPropagation(),
7644
- children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
8184
+ children: !reviewButtonsHidden && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
7645
8185
  "div",
7646
8186
  {
7647
8187
  style: {
@@ -7666,6 +8206,58 @@ function AiSectionOverlay({
7666
8206
 
7667
8207
  // src/lib/section-instances.ts
7668
8208
  var SECTION_ORDER_KEY = "__ohw_section_order";
8209
+ function topLevelSections() {
8210
+ return Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
8211
+ (el) => !el.parentElement?.closest("[data-ohw-section]") && !isChromeSection(el)
8212
+ );
8213
+ }
8214
+ function instanceIdOf(el) {
8215
+ return el.dataset.ohwInstance ?? el.dataset.ohwSection ?? "";
8216
+ }
8217
+ function planSectionMove(instanceId, targetIndex, currentPath) {
8218
+ const sections = topLevelSections();
8219
+ const index = sections.findIndex((el) => instanceIdOf(el) === instanceId);
8220
+ if (index === -1) return null;
8221
+ const dragged = sections[index];
8222
+ const others = sections.filter((_, i) => i !== index);
8223
+ const clamped = Math.max(0, Math.min(targetIndex, others.length));
8224
+ const reordered = [...others.slice(0, clamped), dragged, ...others.slice(clamped)];
8225
+ return reordered.map((el, order) => ({
8226
+ instanceId: instanceIdOf(el),
8227
+ type: el.getAttribute("data-ohw-section") ?? "",
8228
+ order,
8229
+ pagePath: currentPath
8230
+ }));
8231
+ }
8232
+ function moveSectionInstance(instanceId, direction, currentPath) {
8233
+ const sections = topLevelSections();
8234
+ const index = sections.findIndex((el) => instanceIdOf(el) === instanceId);
8235
+ if (index === -1) return null;
8236
+ const siblingIndex = direction === "up" ? index - 1 : index + 1;
8237
+ if (siblingIndex < 0 || siblingIndex >= sections.length) return null;
8238
+ const entries = planSectionMove(instanceId, siblingIndex, currentPath);
8239
+ if (!entries) return null;
8240
+ applyPersistedOrder(entries);
8241
+ return entries;
8242
+ }
8243
+ function applyPersistedOrder(entries) {
8244
+ if (entries.length === 0) return;
8245
+ const sections = topLevelSections();
8246
+ if (sections.length === 0) return;
8247
+ const orderIndex = new Map(entries.map((e) => [e.instanceId, e.order]));
8248
+ const ordered = [...sections].sort((a, b) => {
8249
+ const aOrder = orderIndex.get(instanceIdOf(a));
8250
+ const bOrder = orderIndex.get(instanceIdOf(b));
8251
+ if (aOrder === void 0 && bOrder === void 0) return 0;
8252
+ if (aOrder === void 0) return 1;
8253
+ if (bOrder === void 0) return -1;
8254
+ return aOrder - bOrder;
8255
+ });
8256
+ const parent = sections[0].parentElement;
8257
+ if (!parent) return;
8258
+ const anchor = sections[sections.length - 1].nextSibling;
8259
+ ordered.forEach((el) => parent.insertBefore(el, anchor));
8260
+ }
7669
8261
  function getPageSectionOrderEntries(raw, currentPath) {
7670
8262
  if (!raw) return [];
7671
8263
  try {
@@ -7703,6 +8295,7 @@ function initSectionInstancesFromContent(content, currentPath) {
7703
8295
  rekeySectionSubtree(clone, entry.instanceId);
7704
8296
  original.insertAdjacentElement("afterend", clone);
7705
8297
  }
8298
+ applyPersistedOrder(entries);
7706
8299
  }
7707
8300
 
7708
8301
  // src/OhhwellsBridge.tsx
@@ -10247,8 +10840,13 @@ var GLYPH_SELECTOR = "svg, img";
10247
10840
  function referenceBox(slot) {
10248
10841
  const row = slot.closest("[data-ohw-socials-row]") ?? slot.closest("a")?.parentElement ?? null;
10249
10842
  const neighbour = row ? Array.from(row.querySelectorAll('[data-ohw-editable="icon"]')).find((el) => el !== slot) : null;
10250
- const source = (neighbour ?? slot).querySelector(GLYPH_SELECTOR);
10251
- const box = source?.getBoundingClientRect() ?? null;
10843
+ if (neighbour) {
10844
+ const box2 = neighbour.querySelector(GLYPH_SELECTOR)?.getBoundingClientRect();
10845
+ if (box2?.width && box2.height) return box2;
10846
+ }
10847
+ const own = slot.getBoundingClientRect();
10848
+ if (own.width && own.height) return own;
10849
+ const box = slot.querySelector(GLYPH_SELECTOR)?.getBoundingClientRect() ?? null;
10252
10850
  return box?.width && box.height ? box : null;
10253
10851
  }
10254
10852
  function iconMarkupSizedFor(slot, markup) {
@@ -10327,7 +10925,8 @@ function findSocialsRow(el) {
10327
10925
  }
10328
10926
  function isSocialsRow(el) {
10329
10927
  const anchors = Array.from(el.querySelectorAll("a"));
10330
- return anchors.length > 0 && anchors.every((anchor) => isSocialItem(anchor));
10928
+ if (!anchors.length || !anchors.some((anchor) => isSocialItem(anchor))) return false;
10929
+ return findSocialsRow(anchors[0]) === el;
10331
10930
  }
10332
10931
  var MAX_SOCIAL_ITEMS_PER_ROW = 7;
10333
10932
  function canAddSocialItem(row) {
@@ -10365,6 +10964,7 @@ function markSocialsRows(root = document) {
10365
10964
  });
10366
10965
  listSocialsRows(root).forEach((row) => {
10367
10966
  row.setAttribute(SOCIALS_ROW_ATTR, "");
10967
+ allowRowToWrap(row);
10368
10968
  const items = listSocialItems(row);
10369
10969
  const firstUnit = items[0] ? socialRowUnit(items[0], row) : null;
10370
10970
  if (firstUnit) rowTemplates.set(rowKeyOf(row), firstUnit.outerHTML);
@@ -10659,7 +11259,29 @@ function applySocialsDisplayToRow(row, display) {
10659
11259
  const icon = item.querySelector(ICON_SELECTOR);
10660
11260
  if (label) label.style.display = display.text ? "" : "none";
10661
11261
  if (icon) icon.style.display = display.icon ? "" : "none";
11262
+ layOutIconAndLabel(item, Boolean(display.text && display.icon));
10662
11263
  });
11264
+ allowRowToWrap(row);
11265
+ }
11266
+ function layOutIconAndLabel(item, on) {
11267
+ const hasBoth = Boolean(item.querySelector(ICON_SELECTOR)) && Boolean(socialLabelElement(item));
11268
+ if (!hasBoth) {
11269
+ item.style.display = "";
11270
+ item.style.alignItems = "";
11271
+ item.style.gap = "";
11272
+ item.style.whiteSpace = "";
11273
+ item.style.flex = "";
11274
+ return;
11275
+ }
11276
+ item.style.display = on ? "inline-flex" : "";
11277
+ item.style.alignItems = on ? "center" : "";
11278
+ item.style.gap = on ? "8px" : "";
11279
+ item.style.whiteSpace = on ? "nowrap" : "";
11280
+ item.style.flex = on ? "0 0 auto" : "";
11281
+ }
11282
+ function allowRowToWrap(row) {
11283
+ const display = row.ownerDocument.defaultView?.getComputedStyle(row).display ?? "";
11284
+ if (display === "flex" || display === "inline-flex") row.style.flexWrap = "wrap";
10663
11285
  }
10664
11286
  function applySocialsDisplayFromContent(content, root = document) {
10665
11287
  const stored = parseSocialsDisplay(content[SOCIALS_DISPLAY_KEY]);
@@ -11869,6 +12491,7 @@ function readLogoSizeState(content, placement) {
11869
12491
  function getLogoElement(el) {
11870
12492
  const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
11871
12493
  if (marked) return marked;
12494
+ if (el.closest('[data-ohw-editable="icon"]')) return null;
11872
12495
  const root = el.closest("nav, [data-ohw-nav-root], footer");
11873
12496
  if (!root) return null;
11874
12497
  const anchor = el.closest("a");
@@ -12742,98 +13365,425 @@ function useNavItemDrag({
12742
13365
  };
12743
13366
  }
12744
13367
 
12745
- // src/ui/footer-container-chrome.tsx
12746
- var import_lucide_react15 = require("lucide-react");
12747
- var import_jsx_runtime29 = require("react/jsx-runtime");
12748
- function FooterContainerChrome({
12749
- rect,
12750
- onAdd,
12751
- addDisabled = false
12752
- }) {
12753
- const chromeGap = 6;
12754
- const buttonMargin = 7;
12755
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
12756
- "div",
12757
- {
12758
- "data-ohw-footer-container-chrome": "",
12759
- "data-ohw-bridge": "",
12760
- className: "pointer-events-none fixed z-[2147483647]",
12761
- style: {
12762
- top: rect.top - chromeGap,
12763
- left: rect.left - chromeGap,
12764
- width: rect.width + chromeGap * 2,
12765
- height: rect.height + chromeGap * 2
12766
- },
12767
- children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(Tooltip, { children: [
12768
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
12769
- "button",
12770
- {
12771
- type: "button",
12772
- "data-ohw-footer-add-button": "",
12773
- disabled: addDisabled,
12774
- 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",
12775
- style: { top: chromeGap - buttonMargin },
12776
- "aria-label": "Add item",
12777
- onMouseDown: (e) => {
12778
- e.preventDefault();
12779
- e.stopPropagation();
12780
- },
12781
- onClick: (e) => {
12782
- e.preventDefault();
12783
- e.stopPropagation();
12784
- if (addDisabled) return;
12785
- onAdd();
12786
- },
12787
- children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react15.Plus, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
12788
- }
12789
- ) }),
12790
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(TooltipContent, { side: "bottom", sideOffset: 9, children: addDisabled ? "Maximum columns reached" : "Add item" })
12791
- ] })
12792
- }
12793
- ) });
12794
- }
12795
-
12796
- // src/lib/carousel.ts
13368
+ // src/useSectionDrag.ts
12797
13369
  var import_react15 = require("react");
12798
- var CAROUSEL_ATTR = "data-ohw-carousel";
12799
- var CAROUSEL_VALUE_ATTR = "data-ohw-carousel-value";
12800
- var CAROUSEL_SLIDE_ATTR = "data-ohw-carousel-slide";
12801
- var CAROUSEL_EVENT = "ohw:carousel-change";
12802
- function listCarouselKeys() {
12803
- const keys = /* @__PURE__ */ new Set();
12804
- document.querySelectorAll(`[${CAROUSEL_ATTR}]`).forEach((el) => {
12805
- const key = el.getAttribute("data-ohw-key");
12806
- if (key) keys.add(key);
12807
- });
12808
- return [...keys];
13370
+
13371
+ // src/lib/section-dnd.ts
13372
+ function isFooterSection(el) {
13373
+ return el.dataset.ohwSection === "footer";
12809
13374
  }
12810
- function containersForKey(key) {
12811
- return Array.from(
12812
- document.querySelectorAll(`[${CAROUSEL_ATTR}][data-ohw-key="${CSS.escape(key)}"]`)
13375
+ function buildSectionDropSlots(draggedInstanceId) {
13376
+ const sections = topLevelSections().filter(
13377
+ (el) => instanceIdOf(el) !== draggedInstanceId && !isFooterSection(el)
12813
13378
  );
13379
+ const slots = [];
13380
+ if (sections.length === 0) return slots;
13381
+ const left = 0;
13382
+ const width = document.documentElement.clientWidth;
13383
+ for (let i = 0; i <= sections.length; i++) {
13384
+ let y;
13385
+ if (i === 0) {
13386
+ y = sections[0].getBoundingClientRect().top;
13387
+ } else if (i === sections.length) {
13388
+ y = sections[sections.length - 1].getBoundingClientRect().bottom;
13389
+ } else {
13390
+ const prev = sections[i - 1].getBoundingClientRect();
13391
+ const next = sections[i].getBoundingClientRect();
13392
+ y = (prev.bottom + next.top) / 2;
13393
+ }
13394
+ slots.push({ insertIndex: i, y, left, width });
13395
+ }
13396
+ return slots;
12814
13397
  }
12815
- function isCarouselKey(key) {
12816
- return containersForKey(key).length > 0;
12817
- }
12818
- function parseSlides(raw) {
12819
- if (!raw) return [];
12820
- try {
12821
- const parsed = JSON.parse(raw);
12822
- if (!Array.isArray(parsed)) return [];
12823
- return parsed.filter((s) => Boolean(s) && typeof s === "object").map((s) => ({ src: String(s.src ?? ""), alt: String(s.alt ?? "") }));
12824
- } catch {
12825
- return [];
13398
+ function hitTestSectionDropSlot(y, slots) {
13399
+ let best = null;
13400
+ for (const slot of slots) {
13401
+ const dist = Math.abs(y - slot.y);
13402
+ if (!best || dist < best.dist) best = { slot, dist };
12826
13403
  }
13404
+ return best?.slot ?? null;
12827
13405
  }
12828
- function readCarouselValue(key) {
12829
- const container = containersForKey(key)[0];
12830
- if (!container) return [];
12831
- const fromAttr = parseSlides(container.getAttribute(CAROUSEL_VALUE_ATTR));
12832
- if (fromAttr.length > 0) return fromAttr;
12833
- return Array.from(container.querySelectorAll(`[${CAROUSEL_SLIDE_ATTR}]`)).filter((slide) => slide.closest(`[${CAROUSEL_ATTR}]`) === container).sort((a, b) => slideIndex(a) - slideIndex(b)).map((slide) => {
12834
- const img = slide instanceof HTMLImageElement ? slide : slide.querySelector("img");
12835
- return { src: img?.src ?? "", alt: img?.alt ?? "" };
12836
- });
13406
+
13407
+ // src/useSectionDrag.ts
13408
+ var PRESS_THRESHOLD = 10;
13409
+ var EDGE_ZONE = 60;
13410
+ var MAX_AUTO_SCROLL_SPEED = 18;
13411
+ var SECTION_DRAG_EXCLUDED_SELECTOR = [
13412
+ "[data-ohw-toolbar]",
13413
+ "[data-ohw-edit-chrome]",
13414
+ "[data-ohw-item-interaction]",
13415
+ "[data-ohw-drag-handle-container]",
13416
+ '[data-slot="drag-handle"]',
13417
+ "[data-ohw-item-toolbar-anchor]",
13418
+ "[data-ohw-item-drag-surface]",
13419
+ "[data-ohw-more-menu]",
13420
+ '[data-slot="dropdown-menu-content"]',
13421
+ '[data-slot="dropdown-menu-item"]',
13422
+ "[data-ohw-state-toggle]",
13423
+ "[data-ohw-max-badge]",
13424
+ "[data-ohw-floating-panel]",
13425
+ "[data-ohw-section-picker]",
13426
+ "[data-ohw-link-popover-root]",
13427
+ "[data-ohw-link-modal-root]",
13428
+ "[data-ohw-link-page-dropdown]",
13429
+ '[data-slot="popover-content"]',
13430
+ '[data-slot="dialog-content"]',
13431
+ '[data-slot="dialog-overlay"]',
13432
+ "[data-ohw-ai-review]",
13433
+ "[data-ohw-editable]",
13434
+ "[data-ohw-editable-state]",
13435
+ "[contenteditable]",
13436
+ "[data-ohw-href-key]",
13437
+ "[data-ohw-footer-col]",
13438
+ "[data-ohw-social-label]",
13439
+ "a",
13440
+ "button",
13441
+ '[role="button"]',
13442
+ '[data-ohw-role="navbar-button"]',
13443
+ '[data-ohw-role="button"]',
13444
+ "[data-ohw-carousel]",
13445
+ "[data-ohw-carousel-value]",
13446
+ "[data-ohw-carousel-slide]",
13447
+ "[data-ohw-carousel-overlay]",
13448
+ "[data-ohw-media-chrome]",
13449
+ "[data-ohw-media-overlay]",
13450
+ "[data-ohw-media-skeleton]"
13451
+ ].join(", ");
13452
+ function visibleClip(ps) {
13453
+ if (!ps) return null;
13454
+ const top = Math.max(0, ps.headerH - ps.iframeOffsetTop);
13455
+ const bottom = Math.min(window.innerHeight, ps.headerH + ps.canvasH - ps.iframeOffsetTop);
13456
+ return { top, bottom: Math.max(top, bottom) };
13457
+ }
13458
+ function useSectionDrag({
13459
+ isEditMode,
13460
+ editContentRef,
13461
+ postToParentRef,
13462
+ parentScrollRef,
13463
+ navDragRef,
13464
+ footerDragRef,
13465
+ suppressNextClickRef,
13466
+ suppressClickUntilRef
13467
+ }) {
13468
+ const sectionDragRef = (0, import_react15.useRef)(null);
13469
+ const [sectionDropSlots, setSectionDropSlots] = (0, import_react15.useState)([]);
13470
+ const [activeSectionDropIndex, setActiveSectionDropIndex] = (0, import_react15.useState)(null);
13471
+ const [isSectionDragging, setIsSectionDragging] = (0, import_react15.useState)(false);
13472
+ const sectionPointerDragRef = (0, import_react15.useRef)(null);
13473
+ const autoScrollRafRef = (0, import_react15.useRef)(null);
13474
+ const autoScrollDeltaRef = (0, import_react15.useRef)(0);
13475
+ const stopAutoScroll = (0, import_react15.useCallback)(() => {
13476
+ if (autoScrollRafRef.current != null) {
13477
+ cancelAnimationFrame(autoScrollRafRef.current);
13478
+ autoScrollRafRef.current = null;
13479
+ }
13480
+ autoScrollDeltaRef.current = 0;
13481
+ }, []);
13482
+ const tickAutoScroll = (0, import_react15.useCallback)(() => {
13483
+ if (!sectionDragRef.current) {
13484
+ stopAutoScroll();
13485
+ return;
13486
+ }
13487
+ if (autoScrollDeltaRef.current !== 0) {
13488
+ postToParentRef.current({ type: "ow:request-scroll", deltaY: autoScrollDeltaRef.current });
13489
+ }
13490
+ autoScrollRafRef.current = requestAnimationFrame(tickAutoScroll);
13491
+ }, [postToParentRef, stopAutoScroll]);
13492
+ const updateAutoScroll = (0, import_react15.useCallback)(
13493
+ (clientY) => {
13494
+ const clip = visibleClip(parentScrollRef.current);
13495
+ let delta = 0;
13496
+ if (clip) {
13497
+ const distTop = clientY - clip.top;
13498
+ const distBottom = clip.bottom - clientY;
13499
+ if (distTop >= 0 && distTop < EDGE_ZONE) {
13500
+ delta = -MAX_AUTO_SCROLL_SPEED * (1 - distTop / EDGE_ZONE);
13501
+ } else if (distBottom >= 0 && distBottom < EDGE_ZONE) {
13502
+ delta = MAX_AUTO_SCROLL_SPEED * (1 - distBottom / EDGE_ZONE);
13503
+ }
13504
+ }
13505
+ autoScrollDeltaRef.current = delta;
13506
+ if (delta !== 0 && autoScrollRafRef.current == null) {
13507
+ autoScrollRafRef.current = requestAnimationFrame(tickAutoScroll);
13508
+ } else if (delta === 0) {
13509
+ stopAutoScroll();
13510
+ }
13511
+ },
13512
+ [parentScrollRef, stopAutoScroll, tickAutoScroll]
13513
+ );
13514
+ const clearSectionDragVisuals = (0, import_react15.useCallback)(() => {
13515
+ sectionDragRef.current?.draggedEl.removeAttribute("data-ohw-section-dragging");
13516
+ sectionDragRef.current = null;
13517
+ setSectionDropSlots([]);
13518
+ setActiveSectionDropIndex(null);
13519
+ setIsSectionDragging(false);
13520
+ stopAutoScroll();
13521
+ document.documentElement.removeAttribute("data-ohw-section-dragging-root");
13522
+ unlockItemDragInteraction();
13523
+ }, [stopAutoScroll]);
13524
+ const refreshSectionDragVisuals = (0, import_react15.useCallback)(
13525
+ (session, clientX, clientY) => {
13526
+ session.lastClientX = clientX;
13527
+ session.lastClientY = clientY;
13528
+ const slots = buildSectionDropSlots(session.instanceId);
13529
+ const activeSlot = hitTestSectionDropSlot(clientY, slots);
13530
+ session.activeSlot = activeSlot;
13531
+ setSectionDropSlots(slots);
13532
+ const activeIdx = activeSlot ? slots.findIndex((s) => s.insertIndex === activeSlot.insertIndex) : -1;
13533
+ setActiveSectionDropIndex(activeIdx >= 0 ? activeIdx : null);
13534
+ updateAutoScroll(clientY);
13535
+ },
13536
+ [updateAutoScroll]
13537
+ );
13538
+ const beginSectionDrag = (0, import_react15.useCallback)(
13539
+ (session) => {
13540
+ sectionDragRef.current = session;
13541
+ setIsSectionDragging(true);
13542
+ lockItemDuringDrag();
13543
+ document.documentElement.setAttribute("data-ohw-section-dragging-root", "");
13544
+ session.draggedEl.setAttribute("data-ohw-section-dragging", "");
13545
+ refreshSectionDragVisuals(session, session.lastClientX, session.lastClientY);
13546
+ },
13547
+ [refreshSectionDragVisuals]
13548
+ );
13549
+ const commitSectionDrag = (0, import_react15.useCallback)(() => {
13550
+ const session = sectionDragRef.current;
13551
+ if (!session) {
13552
+ clearSectionDragVisuals();
13553
+ return;
13554
+ }
13555
+ const slot = session.activeSlot ?? hitTestSectionDropSlot(session.lastClientY, buildSectionDropSlots(session.instanceId));
13556
+ const entries = slot ? planSectionMove(session.instanceId, slot.insertIndex, window.location.pathname) : null;
13557
+ if (!entries) {
13558
+ clearSectionDragVisuals();
13559
+ return;
13560
+ }
13561
+ const orderJson = JSON.stringify(entries);
13562
+ editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
13563
+ postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
13564
+ applyPersistedOrder(entries);
13565
+ clearSectionDragVisuals();
13566
+ requestAnimationFrame(() => {
13567
+ if (editContentRef.current[SECTION_ORDER_KEY] === orderJson) {
13568
+ applyPersistedOrder(entries);
13569
+ }
13570
+ requestAnimationFrame(() => {
13571
+ window.dispatchEvent(new Event("resize"));
13572
+ });
13573
+ });
13574
+ }, [clearSectionDragVisuals, editContentRef, postToParentRef]);
13575
+ const startSectionPressDrag = (0, import_react15.useCallback)(
13576
+ (el, clientX, clientY, pointerId) => {
13577
+ if (navDragRef.current || footerDragRef.current || sectionDragRef.current) return false;
13578
+ const instanceId = instanceIdOf(el);
13579
+ if (!instanceId) return false;
13580
+ sectionPointerDragRef.current = {
13581
+ el,
13582
+ instanceId,
13583
+ startX: clientX,
13584
+ startY: clientY,
13585
+ pointerId,
13586
+ started: false
13587
+ };
13588
+ return true;
13589
+ },
13590
+ [footerDragRef, navDragRef]
13591
+ );
13592
+ (0, import_react15.useEffect)(() => {
13593
+ if (!isEditMode) return;
13594
+ const onPointerDown = (e) => {
13595
+ if (e.button !== 0) return;
13596
+ if (navDragRef.current || footerDragRef.current || sectionDragRef.current) return;
13597
+ if (sectionPointerDragRef.current) return;
13598
+ const target = e.target;
13599
+ if (!(target instanceof HTMLElement)) return;
13600
+ if (target.closest(SECTION_DRAG_EXCLUDED_SELECTOR)) return;
13601
+ const sectionEl = target.closest("[data-ohw-section]");
13602
+ if (!sectionEl || isChromeSection(sectionEl) || sectionEl.dataset.ohwSection === "footer") return;
13603
+ if (!topLevelSections().includes(sectionEl)) return;
13604
+ startSectionPressDrag(sectionEl, e.clientX, e.clientY, e.pointerId);
13605
+ };
13606
+ const onPointerMove = (e) => {
13607
+ const pending = sectionPointerDragRef.current;
13608
+ if (!pending) return;
13609
+ if (pending.started) {
13610
+ e.preventDefault();
13611
+ clearTextSelection();
13612
+ const session = sectionDragRef.current;
13613
+ if (!session) return;
13614
+ refreshSectionDragVisuals(session, e.clientX, e.clientY);
13615
+ return;
13616
+ }
13617
+ const dx = e.clientX - pending.startX;
13618
+ const dy = e.clientY - pending.startY;
13619
+ if (dx * dx + dy * dy < PRESS_THRESHOLD * PRESS_THRESHOLD) return;
13620
+ e.preventDefault();
13621
+ pending.started = true;
13622
+ armItemPressDrag();
13623
+ clearTextSelection();
13624
+ try {
13625
+ document.body.setPointerCapture(pending.pointerId);
13626
+ } catch {
13627
+ }
13628
+ beginSectionDrag({
13629
+ instanceId: pending.instanceId,
13630
+ draggedEl: pending.el,
13631
+ lastClientX: e.clientX,
13632
+ lastClientY: e.clientY,
13633
+ activeSlot: null
13634
+ });
13635
+ };
13636
+ const endPointerDrag = (e) => {
13637
+ const pending = sectionPointerDragRef.current;
13638
+ sectionPointerDragRef.current = null;
13639
+ try {
13640
+ if (document.body.hasPointerCapture(e.pointerId)) {
13641
+ document.body.releasePointerCapture(e.pointerId);
13642
+ }
13643
+ } catch {
13644
+ }
13645
+ if (!pending) return;
13646
+ if (!pending.started) {
13647
+ unlockItemDragInteraction();
13648
+ return;
13649
+ }
13650
+ suppressNextClickRef.current = true;
13651
+ suppressClickUntilRef.current = Date.now() + 500;
13652
+ commitSectionDrag();
13653
+ };
13654
+ const onKeyDown = (e) => {
13655
+ if (e.key !== "Escape") return;
13656
+ if (!sectionDragRef.current && !sectionPointerDragRef.current) return;
13657
+ sectionPointerDragRef.current = null;
13658
+ clearSectionDragVisuals();
13659
+ };
13660
+ document.addEventListener("pointerdown", onPointerDown, true);
13661
+ document.addEventListener("pointermove", onPointerMove, true);
13662
+ document.addEventListener("pointerup", endPointerDrag, true);
13663
+ document.addEventListener("pointercancel", endPointerDrag, true);
13664
+ document.addEventListener("keydown", onKeyDown, true);
13665
+ return () => {
13666
+ document.removeEventListener("pointerdown", onPointerDown, true);
13667
+ document.removeEventListener("pointermove", onPointerMove, true);
13668
+ document.removeEventListener("pointerup", endPointerDrag, true);
13669
+ document.removeEventListener("pointercancel", endPointerDrag, true);
13670
+ document.removeEventListener("keydown", onKeyDown, true);
13671
+ unlockItemDragInteraction();
13672
+ stopAutoScroll();
13673
+ };
13674
+ }, [
13675
+ beginSectionDrag,
13676
+ clearSectionDragVisuals,
13677
+ commitSectionDrag,
13678
+ footerDragRef,
13679
+ isEditMode,
13680
+ navDragRef,
13681
+ refreshSectionDragVisuals,
13682
+ startSectionPressDrag,
13683
+ stopAutoScroll,
13684
+ suppressClickUntilRef,
13685
+ suppressNextClickRef
13686
+ ]);
13687
+ return {
13688
+ sectionDragRef,
13689
+ sectionDropSlots,
13690
+ activeSectionDropIndex,
13691
+ isSectionDragging
13692
+ };
13693
+ }
13694
+
13695
+ // src/ui/footer-container-chrome.tsx
13696
+ var import_lucide_react15 = require("lucide-react");
13697
+ var import_jsx_runtime29 = require("react/jsx-runtime");
13698
+ function FooterContainerChrome({
13699
+ rect,
13700
+ onAdd,
13701
+ addDisabled = false
13702
+ }) {
13703
+ const chromeGap = 6;
13704
+ const buttonMargin = 7;
13705
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
13706
+ "div",
13707
+ {
13708
+ "data-ohw-footer-container-chrome": "",
13709
+ "data-ohw-bridge": "",
13710
+ className: "pointer-events-none fixed z-[2147483647]",
13711
+ style: {
13712
+ top: rect.top - chromeGap,
13713
+ left: rect.left - chromeGap,
13714
+ width: rect.width + chromeGap * 2,
13715
+ height: rect.height + chromeGap * 2
13716
+ },
13717
+ children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(Tooltip, { children: [
13718
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
13719
+ "button",
13720
+ {
13721
+ type: "button",
13722
+ "data-ohw-footer-add-button": "",
13723
+ disabled: addDisabled,
13724
+ 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",
13725
+ style: { top: chromeGap - buttonMargin },
13726
+ "aria-label": "Add item",
13727
+ onMouseDown: (e) => {
13728
+ e.preventDefault();
13729
+ e.stopPropagation();
13730
+ },
13731
+ onClick: (e) => {
13732
+ e.preventDefault();
13733
+ e.stopPropagation();
13734
+ if (addDisabled) return;
13735
+ onAdd();
13736
+ },
13737
+ children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react15.Plus, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
13738
+ }
13739
+ ) }),
13740
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(TooltipContent, { side: "bottom", sideOffset: 9, children: addDisabled ? "Maximum columns reached" : "Add item" })
13741
+ ] })
13742
+ }
13743
+ ) });
13744
+ }
13745
+
13746
+ // src/lib/carousel.ts
13747
+ var import_react16 = require("react");
13748
+ var CAROUSEL_ATTR = "data-ohw-carousel";
13749
+ var CAROUSEL_VALUE_ATTR = "data-ohw-carousel-value";
13750
+ var CAROUSEL_SLIDE_ATTR = "data-ohw-carousel-slide";
13751
+ var CAROUSEL_EVENT = "ohw:carousel-change";
13752
+ function listCarouselKeys() {
13753
+ const keys = /* @__PURE__ */ new Set();
13754
+ document.querySelectorAll(`[${CAROUSEL_ATTR}]`).forEach((el) => {
13755
+ const key = el.getAttribute("data-ohw-key");
13756
+ if (key) keys.add(key);
13757
+ });
13758
+ return [...keys];
13759
+ }
13760
+ function containersForKey(key) {
13761
+ return Array.from(
13762
+ document.querySelectorAll(`[${CAROUSEL_ATTR}][data-ohw-key="${CSS.escape(key)}"]`)
13763
+ );
13764
+ }
13765
+ function isCarouselKey(key) {
13766
+ return containersForKey(key).length > 0;
13767
+ }
13768
+ function parseSlides(raw) {
13769
+ if (!raw) return [];
13770
+ try {
13771
+ const parsed = JSON.parse(raw);
13772
+ if (!Array.isArray(parsed)) return [];
13773
+ return parsed.filter((s) => Boolean(s) && typeof s === "object").map((s) => ({ src: String(s.src ?? ""), alt: String(s.alt ?? "") }));
13774
+ } catch {
13775
+ return [];
13776
+ }
13777
+ }
13778
+ function readCarouselValue(key) {
13779
+ const container = containersForKey(key)[0];
13780
+ if (!container) return [];
13781
+ const fromAttr = parseSlides(container.getAttribute(CAROUSEL_VALUE_ATTR));
13782
+ if (fromAttr.length > 0) return fromAttr;
13783
+ return Array.from(container.querySelectorAll(`[${CAROUSEL_SLIDE_ATTR}]`)).filter((slide) => slide.closest(`[${CAROUSEL_ATTR}]`) === container).sort((a, b) => slideIndex(a) - slideIndex(b)).map((slide) => {
13784
+ const img = slide instanceof HTMLImageElement ? slide : slide.querySelector("img");
13785
+ return { src: img?.src ?? "", alt: img?.alt ?? "" };
13786
+ });
12837
13787
  }
12838
13788
  function slideIndex(el) {
12839
13789
  const raw = el.getAttribute(CAROUSEL_SLIDE_ATTR);
@@ -12856,8 +13806,8 @@ function applyCarouselNode(key, val) {
12856
13806
  return true;
12857
13807
  }
12858
13808
  function useOhwCarousel(key, initial) {
12859
- const [images, setImages] = (0, import_react15.useState)(initial);
12860
- (0, import_react15.useEffect)(() => {
13809
+ const [images, setImages] = (0, import_react16.useState)(initial);
13810
+ (0, import_react16.useEffect)(() => {
12861
13811
  const el = document.querySelector(
12862
13812
  `[${CAROUSEL_ATTR}][data-ohw-key="${CSS.escape(key)}"]`
12863
13813
  );
@@ -12935,7 +13885,7 @@ function collectEditableNodes(extraContent, root = document) {
12935
13885
  nodes.push({ key, type: "link", text: href });
12936
13886
  }
12937
13887
  if (extraContent) {
12938
- for (const key of [NAV_ORDER_KEY, FOOTER_ORDER_KEY, NAV_COUNT_KEY]) {
13888
+ for (const key of [NAV_ORDER_KEY, FOOTER_ORDER_KEY, NAV_COUNT_KEY, SECTION_ORDER_KEY]) {
12939
13889
  const text = extraContent[key];
12940
13890
  if (typeof text === "string" && text.length > 0) {
12941
13891
  nodes.push({ key, type: "meta", text });
@@ -12972,6 +13922,18 @@ function collectEditableNodes(extraContent, root = document) {
12972
13922
  }
12973
13923
  if (extraContent && !isScoped) {
12974
13924
  applyNavFooterDeleteOverrides(byKey, extraContent);
13925
+ for (const key of LOGO_IMAGE_KEYS) {
13926
+ if (!(key in extraContent)) continue;
13927
+ byKey.set(key, { key, type: "image", text: extraContent[key] ?? "" });
13928
+ }
13929
+ for (const key of [LOGO_PLACEHOLDER_KEY, LOGO_ALT_KEY]) {
13930
+ if (!(key in extraContent)) continue;
13931
+ byKey.set(key, { key, type: "meta", text: extraContent[key] ?? "" });
13932
+ }
13933
+ for (const key of LOGO_SIZE_KEYS) {
13934
+ if (!(key in extraContent)) continue;
13935
+ byKey.set(key, { key, type: "meta", text: extraContent[key] ?? "" });
13936
+ }
12975
13937
  }
12976
13938
  return Array.from(byKey.values());
12977
13939
  }
@@ -13376,6 +14338,7 @@ function fadeInImageElement(img, onReady) {
13376
14338
  function applyEditableImageSrc(img, url) {
13377
14339
  img.removeAttribute("srcset");
13378
14340
  img.removeAttribute("sizes");
14341
+ if (img.loading === "lazy") img.loading = "eager";
13379
14342
  img.src = url;
13380
14343
  }
13381
14344
  function fadeInBgImage(el, url, onReady) {
@@ -13440,21 +14403,10 @@ function parseSchedulingInsertAfter(insertAfter) {
13440
14403
  insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
13441
14404
  };
13442
14405
  }
13443
- function resolveSchedulingInsert(insertAfter, explicitInsertBefore) {
13444
- const parsed = parseSchedulingInsertAfter(insertAfter);
13445
- const insertBefore = explicitInsertBefore ?? parsed.insertBefore;
13446
- const effectiveInsertAfter = insertBefore && parsed.insertBefore === null ? `${insertAfter}${INSERT_BEFORE_MARKER}${insertBefore}` : insertAfter;
13447
- return { effectiveInsertAfter, insertBefore };
13448
- }
13449
- function getSchedulingMountPoint(insertAfter) {
13450
- const { anchor } = parseSchedulingInsertAfter(insertAfter);
13451
- let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
13452
- if (!anchorEl && anchor === "scheduling") {
13453
- const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
13454
- anchorEl = widgets.at(-1) ?? null;
13455
- }
13456
- if (!anchorEl) return null;
13457
- return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
14406
+ function resolveEntryAnchor(entry) {
14407
+ if (entry.anchorId !== void 0) return { anchorId: entry.anchorId, beforeId: entry.beforeId ?? null };
14408
+ const parsed = parseSchedulingInsertAfter(entry.insertAfter);
14409
+ return { anchorId: parsed.anchor, beforeId: parsed.insertBefore };
13458
14410
  }
13459
14411
  function schedulingMountDepth(insertAfter) {
13460
14412
  if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
@@ -13471,8 +14423,7 @@ function getPageSchedulingEntries(raw) {
13471
14423
  }
13472
14424
  }
13473
14425
  function isSchedulingWidgetMissing(entry) {
13474
- const { effectiveInsertAfter } = resolveSchedulingInsert(entry.insertAfter);
13475
- return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
14426
+ return !document.querySelector(`[data-ohw-section="${schedulingSectionId(entry.insertAfter)}"]`);
13476
14427
  }
13477
14428
  function hasMissingSchedulingWidgets(entries) {
13478
14429
  return entries.some(isSchedulingWidgetMissing);
@@ -13502,16 +14453,17 @@ function initSectionsFromContent(content, removeExisting = false) {
13502
14453
  } catch {
13503
14454
  }
13504
14455
  }
13505
- function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId, explicitInsertBefore) {
13506
- const { effectiveInsertAfter, insertBefore } = resolveSchedulingInsert(insertAfter, explicitInsertBefore);
13507
- const sectionId = schedulingSectionId(effectiveInsertAfter);
14456
+ function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, beforeId, existingWidgetId) {
14457
+ const widgetId = existingWidgetId ?? (beforeId ? `${anchorId}${INSERT_BEFORE_MARKER}${beforeId}` : anchorId);
14458
+ const sectionId = schedulingSectionId(widgetId);
13508
14459
  if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
13509
- const mountPoint = getSchedulingMountPoint(effectiveInsertAfter);
13510
- if (!mountPoint) return false;
14460
+ const anchorEl = document.querySelector(`[data-ohw-section="${anchorId}"]`);
14461
+ if (!anchorEl) return false;
14462
+ const mountPoint = anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
13511
14463
  const container = document.createElement("div");
13512
14464
  container.dataset.ohwSectionContainer = "scheduling";
13513
- if (insertBefore) {
13514
- const beforeAnchor = document.querySelector(`[data-ohw-section="${insertBefore}"]`);
14465
+ if (beforeId) {
14466
+ const beforeAnchor = document.querySelector(`[data-ohw-section="${beforeId}"]`);
13515
14467
  const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
13516
14468
  if (!beforePoint) return false;
13517
14469
  beforePoint.insertAdjacentElement("beforebegin", container);
@@ -13522,19 +14474,25 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
13522
14474
  }
13523
14475
  tail.insertAdjacentElement("afterend", container);
13524
14476
  }
13525
- const root = (0, import_client2.createRoot)(container);
13526
- (0, import_react_dom3.flushSync)(() => {
13527
- root.render(
13528
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
13529
- SchedulingWidget,
13530
- {
13531
- notifyOnConnect,
13532
- initialScheduleId: scheduleId,
13533
- insertAfter: effectiveInsertAfter
13534
- }
13535
- )
13536
- );
13537
- });
14477
+ try {
14478
+ const root = (0, import_client2.createRoot)(container);
14479
+ (0, import_react_dom3.flushSync)(() => {
14480
+ root.render(
14481
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
14482
+ SchedulingWidget,
14483
+ {
14484
+ notifyOnConnect,
14485
+ initialScheduleId: scheduleId,
14486
+ insertAfter: widgetId
14487
+ }
14488
+ )
14489
+ );
14490
+ });
14491
+ } catch (err) {
14492
+ console.error("[ow:scheduling] render threw", err);
14493
+ container.remove();
14494
+ return false;
14495
+ }
13538
14496
  const tracker = getSectionsTracker();
13539
14497
  let sections = [];
13540
14498
  try {
@@ -13542,10 +14500,12 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
13542
14500
  } catch {
13543
14501
  }
13544
14502
  const inEditor = typeof window !== "undefined" && window.self !== window.top;
13545
- if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === effectiveInsertAfter)) {
14503
+ if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === widgetId)) {
13546
14504
  sections.push({
13547
14505
  type: "scheduling",
13548
- insertAfter: effectiveInsertAfter,
14506
+ insertAfter: widgetId,
14507
+ anchorId,
14508
+ beforeId: beforeId ?? null,
13549
14509
  pagePath: window.location.pathname,
13550
14510
  ...scheduleId ? { scheduleId } : {}
13551
14511
  });
@@ -13559,7 +14519,8 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
13559
14519
  for (let i = pending.length - 1; i >= 0; i--) {
13560
14520
  const entry = pending[i];
13561
14521
  const shouldNotify = typeof notifyOnConnect === "function" ? notifyOnConnect(entry) : notifyOnConnect;
13562
- if (mountSchedulingWidget(entry.insertAfter, shouldNotify, entry.scheduleId)) {
14522
+ const { anchorId, beforeId } = resolveEntryAnchor(entry);
14523
+ if (mountSchedulingWidget(anchorId, shouldNotify, entry.scheduleId ?? null, beforeId, entry.insertAfter)) {
13563
14524
  pending.splice(i, 1);
13564
14525
  }
13565
14526
  }
@@ -13707,6 +14668,13 @@ function isInsideLinkEditor(target) {
13707
14668
  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"]')
13708
14669
  );
13709
14670
  }
14671
+ function isInsideFloatingPanel(target) {
14672
+ return Boolean(target.closest("[data-ohw-floating-panel]"));
14673
+ }
14674
+ function isPointOverFloatingPanel(clientX, clientY) {
14675
+ const el = document.elementFromPoint(clientX, clientY);
14676
+ return Boolean(el instanceof Element && el.closest("[data-ohw-floating-panel]"));
14677
+ }
13710
14678
  function getHrefKeyFromElement(el) {
13711
14679
  if (!el) return null;
13712
14680
  const anchor = el.closest("[data-ohw-href-key]");
@@ -13952,7 +14920,7 @@ function getNavigationSelectionParent(el) {
13952
14920
  if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
13953
14921
  return getFooterLinksContainer();
13954
14922
  }
13955
- if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
14923
+ 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)) {
13956
14924
  return getNavigationRoot(el);
13957
14925
  }
13958
14926
  return null;
@@ -14421,9 +15389,9 @@ function FloatingToolbar({
14421
15389
  showEditLink,
14422
15390
  onEditLink
14423
15391
  }) {
14424
- const localRef = import_react16.default.useRef(null);
14425
- const [measuredW, setMeasuredW] = import_react16.default.useState(330);
14426
- const setRefs = import_react16.default.useCallback(
15392
+ const localRef = import_react17.default.useRef(null);
15393
+ const [measuredW, setMeasuredW] = import_react17.default.useState(330);
15394
+ const setRefs = import_react17.default.useCallback(
14427
15395
  (node) => {
14428
15396
  localRef.current = node;
14429
15397
  if (typeof elRef === "function") elRef(node);
@@ -14435,7 +15403,7 @@ function FloatingToolbar({
14435
15403
  },
14436
15404
  [elRef]
14437
15405
  );
14438
- import_react16.default.useLayoutEffect(() => {
15406
+ import_react17.default.useLayoutEffect(() => {
14439
15407
  const node = localRef.current;
14440
15408
  if (!node) return;
14441
15409
  const update = () => {
@@ -14461,7 +15429,7 @@ function FloatingToolbar({
14461
15429
  pointerEvents: "auto"
14462
15430
  },
14463
15431
  children: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(CustomToolbar, { children: [
14464
- TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_react16.default.Fragment, { children: [
15432
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_react17.default.Fragment, { children: [
14465
15433
  gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(CustomToolbarDivider, {}),
14466
15434
  btns.map((btn) => {
14467
15435
  const isActive = activeCommands.has(btn.cmd);
@@ -14545,6 +15513,45 @@ function StateToggle({
14545
15513
  );
14546
15514
  }
14547
15515
  var contentCache = /* @__PURE__ */ new Map();
15516
+ var fetchedContentPaths = /* @__PURE__ */ new Set();
15517
+ var OHW_LOADER_STYLE = {
15518
+ position: "fixed",
15519
+ inset: 0,
15520
+ background: "#fff",
15521
+ zIndex: 2147483646,
15522
+ display: "flex",
15523
+ alignItems: "center",
15524
+ justifyContent: "center"
15525
+ };
15526
+ function OhwLoaderSpinner() {
15527
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("svg", { width: "28", height: "28", viewBox: "0 0 28 28", fill: "none", "aria-hidden": true, children: [
15528
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("circle", { cx: "14", cy: "14", r: "11", stroke: "#E7E5E4", strokeWidth: "3" }),
15529
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
15530
+ "circle",
15531
+ {
15532
+ cx: "14",
15533
+ cy: "14",
15534
+ r: "11",
15535
+ stroke: "#1C1917",
15536
+ strokeWidth: "3",
15537
+ strokeDasharray: "17 52",
15538
+ strokeLinecap: "round",
15539
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
15540
+ "animateTransform",
15541
+ {
15542
+ attributeName: "transform",
15543
+ type: "rotate",
15544
+ from: "0 14 14",
15545
+ to: "360 14 14",
15546
+ dur: "0.7s",
15547
+ repeatCount: "indefinite"
15548
+ }
15549
+ )
15550
+ }
15551
+ )
15552
+ ] });
15553
+ }
15554
+ 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){}})();`;
14548
15555
  function resolveSubdomain(subdomainFromQuery) {
14549
15556
  if (subdomainFromQuery) return subdomainFromQuery;
14550
15557
  if (typeof window !== "undefined") {
@@ -14567,8 +15574,8 @@ function OhhwellsBridge() {
14567
15574
  const router = (0, import_navigation3.useRouter)();
14568
15575
  const searchParams = (0, import_navigation3.useSearchParams)();
14569
15576
  const isEditMode = isEditSessionActive();
14570
- const [bridgeRoot, setBridgeRoot] = (0, import_react16.useState)(null);
14571
- (0, import_react16.useEffect)(() => {
15577
+ const [bridgeRoot, setBridgeRoot] = (0, import_react17.useState)(null);
15578
+ (0, import_react17.useEffect)(() => {
14572
15579
  const figtreeFontId = "ohw-figtree-font";
14573
15580
  if (!document.getElementById(figtreeFontId)) {
14574
15581
  const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
@@ -14597,88 +15604,152 @@ function OhhwellsBridge() {
14597
15604
  const subdomain = resolveSubdomain(subdomainFromQuery);
14598
15605
  useLinkHrefGuardian(pathname, subdomain, isEditMode);
14599
15606
  useSavedLinkNavigation(isEditMode);
14600
- const postToParent2 = (0, import_react16.useCallback)((data) => {
15607
+ const postToParent2 = (0, import_react17.useCallback)((data) => {
14601
15608
  if (typeof window !== "undefined" && window.parent !== window) {
14602
15609
  window.parent.postMessage(data, "*");
14603
15610
  }
14604
15611
  }, []);
14605
- const [fetchState, setFetchState] = (0, import_react16.useState)("idle");
14606
- const autoSaveTimers = (0, import_react16.useRef)(/* @__PURE__ */ new Map());
14607
- const activeElRef = (0, import_react16.useRef)(null);
14608
- const pointerHeldRef = (0, import_react16.useRef)(false);
14609
- const selectedElRef = (0, import_react16.useRef)(null);
14610
- const selectedHrefKeyRef = (0, import_react16.useRef)(null);
14611
- const selectedFooterColAttrRef = (0, import_react16.useRef)(null);
14612
- const originalContentRef = (0, import_react16.useRef)(null);
14613
- const activeStateElRef = (0, import_react16.useRef)(null);
14614
- const parentScrollRef = (0, import_react16.useRef)(null);
14615
- const visibleViewportRef = (0, import_react16.useRef)(null);
14616
- const [dialogPortalContainer, setDialogPortalContainer] = (0, import_react16.useState)(null);
14617
- const attachVisibleViewport = (0, import_react16.useCallback)((node) => {
15612
+ const [fetchState, setFetchState] = (0, import_react17.useState)("idle");
15613
+ const autoSaveTimers = (0, import_react17.useRef)(/* @__PURE__ */ new Map());
15614
+ const activeElRef = (0, import_react17.useRef)(null);
15615
+ const pointerHeldRef = (0, import_react17.useRef)(false);
15616
+ const selectedElRef = (0, import_react17.useRef)(null);
15617
+ const selectedHrefKeyRef = (0, import_react17.useRef)(null);
15618
+ const selectedFooterColAttrRef = (0, import_react17.useRef)(null);
15619
+ const originalContentRef = (0, import_react17.useRef)(null);
15620
+ const activeStateElRef = (0, import_react17.useRef)(null);
15621
+ const parentScrollRef = (0, import_react17.useRef)(null);
15622
+ const visibleViewportRef = (0, import_react17.useRef)(null);
15623
+ const [dialogPortalContainer, setDialogPortalContainer] = (0, import_react17.useState)(null);
15624
+ const attachVisibleViewport = (0, import_react17.useCallback)((node) => {
14618
15625
  visibleViewportRef.current = node;
14619
15626
  setDialogPortalContainer(node);
14620
15627
  if (node) applyVisibleViewport(node, parentScrollRef.current);
14621
15628
  }, []);
14622
- const toolbarElRef = (0, import_react16.useRef)(null);
14623
- const glowElRef = (0, import_react16.useRef)(null);
14624
- const hoveredImageRef = (0, import_react16.useRef)(null);
14625
- const hoveredImageHasTextOverlapRef = (0, import_react16.useRef)(false);
14626
- const dragOverElRef = (0, import_react16.useRef)(null);
14627
- const [mediaHover, setMediaHover] = (0, import_react16.useState)(null);
14628
- const [carouselHover, setCarouselHover] = (0, import_react16.useState)(null);
14629
- const [uploadingRects, setUploadingRects] = (0, import_react16.useState)({});
14630
- const hoveredGapRef = (0, import_react16.useRef)(null);
14631
- const imageUnhoverTimerRef = (0, import_react16.useRef)(null);
14632
- const imageShowTimerRef = (0, import_react16.useRef)(null);
14633
- const editStylesRef = (0, import_react16.useRef)(null);
14634
- const activateRef = (0, import_react16.useRef)(() => {
14635
- });
14636
- const deactivateRef = (0, import_react16.useRef)(() => {
14637
- });
14638
- const selectRef = (0, import_react16.useRef)(() => {
14639
- });
14640
- const selectFrameRef = (0, import_react16.useRef)(() => {
14641
- });
14642
- const selectLogoRef = (0, import_react16.useRef)(() => {
14643
- });
14644
- const openLogoSizePanelRef = (0, import_react16.useRef)(() => {
14645
- });
14646
- const deselectRef = (0, import_react16.useRef)(() => {
14647
- });
14648
- const closeFloatingPanelOnlyRef = (0, import_react16.useRef)(() => {
14649
- });
14650
- const reselectNavigationItemRef = (0, import_react16.useRef)(() => {
14651
- });
14652
- const commitNavigationTextEditRef = (0, import_react16.useRef)(() => {
14653
- });
14654
- const handleDeleteSelectedRef = (0, import_react16.useRef)(() => false);
14655
- const runPendingDeleteUndoRef = (0, import_react16.useRef)(() => false);
14656
- const isFooterFrameSelectionRef = (0, import_react16.useRef)(false);
14657
- const refreshActiveCommandsRef = (0, import_react16.useRef)(() => {
14658
- });
14659
- const postToParentRef = (0, import_react16.useRef)(postToParent2);
14660
- postToParentRef.current = postToParent2;
14661
- const aiSectionApiRef = (0, import_react16.useRef)(null);
14662
- const sectionsLoadedRef = (0, import_react16.useRef)(false);
14663
- const pendingScheduleConfigRequests = (0, import_react16.useRef)([]);
14664
- const [toolbarRect, setToolbarRect] = (0, import_react16.useState)(null);
14665
- const [formPickRect, setFormPickRect] = (0, import_react16.useState)(null);
14666
- const formPickElRef = (0, import_react16.useRef)(null);
14667
- const [formViewState, setFormViewStateUi] = (0, import_react16.useState)("default");
14668
- const [formPickCount, setFormPickCount] = (0, import_react16.useState)(null);
14669
- const [formHoverRect, setFormHoverRect] = (0, import_react16.useState)(null);
14670
- const formHoverElRef = (0, import_react16.useRef)(null);
14671
- const [fieldPickRect, setFieldPickRect] = (0, import_react16.useState)(null);
14672
- const fieldPickElRef = (0, import_react16.useRef)(null);
14673
- const [fieldPickState, setFieldPickState] = (0, import_react16.useState)(null);
14674
- const [fieldTypePickerOpen, setFieldTypePickerOpen] = (0, import_react16.useState)(false);
14675
- const clearFormPick = (0, import_react16.useCallback)(() => {
14676
- const form = formPickElRef.current;
14677
- const editing = fieldPickElRef.current;
14678
- if (commitPlaceholderEdit(editing) && editing) {
14679
- const owner = editing.closest('[data-ohw-editable="form"]');
14680
- if (owner) persistFieldsRef.current(owner);
14681
- }
15629
+ const toolbarElRef = (0, import_react17.useRef)(null);
15630
+ const glowElRef = (0, import_react17.useRef)(null);
15631
+ const hoveredImageRef = (0, import_react17.useRef)(null);
15632
+ const hoveredImageHasTextOverlapRef = (0, import_react17.useRef)(false);
15633
+ const dragOverElRef = (0, import_react17.useRef)(null);
15634
+ const [mediaHover, setMediaHover] = (0, import_react17.useState)(null);
15635
+ const [selectedMedia, setSelectedMedia] = (0, import_react17.useState)(null);
15636
+ const selectedMediaElRef = (0, import_react17.useRef)(null);
15637
+ const clearMediaSelection = (0, import_react17.useCallback)(() => {
15638
+ const prev = selectedMediaElRef.current;
15639
+ selectedMediaElRef.current = null;
15640
+ setSelectedMedia(null);
15641
+ const sectionEl = prev?.closest("[data-ohw-section]") ?? null;
15642
+ if (sectionEl) {
15643
+ postToParentRef.current({
15644
+ type: "ow:section-selected",
15645
+ sectionId: sectionEl.dataset.ohwSection ?? null,
15646
+ sectionLabel: sectionEl.dataset.ohwSectionLabel ?? titleCaseSectionId(sectionEl.dataset.ohwSection ?? ""),
15647
+ key: null
15648
+ });
15649
+ }
15650
+ }, []);
15651
+ const clearMediaSelectionRef = (0, import_react17.useRef)(clearMediaSelection);
15652
+ clearMediaSelectionRef.current = clearMediaSelection;
15653
+ const selectMediaElement = (0, import_react17.useCallback)((el) => {
15654
+ const r2 = el.getBoundingClientRect();
15655
+ const video = el.dataset.ohwEditable === "video" ? el.querySelector("video") : null;
15656
+ selectedMediaElRef.current = el;
15657
+ setSelectedMedia({
15658
+ key: el.dataset.ohwKey ?? "",
15659
+ rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height },
15660
+ elementType: el.dataset.ohwEditable ?? "image",
15661
+ hasTextOverlap: false,
15662
+ isDragOver: false,
15663
+ ...video ? { videoAutoplay: video.autoplay, videoMuted: video.muted } : {}
15664
+ });
15665
+ const sectionEl = el.closest("[data-ohw-section]");
15666
+ aiSectionApiRef.current?.selectFromElement(el, { report: false });
15667
+ postToParentRef.current({
15668
+ type: "ow:section-selected",
15669
+ sectionId: sectionEl?.dataset.ohwSection ?? null,
15670
+ sectionLabel: sectionEl ? sectionEl.dataset.ohwSectionLabel ?? titleCaseSectionId(sectionEl.dataset.ohwSection ?? "") : null,
15671
+ key: el.dataset.ohwKey ?? null,
15672
+ // Display name for the pill — the raw key prettifies into fragments ("Img"); the
15673
+ // bridge knows what the node IS, so it names it.
15674
+ keyLabel: el.dataset.ohwEditable === "video" ? "Video" : el.dataset.ohwEditable === "bg-image" ? "Background" : "Image"
15675
+ });
15676
+ }, []);
15677
+ const selectMediaElementRef = (0, import_react17.useRef)(selectMediaElement);
15678
+ selectMediaElementRef.current = selectMediaElement;
15679
+ (0, import_react17.useEffect)(() => {
15680
+ if (!selectedMedia) return;
15681
+ const update = () => {
15682
+ const el = selectedMediaElRef.current;
15683
+ if (!el || !el.isConnected) {
15684
+ clearMediaSelection();
15685
+ return;
15686
+ }
15687
+ const r2 = el.getBoundingClientRect();
15688
+ setSelectedMedia(
15689
+ (prev) => prev ? { ...prev, rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height } } : prev
15690
+ );
15691
+ };
15692
+ window.addEventListener("scroll", update, true);
15693
+ window.addEventListener("resize", update);
15694
+ return () => {
15695
+ window.removeEventListener("scroll", update, true);
15696
+ window.removeEventListener("resize", update);
15697
+ };
15698
+ }, [selectedMedia !== null]);
15699
+ const [carouselHover, setCarouselHover] = (0, import_react17.useState)(null);
15700
+ const [uploadingRects, setUploadingRects] = (0, import_react17.useState)({});
15701
+ const hoveredGapRef = (0, import_react17.useRef)(null);
15702
+ const imageUnhoverTimerRef = (0, import_react17.useRef)(null);
15703
+ const imageShowTimerRef = (0, import_react17.useRef)(null);
15704
+ const editStylesRef = (0, import_react17.useRef)(null);
15705
+ const activateRef = (0, import_react17.useRef)(() => {
15706
+ });
15707
+ const deactivateRef = (0, import_react17.useRef)(() => {
15708
+ });
15709
+ const selectRef = (0, import_react17.useRef)(() => {
15710
+ });
15711
+ const selectFrameRef = (0, import_react17.useRef)(() => {
15712
+ });
15713
+ const selectLogoRef = (0, import_react17.useRef)(() => {
15714
+ });
15715
+ const openLogoSizePanelRef = (0, import_react17.useRef)(() => {
15716
+ });
15717
+ const deselectRef = (0, import_react17.useRef)(() => {
15718
+ });
15719
+ const closeFloatingPanelOnlyRef = (0, import_react17.useRef)(() => {
15720
+ });
15721
+ const reselectNavigationItemRef = (0, import_react17.useRef)(() => {
15722
+ });
15723
+ const commitNavigationTextEditRef = (0, import_react17.useRef)(() => {
15724
+ });
15725
+ const handleDeleteSelectedRef = (0, import_react17.useRef)(() => false);
15726
+ const runPendingDeleteUndoRef = (0, import_react17.useRef)(() => false);
15727
+ const isFooterFrameSelectionRef = (0, import_react17.useRef)(false);
15728
+ const refreshActiveCommandsRef = (0, import_react17.useRef)(() => {
15729
+ });
15730
+ const postToParentRef = (0, import_react17.useRef)(postToParent2);
15731
+ postToParentRef.current = postToParent2;
15732
+ const aiSectionApiRef = (0, import_react17.useRef)(null);
15733
+ const sectionsLoadedRef = (0, import_react17.useRef)(false);
15734
+ const pendingScheduleConfigRequests = (0, import_react17.useRef)([]);
15735
+ const [toolbarRect, setToolbarRect] = (0, import_react17.useState)(null);
15736
+ const [formPickRect, setFormPickRect] = (0, import_react17.useState)(null);
15737
+ const formPickElRef = (0, import_react17.useRef)(null);
15738
+ const [formViewState, setFormViewStateUi] = (0, import_react17.useState)("default");
15739
+ const [formPickCount, setFormPickCount] = (0, import_react17.useState)(null);
15740
+ const [formHoverRect, setFormHoverRect] = (0, import_react17.useState)(null);
15741
+ const formHoverElRef = (0, import_react17.useRef)(null);
15742
+ const [fieldPickRect, setFieldPickRect] = (0, import_react17.useState)(null);
15743
+ const fieldPickElRef = (0, import_react17.useRef)(null);
15744
+ const [fieldPickState, setFieldPickState] = (0, import_react17.useState)(null);
15745
+ const [fieldTypePickerOpen, setFieldTypePickerOpen] = (0, import_react17.useState)(false);
15746
+ const clearFormPick = (0, import_react17.useCallback)(() => {
15747
+ const form = formPickElRef.current;
15748
+ const editing = fieldPickElRef.current;
15749
+ if (commitPlaceholderEdit(editing) && editing) {
15750
+ const owner = editing.closest('[data-ohw-editable="form"]');
15751
+ if (owner) persistFieldsRef.current(owner);
15752
+ }
14682
15753
  if (form) {
14683
15754
  const key = formKeyOf(form);
14684
15755
  if (key) setFormViewState(form, key, "default", successInitialFor(form, key, editContentRef.current));
@@ -14692,7 +15763,7 @@ function OhhwellsBridge() {
14692
15763
  formPickElRef.current = null;
14693
15764
  setFormPickRect(null);
14694
15765
  }, []);
14695
- const clearFieldPick = (0, import_react16.useCallback)(() => {
15766
+ const clearFieldPick = (0, import_react17.useCallback)(() => {
14696
15767
  const wrapper = fieldPickElRef.current;
14697
15768
  if (commitPlaceholderEdit(wrapper) && wrapper) {
14698
15769
  const form = wrapper.closest('[data-ohw-editable="form"]');
@@ -14702,9 +15773,9 @@ function OhhwellsBridge() {
14702
15773
  setFieldPickRect(null);
14703
15774
  setFieldPickState(null);
14704
15775
  }, []);
14705
- const persistFieldsRef = (0, import_react16.useRef)(() => {
15776
+ const persistFieldsRef = (0, import_react17.useRef)(() => {
14706
15777
  });
14707
- const persistFields = (0, import_react16.useCallback)(
15778
+ const persistFields = (0, import_react17.useCallback)(
14708
15779
  (form) => {
14709
15780
  const key = formKeyOf(form);
14710
15781
  if (!key) return;
@@ -14715,7 +15786,7 @@ function OhhwellsBridge() {
14715
15786
  []
14716
15787
  );
14717
15788
  persistFieldsRef.current = persistFields;
14718
- const selectField = (0, import_react16.useCallback)((wrapper) => {
15789
+ const selectField = (0, import_react17.useCallback)((wrapper) => {
14719
15790
  if (fieldPickElRef.current && fieldPickElRef.current !== wrapper) {
14720
15791
  commitPlaceholderEdit(fieldPickElRef.current);
14721
15792
  }
@@ -14728,7 +15799,7 @@ function OhhwellsBridge() {
14728
15799
  setFieldPickState({ type: fieldTypeOf(wrapper), required: isFieldRequired(wrapper) });
14729
15800
  setFieldTypePickerOpen(false);
14730
15801
  }, []);
14731
- const withSelectedField = (0, import_react16.useCallback)(
15802
+ const withSelectedField = (0, import_react17.useCallback)(
14732
15803
  (run) => {
14733
15804
  const wrapper = fieldPickElRef.current;
14734
15805
  const form = formPickElRef.current;
@@ -14741,28 +15812,28 @@ function OhhwellsBridge() {
14741
15812
  },
14742
15813
  [persistFields]
14743
15814
  );
14744
- const handleFieldTypeChange = (0, import_react16.useCallback)(
15815
+ const handleFieldTypeChange = (0, import_react17.useCallback)(
14745
15816
  (type) => withSelectedField((_form, wrapper) => {
14746
15817
  applyFieldType(wrapper, type);
14747
15818
  selectField(wrapper);
14748
15819
  }),
14749
15820
  [selectField, withSelectedField]
14750
15821
  );
14751
- const handleFieldRequiredToggle = (0, import_react16.useCallback)(
15822
+ const handleFieldRequiredToggle = (0, import_react17.useCallback)(
14752
15823
  () => withSelectedField((_form, wrapper) => {
14753
15824
  setFieldRequired(wrapper, !isFieldRequired(wrapper));
14754
15825
  selectField(wrapper);
14755
15826
  }),
14756
15827
  [selectField, withSelectedField]
14757
15828
  );
14758
- const handleFieldDuplicate = (0, import_react16.useCallback)(
15829
+ const handleFieldDuplicate = (0, import_react17.useCallback)(
14759
15830
  () => withSelectedField((form, wrapper) => {
14760
15831
  const copy = duplicateField(form, wrapper);
14761
15832
  selectField(copy);
14762
15833
  }),
14763
15834
  [selectField, withSelectedField]
14764
15835
  );
14765
- const handleFieldDelete = (0, import_react16.useCallback)(
15836
+ const handleFieldDelete = (0, import_react17.useCallback)(
14766
15837
  () => withSelectedField((_form, wrapper) => {
14767
15838
  removeField(wrapper);
14768
15839
  clearFieldPick();
@@ -14770,7 +15841,7 @@ function OhhwellsBridge() {
14770
15841
  }),
14771
15842
  [clearFieldPick, withSelectedField]
14772
15843
  );
14773
- const handleAddField = (0, import_react16.useCallback)(
15844
+ const handleAddField = (0, import_react17.useCallback)(
14774
15845
  (type) => {
14775
15846
  const form = formPickElRef.current;
14776
15847
  if (!form) return;
@@ -14786,8 +15857,8 @@ function OhhwellsBridge() {
14786
15857
  },
14787
15858
  [persistFields, selectField]
14788
15859
  );
14789
- const fieldDragRef = (0, import_react16.useRef)(null);
14790
- const buildFieldDropSlots = (0, import_react16.useCallback)((form, draggedKey) => {
15860
+ const fieldDragRef = (0, import_react17.useRef)(null);
15861
+ const buildFieldDropSlots = (0, import_react17.useCallback)((form, draggedKey) => {
14791
15862
  const others = listFieldWrappers(form).filter((el) => fieldKeyOf(el) !== draggedKey);
14792
15863
  const slots = others.map((el) => {
14793
15864
  const rect = el.getBoundingClientRect();
@@ -14800,7 +15871,7 @@ function OhhwellsBridge() {
14800
15871
  }
14801
15872
  return slots;
14802
15873
  }, []);
14803
- const handleFieldDragStart = (0, import_react16.useCallback)(() => {
15874
+ const handleFieldDragStart = (0, import_react17.useCallback)(() => {
14804
15875
  const wrapper = fieldPickElRef.current;
14805
15876
  const form = formPickElRef.current;
14806
15877
  if (!wrapper || !form) return;
@@ -14809,18 +15880,18 @@ function OhhwellsBridge() {
14809
15880
  setFieldDragging(true);
14810
15881
  setFieldDropSlots(buildFieldDropSlots(form, key));
14811
15882
  }, [buildFieldDropSlots]);
14812
- const handleFieldDragEnd = (0, import_react16.useCallback)(() => {
15883
+ const handleFieldDragEnd = (0, import_react17.useCallback)(() => {
14813
15884
  fieldDragRef.current = null;
14814
15885
  setFieldDropIndex(null);
14815
15886
  setFieldDropSlots([]);
14816
15887
  setFieldDragging(false);
14817
15888
  }, []);
14818
- const [fieldDropIndex, setFieldDropIndex] = (0, import_react16.useState)(null);
14819
- const [fieldDropSlots, setFieldDropSlots] = (0, import_react16.useState)([]);
14820
- const [fieldDragging, setFieldDragging] = (0, import_react16.useState)(false);
14821
- const clearFormPickRef = (0, import_react16.useRef)(clearFormPick);
15889
+ const [fieldDropIndex, setFieldDropIndex] = (0, import_react17.useState)(null);
15890
+ const [fieldDropSlots, setFieldDropSlots] = (0, import_react17.useState)([]);
15891
+ const [fieldDragging, setFieldDragging] = (0, import_react17.useState)(false);
15892
+ const clearFormPickRef = (0, import_react17.useRef)(clearFormPick);
14822
15893
  clearFormPickRef.current = clearFormPick;
14823
- (0, import_react16.useEffect)(() => {
15894
+ (0, import_react17.useEffect)(() => {
14824
15895
  const el = fieldPickElRef.current;
14825
15896
  if (!el || fieldPickRect === null) return;
14826
15897
  const observer = new ResizeObserver(() => {
@@ -14829,7 +15900,7 @@ function OhhwellsBridge() {
14829
15900
  observer.observe(el);
14830
15901
  return () => observer.disconnect();
14831
15902
  }, [fieldPickRect !== null, fieldPickState]);
14832
- (0, import_react16.useEffect)(() => {
15903
+ (0, import_react17.useEffect)(() => {
14833
15904
  const el = formPickElRef.current;
14834
15905
  if (!el || formPickRect === null) return;
14835
15906
  const observer = new ResizeObserver(() => {
@@ -14838,25 +15909,25 @@ function OhhwellsBridge() {
14838
15909
  observer.observe(el);
14839
15910
  return () => observer.disconnect();
14840
15911
  }, [formPickRect !== null, formViewState]);
14841
- const [toolbarVariant, setToolbarVariant] = (0, import_react16.useState)("none");
14842
- const toolbarVariantRef = (0, import_react16.useRef)("none");
15912
+ const [toolbarVariant, setToolbarVariant] = (0, import_react17.useState)("none");
15913
+ const toolbarVariantRef = (0, import_react17.useRef)("none");
14843
15914
  toolbarVariantRef.current = toolbarVariant;
14844
- const [selectedIsCta, setSelectedIsCta] = (0, import_react16.useState)(false);
14845
- const [selectedIsSocial, setSelectedIsSocial] = (0, import_react16.useState)(false);
14846
- const [selectedIsSocialsRow, setSelectedIsSocialsRow] = (0, import_react16.useState)(false);
14847
- const [reorderHrefKey, setReorderHrefKey] = (0, import_react16.useState)(null);
14848
- const [reorderDragDisabled, setReorderDragDisabled] = (0, import_react16.useState)(false);
14849
- const [toggleState, setToggleState] = (0, import_react16.useState)(null);
14850
- const [maxBadge, setMaxBadge] = (0, import_react16.useState)(null);
14851
- const [activeCommands, setActiveCommands] = (0, import_react16.useState)(/* @__PURE__ */ new Set());
14852
- const [sectionGap, setSectionGap] = (0, import_react16.useState)(null);
14853
- const [toolbarShowEditLink, setToolbarShowEditLink] = (0, import_react16.useState)(false);
14854
- const hoveredNavContainerRef = (0, import_react16.useRef)(null);
14855
- const [hoveredNavContainerRect, setHoveredNavContainerRect] = (0, import_react16.useState)(null);
14856
- const hoveredItemElRef = (0, import_react16.useRef)(null);
14857
- const [hoveredItemRect, setHoveredItemRect] = (0, import_react16.useState)(null);
14858
- const [hoveredTextRect, setHoveredTextRect] = (0, import_react16.useState)(null);
14859
- (0, import_react16.useEffect)(() => {
15915
+ const [selectedIsCta, setSelectedIsCta] = (0, import_react17.useState)(false);
15916
+ const [selectedIsSocial, setSelectedIsSocial] = (0, import_react17.useState)(false);
15917
+ const [selectedIsSocialsRow, setSelectedIsSocialsRow] = (0, import_react17.useState)(false);
15918
+ const [reorderHrefKey, setReorderHrefKey] = (0, import_react17.useState)(null);
15919
+ const [reorderDragDisabled, setReorderDragDisabled] = (0, import_react17.useState)(false);
15920
+ const [toggleState, setToggleState] = (0, import_react17.useState)(null);
15921
+ const [maxBadge, setMaxBadge] = (0, import_react17.useState)(null);
15922
+ const [activeCommands, setActiveCommands] = (0, import_react17.useState)(/* @__PURE__ */ new Set());
15923
+ const [sectionGap, setSectionGap] = (0, import_react17.useState)(null);
15924
+ const [toolbarShowEditLink, setToolbarShowEditLink] = (0, import_react17.useState)(false);
15925
+ const hoveredNavContainerRef = (0, import_react17.useRef)(null);
15926
+ const [hoveredNavContainerRect, setHoveredNavContainerRect] = (0, import_react17.useState)(null);
15927
+ const hoveredItemElRef = (0, import_react17.useRef)(null);
15928
+ const [hoveredItemRect, setHoveredItemRect] = (0, import_react17.useState)(null);
15929
+ const [hoveredTextRect, setHoveredTextRect] = (0, import_react17.useState)(null);
15930
+ (0, import_react17.useEffect)(() => {
14860
15931
  const sync = () => {
14861
15932
  const el = document.querySelector(
14862
15933
  '[data-ohw-hovered]:not([contenteditable]):not([data-ohw-href-key]):not([data-ohw-editable="form"] *)'
@@ -14877,43 +15948,56 @@ function OhhwellsBridge() {
14877
15948
  });
14878
15949
  return () => observer.disconnect();
14879
15950
  }, []);
14880
- const siblingHintElRef = (0, import_react16.useRef)(null);
14881
- const [siblingHintRect, setSiblingHintRect] = (0, import_react16.useState)(null);
14882
- const [siblingHintRects, setSiblingHintRects] = (0, import_react16.useState)([]);
14883
- const [isItemDragging, setIsItemDragging] = (0, import_react16.useState)(false);
14884
- const [isFooterFrameSelection, setIsFooterFrameSelection] = (0, import_react16.useState)(false);
15951
+ const siblingHintElRef = (0, import_react17.useRef)(null);
15952
+ const [siblingHintRect, setSiblingHintRect] = (0, import_react17.useState)(null);
15953
+ const [siblingHintRects, setSiblingHintRects] = (0, import_react17.useState)([]);
15954
+ const [isItemDragging, setIsItemDragging] = (0, import_react17.useState)(false);
15955
+ const [isFooterFrameSelection, setIsFooterFrameSelection] = (0, import_react17.useState)(false);
14885
15956
  isFooterFrameSelectionRef.current = isFooterFrameSelection;
14886
- const [floatingPanel, setFloatingPanel] = (0, import_react16.useState)(null);
14887
- const floatingPanelOpenRef = (0, import_react16.useRef)(false);
14888
- floatingPanelOpenRef.current = floatingPanel !== null;
14889
- const [floatingPanelPos, setFloatingPanelPos] = (0, import_react16.useState)(null);
14890
- const [logoSizeDraft, setLogoSizeDraft] = (0, import_react16.useState)(null);
14891
- const [editorViewport, setEditorViewport] = (0, import_react16.useState)("desktop");
14892
- const [parentScrollSnap, setParentScrollSnap] = (0, import_react16.useState)(null);
14893
- const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = (0, import_react16.useState)(null);
14894
- const [footerHeadingVisible, setFooterHeadingVisible] = (0, import_react16.useState)(null);
14895
- const footerDragRef = (0, import_react16.useRef)(null);
14896
- const [footerDropSlots, setFooterDropSlots] = (0, import_react16.useState)([]);
14897
- const [activeFooterDropIndex, setActiveFooterDropIndex] = (0, import_react16.useState)(null);
14898
- const [draggedItemRect, setDraggedItemRect] = (0, import_react16.useState)(null);
14899
- const footerPointerDragRef = (0, import_react16.useRef)(null);
14900
- const suppressNextClickRef = (0, import_react16.useRef)(false);
14901
- const suppressClickUntilRef = (0, import_react16.useRef)(0);
14902
- const [linkPopover, setLinkPopover] = (0, import_react16.useState)(null);
14903
- const linkPopoverSessionRef = (0, import_react16.useRef)(null);
14904
- const addNavAfterAnchorRef = (0, import_react16.useRef)(null);
14905
- const editContentRef = (0, import_react16.useRef)({});
14906
- const aiSectionsRef = (0, import_react16.useRef)("");
14907
- const pendingDeleteUndoRef = (0, import_react16.useRef)(null);
14908
- const [sitePages, setSitePages] = (0, import_react16.useState)([]);
14909
- const [sectionsByPath, setSectionsByPath] = (0, import_react16.useState)({});
14910
- const sectionsPrefetchGenRef = (0, import_react16.useRef)(0);
14911
- const setLinkPopoverRef = (0, import_react16.useRef)(setLinkPopover);
14912
- const linkPopoverPanelRef = (0, import_react16.useRef)(null);
14913
- const linkPopoverOpenRef = (0, import_react16.useRef)(false);
14914
- const linkPopoverGraceUntilRef = (0, import_react16.useRef)(0);
15957
+ const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = (0, import_react17.useState)(null);
15958
+ const [footerHeadingVisible, setFooterHeadingVisible] = (0, import_react17.useState)(null);
15959
+ const footerDragRef = (0, import_react17.useRef)(null);
15960
+ const [footerDropSlots, setFooterDropSlots] = (0, import_react17.useState)([]);
15961
+ const [activeFooterDropIndex, setActiveFooterDropIndex] = (0, import_react17.useState)(null);
15962
+ const [draggedItemRect, setDraggedItemRect] = (0, import_react17.useState)(null);
15963
+ const footerPointerDragRef = (0, import_react17.useRef)(null);
15964
+ const suppressNextClickRef = (0, import_react17.useRef)(false);
15965
+ const suppressClickUntilRef = (0, import_react17.useRef)(0);
15966
+ const [linkPopover, setLinkPopover] = (0, import_react17.useState)(null);
15967
+ const linkPopoverSessionRef = (0, import_react17.useRef)(null);
15968
+ const addNavAfterAnchorRef = (0, import_react17.useRef)(null);
15969
+ const editContentRef = (0, import_react17.useRef)({});
15970
+ const aiSectionsRef = (0, import_react17.useRef)("");
15971
+ const brandKitRef = (0, import_react17.useRef)("");
15972
+ const stylesRef = (0, import_react17.useRef)("");
15973
+ const pendingDeleteUndoRef = (0, import_react17.useRef)(null);
15974
+ const [floatingPanel, setFloatingPanel] = (0, import_react17.useState)(null);
15975
+ const floatingPanelOpenRef = (0, import_react17.useRef)(false);
15976
+ const setFloatingPanelRef = (0, import_react17.useRef)(setFloatingPanel);
15977
+ const [floatingPanelPos, setFloatingPanelPos] = (0, import_react17.useState)(null);
15978
+ const [logoSizeDraft, setLogoSizeDraft] = (0, import_react17.useState)(null);
15979
+ const [editorViewport, setEditorViewport] = (0, import_react17.useState)("desktop");
15980
+ const [parentScrollSnap, setParentScrollSnap] = (0, import_react17.useState)(null);
15981
+ const [sitePages, setSitePages] = (0, import_react17.useState)([]);
15982
+ const [sectionsByPath, setSectionsByPath] = (0, import_react17.useState)({});
15983
+ const sectionsPrefetchGenRef = (0, import_react17.useRef)(0);
15984
+ const setLinkPopoverRef = (0, import_react17.useRef)(setLinkPopover);
15985
+ const linkPopoverPanelRef = (0, import_react17.useRef)(null);
15986
+ const linkPopoverOpenRef = (0, import_react17.useRef)(false);
15987
+ const linkPopoverGraceUntilRef = (0, import_react17.useRef)(0);
14915
15988
  setLinkPopoverRef.current = setLinkPopover;
15989
+ setFloatingPanelRef.current = setFloatingPanel;
14916
15990
  linkPopoverSessionRef.current = linkPopover;
15991
+ floatingPanelOpenRef.current = Boolean(floatingPanel);
15992
+ (0, import_react17.useEffect)(() => {
15993
+ const syncViewport = () => {
15994
+ const next = window.innerWidth <= 480 ? "mobile" : "desktop";
15995
+ setEditorViewport((prev) => prev === next ? prev : next);
15996
+ };
15997
+ syncViewport();
15998
+ window.addEventListener("resize", syncViewport);
15999
+ return () => window.removeEventListener("resize", syncViewport);
16000
+ }, []);
14917
16001
  const {
14918
16002
  navDragRef,
14919
16003
  navDropSlots,
@@ -14946,10 +16030,20 @@ function OhhwellsBridge() {
14946
16030
  getNavigationItemAnchor,
14947
16031
  isDragHandleDisabled
14948
16032
  });
16033
+ const { sectionDropSlots, activeSectionDropIndex, isSectionDragging } = useSectionDrag({
16034
+ isEditMode,
16035
+ editContentRef,
16036
+ postToParentRef,
16037
+ parentScrollRef,
16038
+ navDragRef,
16039
+ footerDragRef,
16040
+ suppressNextClickRef,
16041
+ suppressClickUntilRef
16042
+ });
14949
16043
  const bumpLinkPopoverGrace = () => {
14950
16044
  linkPopoverGraceUntilRef.current = Date.now() + 350;
14951
16045
  };
14952
- const runSectionsPrefetch = (0, import_react16.useCallback)((pages) => {
16046
+ const runSectionsPrefetch = (0, import_react17.useCallback)((pages) => {
14953
16047
  if (!isEditMode || shouldUseDevFixtures() || pages.length === 0) return;
14954
16048
  const gen = ++sectionsPrefetchGenRef.current;
14955
16049
  const paths = pages.map((p) => p.path);
@@ -14968,9 +16062,9 @@ function OhhwellsBridge() {
14968
16062
  );
14969
16063
  });
14970
16064
  }, [isEditMode, pathname]);
14971
- const runSectionsPrefetchRef = (0, import_react16.useRef)(runSectionsPrefetch);
16065
+ const runSectionsPrefetchRef = (0, import_react17.useRef)(runSectionsPrefetch);
14972
16066
  runSectionsPrefetchRef.current = runSectionsPrefetch;
14973
- (0, import_react16.useEffect)(() => {
16067
+ (0, import_react17.useEffect)(() => {
14974
16068
  if (!linkPopover) {
14975
16069
  document.documentElement.removeAttribute("data-ohw-link-popover-open");
14976
16070
  return;
@@ -14998,7 +16092,7 @@ function OhhwellsBridge() {
14998
16092
  document.documentElement.removeAttribute("data-ohw-link-popover-open");
14999
16093
  };
15000
16094
  }, [linkPopover, postToParent2]);
15001
- (0, import_react16.useEffect)(() => {
16095
+ (0, import_react17.useEffect)(() => {
15002
16096
  if (!isEditMode) return;
15003
16097
  const useFixtures = shouldUseDevFixtures();
15004
16098
  if (useFixtures) {
@@ -15022,14 +16116,14 @@ function OhhwellsBridge() {
15022
16116
  if (!useFixtures) postToParent2({ type: "ow:request-site-pages" });
15023
16117
  return () => window.removeEventListener("message", onSitePages);
15024
16118
  }, [isEditMode, postToParent2]);
15025
- (0, import_react16.useEffect)(() => {
16119
+ (0, import_react17.useEffect)(() => {
15026
16120
  if (!isEditMode || shouldUseDevFixtures()) return;
15027
16121
  void loadAllSectionsManifest().then((manifest) => {
15028
16122
  if (Object.keys(manifest).length === 0) return;
15029
16123
  setSectionsByPath((prev) => ({ ...manifest, ...prev }));
15030
16124
  });
15031
16125
  }, [isEditMode]);
15032
- (0, import_react16.useEffect)(() => {
16126
+ (0, import_react17.useEffect)(() => {
15033
16127
  const update = () => {
15034
16128
  const el = activeElRef.current ?? selectedElRef.current;
15035
16129
  if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
@@ -15053,10 +16147,10 @@ function OhhwellsBridge() {
15053
16147
  vvp.removeEventListener("resize", update);
15054
16148
  };
15055
16149
  }, []);
15056
- const refreshStateRules = (0, import_react16.useCallback)(() => {
16150
+ const refreshStateRules = (0, import_react17.useCallback)(() => {
15057
16151
  editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
15058
16152
  }, []);
15059
- const processConfigRequest = (0, import_react16.useCallback)((insertAfterVal) => {
16153
+ const processConfigRequest = (0, import_react17.useCallback)((insertAfterVal) => {
15060
16154
  const tracker = getSectionsTracker();
15061
16155
  let entries = [];
15062
16156
  try {
@@ -15079,7 +16173,7 @@ function OhhwellsBridge() {
15079
16173
  }
15080
16174
  window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: null }, "*");
15081
16175
  }, [isEditMode]);
15082
- const deactivate = (0, import_react16.useCallback)(() => {
16176
+ const deactivate = (0, import_react17.useCallback)(() => {
15083
16177
  const el = activeElRef.current;
15084
16178
  if (!el) return;
15085
16179
  const isFormBlock = el.dataset.ohwEditable === "form";
@@ -15120,12 +16214,12 @@ function OhhwellsBridge() {
15120
16214
  setToolbarShowEditLink(false);
15121
16215
  postToParent2({ type: "ow:exit-edit" });
15122
16216
  }, [postToParent2]);
15123
- const clearSelectedAttr = (0, import_react16.useCallback)(() => {
16217
+ const clearSelectedAttr = (0, import_react17.useCallback)(() => {
15124
16218
  document.querySelectorAll("[data-ohw-selected]").forEach((el) => {
15125
16219
  el.removeAttribute("data-ohw-selected");
15126
16220
  });
15127
16221
  }, []);
15128
- const deselect = (0, import_react16.useCallback)(() => {
16222
+ const deselect = (0, import_react17.useCallback)(() => {
15129
16223
  clearSelectedAttr();
15130
16224
  selectedElRef.current = null;
15131
16225
  selectedHrefKeyRef.current = null;
@@ -15154,11 +16248,12 @@ function OhhwellsBridge() {
15154
16248
  setToolbarVariant("none");
15155
16249
  }
15156
16250
  }, [clearSelectedAttr]);
15157
- const markSelected = (0, import_react16.useCallback)((el) => {
16251
+ const markSelected = (0, import_react17.useCallback)((el) => {
15158
16252
  clearSelectedAttr();
16253
+ el.removeAttribute("data-ohw-hovered");
15159
16254
  el.setAttribute("data-ohw-selected", "");
15160
16255
  }, [clearSelectedAttr]);
15161
- const resolveHrefKeyElement = (0, import_react16.useCallback)((hrefKey) => {
16256
+ const resolveHrefKeyElement = (0, import_react17.useCallback)((hrefKey) => {
15162
16257
  if (isFooterHrefKey(hrefKey)) {
15163
16258
  return document.querySelector(
15164
16259
  `footer [data-ohw-href-key="${CSS.escape(hrefKey)}"]`
@@ -15173,7 +16268,7 @@ function OhhwellsBridge() {
15173
16268
  `[data-ohw-href-key="${CSS.escape(hrefKey)}"]`
15174
16269
  );
15175
16270
  }, []);
15176
- const resyncSelectedNavigationItem = (0, import_react16.useCallback)(() => {
16271
+ const resyncSelectedNavigationItem = (0, import_react17.useCallback)(() => {
15177
16272
  const hrefKey = selectedHrefKeyRef.current;
15178
16273
  if (hrefKey) {
15179
16274
  const link = resolveHrefKeyElement(hrefKey);
@@ -15211,7 +16306,7 @@ function OhhwellsBridge() {
15211
16306
  );
15212
16307
  }
15213
16308
  }, [resolveHrefKeyElement]);
15214
- const reselectNavigationItem = (0, import_react16.useCallback)((navAnchor) => {
16309
+ const reselectNavigationItem = (0, import_react17.useCallback)((navAnchor) => {
15215
16310
  selectedElRef.current = navAnchor;
15216
16311
  selectedHrefKeyRef.current = navAnchor.getAttribute("data-ohw-href-key");
15217
16312
  selectedFooterColAttrRef.current = null;
@@ -15242,7 +16337,7 @@ function OhhwellsBridge() {
15242
16337
  setToolbarShowEditLink(false);
15243
16338
  setActiveCommands(/* @__PURE__ */ new Set());
15244
16339
  }, [markSelected]);
15245
- const commitNavigationTextEdit = (0, import_react16.useCallback)((navAnchor) => {
16340
+ const commitNavigationTextEdit = (0, import_react17.useCallback)((navAnchor) => {
15246
16341
  const el = activeElRef.current;
15247
16342
  if (!el) return;
15248
16343
  const key = el.dataset.ohwKey;
@@ -15275,7 +16370,7 @@ function OhhwellsBridge() {
15275
16370
  postToParent2({ type: "ow:exit-edit" });
15276
16371
  reselectNavigationItem(navAnchor);
15277
16372
  }, [postToParent2, reselectNavigationItem]);
15278
- const handleAddTopLevelNavItem = (0, import_react16.useCallback)(() => {
16373
+ const handleAddTopLevelNavItem = (0, import_react17.useCallback)(() => {
15279
16374
  const items = listNavbarRootItems();
15280
16375
  addNavAfterAnchorRef.current = items[items.length - 1] ?? null;
15281
16376
  deselectRef.current();
@@ -15287,7 +16382,7 @@ function OhhwellsBridge() {
15287
16382
  intent: "add-nav"
15288
16383
  });
15289
16384
  }, []);
15290
- const maybeWarnNavLinkDropdownConflict = (0, import_react16.useCallback)(
16385
+ const maybeWarnNavLinkDropdownConflict = (0, import_react17.useCallback)(
15291
16386
  (anchor) => {
15292
16387
  if (!isNavbarHrefKey(anchor.getAttribute("data-ohw-href-key"))) return;
15293
16388
  if (!navDropdownsOpenOnClick()) return;
@@ -15300,7 +16395,7 @@ function OhhwellsBridge() {
15300
16395
  },
15301
16396
  [postToParent2]
15302
16397
  );
15303
- const handleNavDropdownOpenChange = (0, import_react16.useCallback)((open) => {
16398
+ const handleNavDropdownOpenChange = (0, import_react17.useCallback)((open) => {
15304
16399
  const selected = selectedElRef.current;
15305
16400
  if (!selected || !isNavigationItem2(selected)) return;
15306
16401
  setNavGroupForceOpen(selected, open);
@@ -15312,7 +16407,7 @@ function OhhwellsBridge() {
15312
16407
  }
15313
16408
  });
15314
16409
  }, []);
15315
- const handleFooterHeadingVisibleChange = (0, import_react16.useCallback)(
16410
+ const handleFooterHeadingVisibleChange = (0, import_react17.useCallback)(
15316
16411
  (visible) => {
15317
16412
  const selected = selectedElRef.current;
15318
16413
  if (!selected || !isFooterFrameSelectionRef.current) return;
@@ -15336,7 +16431,7 @@ function OhhwellsBridge() {
15336
16431
  },
15337
16432
  [postToParent2]
15338
16433
  );
15339
- const enterEditOnNewItem = (0, import_react16.useCallback)((anchor) => {
16434
+ const enterEditOnNewItem = (0, import_react17.useCallback)((anchor) => {
15340
16435
  const label = anchor.querySelector('[data-ohw-editable="text"]');
15341
16436
  if (!label) {
15342
16437
  selectRef.current(anchor);
@@ -15345,7 +16440,7 @@ function OhhwellsBridge() {
15345
16440
  setNavGroupForceOpen(anchor, true);
15346
16441
  activateRef.current(label);
15347
16442
  }, []);
15348
- const handleAddChildItem = (0, import_react16.useCallback)(() => {
16443
+ const handleAddChildItem = (0, import_react17.useCallback)(() => {
15349
16444
  const selected = selectedElRef.current;
15350
16445
  if (!selected) return;
15351
16446
  const socialsRow = isSocialsRow(selected) ? selected : findSocialsRow(selected);
@@ -15457,7 +16552,7 @@ function OhhwellsBridge() {
15457
16552
  enterEditOnNewItem(result.anchor);
15458
16553
  });
15459
16554
  }, [enterEditOnNewItem, isFooterFrameSelection, maybeWarnNavLinkDropdownConflict, postToParent2]);
15460
- const handleAddFooterColumn = (0, import_react16.useCallback)(() => {
16555
+ const handleAddFooterColumn = (0, import_react17.useCallback)(() => {
15461
16556
  if (!canAddFooterColumn()) {
15462
16557
  postToParent2({
15463
16558
  type: "ow:toast",
@@ -15478,7 +16573,7 @@ function OhhwellsBridge() {
15478
16573
  selectRef.current(result.firstLink);
15479
16574
  });
15480
16575
  }, [postToParent2]);
15481
- const clearFooterDragVisuals = (0, import_react16.useCallback)(() => {
16576
+ const clearFooterDragVisuals = (0, import_react17.useCallback)(() => {
15482
16577
  footerDragRef.current = null;
15483
16578
  setSiblingHintRects([]);
15484
16579
  setFooterDropSlots([]);
@@ -15487,7 +16582,7 @@ function OhhwellsBridge() {
15487
16582
  setIsItemDragging(false);
15488
16583
  unlockFooterDragInteraction();
15489
16584
  }, []);
15490
- const refreshFooterDragVisuals = (0, import_react16.useCallback)((session, activeSlot, clientX, clientY) => {
16585
+ const refreshFooterDragVisuals = (0, import_react17.useCallback)((session, activeSlot, clientX, clientY) => {
15491
16586
  const dragged = session.draggedEl;
15492
16587
  setDraggedItemRect(dragged.getBoundingClientRect());
15493
16588
  if (typeof clientX === "number" && typeof clientY === "number") {
@@ -15519,13 +16614,13 @@ function OhhwellsBridge() {
15519
16614
  const activeIdx = activeSlot ? slots.findIndex((s) => s.insertIndex === activeSlot.insertIndex) : -1;
15520
16615
  setActiveFooterDropIndex(activeIdx >= 0 ? activeIdx : null);
15521
16616
  }, []);
15522
- const refreshFooterDragVisualsRef = (0, import_react16.useRef)(refreshFooterDragVisuals);
16617
+ const refreshFooterDragVisualsRef = (0, import_react17.useRef)(refreshFooterDragVisuals);
15523
16618
  refreshFooterDragVisualsRef.current = refreshFooterDragVisuals;
15524
- const commitFooterDragRef = (0, import_react16.useRef)(() => {
16619
+ const commitFooterDragRef = (0, import_react17.useRef)(() => {
15525
16620
  });
15526
- const beginFooterDragRef = (0, import_react16.useRef)(() => {
16621
+ const beginFooterDragRef = (0, import_react17.useRef)(() => {
15527
16622
  });
15528
- const beginFooterDrag = (0, import_react16.useCallback)(
16623
+ const beginFooterDrag = (0, import_react17.useCallback)(
15529
16624
  (session) => {
15530
16625
  const rect = session.draggedEl.getBoundingClientRect();
15531
16626
  session.lastClientX = session.lastClientX || rect.left + rect.width / 2;
@@ -15545,7 +16640,7 @@ function OhhwellsBridge() {
15545
16640
  [refreshFooterDragVisuals]
15546
16641
  );
15547
16642
  beginFooterDragRef.current = beginFooterDrag;
15548
- const commitFooterDrag = (0, import_react16.useCallback)(
16643
+ const commitFooterDrag = (0, import_react17.useCallback)(
15549
16644
  (clientX, clientY) => {
15550
16645
  const session = footerDragRef.current;
15551
16646
  if (!session) {
@@ -15673,7 +16768,7 @@ function OhhwellsBridge() {
15673
16768
  [clearFooterDragVisuals, resolveHrefKeyElement, resyncSelectedNavigationItem]
15674
16769
  );
15675
16770
  commitFooterDragRef.current = commitFooterDrag;
15676
- const startFooterLinkDrag = (0, import_react16.useCallback)(
16771
+ const startFooterLinkDrag = (0, import_react17.useCallback)(
15677
16772
  (anchor, clientX, clientY, wasSelected) => {
15678
16773
  const hrefKey = anchor.getAttribute("data-ohw-href-key");
15679
16774
  if (!hrefKey) return false;
@@ -15709,7 +16804,7 @@ function OhhwellsBridge() {
15709
16804
  },
15710
16805
  [beginFooterDrag]
15711
16806
  );
15712
- const startFooterColumnDrag = (0, import_react16.useCallback)(
16807
+ const startFooterColumnDrag = (0, import_react17.useCallback)(
15713
16808
  (columnEl, clientX, clientY, wasSelected) => {
15714
16809
  const columns = listFooterColumns();
15715
16810
  const idx = columns.indexOf(columnEl);
@@ -15729,7 +16824,7 @@ function OhhwellsBridge() {
15729
16824
  },
15730
16825
  [beginFooterDrag]
15731
16826
  );
15732
- const handleItemDragStart = (0, import_react16.useCallback)(
16827
+ const handleItemDragStart = (0, import_react17.useCallback)(
15733
16828
  (e) => {
15734
16829
  const selected = selectedElRef.current;
15735
16830
  if (!selected) {
@@ -15749,7 +16844,7 @@ function OhhwellsBridge() {
15749
16844
  },
15750
16845
  [startFooterColumnDrag, startFooterLinkDrag, startNavLinkDrag]
15751
16846
  );
15752
- const handleItemDragEnd = (0, import_react16.useCallback)(
16847
+ const handleItemDragEnd = (0, import_react17.useCallback)(
15753
16848
  (e) => {
15754
16849
  if (footerDragRef.current) {
15755
16850
  const x = e?.clientX;
@@ -15775,7 +16870,7 @@ function OhhwellsBridge() {
15775
16870
  },
15776
16871
  [commitFooterDrag, commitNavDrag, navDragRef]
15777
16872
  );
15778
- const handleItemChromePointerDown = (0, import_react16.useCallback)((e) => {
16873
+ const handleItemChromePointerDown = (0, import_react17.useCallback)((e) => {
15779
16874
  if (e.button !== 0) return;
15780
16875
  const selected = selectedElRef.current;
15781
16876
  if (!selected) return;
@@ -15806,7 +16901,7 @@ function OhhwellsBridge() {
15806
16901
  }
15807
16902
  if (armNavPressFromChrome(selected, e.clientX, e.clientY, e.pointerId)) return;
15808
16903
  }, [armNavPressFromChrome]);
15809
- const handleItemChromeClick = (0, import_react16.useCallback)((clientX, clientY) => {
16904
+ const handleItemChromeClick = (0, import_react17.useCallback)((clientX, clientY) => {
15810
16905
  if (suppressNextClickRef.current || Date.now() < suppressClickUntilRef.current) {
15811
16906
  suppressNextClickRef.current = false;
15812
16907
  return;
@@ -15819,7 +16914,7 @@ function OhhwellsBridge() {
15819
16914
  }, []);
15820
16915
  reselectNavigationItemRef.current = reselectNavigationItem;
15821
16916
  commitNavigationTextEditRef.current = commitNavigationTextEdit;
15822
- const select = (0, import_react16.useCallback)((anchor) => {
16917
+ const select = (0, import_react17.useCallback)((anchor) => {
15823
16918
  if (!isNavigationItem2(anchor)) return;
15824
16919
  if (activeElRef.current) deactivate();
15825
16920
  aiSectionApiRef.current?.selectFromElement(anchor);
@@ -15862,7 +16957,7 @@ function OhhwellsBridge() {
15862
16957
  setFloatingPanel(null);
15863
16958
  setLogoSizeDraft(null);
15864
16959
  }, [deactivate, markSelected]);
15865
- const selectFrame = (0, import_react16.useCallback)((el) => {
16960
+ const selectFrame = (0, import_react17.useCallback)((el) => {
15866
16961
  if (!isNavigationContainer(el)) return;
15867
16962
  if (activeElRef.current) deactivate();
15868
16963
  aiSectionApiRef.current?.selectFromElement(el);
@@ -15913,7 +17008,7 @@ function OhhwellsBridge() {
15913
17008
  setFloatingPanel(null);
15914
17009
  setLogoSizeDraft(null);
15915
17010
  }, [deactivate, markSelected, postToParent2]);
15916
- const selectLogo = (0, import_react16.useCallback)(
17011
+ const selectLogo = (0, import_react17.useCallback)(
15917
17012
  (logoEl) => {
15918
17013
  if (activeElRef.current) deactivate();
15919
17014
  selectedElRef.current = logoEl;
@@ -15942,7 +17037,7 @@ function OhhwellsBridge() {
15942
17037
  },
15943
17038
  [deactivate, markSelected]
15944
17039
  );
15945
- const openLogoSizePanel = (0, import_react16.useCallback)((logoEl) => {
17040
+ const openLogoSizePanel = (0, import_react17.useCallback)((logoEl) => {
15946
17041
  const placement = getLogoPlacement(logoEl);
15947
17042
  const draft = readLogoSizeState(editContentRef.current, placement);
15948
17043
  setLogoSizeDraft(draft);
@@ -15955,7 +17050,7 @@ function OhhwellsBridge() {
15955
17050
  placement
15956
17051
  });
15957
17052
  }, []);
15958
- const openSocialsDisplayPanel = (0, import_react16.useCallback)((row) => {
17053
+ const openSocialsDisplayPanel = (0, import_react17.useCallback)((row) => {
15959
17054
  setParentScrollSnap(parentScrollRef.current);
15960
17055
  setFloatingPanel({
15961
17056
  key: "socials-display",
@@ -15965,7 +17060,7 @@ function OhhwellsBridge() {
15965
17060
  row
15966
17061
  });
15967
17062
  }, []);
15968
- const changeSocialsDisplay = (0, import_react16.useCallback)(
17063
+ const changeSocialsDisplay = (0, import_react17.useCallback)(
15969
17064
  (row, next) => {
15970
17065
  if (next.icon) {
15971
17066
  const missing = socialsMissingIcons(row);
@@ -15988,17 +17083,17 @@ function OhhwellsBridge() {
15988
17083
  },
15989
17084
  []
15990
17085
  );
15991
- const closeFloatingPanelOnly = (0, import_react16.useCallback)(() => {
17086
+ const closeFloatingPanelOnly = (0, import_react17.useCallback)(() => {
15992
17087
  setFloatingPanel(null);
15993
17088
  setLogoSizeDraft(null);
15994
17089
  }, []);
15995
17090
  closeFloatingPanelOnlyRef.current = closeFloatingPanelOnly;
15996
- const closeFloatingPanelAndDeselect = (0, import_react16.useCallback)(() => {
17091
+ const closeFloatingPanelAndDeselect = (0, import_react17.useCallback)(() => {
15997
17092
  setFloatingPanel(null);
15998
17093
  setLogoSizeDraft(null);
15999
17094
  deselectRef.current();
16000
17095
  }, []);
16001
- (0, import_react16.useEffect)(() => {
17096
+ (0, import_react17.useEffect)(() => {
16002
17097
  const session = floatingPanel && floatingPanel.kind === "logo-size" ? floatingPanel : null;
16003
17098
  if (!session || !logoSizeDraft) {
16004
17099
  postToParentRef.current({ type: "ow:logo-size-panel", open: false });
@@ -16017,7 +17112,7 @@ function OhhwellsBridge() {
16017
17112
  max: LOGO_SIZE_MAX
16018
17113
  });
16019
17114
  }, [floatingPanel, logoSizeDraft, editorViewport]);
16020
- const persistLogoSizeDraft = (0, import_react16.useCallback)(
17115
+ const persistLogoSizeDraft = (0, import_react17.useCallback)(
16021
17116
  (placement, draft) => {
16022
17117
  const desktopKey = LOGO_SIZE_DESKTOP_KEYS[placement];
16023
17118
  const mobileKey = LOGO_SIZE_MOBILE_KEYS[placement];
@@ -16057,7 +17152,7 @@ function OhhwellsBridge() {
16057
17152
  },
16058
17153
  [postToParent2]
16059
17154
  );
16060
- const activate = (0, import_react16.useCallback)((el, options) => {
17155
+ const activate = (0, import_react17.useCallback)((el, options) => {
16061
17156
  if (activeElRef.current === el) return;
16062
17157
  if (isIconEditable(el)) return;
16063
17158
  if (el.hasAttribute("data-ohw-social-label")) return;
@@ -16141,8 +17236,8 @@ function OhhwellsBridge() {
16141
17236
  openLogoSizePanelRef.current = openLogoSizePanel;
16142
17237
  deselectRef.current = deselect;
16143
17238
  closeFloatingPanelOnlyRef.current = closeFloatingPanelOnly;
16144
- const lastSiteWideScopeRef = (0, import_react16.useRef)(null);
16145
- (0, import_react16.useEffect)(() => {
17239
+ const lastSiteWideScopeRef = (0, import_react17.useRef)(null);
17240
+ (0, import_react17.useEffect)(() => {
16146
17241
  if (!isEditMode) {
16147
17242
  if (lastSiteWideScopeRef.current !== false) {
16148
17243
  lastSiteWideScopeRef.current = false;
@@ -16168,22 +17263,34 @@ function OhhwellsBridge() {
16168
17263
  isFooterFrameSelection,
16169
17264
  postToParent2
16170
17265
  ]);
16171
- (0, import_react16.useLayoutEffect)(() => {
17266
+ (0, import_react17.useLayoutEffect)(() => {
16172
17267
  if (!subdomain || isEditMode) {
16173
17268
  setFetchState("done");
16174
17269
  return;
16175
17270
  }
16176
17271
  const applyContent = (content) => {
16177
17272
  const imageLoads = [];
17273
+ if (typeof content[BRAND_KIT_KEY] === "string") {
17274
+ brandKitRef.current = content[BRAND_KIT_KEY];
17275
+ applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
17276
+ }
16178
17277
  if (typeof content[AI_SECTIONS_KEY] === "string") {
16179
17278
  aiSectionsRef.current = content[AI_SECTIONS_KEY];
16180
17279
  applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
16181
17280
  }
17281
+ if (typeof content[STYLE_STORE_KEY] === "string") {
17282
+ stylesRef.current = content[STYLE_STORE_KEY];
17283
+ applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
17284
+ }
17285
+ applyBrandChrome(content);
16182
17286
  for (const [key, val] of Object.entries(content)) {
16183
17287
  if (key === "__ohw_sections") continue;
16184
17288
  if (key === AI_SECTIONS_KEY) continue;
16185
17289
  if (key === LOGO_PLACEHOLDER_KEY) continue;
16186
17290
  if (LOGO_IMAGE_KEYS.includes(key)) continue;
17291
+ if (key === BRAND_KIT_KEY) continue;
17292
+ if (key === STYLE_STORE_KEY) continue;
17293
+ if (BRAND_CHROME_KEYS.has(key)) continue;
16187
17294
  if (applyVideoSettingNode(key, val)) continue;
16188
17295
  if (applyCarouselNode(key, val)) continue;
16189
17296
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -16241,7 +17348,9 @@ function OhhwellsBridge() {
16241
17348
  let cancelled = false;
16242
17349
  setFetchState("loading");
16243
17350
  const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
16244
- fetch(`${apiUrl}/api/public/sites/${subdomain}/content`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
17351
+ const initialPath = pathname;
17352
+ fetchedContentPaths.add(`${subdomain}::${initialPath}`);
17353
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
16245
17354
  if (cancelled) return;
16246
17355
  const content = data?.content ?? {};
16247
17356
  contentCache.set(subdomain, content);
@@ -16254,7 +17363,7 @@ function OhhwellsBridge() {
16254
17363
  cancelled = true;
16255
17364
  };
16256
17365
  }, [subdomain, isEditMode]);
16257
- (0, import_react16.useEffect)(() => {
17366
+ (0, import_react17.useEffect)(() => {
16258
17367
  if (!isEditMode) return;
16259
17368
  const resolveIndex = (form, clientY) => {
16260
17369
  const wrappers = listFieldWrappers(form).filter((el) => fieldKeyOf(el) !== fieldDragRef.current?.key);
@@ -16295,7 +17404,7 @@ function OhhwellsBridge() {
16295
17404
  window.removeEventListener("drop", onDrop, true);
16296
17405
  };
16297
17406
  }, [buildFieldDropSlots, isEditMode, persistFields, selectField]);
16298
- (0, import_react16.useEffect)(() => {
17407
+ (0, import_react17.useEffect)(() => {
16299
17408
  if (!isEditMode) return;
16300
17409
  const mark = () => document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
16301
17410
  markFormFields(form);
@@ -16307,7 +17416,7 @@ function OhhwellsBridge() {
16307
17416
  });
16308
17417
  return () => observer.disconnect();
16309
17418
  }, [isEditMode, fetchState, pathname]);
16310
- (0, import_react16.useEffect)(() => {
17419
+ (0, import_react17.useEffect)(() => {
16311
17420
  if (!isEditMode) return;
16312
17421
  let saveTimer = null;
16313
17422
  const onInput = (e) => {
@@ -16329,14 +17438,14 @@ function OhhwellsBridge() {
16329
17438
  document.addEventListener("input", onInput, true);
16330
17439
  return () => document.removeEventListener("input", onInput, true);
16331
17440
  }, [isEditMode, persistFields]);
16332
- (0, import_react16.useEffect)(() => {
17441
+ (0, import_react17.useEffect)(() => {
16333
17442
  if (isEditMode || fetchState !== "done") return;
16334
17443
  const content = contentCache.get(subdomain) ?? {};
16335
17444
  document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
16336
17445
  reconcileFieldsFromContent(form, content);
16337
17446
  });
16338
17447
  }, [isEditMode, fetchState, subdomain]);
16339
- (0, import_react16.useEffect)(() => {
17448
+ (0, import_react17.useEffect)(() => {
16340
17449
  if (!isEditMode) return;
16341
17450
  const swallow = (e) => {
16342
17451
  const target = e.target;
@@ -16345,12 +17454,12 @@ function OhhwellsBridge() {
16345
17454
  document.addEventListener("submit", swallow, true);
16346
17455
  return () => document.removeEventListener("submit", swallow, true);
16347
17456
  }, [isEditMode]);
16348
- (0, import_react16.useEffect)(() => {
17457
+ (0, import_react17.useEffect)(() => {
16349
17458
  if (isEditMode || fetchState !== "done") return;
16350
17459
  const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
16351
17460
  bindPublishedForms(apiUrl, subdomain, contentCache.get(subdomain) ?? {});
16352
17461
  }, [isEditMode, fetchState, subdomain]);
16353
- (0, import_react16.useEffect)(() => {
17462
+ (0, import_react17.useEffect)(() => {
16354
17463
  if (!subdomain || isEditMode) return;
16355
17464
  let debounceTimer = null;
16356
17465
  let observer = null;
@@ -16361,10 +17470,21 @@ function OhhwellsBridge() {
16361
17470
  initSectionInstancesFromContent(content, window.location.pathname);
16362
17471
  observer?.disconnect();
16363
17472
  try {
17473
+ applyBrandChrome(content);
17474
+ if (typeof content[BRAND_KIT_KEY] === "string") {
17475
+ applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
17476
+ }
17477
+ if (typeof content[STYLE_STORE_KEY] === "string") {
17478
+ applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
17479
+ }
16364
17480
  for (const [key, val] of Object.entries(content)) {
16365
17481
  if (key === "__ohw_sections") continue;
16366
17482
  if (key === LOGO_PLACEHOLDER_KEY) continue;
16367
17483
  if (LOGO_IMAGE_KEYS.includes(key)) continue;
17484
+ if (key === BRAND_KIT_KEY) continue;
17485
+ if (key === STYLE_STORE_KEY) continue;
17486
+ if (key === STYLE_STORE_KEY) continue;
17487
+ if (BRAND_CHROME_KEYS.has(key)) continue;
16368
17488
  if (applyVideoSettingNode(key, val)) continue;
16369
17489
  if (applyCarouselNode(key, val)) continue;
16370
17490
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -16407,6 +17527,17 @@ function OhhwellsBridge() {
16407
17527
  debounceTimer = setTimeout(applyFromCache, 150);
16408
17528
  };
16409
17529
  applyFromCache();
17530
+ const pathCacheKey = `${subdomain}::${pathname}`;
17531
+ if (!fetchedContentPaths.has(pathCacheKey)) {
17532
+ fetchedContentPaths.add(pathCacheKey);
17533
+ const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
17534
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(pathname)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
17535
+ if (!data?.content) return;
17536
+ contentCache.set(subdomain, data.content);
17537
+ applyFromCache();
17538
+ }).catch(() => {
17539
+ });
17540
+ }
16410
17541
  observer = new MutationObserver(scheduleApply);
16411
17542
  observer.observe(document.body, { childList: true, subtree: true });
16412
17543
  return () => {
@@ -16414,16 +17545,16 @@ function OhhwellsBridge() {
16414
17545
  if (debounceTimer) clearTimeout(debounceTimer);
16415
17546
  };
16416
17547
  }, [subdomain, isEditMode, pathname]);
16417
- (0, import_react16.useLayoutEffect)(() => {
17548
+ (0, import_react17.useLayoutEffect)(() => {
16418
17549
  const el = document.getElementById("ohw-loader");
16419
17550
  if (!el) return;
16420
17551
  const visible = Boolean(subdomain) && fetchState !== "done";
16421
17552
  el.style.display = visible ? "flex" : "none";
16422
17553
  }, [subdomain, fetchState]);
16423
- (0, import_react16.useEffect)(() => {
17554
+ (0, import_react17.useEffect)(() => {
16424
17555
  postToParent2({ type: "ow:navigation", path: pathname });
16425
17556
  }, [pathname, postToParent2]);
16426
- (0, import_react16.useEffect)(() => {
17557
+ (0, import_react17.useEffect)(() => {
16427
17558
  if (!isEditMode) return;
16428
17559
  if (linkPopoverSessionRef.current?.intent === "add-nav") return;
16429
17560
  if (document.querySelector("[data-ohw-section-picker]")) return;
@@ -16431,7 +17562,7 @@ function OhhwellsBridge() {
16431
17562
  deselectRef.current();
16432
17563
  deactivateRef.current();
16433
17564
  }, [pathname, isEditMode]);
16434
- (0, import_react16.useEffect)(() => {
17565
+ (0, import_react17.useEffect)(() => {
16435
17566
  const contentForNav = () => {
16436
17567
  if (isEditMode) return editContentRef.current;
16437
17568
  if (!subdomain) return {};
@@ -16498,31 +17629,36 @@ function OhhwellsBridge() {
16498
17629
  observer?.disconnect();
16499
17630
  };
16500
17631
  }, [isEditMode, pathname, subdomain, fetchState, resyncSelectedNavigationItem]);
16501
- (0, import_react16.useEffect)(() => {
17632
+ (0, import_react17.useEffect)(() => {
16502
17633
  if (!isEditMode) return;
17634
+ let lastPosted = 0;
16503
17635
  const measure = () => {
16504
17636
  const h = document.body.scrollHeight;
16505
- if (h > 50) postToParent2({ type: "ow:height", height: h });
17637
+ if (h > 50 && Math.abs(h - lastPosted) > 1) {
17638
+ lastPosted = h;
17639
+ postToParent2({ type: "ow:height", height: h });
17640
+ }
17641
+ };
17642
+ let raf = null;
17643
+ const schedule = () => {
17644
+ if (raf != null) return;
17645
+ raf = requestAnimationFrame(() => {
17646
+ raf = null;
17647
+ measure();
17648
+ });
16506
17649
  };
16507
17650
  const t1 = setTimeout(measure, 50);
16508
17651
  const t2 = setTimeout(measure, 500);
16509
- let lastWidth = window.innerWidth;
16510
- let resizeTimer = null;
16511
- const handleResize = () => {
16512
- if (window.innerWidth === lastWidth) return;
16513
- lastWidth = window.innerWidth;
16514
- if (resizeTimer) clearTimeout(resizeTimer);
16515
- resizeTimer = setTimeout(measure, 150);
16516
- };
16517
- window.addEventListener("resize", handleResize);
17652
+ const ro = new ResizeObserver(schedule);
17653
+ ro.observe(document.body);
16518
17654
  return () => {
16519
17655
  clearTimeout(t1);
16520
17656
  clearTimeout(t2);
16521
- if (resizeTimer) clearTimeout(resizeTimer);
16522
- window.removeEventListener("resize", handleResize);
17657
+ if (raf != null) cancelAnimationFrame(raf);
17658
+ ro.disconnect();
16523
17659
  };
16524
17660
  }, [pathname, isEditMode, postToParent2]);
16525
- (0, import_react16.useEffect)(() => {
17661
+ (0, import_react17.useEffect)(() => {
16526
17662
  if (!subdomainFromQuery || isEditMode) return;
16527
17663
  const handleClick = (e) => {
16528
17664
  const anchor = e.target.closest("a");
@@ -16538,7 +17674,7 @@ function OhhwellsBridge() {
16538
17674
  document.addEventListener("click", handleClick, true);
16539
17675
  return () => document.removeEventListener("click", handleClick, true);
16540
17676
  }, [subdomainFromQuery, isEditMode, router]);
16541
- (0, import_react16.useEffect)(() => {
17677
+ (0, import_react17.useEffect)(() => {
16542
17678
  if (!isEditMode) {
16543
17679
  editStylesRef.current?.base.remove();
16544
17680
  editStylesRef.current?.forceHover.remove();
@@ -16700,16 +17836,21 @@ function OhhwellsBridge() {
16700
17836
  return;
16701
17837
  }
16702
17838
  const target = e.target;
17839
+ if (target.closest("[data-ohw-ai-review]")) return;
16703
17840
  if (target.closest("[data-ohw-toolbar]")) return;
16704
17841
  if (target.closest("[data-ohw-state-toggle]")) return;
16705
17842
  if (target.closest("[data-ohw-max-badge]")) return;
16706
17843
  if (isInsideLinkEditor(target)) return;
17844
+ if (isInsideFloatingPanel(target)) return;
16707
17845
  if (target.closest("[data-ohw-form-toolbar]")) return;
16708
17846
  if (target.closest(
16709
17847
  '[data-ohw-field-toolbar], [data-ohw-field-type-picker], [data-radix-popper-content-wrapper], [role="menu"], [data-slot="dropdown-menu-content"]'
16710
17848
  )) {
16711
17849
  return;
16712
17850
  }
17851
+ if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
17852
+ clearMediaSelectionRef.current();
17853
+ }
16713
17854
  {
16714
17855
  const formEl = getFormElement(target);
16715
17856
  const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
@@ -16857,8 +17998,11 @@ function OhhwellsBridge() {
16857
17998
  if (isMediaEditable(editable) && !buttonOnMedia) {
16858
17999
  e.preventDefault();
16859
18000
  e.stopPropagation();
16860
- aiSectionApiRef.current?.selectFromElement(editable);
16861
- postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "", elementType: editable.dataset.ohwEditable ?? "image" });
18001
+ if (selectedMediaElRef.current === editable) {
18002
+ postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "", elementType: editable.dataset.ohwEditable ?? "image" });
18003
+ } else {
18004
+ selectMediaElementRef.current(editable);
18005
+ }
16862
18006
  return;
16863
18007
  }
16864
18008
  const hrefLookupTarget = buttonOnMedia ? clickedButton : editable;
@@ -16977,10 +18121,12 @@ function OhhwellsBridge() {
16977
18121
  };
16978
18122
  const handleDblClick = (e) => {
16979
18123
  const target = e.target;
18124
+ if (target.closest("[data-ohw-ai-review]")) return;
16980
18125
  if (target.closest("[data-ohw-toolbar]")) return;
16981
18126
  if (target.closest("[data-ohw-state-toggle]")) return;
16982
18127
  if (target.closest("[data-ohw-max-badge]")) return;
16983
18128
  if (isInsideLinkEditor(target)) return;
18129
+ if (isInsideFloatingPanel(target)) return;
16984
18130
  if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
16985
18131
  return;
16986
18132
  }
@@ -17021,11 +18167,14 @@ function OhhwellsBridge() {
17021
18167
  setSiblingHintRects([]);
17022
18168
  return;
17023
18169
  }
17024
- if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || target.closest("[data-ohw-floating-panel]")) {
18170
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || isInsideFloatingPanel(target) || isPointOverFloatingPanel(e.clientX, e.clientY)) {
17025
18171
  hoveredItemElRef.current = null;
17026
18172
  setHoveredItemRect(null);
17027
18173
  hoveredNavContainerRef.current = null;
17028
18174
  setHoveredNavContainerRect(null);
18175
+ siblingHintElRef.current = null;
18176
+ setSiblingHintRect(null);
18177
+ setSiblingHintRects([]);
17029
18178
  return;
17030
18179
  }
17031
18180
  {
@@ -17043,6 +18192,16 @@ function OhhwellsBridge() {
17043
18192
  return;
17044
18193
  }
17045
18194
  }
18195
+ if (allowNavContainerHover) {
18196
+ const socialsRow = isSocialsRow(target) ? target : null;
18197
+ if (socialsRow && !getSocialItem(target) && selected2 !== socialsRow) {
18198
+ hoveredNavContainerRef.current = socialsRow;
18199
+ setHoveredNavContainerRect(socialsRow.getBoundingClientRect());
18200
+ hoveredItemElRef.current = null;
18201
+ setHoveredItemRect(null);
18202
+ return;
18203
+ }
18204
+ }
17046
18205
  if (allowFooterLinksHover) {
17047
18206
  const navContainer = target.closest("[data-ohw-nav-container]");
17048
18207
  if (!navContainer) {
@@ -17128,13 +18287,12 @@ function OhhwellsBridge() {
17128
18287
  clearHrefKeyHover(hoverTarget);
17129
18288
  hoveredItemElRef.current = hoverTarget;
17130
18289
  setHoveredItemRect(hoverTarget.getBoundingClientRect());
17131
- } else if (!isInsideNavigationItem(editable)) {
18290
+ } else if (!isInsideNavigationItem(editable) && hoverTarget !== selectedElRef.current) {
17132
18291
  hoverTarget.setAttribute("data-ohw-hovered", "");
17133
18292
  if (editable.closest("footer") || editable.closest('[data-ohw-section="footer"]')) {
17134
18293
  hoveredNavContainerRef.current = null;
17135
18294
  setHoveredNavContainerRect(null);
17136
18295
  hoveredItemElRef.current = editable;
17137
- setHoveredItemRect(editable.getBoundingClientRect());
17138
18296
  }
17139
18297
  }
17140
18298
  }
@@ -17431,7 +18589,7 @@ function OhhwellsBridge() {
17431
18589
  }
17432
18590
  };
17433
18591
  const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
17434
- if (linkPopoverOpenRef.current) {
18592
+ if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
17435
18593
  if (hoveredImageRef.current) {
17436
18594
  hoveredImageRef.current = null;
17437
18595
  hoveredImageHasTextOverlapRef.current = false;
@@ -17690,7 +18848,7 @@ function OhhwellsBridge() {
17690
18848
  }
17691
18849
  };
17692
18850
  const probeHoverCardsAt = (clientX, clientY, fromParentViewport = false) => {
17693
- if (linkPopoverOpenRef.current || document.documentElement.hasAttribute("data-ohw-section-picking")) {
18851
+ if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY) || document.documentElement.hasAttribute("data-ohw-section-picking")) {
17694
18852
  if (activeStateElRef.current) {
17695
18853
  activeStateElRef.current.removeAttribute("data-ohw-state-hovered");
17696
18854
  activeStateElRef.current = null;
@@ -17727,6 +18885,35 @@ function OhhwellsBridge() {
17727
18885
  }
17728
18886
  }
17729
18887
  };
18888
+ const probeSocialsRowAt = (clientX, clientY, fromParentViewport = false) => {
18889
+ const wasSocialsRow = Boolean(hoveredNavContainerRef.current?.hasAttribute(SOCIALS_ROW_ATTR));
18890
+ const clear = () => {
18891
+ if (!wasSocialsRow) return false;
18892
+ hoveredNavContainerRef.current = null;
18893
+ setHoveredNavContainerRect(null);
18894
+ return false;
18895
+ };
18896
+ if (linkPopoverOpenRef.current || toolbarVariantRef.current === "select-frame") return clear();
18897
+ const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
18898
+ const SLACK = 6;
18899
+ for (const row of Array.from(document.querySelectorAll(`[${SOCIALS_ROW_ATTR}]`))) {
18900
+ const rect = row.getBoundingClientRect();
18901
+ const inside = x >= rect.left - SLACK && x <= rect.right + SLACK && y >= rect.top - SLACK && y <= rect.bottom + SLACK;
18902
+ if (!inside) continue;
18903
+ if (selectedElRef.current === row) return clear();
18904
+ const overItem = listSocialItems(row).some((item) => {
18905
+ const box = item.getBoundingClientRect();
18906
+ return x >= box.left && x <= box.right && y >= box.top && y <= box.bottom;
18907
+ });
18908
+ if (overItem) return clear();
18909
+ hoveredNavContainerRef.current = row;
18910
+ setHoveredNavContainerRect(rect);
18911
+ hoveredItemElRef.current = null;
18912
+ setHoveredItemRect(null);
18913
+ return true;
18914
+ }
18915
+ return clear();
18916
+ };
17730
18917
  const probeSectionGapAt = (clientX, clientY, fromParentViewport = false) => {
17731
18918
  if (linkPopoverOpenRef.current) {
17732
18919
  if (hoveredGapRef.current) {
@@ -17736,7 +18923,9 @@ function OhhwellsBridge() {
17736
18923
  return;
17737
18924
  }
17738
18925
  const { y } = toProbeCoords(clientX, clientY, fromParentViewport);
17739
- const sections = Array.from(document.querySelectorAll("[data-ohw-section]")).sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);
18926
+ const sections = Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
18927
+ (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
18928
+ ).sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);
17740
18929
  const ZONE = 20;
17741
18930
  for (let i = 0; i < sections.length; i++) {
17742
18931
  const a = sections[i];
@@ -17765,8 +18954,7 @@ function OhhwellsBridge() {
17765
18954
  };
17766
18955
  const handleMouseMove = (e) => {
17767
18956
  const { clientX, clientY } = e;
17768
- if (pointOwnedByFloatingPanel(clientX, clientY)) return;
17769
- if (isOverEditorChrome(clientX, clientY)) {
18957
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY) || isOverEditorChrome(clientX, clientY)) {
17770
18958
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
17771
18959
  formHoverElRef.current = null;
17772
18960
  setFormHoverRect(null);
@@ -17774,8 +18962,15 @@ function OhhwellsBridge() {
17774
18962
  setHoveredItemRect(null);
17775
18963
  hoveredNavContainerRef.current = null;
17776
18964
  setHoveredNavContainerRect(null);
18965
+ siblingHintElRef.current = null;
18966
+ setSiblingHintRect(null);
18967
+ setSiblingHintRects([]);
18968
+ dismissImageHover();
18969
+ clearImageHover();
18970
+ setSectionGap(null);
17777
18971
  return;
17778
18972
  }
18973
+ if (probeSocialsRowAt(clientX, clientY)) return;
17779
18974
  probeSectionGapAt(clientX, clientY);
17780
18975
  probeImageAt(clientX, clientY);
17781
18976
  probeHoverCardsAt(clientX, clientY);
@@ -17784,7 +18979,12 @@ function OhhwellsBridge() {
17784
18979
  if (e.data?.type !== "ow:pointer-sync") return;
17785
18980
  const { clientX, clientY } = e.data;
17786
18981
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
17787
- if (pointOwnedByFloatingPanel(clientX, clientY)) return;
18982
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
18983
+ dismissImageHover();
18984
+ clearImageHover();
18985
+ return;
18986
+ }
18987
+ if (probeSocialsRowAt(clientX, clientY)) return;
17788
18988
  probeSectionGapAt(clientX, clientY);
17789
18989
  probeImageAt(clientX, clientY);
17790
18990
  probeHoverCardsAt(clientX, clientY);
@@ -18030,10 +19230,19 @@ function OhhwellsBridge() {
18030
19230
  if (e.data?.type !== "ow:hydrate") return;
18031
19231
  const content = e.data.content;
18032
19232
  if (!content) return;
19233
+ if (typeof content[BRAND_KIT_KEY] === "string") {
19234
+ brandKitRef.current = content[BRAND_KIT_KEY];
19235
+ applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
19236
+ }
18033
19237
  if (typeof content[AI_SECTIONS_KEY] === "string") {
18034
19238
  aiSectionsRef.current = content[AI_SECTIONS_KEY];
18035
19239
  applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
18036
19240
  }
19241
+ if (typeof content[STYLE_STORE_KEY] === "string") {
19242
+ stylesRef.current = content[STYLE_STORE_KEY];
19243
+ applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
19244
+ }
19245
+ applyBrandChrome(content);
18037
19246
  let sectionsJson = null;
18038
19247
  for (const [key, val] of Object.entries(content)) {
18039
19248
  if (key === "__ohw_sections") {
@@ -18043,6 +19252,9 @@ function OhhwellsBridge() {
18043
19252
  if (key === AI_SECTIONS_KEY) continue;
18044
19253
  if (key === LOGO_PLACEHOLDER_KEY) continue;
18045
19254
  if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
19255
+ if (key === BRAND_KIT_KEY) continue;
19256
+ if (key === STYLE_STORE_KEY) continue;
19257
+ if (BRAND_CHROME_KEYS.has(key)) continue;
18046
19258
  if (applyVideoSettingNode(key, val)) continue;
18047
19259
  if (applyCarouselNode(key, val)) continue;
18048
19260
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -18056,6 +19268,8 @@ function OhhwellsBridge() {
18056
19268
  if (video && video.src !== val) applyVideoSrc(video, val);
18057
19269
  } else if (el.dataset.ohwEditable === "link") {
18058
19270
  applyLinkHref(el, val);
19271
+ } else if (el.dataset.ohwEditable === "icon") {
19272
+ applyIconMarkup(el, val);
18059
19273
  } else {
18060
19274
  el.innerHTML = val;
18061
19275
  }
@@ -18139,12 +19353,21 @@ function OhhwellsBridge() {
18139
19353
  nodes: collectEditableNodes(editContentRef.current)
18140
19354
  });
18141
19355
  };
19356
+ const clearInteractionChrome = () => {
19357
+ deactivateRef.current();
19358
+ deselectRef.current();
19359
+ clearMediaSelectionRef.current();
19360
+ };
18142
19361
  const handleAiApplyTree = (e) => {
18143
19362
  if (e.data?.type !== "ow:ai-apply-tree") return;
18144
19363
  const payload = e.data.payload;
18145
19364
  if (!payload || typeof payload.id !== "string" || !isRenderableTree(payload.tree)) return;
19365
+ clearInteractionChrome();
18146
19366
  const previous = aiSectionsRef.current;
18147
- const nextState = applyTreeToState(parseAiSectionsState(previous), payload);
19367
+ const nextState = applyTreeToState(parseAiSectionsState(previous), {
19368
+ ...payload,
19369
+ path: payload.path ?? window.location.pathname
19370
+ });
18148
19371
  const nextValue = serializeAiSectionsState(nextState);
18149
19372
  aiSectionsRef.current = nextValue;
18150
19373
  applyAiSectionsToDom(nextState);
@@ -18164,6 +19387,7 @@ function OhhwellsBridge() {
18164
19387
  if (!sectionId || sectionId === "navbar" || sectionId === "footer") return;
18165
19388
  const exists = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
18166
19389
  if (!exists) return;
19390
+ clearInteractionChrome();
18167
19391
  const previous = aiSectionsRef.current;
18168
19392
  const nextState = deleteSectionFromState(parseAiSectionsState(previous), sectionId);
18169
19393
  const nextValue = serializeAiSectionsState(nextState);
@@ -18179,14 +19403,58 @@ function OhhwellsBridge() {
18179
19403
  const handleAiSetSections = (e) => {
18180
19404
  if (e.data?.type !== "ow:ai-set-sections") return;
18181
19405
  const value = typeof e.data.value === "string" ? e.data.value : "";
19406
+ clearInteractionChrome();
18182
19407
  aiSectionsRef.current = value;
18183
19408
  applyAiSectionsToDom(parseAiSectionsState(value));
19409
+ applyStylesToDom(parseStyleStore(stylesRef.current));
18184
19410
  const restoredHeight = document.documentElement.scrollHeight;
18185
19411
  if (restoredHeight > 50) postToParentRef.current({ type: "ow:height", height: restoredHeight });
18186
19412
  postToParentRef.current({ type: "ow:change", nodes: [{ key: AI_SECTIONS_KEY, text: value }] });
18187
19413
  postAiSectionsChanged();
18188
19414
  };
18189
19415
  window.addEventListener("message", handleAiSetSections);
19416
+ const handleMoveSection = (e) => {
19417
+ if (e.data?.type !== "ow:move-section") return;
19418
+ const instanceId = typeof e.data.instanceId === "string" ? e.data.instanceId : "";
19419
+ const direction = e.data.direction === "up" || e.data.direction === "down" ? e.data.direction : null;
19420
+ if (!instanceId || !direction) return;
19421
+ const entries = moveSectionInstance(instanceId, direction, window.location.pathname);
19422
+ if (!entries) return;
19423
+ const orderJson = JSON.stringify(entries);
19424
+ editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
19425
+ postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
19426
+ window.dispatchEvent(new Event("resize"));
19427
+ };
19428
+ window.addEventListener("message", handleMoveSection);
19429
+ const handleAiSetBrand = (e) => {
19430
+ if (e.data?.type !== "ow:ai-set-brand") return;
19431
+ const value = typeof e.data.value === "string" ? e.data.value : "";
19432
+ const previous = brandKitRef.current;
19433
+ brandKitRef.current = value;
19434
+ applyBrandToDom(parseBrandKit(value));
19435
+ if (aiSectionsRef.current) applyAiSectionsToDom(parseAiSectionsState(aiSectionsRef.current));
19436
+ applyStylesToDom(parseStyleStore(stylesRef.current));
19437
+ postToParentRef.current({ type: "ow:change", nodes: [{ key: BRAND_KIT_KEY, text: value }] });
19438
+ postToParentRef.current({ type: "ow:ai-brand-applied", previous, value });
19439
+ };
19440
+ window.addEventListener("message", handleAiSetBrand);
19441
+ const handleAiSetStyles = (e) => {
19442
+ if (e.data?.type !== "ow:ai-set-styles") return;
19443
+ const value = typeof e.data.value === "string" ? e.data.value : "";
19444
+ const previous = stylesRef.current;
19445
+ stylesRef.current = value;
19446
+ applyStylesToDom(parseStyleStore(value));
19447
+ postToParentRef.current({ type: "ow:change", nodes: [{ key: STYLE_STORE_KEY, text: value }] });
19448
+ postToParentRef.current({ type: "ow:ai-styles-applied", previous, value });
19449
+ };
19450
+ window.addEventListener("message", handleAiSetStyles);
19451
+ const handleGetBrand = (e) => {
19452
+ if (e.data?.type !== "ow:get-brand") return;
19453
+ const template = deriveTemplateBrand();
19454
+ const value = brandKitRef.current || (template ? JSON.stringify(template) : "");
19455
+ postToParentRef.current({ type: "ow:brand-value", value });
19456
+ };
19457
+ window.addEventListener("message", handleGetBrand);
18190
19458
  const handlePanelDragging = (e) => {
18191
19459
  if (e.data?.type !== "ow:panel-dragging") return;
18192
19460
  if (e.data.dragging) document.documentElement.setAttribute("data-ohw-panel-dragging", "");
@@ -18202,8 +19470,15 @@ function OhhwellsBridge() {
18202
19470
  closeLinkPopoverRef.current();
18203
19471
  return;
18204
19472
  }
19473
+ if (floatingPanelOpenRef.current) {
19474
+ setFloatingPanelRef.current(null);
19475
+ deselectRef.current();
19476
+ deactivateRef.current();
19477
+ return;
19478
+ }
18205
19479
  deselectRef.current();
18206
19480
  deactivateRef.current();
19481
+ clearMediaSelectionRef.current();
18207
19482
  };
18208
19483
  window.addEventListener("message", handleDeactivate);
18209
19484
  const handleToastAction = (e) => {
@@ -18289,6 +19564,10 @@ function OhhwellsBridge() {
18289
19564
  const handleKeyDown = (e) => {
18290
19565
  if (e.key === "Escape" && document.querySelector("[data-ohw-section-picker]")) return;
18291
19566
  if (e.key === "Escape" && document.querySelector("[data-ohw-more-menu]")) return;
19567
+ if (e.key === "Escape" && selectedMediaElRef.current) {
19568
+ clearMediaSelectionRef.current();
19569
+ return;
19570
+ }
18292
19571
  if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "a" && activeElRef.current) {
18293
19572
  e.preventDefault();
18294
19573
  selectAllTextInEditable(activeElRef.current);
@@ -18448,6 +19727,12 @@ function OhhwellsBridge() {
18448
19727
  if (aiSectionsRef.current) {
18449
19728
  nodes.push({ key: AI_SECTIONS_KEY, type: "sections", text: aiSectionsRef.current });
18450
19729
  }
19730
+ if (stylesRef.current) {
19731
+ nodes.push({ key: STYLE_STORE_KEY, type: "styles", text: stylesRef.current });
19732
+ }
19733
+ if (brandKitRef.current) {
19734
+ nodes.push({ key: BRAND_KIT_KEY, type: "brand", text: brandKitRef.current });
19735
+ }
18451
19736
  document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
18452
19737
  const formKey = formKeyOf(form);
18453
19738
  if (!formKey) return;
@@ -18465,8 +19750,12 @@ function OhhwellsBridge() {
18465
19750
  if (inserted) {
18466
19751
  const tracker = getSectionsTracker();
18467
19752
  postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
18468
- const h = document.documentElement.scrollHeight;
18469
- if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
19753
+ const reportHeight = () => {
19754
+ const h = document.body.scrollHeight;
19755
+ if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
19756
+ };
19757
+ reportHeight();
19758
+ setTimeout(reportHeight, 500);
18470
19759
  }
18471
19760
  };
18472
19761
  const handleSwitchSchedule = (e) => {
@@ -18853,19 +20142,23 @@ function OhhwellsBridge() {
18853
20142
  window.removeEventListener("message", handleAiApplyTree);
18854
20143
  window.removeEventListener("message", handleAiDeleteSection);
18855
20144
  window.removeEventListener("message", handleAiSetSections);
20145
+ window.removeEventListener("message", handleMoveSection);
20146
+ window.removeEventListener("message", handleAiSetBrand);
20147
+ window.removeEventListener("message", handleAiSetStyles);
20148
+ window.removeEventListener("message", handleGetBrand);
18856
20149
  window.removeEventListener("message", handlePanelDragging);
18857
20150
  window.removeEventListener("message", handleDeactivate);
18858
- document.documentElement.removeAttribute("data-ohw-panel-dragging");
18859
20151
  window.removeEventListener("message", handleToastAction);
18860
20152
  window.removeEventListener("message", handleFormCount);
18861
20153
  window.removeEventListener("message", handleUiEscape);
20154
+ document.documentElement.removeAttribute("data-ohw-panel-dragging");
18862
20155
  autoSaveTimers.current.forEach(clearTimeout);
18863
20156
  autoSaveTimers.current.clear();
18864
20157
  if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
18865
20158
  if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
18866
20159
  };
18867
20160
  }, [isEditMode, refreshStateRules]);
18868
- (0, import_react16.useEffect)(() => {
20161
+ (0, import_react17.useEffect)(() => {
18869
20162
  if (!isEditMode) return;
18870
20163
  const THRESHOLD = 10;
18871
20164
  const resolveWasSelected = (el) => {
@@ -19021,7 +20314,7 @@ function OhhwellsBridge() {
19021
20314
  unlockFooterDragInteraction();
19022
20315
  };
19023
20316
  }, [isEditMode]);
19024
- (0, import_react16.useEffect)(() => {
20317
+ (0, import_react17.useEffect)(() => {
19025
20318
  const handler = (e) => {
19026
20319
  if (e.data?.type !== "ow:request-schedule-config") return;
19027
20320
  const insertAfterVal = e.data.insertAfter;
@@ -19037,7 +20330,7 @@ function OhhwellsBridge() {
19037
20330
  window.addEventListener("message", handler);
19038
20331
  return () => window.removeEventListener("message", handler);
19039
20332
  }, [processConfigRequest]);
19040
- (0, import_react16.useEffect)(() => {
20333
+ (0, import_react17.useEffect)(() => {
19041
20334
  if (!isEditMode) return;
19042
20335
  document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
19043
20336
  el.removeAttribute("data-ohw-active-state");
@@ -19061,7 +20354,7 @@ function OhhwellsBridge() {
19061
20354
  postToParent2({
19062
20355
  type: "ow:ready",
19063
20356
  version: "1",
19064
- bridgeVersion: "0.1.67",
20357
+ bridgeVersion: "0.1.69",
19065
20358
  path: pathname,
19066
20359
  nodes: collectEditableNodes(editContentRef.current),
19067
20360
  sections
@@ -19073,13 +20366,13 @@ function OhhwellsBridge() {
19073
20366
  clearTimeout(timer);
19074
20367
  };
19075
20368
  }, [pathname, isEditMode, refreshStateRules, postToParent2]);
19076
- (0, import_react16.useEffect)(() => {
20369
+ (0, import_react17.useEffect)(() => {
19077
20370
  scrollToHashSectionWhenReady();
19078
20371
  const onHashChange = () => scrollToHashSectionWhenReady();
19079
20372
  window.addEventListener("hashchange", onHashChange);
19080
20373
  return () => window.removeEventListener("hashchange", onHashChange);
19081
20374
  }, [pathname]);
19082
- const handleCommand = (0, import_react16.useCallback)((cmd) => {
20375
+ const handleCommand = (0, import_react17.useCallback)((cmd) => {
19083
20376
  const el = activeElRef.current;
19084
20377
  const selBefore = window.getSelection();
19085
20378
  let savedOffsets = null;
@@ -19115,7 +20408,7 @@ function OhhwellsBridge() {
19115
20408
  if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
19116
20409
  refreshActiveCommandsRef.current();
19117
20410
  }, []);
19118
- (0, import_react16.useEffect)(() => {
20411
+ (0, import_react17.useEffect)(() => {
19119
20412
  const session = floatingPanel && floatingPanel.kind === "logo-size" ? floatingPanel : null;
19120
20413
  if (!session || !logoSizeDraft) return;
19121
20414
  const onPanelAction = (e) => {
@@ -19153,7 +20446,7 @@ function OhhwellsBridge() {
19153
20446
  window.addEventListener("message", onPanelAction);
19154
20447
  return () => window.removeEventListener("message", onPanelAction);
19155
20448
  }, [floatingPanel, logoSizeDraft, editorViewport, persistLogoSizeDraft, closeFloatingPanelAndDeselect]);
19156
- const handleStateChange = (0, import_react16.useCallback)((state) => {
20449
+ const handleStateChange = (0, import_react17.useCallback)((state) => {
19157
20450
  if (!activeStateElRef.current) return;
19158
20451
  const el = activeStateElRef.current;
19159
20452
  if (state === "Default") {
@@ -19166,7 +20459,7 @@ function OhhwellsBridge() {
19166
20459
  }
19167
20460
  setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
19168
20461
  }, [deactivate]);
19169
- const reselectAfterLinkPopover = (0, import_react16.useCallback)(
20462
+ const reselectAfterLinkPopover = (0, import_react17.useCallback)(
19170
20463
  (hrefKey) => {
19171
20464
  requestAnimationFrame(() => {
19172
20465
  const el = resolveHrefKeyElement(hrefKey);
@@ -19175,7 +20468,7 @@ function OhhwellsBridge() {
19175
20468
  },
19176
20469
  [resolveHrefKeyElement]
19177
20470
  );
19178
- const closeLinkPopover = (0, import_react16.useCallback)(() => {
20471
+ const closeLinkPopover = (0, import_react17.useCallback)(() => {
19179
20472
  const session = linkPopoverSessionRef.current;
19180
20473
  addNavAfterAnchorRef.current = null;
19181
20474
  setLinkPopover(null);
@@ -19183,9 +20476,9 @@ function OhhwellsBridge() {
19183
20476
  reselectAfterLinkPopover(session.key);
19184
20477
  }
19185
20478
  }, [reselectAfterLinkPopover]);
19186
- const closeLinkPopoverRef = (0, import_react16.useRef)(closeLinkPopover);
20479
+ const closeLinkPopoverRef = (0, import_react17.useRef)(closeLinkPopover);
19187
20480
  closeLinkPopoverRef.current = closeLinkPopover;
19188
- const openLinkPopoverForActive = (0, import_react16.useCallback)(() => {
20481
+ const openLinkPopoverForActive = (0, import_react17.useCallback)(() => {
19189
20482
  const hrefCtx = getHrefKeyFromElement(activeElRef.current);
19190
20483
  if (!hrefCtx) return;
19191
20484
  bumpLinkPopoverGrace();
@@ -19196,7 +20489,7 @@ function OhhwellsBridge() {
19196
20489
  });
19197
20490
  deactivate();
19198
20491
  }, [deactivate]);
19199
- const openLinkPopoverForSelected = (0, import_react16.useCallback)(() => {
20492
+ const openLinkPopoverForSelected = (0, import_react17.useCallback)(() => {
19200
20493
  const anchor = selectedElRef.current;
19201
20494
  if (!anchor) return;
19202
20495
  const key = anchor.getAttribute("data-ohw-href-key");
@@ -19213,7 +20506,7 @@ function OhhwellsBridge() {
19213
20506
  });
19214
20507
  deselect();
19215
20508
  }, [deselect]);
19216
- const handleSelectParent = (0, import_react16.useCallback)(() => {
20509
+ const handleSelectParent = (0, import_react17.useCallback)(() => {
19217
20510
  const selected = selectedElRef.current;
19218
20511
  if (!selected) return;
19219
20512
  if (toolbarVariantRef.current === "select-frame") {
@@ -19240,7 +20533,7 @@ function OhhwellsBridge() {
19240
20533
  }
19241
20534
  deselectRef.current();
19242
20535
  }, []);
19243
- const handleDuplicateSelected = (0, import_react16.useCallback)(() => {
20536
+ const handleDuplicateSelected = (0, import_react17.useCallback)(() => {
19244
20537
  const selected = selectedElRef.current;
19245
20538
  if (!selected || !isNavigationItem2(selected)) return;
19246
20539
  const hrefKey = selected.getAttribute("data-ohw-href-key");
@@ -19356,7 +20649,7 @@ function OhhwellsBridge() {
19356
20649
  });
19357
20650
  }
19358
20651
  }, [postToParent2]);
19359
- const runPendingDeleteUndo = (0, import_react16.useCallback)(() => {
20652
+ const runPendingDeleteUndo = (0, import_react17.useCallback)(() => {
19360
20653
  const pending = pendingDeleteUndoRef.current;
19361
20654
  if (!pending) return false;
19362
20655
  pendingDeleteUndoRef.current = null;
@@ -19364,7 +20657,7 @@ function OhhwellsBridge() {
19364
20657
  enforceLinkHrefs();
19365
20658
  return true;
19366
20659
  }, []);
19367
- const handleDeleteSelected = (0, import_react16.useCallback)(() => {
20660
+ const handleDeleteSelected = (0, import_react17.useCallback)(() => {
19368
20661
  const selected = selectedElRef.current;
19369
20662
  if (!selected) return false;
19370
20663
  return deleteSelectedNavFooterItem({
@@ -19385,7 +20678,7 @@ function OhhwellsBridge() {
19385
20678
  }, [postToParent2]);
19386
20679
  handleDeleteSelectedRef.current = handleDeleteSelected;
19387
20680
  runPendingDeleteUndoRef.current = runPendingDeleteUndo;
19388
- const handleLinkPopoverSubmit = (0, import_react16.useCallback)(
20681
+ const handleLinkPopoverSubmit = (0, import_react17.useCallback)(
19389
20682
  (target) => {
19390
20683
  const session = linkPopoverSessionRef.current;
19391
20684
  if (!session) return;
@@ -19451,19 +20744,30 @@ function OhhwellsBridge() {
19451
20744
  const showEditLink = toolbarShowEditLink;
19452
20745
  const currentSections = sectionsByPath[pathname] ?? [];
19453
20746
  linkPopoverOpenRef.current = linkPopover !== null;
19454
- const handleMediaReplace = (0, import_react16.useCallback)(
20747
+ const handleMediaSelect = (0, import_react17.useCallback)((key) => {
20748
+ const el = hoveredImageRef.current?.dataset.ohwKey === key ? hoveredImageRef.current : Array.from(document.querySelectorAll(MEDIA_SELECTOR)).find(
20749
+ (m) => (m.dataset.ohwKey ?? "") === key
20750
+ ) ?? null;
20751
+ if (!el) return;
20752
+ selectMediaElementRef.current(el);
20753
+ }, []);
20754
+ const handleMediaReplace = (0, import_react17.useCallback)(
19455
20755
  (key) => {
19456
- postToParent2({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
20756
+ postToParent2({
20757
+ type: "ow:image-pick",
20758
+ key,
20759
+ elementType: mediaHover?.elementType ?? selectedMedia?.elementType ?? "image"
20760
+ });
19457
20761
  },
19458
- [postToParent2, mediaHover?.elementType]
20762
+ [postToParent2, mediaHover?.elementType, selectedMedia?.elementType]
19459
20763
  );
19460
- const handleEditCarousel = (0, import_react16.useCallback)(
20764
+ const handleEditCarousel = (0, import_react17.useCallback)(
19461
20765
  (key) => {
19462
20766
  postToParent2({ type: "ow:carousel-open", key, images: readCarouselValue(key) });
19463
20767
  },
19464
20768
  [postToParent2]
19465
20769
  );
19466
- const handleMediaFadeOutComplete = (0, import_react16.useCallback)((key) => {
20770
+ const handleMediaFadeOutComplete = (0, import_react17.useCallback)((key) => {
19467
20771
  setUploadingRects((prev) => {
19468
20772
  if (!(key in prev)) return prev;
19469
20773
  const next = { ...prev };
@@ -19471,7 +20775,7 @@ function OhhwellsBridge() {
19471
20775
  return next;
19472
20776
  });
19473
20777
  }, []);
19474
- const handleVideoSettingsChange = (0, import_react16.useCallback)(
20778
+ const handleVideoSettingsChange = (0, import_react17.useCallback)(
19475
20779
  (key, settings) => {
19476
20780
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
19477
20781
  const video = getVideoEl2(el);
@@ -19493,420 +20797,506 @@ function OhhwellsBridge() {
19493
20797
  },
19494
20798
  [postToParent2]
19495
20799
  );
19496
- return bridgeRoot ? (0, import_react_dom4.createPortal)(
19497
- /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
19498
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
19499
- isEditMode && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(AiSectionOverlay, { postToParent: postToParent2, apiRef: aiSectionApiRef }),
19500
- Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
19501
- MediaOverlay,
19502
- {
19503
- hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
19504
- isUploading: true,
19505
- fadingOut,
19506
- onFadeOutComplete: handleMediaFadeOutComplete,
19507
- onReplace: handleMediaReplace
19508
- },
19509
- `uploading-${key}`
19510
- )),
19511
- mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
19512
- MediaOverlay,
19513
- {
19514
- hover: mediaHover,
19515
- isUploading: false,
19516
- onReplace: handleMediaReplace,
19517
- onVideoSettingsChange: handleVideoSettingsChange
19518
- }
19519
- ),
19520
- carouselHover && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(CarouselOverlay, { hover: carouselHover, onEdit: handleEditCarousel }),
19521
- siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
19522
- siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
19523
- isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
19524
- isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
19525
- "div",
19526
- {
19527
- className: "pointer-events-none fixed z-2147483646",
19528
- style: {
19529
- left: slot.left,
19530
- top: slot.top,
19531
- width: slot.width,
19532
- height: slot.height
20800
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
20801
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { id: "ohw-loader", suppressHydrationWarning: true, style: { ...OHW_LOADER_STYLE, display: "none" }, children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(OhwLoaderSpinner, {}) }),
20802
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("script", { suppressHydrationWarning: true, dangerouslySetInnerHTML: { __html: OHW_LOADER_PREHYDRATE_SCRIPT } }),
20803
+ bridgeRoot ? (0, import_react_dom4.createPortal)(
20804
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
20805
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
20806
+ isEditMode && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(AiSectionOverlay, { postToParent: postToParent2, apiRef: aiSectionApiRef }),
20807
+ isSectionDragging && sectionDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20808
+ "div",
20809
+ {
20810
+ className: "pointer-events-none fixed z-2147483646",
20811
+ style: { left: slot.left, top: slot.y, width: slot.width, height: 3, transform: "translateY(-50%)" },
20812
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20813
+ DropIndicator,
20814
+ {
20815
+ direction: "horizontal",
20816
+ state: activeSectionDropIndex === i ? "dragActive" : "dragIdle",
20817
+ className: "!h-full !w-full"
20818
+ }
20819
+ )
19533
20820
  },
19534
- children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
19535
- DropIndicator,
19536
- {
19537
- direction: slot.direction,
19538
- state: activeFooterDropIndex === i ? "dragActive" : "dragIdle",
19539
- className: "!h-full !w-full"
19540
- }
19541
- )
19542
- },
19543
- `footer-drop-${slot.direction}-${slot.columnIndex}-${slot.insertIndex}-${i}`
19544
- )),
19545
- isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
19546
- "div",
19547
- {
19548
- className: "pointer-events-none fixed z-2147483646",
19549
- style: {
19550
- left: slot.left,
19551
- top: slot.top,
19552
- width: slot.width,
19553
- height: slot.height
20821
+ `section-drop-${slot.insertIndex}-${i}`
20822
+ )),
20823
+ Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20824
+ MediaOverlay,
20825
+ {
20826
+ hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
20827
+ isUploading: true,
20828
+ fadingOut,
20829
+ onFadeOutComplete: handleMediaFadeOutComplete,
20830
+ onReplace: handleMediaReplace
19554
20831
  },
19555
- children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
19556
- DropIndicator,
19557
- {
19558
- direction: slot.direction,
19559
- state: activeNavDropIndex === i ? "dragActive" : "dragIdle",
19560
- className: "!h-full !w-full"
19561
- }
19562
- )
19563
- },
19564
- `nav-drop-${slot.direction}-${slot.parentId ?? "root"}-${slot.insertIndex}-${i}`
19565
- )),
19566
- hoveredNavContainerRect && (toolbarVariant !== "select-frame" || isFooterFrameSelection) && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
19567
- hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
19568
- hoveredTextRect && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: hoveredTextRect, state: "hover" }),
19569
- formPickRect && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
19570
- ItemInteractionLayer,
19571
- {
19572
- rect: formPickRect,
19573
- state: "active-top",
19574
- itemDragSurface: false,
19575
- toolbarAlign: "left",
19576
- chromeGap: 24,
19577
- toolbar: fieldPickRect ? void 0 : /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
20832
+ `uploading-${key}`
20833
+ )),
20834
+ mediaHover && !(mediaHover.key in uploadingRects) && mediaHover.key !== selectedMedia?.key && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20835
+ MediaOverlay,
20836
+ {
20837
+ hover: mediaHover,
20838
+ isUploading: false,
20839
+ onReplace: handleMediaReplace,
20840
+ onSelect: handleMediaSelect,
20841
+ onVideoSettingsChange: handleVideoSettingsChange
20842
+ }
20843
+ ),
20844
+ selectedMedia && !(selectedMedia.key in uploadingRects) && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20845
+ MediaOverlay,
20846
+ {
20847
+ hover: selectedMedia,
20848
+ selected: true,
20849
+ hovered: mediaHover?.key === selectedMedia.key,
20850
+ isUploading: false,
20851
+ onReplace: handleMediaReplace,
20852
+ onSelect: handleMediaSelect,
20853
+ onVideoSettingsChange: handleVideoSettingsChange
20854
+ }
20855
+ ),
20856
+ carouselHover && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(CarouselOverlay, { hover: carouselHover, onEdit: handleEditCarousel }),
20857
+ siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
20858
+ siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
20859
+ isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
20860
+ isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20861
+ "div",
20862
+ {
20863
+ className: "pointer-events-none fixed z-2147483646",
20864
+ style: {
20865
+ left: slot.left,
20866
+ top: slot.top,
20867
+ width: slot.width,
20868
+ height: slot.height
20869
+ },
20870
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20871
+ DropIndicator,
20872
+ {
20873
+ direction: slot.direction,
20874
+ state: activeFooterDropIndex === i ? "dragActive" : "dragIdle",
20875
+ className: "!h-full !w-full"
20876
+ }
20877
+ )
20878
+ },
20879
+ `footer-drop-${slot.direction}-${slot.columnIndex}-${slot.insertIndex}-${i}`
20880
+ )),
20881
+ isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20882
+ "div",
20883
+ {
20884
+ className: "pointer-events-none fixed z-2147483646",
20885
+ style: {
20886
+ left: slot.left,
20887
+ top: slot.top,
20888
+ width: slot.width,
20889
+ height: slot.height
20890
+ },
20891
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20892
+ DropIndicator,
20893
+ {
20894
+ direction: slot.direction,
20895
+ state: activeNavDropIndex === i ? "dragActive" : "dragIdle",
20896
+ className: "!h-full !w-full"
20897
+ }
20898
+ )
20899
+ },
20900
+ `nav-drop-${slot.direction}-${slot.parentId ?? "root"}-${slot.insertIndex}-${i}`
20901
+ )),
20902
+ hoveredNavContainerRect && (toolbarVariant !== "select-frame" || isFooterFrameSelection) && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
20903
+ hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
20904
+ hoveredTextRect && !hoveredNavContainerRect && !hoveredItemRect && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: hoveredTextRect, state: "hover" }),
20905
+ formPickRect && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20906
+ ItemInteractionLayer,
20907
+ {
20908
+ rect: formPickRect,
20909
+ state: "active-top",
20910
+ itemDragSurface: false,
20911
+ toolbarAlign: "left",
20912
+ chromeGap: 24,
20913
+ toolbar: fieldPickRect ? void 0 : /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
20914
+ "div",
20915
+ {
20916
+ "data-ohw-form-toolbar": "",
20917
+ className: "pointer-events-auto flex items-center gap-0.5 whitespace-nowrap rounded-lg border border-border bg-background p-1 shadow-md",
20918
+ children: [
20919
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20920
+ "button",
20921
+ {
20922
+ type: "button",
20923
+ "aria-label": "Add field",
20924
+ className: "flex h-7 w-7 items-center justify-center rounded-md text-foreground transition-colors hover:bg-muted/80",
20925
+ onClick: () => setFieldTypePickerOpen((open) => !open),
20926
+ "data-ohw-add-field": "",
20927
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react17.Plus, { size: 15, "aria-hidden": true })
20928
+ }
20929
+ ),
20930
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "mx-0.5 h-5 w-px bg-border" }),
20931
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
20932
+ "button",
20933
+ {
20934
+ type: "button",
20935
+ 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",
20936
+ onClick: () => {
20937
+ const form = formPickElRef.current;
20938
+ if (!form) return;
20939
+ postToParent2({
20940
+ type: "ow:form-pick",
20941
+ formKey: formKeyOf(form),
20942
+ hasLongText: formHasLongText(form)
20943
+ });
20944
+ },
20945
+ children: [
20946
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react17.Settings, { size: 14, "aria-hidden": true }),
20947
+ "Form settings",
20948
+ formPickCount ? (
20949
+ // Counter pill, per the design — not a text suffix.
20950
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20951
+ "span",
20952
+ {
20953
+ "data-ohw-form-count": "",
20954
+ 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",
20955
+ children: formPickCount
20956
+ }
20957
+ )
20958
+ ) : null
20959
+ ]
20960
+ }
20961
+ ),
20962
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "mx-0.5 h-5 w-px bg-border" }),
20963
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "flex items-center rounded-md bg-muted/70 p-0.5", children: ["default", "success"].map((state) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20964
+ "button",
20965
+ {
20966
+ type: "button",
20967
+ "aria-pressed": formViewState === state,
20968
+ 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"),
20969
+ onClick: () => {
20970
+ const form = formPickElRef.current;
20971
+ const key = form ? formKeyOf(form) : null;
20972
+ if (!form || !key) return;
20973
+ const initial = successInitialFor(form, key, editContentRef.current);
20974
+ setFormViewState(form, key, state, initial);
20975
+ setFormViewStateUi(state);
20976
+ setFormPickRect(form.getBoundingClientRect());
20977
+ if (state === "success") {
20978
+ const successEl = form.querySelector(`[${SUCCESS_TEXT_ATTR}]`);
20979
+ if (successEl) requestAnimationFrame(() => activateRef.current(successEl));
20980
+ } else {
20981
+ deactivateRef.current();
20982
+ }
20983
+ },
20984
+ children: state
20985
+ },
20986
+ state
20987
+ )) })
20988
+ ]
20989
+ }
20990
+ )
20991
+ }
20992
+ ),
20993
+ formHoverRect && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20994
+ ItemInteractionLayer,
20995
+ {
20996
+ rect: formHoverRect,
20997
+ state: "hover",
20998
+ chromeGap: formHoverElRef.current && getFieldWrapper(formHoverElRef.current) ? 8 : 24
20999
+ }
21000
+ ),
21001
+ fieldPickRect && fieldPickState && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21002
+ ItemInteractionLayer,
21003
+ {
21004
+ rect: fieldPickRect,
21005
+ state: fieldDragging ? "dragging" : "active-top",
21006
+ itemDragSurface: false,
21007
+ toolbarAlign: "left",
21008
+ chromeGap: 10,
21009
+ showHandle: true,
21010
+ dragHandleLabel: "Reorder field",
21011
+ onDragHandleDragStart: handleFieldDragStart,
21012
+ onDragHandleDragEnd: handleFieldDragEnd,
21013
+ toolbar: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21014
+ FormFieldToolbar,
21015
+ {
21016
+ type: fieldPickState.type,
21017
+ required: fieldPickState.required,
21018
+ onTypeChange: handleFieldTypeChange,
21019
+ onRequiredToggle: handleFieldRequiredToggle,
21020
+ onDuplicate: handleFieldDuplicate,
21021
+ onDelete: handleFieldDelete
21022
+ }
21023
+ )
21024
+ }
21025
+ ),
21026
+ fieldDragging ? fieldDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21027
+ "div",
21028
+ {
21029
+ className: "pointer-events-none fixed z-[2147483644]",
21030
+ style: { top: slot.top, left: slot.left, width: slot.width },
21031
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21032
+ DropIndicator,
21033
+ {
21034
+ direction: "horizontal",
21035
+ state: fieldDropIndex === i ? "dragActive" : "dragIdle",
21036
+ className: "!w-full"
21037
+ }
21038
+ )
21039
+ },
21040
+ `field-drop-${i}`
21041
+ )) : null,
21042
+ fieldTypePickerOpen && formPickRect ? (() => {
21043
+ const toolbar = document.querySelector("[data-ohw-form-toolbar]")?.getBoundingClientRect();
21044
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
19578
21045
  "div",
19579
21046
  {
19580
- "data-ohw-form-toolbar": "",
19581
- className: "pointer-events-auto flex items-center gap-0.5 whitespace-nowrap rounded-lg border border-border bg-background p-1 shadow-md",
19582
- children: [
19583
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
19584
- "button",
19585
- {
19586
- type: "button",
19587
- "aria-label": "Add field",
19588
- className: "flex h-7 w-7 items-center justify-center rounded-md text-foreground transition-colors hover:bg-muted/80",
19589
- onClick: () => setFieldTypePickerOpen((open) => !open),
19590
- "data-ohw-add-field": "",
19591
- children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react17.Plus, { size: 15, "aria-hidden": true })
19592
- }
19593
- ),
19594
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "mx-0.5 h-5 w-px bg-border" }),
19595
- /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
19596
- "button",
19597
- {
19598
- type: "button",
19599
- 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",
19600
- onClick: () => {
19601
- const form = formPickElRef.current;
19602
- if (!form) return;
19603
- postToParent2({
19604
- type: "ow:form-pick",
19605
- formKey: formKeyOf(form),
19606
- hasLongText: formHasLongText(form)
19607
- });
19608
- },
19609
- children: [
19610
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react17.Settings, { size: 14, "aria-hidden": true }),
19611
- "Form settings",
19612
- formPickCount ? (
19613
- // Counter pill, per the design — not a text suffix.
19614
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
19615
- "span",
19616
- {
19617
- "data-ohw-form-count": "",
19618
- 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",
19619
- children: formPickCount
19620
- }
19621
- )
19622
- ) : null
19623
- ]
19624
- }
19625
- ),
19626
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "mx-0.5 h-5 w-px bg-border" }),
19627
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "flex items-center rounded-md bg-muted/70 p-0.5", children: ["default", "success"].map((state) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
19628
- "button",
19629
- {
19630
- type: "button",
19631
- "aria-pressed": formViewState === state,
19632
- 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"),
19633
- onClick: () => {
19634
- const form = formPickElRef.current;
19635
- const key = form ? formKeyOf(form) : null;
19636
- if (!form || !key) return;
19637
- const initial = successInitialFor(form, key, editContentRef.current);
19638
- setFormViewState(form, key, state, initial);
19639
- setFormViewStateUi(state);
19640
- setFormPickRect(form.getBoundingClientRect());
19641
- if (state === "success") {
19642
- const successEl = form.querySelector(`[${SUCCESS_TEXT_ATTR}]`);
19643
- if (successEl) requestAnimationFrame(() => activateRef.current(successEl));
19644
- } else {
19645
- deactivateRef.current();
19646
- }
19647
- },
19648
- children: state
19649
- },
19650
- state
19651
- )) })
19652
- ]
21047
+ className: "pointer-events-none fixed z-[2147483645]",
21048
+ style: {
21049
+ top: toolbar ? toolbar.bottom + 6 : formPickRect.top + 16,
21050
+ left: toolbar ? toolbar.left : formPickRect.left + 24
21051
+ },
21052
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(FieldTypePicker, { onPick: handleAddField })
19653
21053
  }
19654
- )
19655
- }
19656
- ),
19657
- formHoverRect && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
19658
- ItemInteractionLayer,
19659
- {
19660
- rect: formHoverRect,
19661
- state: "hover",
19662
- chromeGap: formHoverElRef.current && getFieldWrapper(formHoverElRef.current) ? 8 : 24
19663
- }
19664
- ),
19665
- fieldPickRect && fieldPickState && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
19666
- ItemInteractionLayer,
19667
- {
19668
- rect: fieldPickRect,
19669
- state: fieldDragging ? "dragging" : "active-top",
19670
- itemDragSurface: false,
19671
- toolbarAlign: "left",
19672
- chromeGap: 10,
19673
- showHandle: true,
19674
- dragHandleLabel: "Reorder field",
19675
- onDragHandleDragStart: handleFieldDragStart,
19676
- onDragHandleDragEnd: handleFieldDragEnd,
19677
- toolbar: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
19678
- FormFieldToolbar,
21054
+ );
21055
+ })() : null,
21056
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer2(selectedElRef.current) && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
21057
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isFooterLinksContainer(selectedElRef.current) && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21058
+ FooterContainerChrome,
21059
+ {
21060
+ rect: toolbarRect,
21061
+ onAdd: handleAddFooterColumn,
21062
+ addDisabled: !canAddFooterColumn()
21063
+ }
21064
+ ),
21065
+ toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame" || toolbarVariant === "logo") && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21066
+ ItemInteractionLayer,
21067
+ {
21068
+ rect: isItemDragging && draggedItemRect && (footerDragRef.current?.wasSelected || navDragRef.current?.wasSelected) ? draggedItemRect : toolbarRect,
21069
+ toolbarBelowRect: selectedElRef.current && isNavigationItem2(selectedElRef.current) ? getOpenNavDropdownPanelRect(selectedElRef.current) : null,
21070
+ elRef: glowElRef,
21071
+ state: isItemDragging ? "dragging" : selectedElRef.current && isNavigationItem2(selectedElRef.current) && getOpenNavDropdownPanelRect(selectedElRef.current) ? "active-bottom" : resolveItemInteractionState(toolbarRect, parentScrollRef.current),
21072
+ showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey) || toolbarVariant === "select-frame" && isFooterFrameSelection,
21073
+ dragDisabled: reorderDragDisabled,
21074
+ dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
21075
+ onDragHandleDragStart: handleItemDragStart,
21076
+ onDragHandleDragEnd: handleItemDragEnd,
21077
+ onItemPointerDown: toolbarVariant === "logo" ? void 0 : handleItemChromePointerDown,
21078
+ onItemClick: toolbarVariant === "logo" ? void 0 : handleItemChromeClick,
21079
+ itemDragSurface: toolbarVariant !== "logo" && !isFooterFrameSelection,
21080
+ toolbar: toolbarVariant === "link-action" && !isItemDragging || toolbarVariant === "select-frame" && (isFooterFrameSelection || selectedIsSocialsRow) && !isItemDragging ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21081
+ ItemActionToolbar,
21082
+ {
21083
+ onEditLink: openLinkPopoverForSelected,
21084
+ onStyle: () => {
21085
+ const row = selectedElRef.current;
21086
+ if (!row) return;
21087
+ if (floatingPanel?.kind === "socials-display") closeFloatingPanelOnly();
21088
+ else openSocialsDisplayPanel(row);
21089
+ },
21090
+ showStyle: selectedIsSocialsRow,
21091
+ styleActive: floatingPanel?.kind === "socials-display",
21092
+ onAddItem: handleAddChildItem,
21093
+ onSelectParent: handleSelectParent,
21094
+ onDuplicate: handleDuplicateSelected,
21095
+ onDelete: handleDeleteSelected,
21096
+ addItemDisabled: isFooterFrameSelection && isFooterAddItemDisabled(selectedElRef.current) || selectedElRef.current !== null && (() => {
21097
+ const row = isSocialsRow(selectedElRef.current) ? selectedElRef.current : findSocialsRow(selectedElRef.current);
21098
+ return row ? !canAddSocialItem(row) : false;
21099
+ })(),
21100
+ editLinkDisabled: false,
21101
+ moreDisabled: false,
21102
+ duplicateDisabled: isFooterFrameSelection || selectedIsSocialsRow,
21103
+ showEditLink: !isFooterFrameSelection && !selectedIsSocialsRow && navDropdownPreviewOpen === null,
21104
+ showAddItem: isFooterFrameSelection || selectedIsSocialsRow || !selectedIsCta && Boolean(
21105
+ selectedElRef.current && isNavbarHrefKey(selectedElRef.current.getAttribute("data-ohw-href-key")) && !isNestedNavChild(selectedElRef.current)
21106
+ ),
21107
+ showMore: (!selectedIsCta || isFooterFrameSelection) && !selectedIsSocialsRow,
21108
+ dropdownOpen: navDropdownPreviewOpen,
21109
+ onDropdownOpenChange: handleNavDropdownOpenChange,
21110
+ headingVisible: footerHeadingVisible,
21111
+ onHeadingVisibleChange: handleFooterHeadingVisibleChange
21112
+ }
21113
+ ) : void 0
21114
+ }
21115
+ ),
21116
+ toolbarRect && toolbarVariant === "rich-text" && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
21117
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21118
+ EditGlowChrome,
19679
21119
  {
19680
- type: fieldPickState.type,
19681
- required: fieldPickState.required,
19682
- onTypeChange: handleFieldTypeChange,
19683
- onRequiredToggle: handleFieldRequiredToggle,
19684
- onDuplicate: handleFieldDuplicate,
19685
- onDelete: handleFieldDelete
21120
+ rect: toolbarRect,
21121
+ elRef: glowElRef,
21122
+ reorderHrefKey,
21123
+ dragDisabled: reorderDragDisabled,
21124
+ hideHandle: isItemDragging
19686
21125
  }
19687
- )
19688
- }
19689
- ),
19690
- fieldDragging ? fieldDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
19691
- "div",
19692
- {
19693
- className: "pointer-events-none fixed z-[2147483644]",
19694
- style: { top: slot.top, left: slot.left, width: slot.width },
19695
- children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
19696
- DropIndicator,
21126
+ ),
21127
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21128
+ FloatingToolbar,
19697
21129
  {
19698
- direction: "horizontal",
19699
- state: fieldDropIndex === i ? "dragActive" : "dragIdle",
19700
- className: "!w-full"
21130
+ rect: toolbarRect,
21131
+ parentScroll: parentScrollRef.current,
21132
+ elRef: toolbarElRef,
21133
+ onCommand: handleCommand,
21134
+ activeCommands,
21135
+ showEditLink,
21136
+ onEditLink: openLinkPopoverForActive
19701
21137
  }
19702
21138
  )
19703
- },
19704
- `field-drop-${i}`
19705
- )) : null,
19706
- fieldTypePickerOpen && formPickRect ? (() => {
19707
- const toolbar = document.querySelector("[data-ohw-form-toolbar]")?.getBoundingClientRect();
19708
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21139
+ ] }),
21140
+ maxBadge && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
19709
21141
  "div",
19710
21142
  {
19711
- className: "pointer-events-none fixed z-[2147483645]",
21143
+ "data-ohw-max-badge": "",
19712
21144
  style: {
19713
- top: toolbar ? toolbar.bottom + 6 : formPickRect.top + 16,
19714
- left: toolbar ? toolbar.left : formPickRect.left + 24
21145
+ position: "fixed",
21146
+ top: maxBadge.rect.bottom + 4,
21147
+ left: maxBadge.rect.right,
21148
+ transform: "translateX(-100%)",
21149
+ zIndex: 2147483647,
21150
+ background: maxBadge.current > maxBadge.max ? "#FEF2F2" : "#F5F5F4",
21151
+ color: maxBadge.current > maxBadge.max ? "#DC2626" : "#78716C",
21152
+ border: `1px solid ${maxBadge.current > maxBadge.max ? "#FECACA" : "#E7E5E4"}`,
21153
+ borderRadius: 4,
21154
+ padding: "2px 6px",
21155
+ fontSize: 11,
21156
+ fontWeight: 500,
21157
+ pointerEvents: "none"
19715
21158
  },
19716
- children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(FieldTypePicker, { onPick: handleAddField })
21159
+ children: [
21160
+ maxBadge.current,
21161
+ "/",
21162
+ maxBadge.max
21163
+ ]
19717
21164
  }
19718
- );
19719
- })() : null,
19720
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer2(selectedElRef.current) && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
19721
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isFooterLinksContainer(selectedElRef.current) && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
19722
- FooterContainerChrome,
19723
- {
19724
- rect: toolbarRect,
19725
- onAdd: handleAddFooterColumn,
19726
- addDisabled: !canAddFooterColumn()
19727
- }
19728
- ),
19729
- toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame" || toolbarVariant === "logo") && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
19730
- ItemInteractionLayer,
19731
- {
19732
- rect: isItemDragging && draggedItemRect && (footerDragRef.current?.wasSelected || navDragRef.current?.wasSelected) ? draggedItemRect : toolbarRect,
19733
- toolbarBelowRect: selectedElRef.current && isNavigationItem2(selectedElRef.current) ? getOpenNavDropdownPanelRect(selectedElRef.current) : null,
19734
- elRef: glowElRef,
19735
- state: isItemDragging ? "dragging" : selectedElRef.current && isNavigationItem2(selectedElRef.current) && getOpenNavDropdownPanelRect(selectedElRef.current) ? "active-bottom" : resolveItemInteractionState(toolbarRect, parentScrollRef.current),
19736
- showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey) || toolbarVariant === "select-frame" && isFooterFrameSelection,
19737
- dragDisabled: reorderDragDisabled,
19738
- dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
19739
- onDragHandleDragStart: handleItemDragStart,
19740
- onDragHandleDragEnd: handleItemDragEnd,
19741
- onItemPointerDown: toolbarVariant === "logo" ? void 0 : handleItemChromePointerDown,
19742
- onItemClick: toolbarVariant === "logo" ? void 0 : handleItemChromeClick,
19743
- itemDragSurface: toolbarVariant !== "logo" && !isFooterFrameSelection,
19744
- toolbar: toolbarVariant === "link-action" && !isItemDragging || toolbarVariant === "select-frame" && (isFooterFrameSelection || selectedIsSocialsRow) && !isItemDragging ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
19745
- ItemActionToolbar,
19746
- {
19747
- onEditLink: openLinkPopoverForSelected,
19748
- onStyle: () => {
19749
- const row = selectedElRef.current;
19750
- if (!row) return;
19751
- if (floatingPanel?.kind === "socials-display") closeFloatingPanelOnly();
19752
- else openSocialsDisplayPanel(row);
19753
- },
19754
- showStyle: selectedIsSocialsRow,
19755
- styleActive: floatingPanel?.kind === "socials-display",
19756
- onAddItem: handleAddChildItem,
19757
- onSelectParent: handleSelectParent,
19758
- onDuplicate: handleDuplicateSelected,
19759
- onDelete: handleDeleteSelected,
19760
- addItemDisabled: isFooterFrameSelection && isFooterAddItemDisabled(selectedElRef.current) || selectedElRef.current !== null && (() => {
19761
- const row = isSocialsRow(selectedElRef.current) ? selectedElRef.current : findSocialsRow(selectedElRef.current);
19762
- return row ? !canAddSocialItem(row) : false;
19763
- })(),
19764
- editLinkDisabled: false,
19765
- moreDisabled: false,
19766
- duplicateDisabled: isFooterFrameSelection || selectedIsSocialsRow,
19767
- showEditLink: !isFooterFrameSelection && !selectedIsSocialsRow && navDropdownPreviewOpen === null,
19768
- showAddItem: isFooterFrameSelection || selectedIsSocialsRow || !selectedIsCta && Boolean(
19769
- selectedElRef.current && isNavbarHrefKey(selectedElRef.current.getAttribute("data-ohw-href-key")) && !isNestedNavChild(selectedElRef.current)
19770
- ),
19771
- showMore: (!selectedIsCta || isFooterFrameSelection) && !selectedIsSocialsRow,
19772
- dropdownOpen: navDropdownPreviewOpen,
19773
- onDropdownOpenChange: handleNavDropdownOpenChange,
19774
- headingVisible: footerHeadingVisible,
19775
- onHeadingVisibleChange: handleFooterHeadingVisibleChange
19776
- }
19777
- ) : void 0
19778
- }
19779
- ),
19780
- toolbarRect && toolbarVariant === "rich-text" && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
19781
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
19782
- EditGlowChrome,
21165
+ ),
21166
+ toggleState && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21167
+ StateToggle,
19783
21168
  {
19784
- rect: toolbarRect,
19785
- elRef: glowElRef,
19786
- reorderHrefKey,
19787
- dragDisabled: reorderDragDisabled,
19788
- hideHandle: isItemDragging
21169
+ rect: toggleState.rect,
21170
+ activeState: toggleState.activeState,
21171
+ states: toggleState.states,
21172
+ onStateChange: handleStateChange
19789
21173
  }
19790
21174
  ),
19791
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
19792
- FloatingToolbar,
21175
+ sectionGap && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
21176
+ "div",
19793
21177
  {
19794
- rect: toolbarRect,
19795
- parentScroll: parentScrollRef.current,
19796
- elRef: toolbarElRef,
19797
- onCommand: handleCommand,
19798
- activeCommands,
19799
- showEditLink,
19800
- onEditLink: openLinkPopoverForActive
21178
+ "data-ohw-section-insert-line": "",
21179
+ className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
21180
+ style: { top: sectionGap.y, transform: "translateY(-50%)" },
21181
+ children: [
21182
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
21183
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21184
+ Badge,
21185
+ {
21186
+ 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",
21187
+ onClick: () => {
21188
+ window.parent.postMessage(
21189
+ {
21190
+ type: "ow:add-section",
21191
+ insertAfter: sectionGap.insertAfter,
21192
+ insertBefore: sectionGap.insertBefore
21193
+ },
21194
+ "*"
21195
+ );
21196
+ },
21197
+ children: "Add Section"
21198
+ }
21199
+ ),
21200
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
21201
+ ]
19801
21202
  }
19802
- )
19803
- ] }),
19804
- maxBadge && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
19805
- "div",
19806
- {
19807
- "data-ohw-max-badge": "",
19808
- style: {
19809
- position: "fixed",
19810
- top: maxBadge.rect.bottom + 4,
19811
- left: maxBadge.rect.right,
19812
- transform: "translateX(-100%)",
19813
- zIndex: 2147483647,
19814
- background: maxBadge.current > maxBadge.max ? "#FEF2F2" : "#F5F5F4",
19815
- color: maxBadge.current > maxBadge.max ? "#DC2626" : "#78716C",
19816
- border: `1px solid ${maxBadge.current > maxBadge.max ? "#FECACA" : "#E7E5E4"}`,
19817
- borderRadius: 4,
19818
- padding: "2px 6px",
19819
- fontSize: 11,
19820
- fontWeight: 500,
19821
- pointerEvents: "none"
21203
+ ),
21204
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21205
+ LinkPopover,
21206
+ {
21207
+ panelRef: linkPopoverPanelRef,
21208
+ portalContainer: dialogPortalContainer,
21209
+ open: true,
21210
+ mode: linkPopover.mode ?? "edit",
21211
+ pages: sitePages,
21212
+ sections: currentSections,
21213
+ sectionsByPath,
21214
+ initialTarget: linkPopover.target,
21215
+ existingTargets: linkPopover.intent === "add-nav" ? getNavbarExistingTargets() : [],
21216
+ onClose: closeLinkPopover,
21217
+ onSubmit: handleLinkPopoverSubmit
19822
21218
  },
19823
- children: [
19824
- maxBadge.current,
19825
- "/",
19826
- maxBadge.max
19827
- ]
19828
- }
19829
- ),
19830
- toggleState && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
19831
- StateToggle,
19832
- {
19833
- rect: toggleState.rect,
19834
- activeState: toggleState.activeState,
19835
- states: toggleState.states,
19836
- onStateChange: handleStateChange
19837
- }
19838
- ),
19839
- sectionGap && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
19840
- "div",
19841
- {
19842
- "data-ohw-section-insert-line": "",
19843
- className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
19844
- style: { top: sectionGap.y, transform: "translateY(-50%)" },
19845
- children: [
19846
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
19847
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
19848
- Badge,
21219
+ linkPopover.key
21220
+ ) : null,
21221
+ floatingPanel && floatingPanel.kind === "socials-display" ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21222
+ FloatingPanel,
21223
+ {
21224
+ open: true,
21225
+ title: floatingPanel.title,
21226
+ context: floatingPanel.context,
21227
+ position: floatingPanelPos,
21228
+ onPositionChange: setFloatingPanelPos,
21229
+ parentScroll: parentScrollSnap ?? parentScrollRef.current,
21230
+ onClose: closeFloatingPanelOnly,
21231
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21232
+ SocialsDisplayPanel,
19849
21233
  {
19850
- 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",
19851
- onClick: () => {
19852
- window.parent.postMessage(
19853
- {
19854
- type: "ow:add-section",
19855
- insertAfter: sectionGap.insertAfter,
19856
- insertBefore: sectionGap.insertBefore
19857
- },
19858
- "*"
19859
- );
19860
- },
19861
- children: "Add Section"
21234
+ display: socialsDisplayFor(floatingPanel.row, editContentRef.current),
21235
+ onChange: (next) => {
21236
+ changeSocialsDisplay(floatingPanel.row, next);
21237
+ setFloatingPanel({ ...floatingPanel });
21238
+ }
19862
21239
  }
19863
- ),
19864
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
19865
- ]
19866
- }
19867
- ),
19868
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
19869
- LinkPopover,
19870
- {
19871
- panelRef: linkPopoverPanelRef,
19872
- portalContainer: dialogPortalContainer,
19873
- open: true,
19874
- mode: linkPopover.mode ?? "edit",
19875
- pages: sitePages,
19876
- sections: currentSections,
19877
- sectionsByPath,
19878
- initialTarget: linkPopover.target,
19879
- existingTargets: linkPopover.intent === "add-nav" ? getNavbarExistingTargets() : [],
19880
- onClose: closeLinkPopover,
19881
- onSubmit: handleLinkPopoverSubmit
21240
+ )
21241
+ }
21242
+ ) : null
21243
+ ] }),
21244
+ bridgeRoot
21245
+ ) : null
21246
+ ] });
21247
+ }
21248
+
21249
+ // src/ui/EmptySection.tsx
21250
+ var import_link = __toESM(require("next/link"), 1);
21251
+ var import_jsx_runtime34 = require("react/jsx-runtime");
21252
+ function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey }) {
21253
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
21254
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
21255
+ "p",
21256
+ {
21257
+ style: {
21258
+ fontFamily: "var(--brand-font-body)",
21259
+ fontSize: "0.75rem",
21260
+ fontWeight: 500,
21261
+ letterSpacing: "0.15em",
21262
+ textTransform: "uppercase",
21263
+ color: "var(--brand-accent)",
21264
+ marginBottom: "1.5rem"
19882
21265
  },
19883
- linkPopover.key
19884
- ) : null,
19885
- floatingPanel && floatingPanel.kind === "socials-display" ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
19886
- FloatingPanel,
19887
- {
19888
- open: true,
19889
- title: floatingPanel.title,
19890
- context: floatingPanel.context,
19891
- position: floatingPanelPos,
19892
- onPositionChange: setFloatingPanelPos,
19893
- parentScroll: parentScrollSnap ?? parentScrollRef.current,
19894
- onClose: closeFloatingPanelOnly,
19895
- children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
19896
- SocialsDisplayPanel,
19897
- {
19898
- display: socialsDisplayFor(floatingPanel.row, editContentRef.current),
19899
- onChange: (next) => {
19900
- changeSocialsDisplay(floatingPanel.row, next);
19901
- setFloatingPanel({ ...floatingPanel });
19902
- }
19903
- }
19904
- )
19905
- }
19906
- ) : null
19907
- ] }),
19908
- bridgeRoot
19909
- ) : null;
21266
+ children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_link.default, { href: homeHref, style: { color: "inherit" }, children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { ...eyebrowKey ? { "data-ohw-editable": "text", "data-ohw-key": eyebrowKey } : {}, children: "Home" }) })
21267
+ }
21268
+ ),
21269
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
21270
+ "h1",
21271
+ {
21272
+ style: {
21273
+ fontFamily: "var(--brand-font-heading)",
21274
+ fontSize: "clamp(2rem, 3.5vw, 2.75rem)",
21275
+ lineHeight: 1.1,
21276
+ letterSpacing: "-0.025em",
21277
+ color: "var(--brand-text)",
21278
+ marginBottom: "1rem"
21279
+ },
21280
+ ...titleKey ? { "data-ohw-editable": "text", "data-ohw-key": titleKey, "data-ohw-max-length": 80 } : {},
21281
+ children: title
21282
+ }
21283
+ ),
21284
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
21285
+ "p",
21286
+ {
21287
+ style: {
21288
+ fontFamily: "var(--brand-font-body)",
21289
+ fontSize: "1rem",
21290
+ lineHeight: 1.7,
21291
+ fontWeight: 300,
21292
+ color: "var(--brand-text-muted)",
21293
+ maxWidth: "340px"
21294
+ },
21295
+ ...subtitleKey ? { "data-ohw-editable": "text", "data-ohw-key": subtitleKey, "data-ohw-max-length": 160 } : {},
21296
+ children: "This page doesn't have any content yet."
21297
+ }
21298
+ )
21299
+ ] });
19910
21300
  }
19911
21301
  // Annotate the CommonJS export names for ESM import in node:
19912
21302
  0 && (module.exports = {
@@ -19925,6 +21315,7 @@ function OhhwellsBridge() {
19925
21315
  DropdownMenuItem,
19926
21316
  DropdownMenuSeparator,
19927
21317
  DropdownMenuTrigger,
21318
+ EmptySection,
19928
21319
  ItemActionToolbar,
19929
21320
  ItemInteractionLayer,
19930
21321
  LinkEditorPanel,