@ohhwells/bridge 0.1.64-next.177 → 0.1.64

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -96,7 +96,6 @@ function applyTreeToState(state, payload) {
96
96
  const entry = {
97
97
  id: payload.id,
98
98
  label: payload.label ?? "Generated section",
99
- ...typeof payload.path === "string" && payload.path ? { path: payload.path } : {},
100
99
  afterSection: payload.mode === "insert" && !insertBefore ? payload.targetSectionId ?? null : null,
101
100
  ...insertBefore && payload.targetSectionId ? { beforeSection: payload.targetSectionId } : {},
102
101
  ...payload.mode === "replace" && payload.targetSectionId ? { replaces: payload.targetSectionId } : {},
@@ -119,317 +118,6 @@ function deleteSectionFromState(state, sectionId) {
119
118
  return { ...state, v: 1, sections: state.sections, removed: [...removed, sectionId] };
120
119
  }
121
120
 
122
- // src/lib/brand-chrome.ts
123
- var BRAND_NAME_KEY = "__ohw_brand_name";
124
- var BRAND_TITLE_KEY = "__ohw_site_title";
125
- var BRAND_FAVICON_LETTER_KEY = "__ohw_favicon_letter";
126
- var BRAND_CHROME_KEYS = /* @__PURE__ */ new Set([
127
- BRAND_NAME_KEY,
128
- BRAND_TITLE_KEY,
129
- BRAND_FAVICON_LETTER_KEY
130
- ]);
131
- function upsertMeta(selector, attr, token, value) {
132
- let el = document.head.querySelector(selector);
133
- if (!el) {
134
- el = document.createElement("meta");
135
- el.setAttribute(attr, token);
136
- document.head.appendChild(el);
137
- }
138
- if (el.getAttribute("content") !== value) el.setAttribute("content", value);
139
- }
140
- function escapeXml(value) {
141
- return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
142
- }
143
- function applyLetterFavicon(letter) {
144
- 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>`;
145
- const href = `data:image/svg+xml,${encodeURIComponent(svg)}`;
146
- let link = document.head.querySelector('link[rel="icon"]');
147
- if (!link) {
148
- link = document.createElement("link");
149
- link.rel = "icon";
150
- document.head.appendChild(link);
151
- }
152
- link.type = "image/svg+xml";
153
- if (link.href !== href) link.href = href;
154
- }
155
- function applyBrandChrome(content) {
156
- const name = content[BRAND_NAME_KEY];
157
- if (typeof name === "string" && name.length > 0) {
158
- document.querySelectorAll("[data-ohw-wordmark]").forEach((el) => {
159
- if (el.textContent !== name) el.textContent = name;
160
- if (el.getAttribute("title") !== name) el.setAttribute("title", name);
161
- });
162
- }
163
- const title = content[BRAND_TITLE_KEY];
164
- if (typeof title === "string" && title.length > 0) {
165
- if (document.title !== title) document.title = title;
166
- upsertMeta('meta[property="og:title"]', "property", "og:title", title);
167
- upsertMeta('meta[name="twitter:title"]', "name", "twitter:title", title);
168
- }
169
- const letter = content[BRAND_FAVICON_LETTER_KEY];
170
- if (typeof letter === "string" && letter.length > 0) {
171
- applyLetterFavicon(letter);
172
- }
173
- }
174
-
175
- // src/lib/brand-kit.ts
176
- var BRAND_KIT_KEY = "__ohw_brand";
177
- var BRAND_VAR_PREFIX = "--ohw-brand-";
178
- var BRAND_VAR_NAMES = ["primary", "accent", "light", "dark", "surface", "border", "muted"].map(
179
- (role) => `${BRAND_VAR_PREFIX}${role}`
180
- );
181
- var FONT_VARS = {
182
- heading: ["--font-heading", "--font-display", "--brand-font-heading"],
183
- body: ["--font-body", "--brand-font-body"]
184
- };
185
- var BRAND_FONT_LINK_ID = "ohw-brand-fonts";
186
- function brandColorVars(kit) {
187
- const { dark, primary, accent, light } = kit.palette;
188
- const mix = (a, aPct, b) => `color-mix(in srgb, ${a} ${aPct}%, ${b})`;
189
- return {
190
- [`${BRAND_VAR_PREFIX}primary`]: primary,
191
- [`${BRAND_VAR_PREFIX}accent`]: accent,
192
- [`${BRAND_VAR_PREFIX}light`]: light,
193
- [`${BRAND_VAR_PREFIX}dark`]: dark,
194
- [`${BRAND_VAR_PREFIX}surface`]: mix(light, 95, dark),
195
- [`${BRAND_VAR_PREFIX}border`]: mix(light, 85, dark),
196
- [`${BRAND_VAR_PREFIX}muted`]: mix(dark, 62, light)
197
- };
198
- }
199
- function parseBrandKit(raw) {
200
- if (!raw) return null;
201
- try {
202
- const parsed = JSON.parse(raw);
203
- const p = parsed?.palette;
204
- const f = parsed?.fonts;
205
- 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") {
206
- return null;
207
- }
208
- return {
209
- palette: { dark: p.dark, primary: p.primary, accent: p.accent, light: p.light },
210
- fonts: { heading: f.heading, body: f.body }
211
- };
212
- } catch {
213
- return null;
214
- }
215
- }
216
- function familyOf(stack) {
217
- const first = stack.split(",")[0]?.trim() ?? "";
218
- return first.replace(/^['"]|['"]$/g, "");
219
- }
220
- function loadBrandFonts(families) {
221
- const unique = [...new Set(families.map((f) => f.trim()).filter(Boolean))];
222
- if (unique.length === 0) return;
223
- const spec = unique.map((f) => `${f.replace(/ /g, "+")}:400,500,600,700`).join("|");
224
- const href = `https://fonts.googleapis.com/css?family=${spec}&display=swap`;
225
- let link = document.getElementById(BRAND_FONT_LINK_ID);
226
- if (!link) {
227
- link = document.createElement("link");
228
- link.id = BRAND_FONT_LINK_ID;
229
- link.rel = "stylesheet";
230
- document.head.appendChild(link);
231
- }
232
- if (link.href !== href) link.href = href;
233
- }
234
- function applyBrandToDom(kit) {
235
- const root = document.documentElement;
236
- if (!kit) {
237
- for (const name of BRAND_VAR_NAMES) root.style.removeProperty(name);
238
- for (const name of [...FONT_VARS.heading, ...FONT_VARS.body]) root.style.removeProperty(name);
239
- document.getElementById(BRAND_FONT_LINK_ID)?.remove();
240
- return;
241
- }
242
- for (const [name, value] of Object.entries(brandColorVars(kit))) root.style.setProperty(name, value);
243
- for (const name of FONT_VARS.heading) root.style.setProperty(name, kit.fonts.heading);
244
- for (const name of FONT_VARS.body) root.style.setProperty(name, kit.fonts.body);
245
- loadBrandFonts([familyOf(kit.fonts.heading), familyOf(kit.fonts.body)]);
246
- }
247
-
248
- // src/lib/section-styles.ts
249
- var STYLE_STORE_KEY = "__ohw_styles";
250
- var STYLE_SHEET_ID = "ohw-section-styles";
251
- function parseStyleStore(raw) {
252
- if (!raw) return null;
253
- try {
254
- const parsed = JSON.parse(raw);
255
- if (parsed?.v !== 1) return null;
256
- return {
257
- v: 1,
258
- sections: typeof parsed.sections === "object" && parsed.sections ? parsed.sections : {},
259
- nodes: typeof parsed.nodes === "object" && parsed.nodes ? parsed.nodes : {}
260
- };
261
- } catch {
262
- return null;
263
- }
264
- }
265
- var BG_VALUES = {
266
- surface: "color-mix(in srgb, var(--ohw-brand-light, var(--color-light, #ECECEB)) 94%, var(--ohw-brand-dark, var(--color-dark, #0C0A09)))",
267
- accent: "var(--ohw-brand-primary, var(--color-primary, #0078E5))",
268
- "accent-soft": "color-mix(in srgb, var(--ohw-brand-primary, var(--color-primary, #0078E5)) 12%, var(--ohw-brand-light, var(--color-light, #FFFFFF)))"
269
- };
270
- var HEADLINE_SIZES = { md: 24, lg: 32, xl: 40, display: 56 };
271
- function styleSheetCss() {
272
- const rules = [];
273
- for (const [tone, value] of Object.entries(BG_VALUES)) {
274
- rules.push(`[data-ohw-style-bg="${tone}"] { background: ${value} !important; }`);
275
- }
276
- rules.push(
277
- `[data-ohw-style-bg="accent"] { color: var(--ohw-brand-light, var(--color-light, #FFFFFF)) !important; }`
278
- );
279
- rules.push(
280
- `[data-ohw-style-distribution="space-between"] { display: flex !important; flex-direction: column; justify-content: space-between; }`,
281
- `[data-ohw-style-distribution="center"] { display: flex !important; flex-direction: column; justify-content: center; }`
282
- );
283
- for (const [scale, size] of Object.entries(HEADLINE_SIZES)) {
284
- rules.push(
285
- `[data-ohw-style-headline="${scale}"] :is(h1, h2, h3) { font-size: ${size}px !important; line-height: 1.15 !important; }`
286
- );
287
- }
288
- rules.push(
289
- `[data-ohw-style-aspect="fill-height"] img { height: 100% !important; aspect-ratio: auto !important; object-fit: cover; }`
290
- );
291
- for (const aspect of ["1:1", "4:5", "3:4", "3:2", "16:9", "2:1", "3:1"]) {
292
- rules.push(
293
- `[data-ohw-style-aspect="${aspect.replace(":", "-")}"] img { aspect-ratio: ${aspect.replace(":", " / ")} !important; height: auto !important; object-fit: cover; }`
294
- );
295
- }
296
- const pad = { tight: 40, balanced: 64, airy: 96 };
297
- for (const [spacing, px] of Object.entries(pad)) {
298
- rules.push(
299
- `[data-ohw-style-spacing="${spacing}"] { padding-top: ${px}px !important; padding-bottom: ${px}px !important; }`
300
- );
301
- }
302
- return rules.join("\n");
303
- }
304
- var STYLE_FONT_LINK_ID = "ohw-style-fonts";
305
- function loadStyleFonts(families) {
306
- const unique = [...new Set(families.map((f) => f.trim()).filter(Boolean))];
307
- const existing = document.getElementById(STYLE_FONT_LINK_ID);
308
- if (unique.length === 0) {
309
- existing?.remove();
310
- return;
311
- }
312
- const spec = unique.map((f) => `${f.replace(/ /g, "+")}:400,500,600,700`).join("|");
313
- const href = `https://fonts.googleapis.com/css?family=${spec}&display=swap`;
314
- let link = existing;
315
- if (!link) {
316
- link = document.createElement("link");
317
- link.id = STYLE_FONT_LINK_ID;
318
- link.rel = "stylesheet";
319
- document.head.appendChild(link);
320
- }
321
- if (link.href !== href) link.href = href;
322
- }
323
- var SECTION_ATTRS = {
324
- sectionBackground: "data-ohw-style-bg",
325
- textDistribution: "data-ohw-style-distribution",
326
- headlineScale: "data-ohw-style-headline",
327
- imageAspect: "data-ohw-style-aspect",
328
- spacing: "data-ohw-style-spacing"
329
- };
330
- var NODE_WROTE_ATTR = "data-ohw-style-node";
331
- var NODE_PROPS = ["color", "font-family", "font-size", "background"];
332
- function saveInline(el, prop) {
333
- const attr = `data-ohw-style-prev-${prop}`;
334
- if (!el.hasAttribute(attr)) el.setAttribute(attr, el.style.getPropertyValue(prop));
335
- }
336
- function restoreInline(el, prop) {
337
- const attr = `data-ohw-style-prev-${prop}`;
338
- if (!el.hasAttribute(attr)) return;
339
- const prev = el.getAttribute(attr) ?? "";
340
- if (prev) el.style.setProperty(prop, prev);
341
- else el.style.removeProperty(prop);
342
- el.removeAttribute(attr);
343
- }
344
- function ensureStyleSheet() {
345
- let el = document.getElementById(STYLE_SHEET_ID);
346
- if (!el) {
347
- el = document.createElement("style");
348
- el.id = STYLE_SHEET_ID;
349
- document.head.appendChild(el);
350
- }
351
- const css = styleSheetCss();
352
- if (el.textContent !== css) el.textContent = css;
353
- }
354
- function clearSectionAttrs(root) {
355
- for (const attr of Object.values(SECTION_ATTRS)) {
356
- for (const el of Array.from(root.querySelectorAll(`[${attr}]`))) el.removeAttribute(attr);
357
- }
358
- for (const el of Array.from(root.querySelectorAll("[data-ohw-style-bgcolor]"))) {
359
- restoreInline(el, "background");
360
- el.removeAttribute("data-ohw-style-bgcolor");
361
- }
362
- }
363
- function clearNodeProps(root) {
364
- for (const el of Array.from(root.querySelectorAll(`[${NODE_WROTE_ATTR}]`))) {
365
- const h = el;
366
- for (const prop of NODE_PROPS) restoreInline(h, prop);
367
- h.removeAttribute(NODE_WROTE_ATTR);
368
- }
369
- }
370
- function buttonSurfaceOf(el) {
371
- return el.closest("a, button") ?? el;
372
- }
373
- function applyStylesToDom(store) {
374
- ensureStyleSheet();
375
- clearSectionAttrs(document);
376
- clearNodeProps(document);
377
- loadStyleFonts(
378
- store ? Object.values(store.nodes).flatMap((n) => n.fontFamily ? [n.fontFamily] : []) : []
379
- );
380
- if (!store) return;
381
- for (const [sectionId, override] of Object.entries(store.sections)) {
382
- const sections = document.querySelectorAll(
383
- `[data-ohw-section="${CSS.escape(sectionId)}"]`
384
- );
385
- for (const section of Array.from(sections)) {
386
- for (const [prop, attr] of Object.entries(SECTION_ATTRS)) {
387
- const value = override[prop];
388
- if (value === void 0) continue;
389
- if (prop === "sectionBackground" && override.sectionBackgroundColor !== void 0) continue;
390
- section.setAttribute(attr, String(value).replace(":", "-"));
391
- }
392
- if (override.sectionBackgroundColor !== void 0) {
393
- saveInline(section, "background");
394
- section.style.setProperty("background", override.sectionBackgroundColor, "important");
395
- section.setAttribute("data-ohw-style-bgcolor", "");
396
- }
397
- }
398
- }
399
- for (const [key, override] of Object.entries(store.nodes)) {
400
- const nodes = document.querySelectorAll(`[data-ohw-key="${CSS.escape(key)}"]`);
401
- for (const el of Array.from(nodes)) {
402
- if (override.color !== void 0) {
403
- saveInline(el, "color");
404
- el.style.setProperty("color", override.color, "important");
405
- el.setAttribute(NODE_WROTE_ATTR, "");
406
- }
407
- if (override.fontFamily !== void 0) {
408
- saveInline(el, "font-family");
409
- el.style.setProperty("font-family", `'${override.fontFamily}'`, "important");
410
- el.setAttribute(NODE_WROTE_ATTR, "");
411
- }
412
- if (override.fontSize !== void 0) {
413
- saveInline(el, "font-size");
414
- el.style.setProperty("font-size", `${override.fontSize}px`, "important");
415
- el.setAttribute(NODE_WROTE_ATTR, "");
416
- }
417
- if (override.buttonBackground !== void 0 || override.buttonText !== void 0) {
418
- const surface = buttonSurfaceOf(el);
419
- if (override.buttonBackground !== void 0) {
420
- saveInline(surface, "background");
421
- surface.style.setProperty("background", override.buttonBackground, "important");
422
- }
423
- if (override.buttonText !== void 0) {
424
- saveInline(surface, "color");
425
- surface.style.setProperty("color", override.buttonText, "important");
426
- }
427
- surface.setAttribute(NODE_WROTE_ATTR, "");
428
- }
429
- }
430
- }
431
- }
432
-
433
121
  // src/ui/ai-tree/aiSectionsManager.tsx
434
122
  import { flushSync } from "react-dom";
435
123
  import { createRoot } from "react-dom/client";
@@ -444,8 +132,7 @@ function lucideByName(name) {
444
132
  }
445
133
  var typeStyle = (spec, font) => ({
446
134
  fontFamily: font,
447
- // Headings shrink with the viewport (reaching full size around ~900px wide); body copy stays put.
448
- fontSize: spec.size >= 24 ? `clamp(${Math.max(18, Math.round(spec.size * 0.6))}px, ${(spec.size / 9).toFixed(2)}vw, ${spec.size}px)` : spec.size,
135
+ fontSize: spec.size,
449
136
  lineHeight: spec.line,
450
137
  fontWeight: spec.weight
451
138
  });
@@ -472,8 +159,6 @@ var AI_RESPONSIVE_CSS = [
472
159
  " [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
473
160
  " [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
474
161
  " [data-ai-responsive] [data-ai-section-inner] { padding-left: 20px !important; padding-right: 20px !important; }",
475
- " [data-ai-responsive] { overflow-x: hidden; }",
476
- " [data-ai-responsive] img { max-width: 100%; }",
477
162
  "}"
478
163
  ].join("\n");
479
164
  var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
@@ -1481,20 +1166,6 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1481
1166
  const overlayAlpha = { low: 0.25, medium: 0.45, high: 0.65 }[settings.overlayIntensity ?? "medium"];
1482
1167
  const backgroundUrl = isOverlay && settings.backgroundMedia ? ctx.resolveMedia(settings.backgroundMedia) : null;
1483
1168
  const bgAttrs = ctx.keyFor && isOverlay && settings.backgroundMedia ? { "data-ohw-key": ctx.keyFor("settings.backgroundMedia"), "data-ohw-editable": "bg-image" } : {};
1484
- const toneBackground = (() => {
1485
- const { dark, primary, light } = resolvedBrand.palette;
1486
- switch (settings.sectionBackground) {
1487
- case "surface":
1488
- return `color-mix(in srgb, ${light} 94%, ${dark})`;
1489
- case "accent":
1490
- return primary;
1491
- case "accent-soft":
1492
- return `color-mix(in srgb, ${primary} 12%, ${light})`;
1493
- default:
1494
- return void 0;
1495
- }
1496
- })();
1497
- const distributed = !isOverlay && settings.textDistribution;
1498
1169
  return /* @__PURE__ */ jsxs(
1499
1170
  "section",
1500
1171
  {
@@ -1504,11 +1175,10 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1504
1175
  style: {
1505
1176
  position: "relative",
1506
1177
  padding: `${pad}px 0`,
1507
- background: toneBackground ?? (isOverlay && !backgroundUrl ? "#DBEAFE" : void 0),
1178
+ background: isOverlay && !backgroundUrl ? "#DBEAFE" : void 0,
1508
1179
  backgroundImage: backgroundUrl ? `url(${backgroundUrl})` : void 0,
1509
1180
  backgroundSize: "cover",
1510
- backgroundPosition: "center",
1511
- color: settings.sectionBackground === "accent" ? resolvedBrand.palette.light : void 0
1181
+ backgroundPosition: "center"
1512
1182
  },
1513
1183
  children: [
1514
1184
  /* @__PURE__ */ jsx("style", { children: AI_RESPONSIVE_CSS }),
@@ -1532,24 +1202,10 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1532
1202
  display: "grid",
1533
1203
  gridTemplateColumns: "repeat(12, 1fr)",
1534
1204
  gap: AI_TREE_TOKENS.spacing6,
1535
- alignItems: distributed === "space-between" ? "stretch" : (distributed ?? settings.verticalPosition) === "top" ? "start" : "center",
1205
+ alignItems: settings.verticalPosition === "top" ? "start" : "center",
1536
1206
  marginTop: r2 > 0 ? AI_TREE_TOKENS.spacing8 : 0
1537
1207
  },
1538
- children: (row.blocks ?? []).map((block, b) => /* @__PURE__ */ jsx(
1539
- "div",
1540
- {
1541
- "data-ai-cell": "",
1542
- style: {
1543
- gridColumn: `span ${Math.min(12, Math.max(1, block.span))}`,
1544
- minWidth: 0,
1545
- // space-between: each column becomes a flex column whose content spreads over
1546
- // the full row height instead of clumping at the top.
1547
- ...distributed === "space-between" ? { display: "flex", flexDirection: "column", justifyContent: "space-between" } : {}
1548
- },
1549
- children: renderNode(block, ctx, `r${r2}.b${b}`)
1550
- },
1551
- b
1552
- ))
1208
+ children: (row.blocks ?? []).map((block, b) => /* @__PURE__ */ jsx("div", { style: { gridColumn: `span ${Math.min(12, Math.max(1, block.span))}`, minWidth: 0 }, children: renderNode(block, ctx, `r${r2}.b${b}`) }, b))
1553
1209
  },
1554
1210
  r2
1555
1211
  ))
@@ -1565,34 +1221,17 @@ import { jsx as jsx2 } from "react/jsx-runtime";
1565
1221
  var CONTAINER_ATTR = "data-ohw-ai-generated";
1566
1222
  var REPLACED_ATTR = "data-ohw-ai-replaced-by";
1567
1223
  var REMOVED_ATTR = "data-ohw-ai-removed";
1568
- function readRootVar(name) {
1569
- if (typeof document === "undefined") return "";
1570
- return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
1571
- }
1572
- function deriveBrandOverride() {
1573
- const dark = readRootVar("--ohw-brand-dark");
1574
- const primary = readRootVar("--ohw-brand-primary");
1575
- const light = readRootVar("--ohw-brand-light");
1576
- if (!dark || !primary || !light) return null;
1577
- const accent = readRootVar("--ohw-brand-accent");
1578
- const heading = readRootVar("--font-heading") || readRootVar("--font-display");
1579
- const body = readRootVar("--font-body");
1580
- return {
1581
- palette: { dark, primary, accent: accent || dark, light },
1582
- fonts: {
1583
- heading: heading || AI_DEFAULT_BRAND.fonts.heading,
1584
- body: body || AI_DEFAULT_BRAND.fonts.body
1585
- }
1586
- };
1587
- }
1588
1224
  function deriveTemplateBrand() {
1589
- const dark = readRootVar("--color-dark");
1590
- const primary = readRootVar("--color-primary");
1591
- const light = readRootVar("--color-light");
1225
+ if (typeof document === "undefined") return null;
1226
+ const cs = getComputedStyle(document.documentElement);
1227
+ const read = (name) => cs.getPropertyValue(name).trim();
1228
+ const dark = read("--color-dark");
1229
+ const primary = read("--color-primary");
1230
+ const light = read("--color-light");
1592
1231
  if (!dark || !primary || !light) return null;
1593
- const accent = readRootVar("--color-accent");
1594
- const heading = readRootVar("--font-heading") || readRootVar("--font-display");
1595
- const body = readRootVar("--font-body");
1232
+ const accent = read("--color-accent");
1233
+ const heading = read("--font-heading") || read("--font-display");
1234
+ const body = read("--font-body");
1596
1235
  return {
1597
1236
  palette: { dark, primary, accent: accent || dark, light },
1598
1237
  fonts: {
@@ -1684,12 +1323,8 @@ function syncReplacedOriginals(state) {
1684
1323
  }
1685
1324
  function applyAiSectionsToDom(state, options) {
1686
1325
  if (typeof document === "undefined") return;
1687
- const brandOverride = deriveBrandOverride();
1688
1326
  const templateBrand = deriveTemplateBrand();
1689
- const brandKey = brandOverride ? JSON.stringify(brandOverride) : "";
1690
- const pagePath = window.location.pathname;
1691
- const pageSections = state.sections.filter((entry) => !entry.path || entry.path === pagePath);
1692
- const activeIds = new Set(pageSections.map((entry) => entry.id));
1327
+ const activeIds = new Set(state.sections.map((entry) => entry.id));
1693
1328
  for (const [id, section] of mounted) {
1694
1329
  if (!activeIds.has(id)) {
1695
1330
  section.root.unmount();
@@ -1697,8 +1332,8 @@ function applyAiSectionsToDom(state, options) {
1697
1332
  mounted.delete(id);
1698
1333
  }
1699
1334
  }
1700
- for (const entry of pageSections) {
1701
- const serialized = JSON.stringify(entry) + brandKey;
1335
+ for (const entry of state.sections) {
1336
+ const serialized = JSON.stringify(entry);
1702
1337
  const existing = mounted.get(entry.id);
1703
1338
  if (existing && existing.serialized === serialized && existing.container.isConnected) {
1704
1339
  continue;
@@ -1712,7 +1347,6 @@ function applyAiSectionsToDom(state, options) {
1712
1347
  mounted.delete(entry.id);
1713
1348
  }
1714
1349
  container.setAttribute("data-ohw-section", entry.id);
1715
- container.setAttribute("data-ohw-instance", entry.id);
1716
1350
  container.setAttribute("data-ohw-section-label", entry.label);
1717
1351
  placeContainer(container, entry);
1718
1352
  const root = mounted.get(entry.id)?.root ?? createRoot(container);
@@ -1723,7 +1357,7 @@ function applyAiSectionsToDom(state, options) {
1723
1357
  AiTreeRenderer,
1724
1358
  {
1725
1359
  tree: entry.tree,
1726
- brand: brandOverride ?? entry.brand ?? options?.brand ?? templateBrand ?? void 0,
1360
+ brand: entry.brand ?? options?.brand ?? templateBrand ?? void 0,
1727
1361
  resolveMedia,
1728
1362
  editKeyPrefix: `ai.${entry.id}`
1729
1363
  }
@@ -2340,7 +1974,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
2340
1974
  const autoId = useId();
2341
1975
  const insertAfter = insertAfterProp ?? autoId;
2342
1976
  const [schedule, setSchedule] = useState2(null);
2343
- const [loading, setLoading] = useState2(initialScheduleId !== null);
1977
+ const [loading, setLoading] = useState2(true);
2344
1978
  const [inEditor, setInEditor] = useState2(false);
2345
1979
  const [isHovered, setIsHovered] = useState2(false);
2346
1980
  const [modalState, setModalState] = useState2(null);
@@ -2514,10 +2148,8 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
2514
2148
  "*"
2515
2149
  );
2516
2150
  };
2151
+ if (!inEditor && !loading && !schedule) return null;
2517
2152
  const sectionId = `scheduling-${insertAfter}`;
2518
- if (!inEditor && !loading && !schedule) {
2519
- return /* @__PURE__ */ jsx4("section", { "data-ohw-section": sectionId, "data-ohw-scheduling-anchor": insertAfter, style: { display: "none" } });
2520
- }
2521
2153
  return /* @__PURE__ */ jsxs3(
2522
2154
  "section",
2523
2155
  {
@@ -6774,14 +6406,14 @@ function showSuccess(form, message) {
6774
6406
  });
6775
6407
  return;
6776
6408
  }
6777
- const text = message ?? form.getAttribute("data-ohw-success-text") ?? DEFAULT_SUCCESS_TEXT;
6409
+ const text = message || form.getAttribute("data-ohw-success-text") || DEFAULT_SUCCESS_TEXT;
6778
6410
  const note = document.createElement("p");
6779
6411
  note.setAttribute("data-ohw-form-success", "");
6780
6412
  note.setAttribute("role", "status");
6781
6413
  note.innerHTML = text;
6782
6414
  form.replaceChildren(note);
6783
6415
  }
6784
- function showSubmitError(form) {
6416
+ function showSubmitError(form, message = "Something went wrong. Please try again.") {
6785
6417
  let note = form.querySelector("[data-ohw-form-error]");
6786
6418
  if (!note) {
6787
6419
  note = document.createElement("p");
@@ -6790,7 +6422,7 @@ function showSubmitError(form) {
6790
6422
  note.style.marginTop = "8px";
6791
6423
  form.appendChild(note);
6792
6424
  }
6793
- note.textContent = "Something went wrong. Please try again.";
6425
+ note.textContent = message;
6794
6426
  note.style.display = "";
6795
6427
  }
6796
6428
  function clearSubmitError(form) {
@@ -6816,6 +6448,9 @@ function ensureSuccessTextEl(form, formKey, initialText) {
6816
6448
  form.appendChild(el);
6817
6449
  return el;
6818
6450
  }
6451
+ function successInitialFor(form, formKey, content) {
6452
+ return content[formSuccessKey(formKey)] || form.getAttribute("data-ohw-success-text") || DEFAULT_SUCCESS_TEXT;
6453
+ }
6819
6454
  function setFormViewState(form, formKey, state, initialText) {
6820
6455
  const success = ensureSuccessTextEl(form, formKey, initialText);
6821
6456
  Array.from(form.children).forEach((child) => {
@@ -6833,38 +6468,54 @@ function setFormViewState(form, formKey, state, initialText) {
6833
6468
  success.style.display = state === "success" ? "" : "none";
6834
6469
  }
6835
6470
  var BOUND_ATTR = "data-ohw-form-bound";
6471
+ var publishedFormContext = {
6472
+ apiUrl: "",
6473
+ subdomain: "",
6474
+ content: {}
6475
+ };
6836
6476
  function bindPublishedForms(apiUrl, subdomain, content = {}) {
6837
- document.querySelectorAll(FORM_SELECTOR).forEach((form) => {
6838
- if (form.hasAttribute(BOUND_ATTR)) return;
6839
- if (!(form instanceof HTMLFormElement)) return;
6840
- const formKey = formKeyOf(form);
6841
- if (!formKey) return;
6842
- form.setAttribute(BOUND_ATTR, "");
6843
- form.addEventListener("submit", async (e) => {
6477
+ publishedFormContext = { apiUrl, subdomain, content };
6478
+ if (document.documentElement.hasAttribute(BOUND_ATTR)) return;
6479
+ document.documentElement.setAttribute(BOUND_ATTR, "");
6480
+ document.addEventListener(
6481
+ "submit",
6482
+ async (e) => {
6483
+ const target = e.target;
6484
+ const form = target?.closest(FORM_SELECTOR);
6485
+ if (!form || !(form instanceof HTMLFormElement)) return;
6486
+ const formKey = formKeyOf(form);
6487
+ if (!formKey) return;
6844
6488
  e.preventDefault();
6845
6489
  clearSubmitError(form);
6490
+ const filled = collectFormFields(form);
6491
+ if (!Object.values(filled).some((value) => value.trim() !== "")) {
6492
+ showSubmitError(form, "Please fill in at least one field.");
6493
+ return;
6494
+ }
6846
6495
  const submitButton = form.querySelector(
6847
6496
  'button[type="submit"], input[type="submit"], button:not([type])'
6848
6497
  );
6849
6498
  if (submitButton) submitButton.disabled = true;
6850
6499
  try {
6851
- const response = await fetch(`${apiUrl}/api/public/sites/${subdomain}/forms/submissions`, {
6500
+ const { apiUrl: api, subdomain: site, content: latest } = publishedFormContext;
6501
+ const response = await fetch(`${api}/api/public/sites/${site}/forms/submissions`, {
6852
6502
  method: "POST",
6853
6503
  headers: { "Content-Type": "application/json" },
6854
6504
  body: JSON.stringify({
6855
6505
  formKey,
6856
- fields: collectFormFields(form),
6506
+ fields: filled,
6857
6507
  hasLongText: formHasLongText(form)
6858
6508
  })
6859
6509
  });
6860
6510
  if (!response.ok) throw new Error(`submit failed: ${response.status}`);
6861
- showSuccess(form, content[formSuccessKey(formKey)]);
6511
+ showSuccess(form, latest[formSuccessKey(formKey)]);
6862
6512
  } catch {
6863
6513
  showSubmitError(form);
6864
6514
  if (submitButton) submitButton.disabled = false;
6865
6515
  }
6866
- });
6867
- });
6516
+ },
6517
+ true
6518
+ );
6868
6519
  }
6869
6520
 
6870
6521
  // src/lib/form-fields.ts
@@ -6882,6 +6533,7 @@ var FIELD_TYPES = [
6882
6533
  ];
6883
6534
  var FIELD_ATTR = "data-ohw-form-field";
6884
6535
  var FIELD_TYPE_ATTR = "data-ohw-field-type";
6536
+ var PLACEHOLDER_EDIT_ATTR = "data-ohw-placeholder-edit";
6885
6537
  function fieldsKey(formKey) {
6886
6538
  return `${formKey}-fields`;
6887
6539
  }
@@ -6919,7 +6571,7 @@ function ensureFieldLabel(wrapper, key) {
6919
6571
  label.style.lineHeight = "1.3";
6920
6572
  const fromPlaceholder = input?.getAttribute("placeholder")?.trim();
6921
6573
  const text = fromPlaceholder && fromPlaceholder.length < 40 ? fromPlaceholder : key.replace(/[-_]/g, " ");
6922
- label.textContent = input?.hasAttribute("required") ? `${text} *` : text;
6574
+ label.textContent = text;
6923
6575
  if (input && input.parentElement === wrapper) wrapper.insertBefore(label, input);
6924
6576
  else wrapper.insertBefore(label, wrapper.firstChild);
6925
6577
  return label;
@@ -6975,8 +6627,11 @@ function readFieldsFromDom(form) {
6975
6627
  return {
6976
6628
  key: fieldKeyOf(wrapper),
6977
6629
  type: fieldTypeOf(wrapper),
6978
- label: fieldLabelOf(wrapper)?.textContent?.trim() ?? "",
6979
- placeholder: input?.getAttribute("placeholder") ?? "",
6630
+ label: fieldLabelText(fieldLabelOf(wrapper)).trim(),
6631
+ // While a field is selected its placeholder text lives in the value (see
6632
+ // beginPlaceholderEdit), so reading the attribute would store an empty one — whoever
6633
+ // happens to save at that moment must still record what the owner typed.
6634
+ placeholder: input ? input.hasAttribute(PLACEHOLDER_EDIT_ATTR) ? input.value : input.getAttribute("placeholder") ?? "" : "",
6980
6635
  required: Boolean(input?.hasAttribute("required"))
6981
6636
  };
6982
6637
  });
@@ -7004,7 +6659,7 @@ function applyFieldType(wrapper, type) {
7004
6659
  const followsDefaults = {
7005
6660
  // A label the owner wrote stays; one that still reads as a type name (or as the old
7006
6661
  // placeholder the template shipped) follows the new type.
7007
- label: isDefaultText(label?.textContent ?? "", (d) => d.label) || (label?.textContent ?? "").trim().replace(/\s*\*$/, "") === placeholder.trim()
6662
+ label: isDefaultText(label?.textContent ?? "", (d) => d.label) || fieldLabelText(label).trim() === placeholder.trim()
7008
6663
  };
7009
6664
  const { tag, inputType } = inputTagFor(type);
7010
6665
  wrapper.setAttribute(FIELD_TYPE_ATTR, type);
@@ -7018,8 +6673,7 @@ function applyFieldType(wrapper, type) {
7018
6673
  const applyDefaults = (el) => {
7019
6674
  el.setAttribute("placeholder", defaults.placeholder);
7020
6675
  if (label && followsDefaults.label) {
7021
- const required = (label.textContent ?? "").trim().endsWith("*");
7022
- label.textContent = required ? `${defaults.label} *` : defaults.label;
6676
+ label.textContent = defaults.label;
7023
6677
  }
7024
6678
  };
7025
6679
  if (input.tagName.toLowerCase() === tag) {
@@ -7039,13 +6693,19 @@ function applyFieldType(wrapper, type) {
7039
6693
  input.replaceWith(next);
7040
6694
  applyDefaults(next);
7041
6695
  }
6696
+ var REQUIRED_MARK_ATTR = "data-ohw-field-required";
6697
+ function fieldLabelText(label) {
6698
+ if (!label) return "";
6699
+ return (label.textContent ?? "").replace(/\s*\*\s*$/, "").trimEnd();
6700
+ }
7042
6701
  function syncRequiredMark(wrapper) {
7043
6702
  const label = fieldLabelOf(wrapper);
7044
6703
  if (!label) return;
7045
- const required = isFieldRequired(wrapper);
7046
- const base = (label.textContent ?? "").replace(/\s*\*\s*$/, "").trimEnd();
7047
- const next = required ? `${base} *` : base;
7048
- if (label.textContent !== next) label.textContent = next;
6704
+ label.querySelectorAll("[data-ohw-required-mark]").forEach((el) => el.remove());
6705
+ const words = fieldLabelText(label);
6706
+ if ((label.textContent ?? "") !== words) label.textContent = words;
6707
+ if (isFieldRequired(wrapper)) label.setAttribute(REQUIRED_MARK_ATTR, "");
6708
+ else label.removeAttribute(REQUIRED_MARK_ATTR);
7049
6709
  }
7050
6710
  function setFieldRequired(wrapper, required) {
7051
6711
  const input = fieldInputOf(wrapper);
@@ -7055,7 +6715,6 @@ function setFieldRequired(wrapper, required) {
7055
6715
  else input.removeAttribute("required");
7056
6716
  if (label) syncRequiredMark(wrapper);
7057
6717
  }
7058
- var PLACEHOLDER_EDIT_ATTR = "data-ohw-placeholder-edit";
7059
6718
  function beginPlaceholderEdit(wrapper) {
7060
6719
  const input = fieldInputOf(wrapper);
7061
6720
  if (!input || input.hasAttribute(PLACEHOLDER_EDIT_ATTR)) return;
@@ -7108,7 +6767,6 @@ function insertField(form, type) {
7108
6767
  const input = fieldInputOf(wrapper);
7109
6768
  if (input) {
7110
6769
  input.setAttribute("name", key);
7111
- input.setAttribute("data-ohw-key", key);
7112
6770
  input.removeAttribute("required");
7113
6771
  input.setAttribute("placeholder", defaults.placeholder);
7114
6772
  input.value = "";
@@ -7125,23 +6783,36 @@ function duplicateField(form, wrapper) {
7125
6783
  const input = fieldInputOf(copy);
7126
6784
  if (input) {
7127
6785
  input.setAttribute("name", key);
7128
- input.setAttribute("data-ohw-key", key);
6786
+ input.removeAttribute("data-ohw-key");
7129
6787
  input.value = "";
7130
6788
  }
6789
+ const label = fieldLabelOf(copy);
6790
+ if (label) label.setAttribute("data-ohw-key", `${key}-label`);
7131
6791
  wrapper.after(copy);
7132
6792
  return copy;
7133
6793
  }
7134
6794
  function removeField(wrapper) {
7135
6795
  wrapper.remove();
7136
6796
  }
7137
- function moveField(form, key, toIndex) {
6797
+ function applyFieldOrder(form, keys) {
7138
6798
  const wrappers = listFieldWrappers(form);
7139
- const moving = wrappers.find((wrapper) => fieldKeyOf(wrapper) === key);
7140
- if (!moving) return;
7141
- const rest = wrappers.filter((wrapper) => wrapper !== moving);
7142
- const target = rest[Math.max(0, Math.min(rest.length, toIndex))];
7143
- if (target) target.before(moving);
7144
- else rest[rest.length - 1]?.after(moving);
6799
+ const byKey = new Map(wrappers.map((wrapper) => [fieldKeyOf(wrapper), wrapper]));
6800
+ const ordered = keys.map((key) => byKey.get(key)).filter((w) => Boolean(w));
6801
+ if (ordered.length !== wrappers.length) return;
6802
+ const slots = wrappers.map((wrapper) => {
6803
+ const slot = document.createComment("ohw-field-slot");
6804
+ wrapper.replaceWith(slot);
6805
+ return slot;
6806
+ });
6807
+ ordered.forEach((wrapper, index) => slots[index].replaceWith(wrapper));
6808
+ }
6809
+ function moveField(form, key, toIndex) {
6810
+ const keys = listFieldWrappers(form).map(fieldKeyOf);
6811
+ const from = keys.indexOf(key);
6812
+ if (from === -1) return;
6813
+ const rest = keys.filter((k) => k !== key);
6814
+ rest.splice(Math.max(0, Math.min(rest.length, toIndex)), 0, key);
6815
+ applyFieldOrder(form, rest);
7145
6816
  }
7146
6817
  function reconcileFieldsFromContent(form, content) {
7147
6818
  const formKey = form.getAttribute("data-ohw-key");
@@ -7159,21 +6830,26 @@ function reconcileFieldsFromContent(form, content) {
7159
6830
  const input = fieldInputOf(wrapper);
7160
6831
  if (input) {
7161
6832
  input.setAttribute("name", spec.key);
7162
- input.setAttribute("data-ohw-key", spec.key);
7163
6833
  }
6834
+ fieldLabelOf(wrapper)?.setAttribute("data-ohw-key", `${spec.key}-label`);
7164
6835
  byKey.set(spec.key, wrapper);
7165
6836
  }
7166
6837
  applyFieldType(wrapper, spec.type);
7167
6838
  setFieldRequired(wrapper, spec.required);
7168
6839
  setFieldPlaceholder(wrapper, spec.placeholder);
7169
6840
  const label = fieldLabelOf(wrapper);
7170
- if (label && spec.label) label.textContent = spec.label;
6841
+ if (label && spec.label) label.textContent = spec.label.replace(/\s*\*\s*$/, "").trimEnd();
6842
+ syncRequiredMark(wrapper);
7171
6843
  });
7172
6844
  const wanted = new Set(stored.map((spec) => spec.key));
7173
6845
  listFieldWrappers(form).forEach((wrapper) => {
7174
6846
  if (!wanted.has(fieldKeyOf(wrapper))) wrapper.remove();
7175
6847
  });
7176
- stored.forEach((spec, index) => moveField(form, spec.key, index));
6848
+ const currentOrder = listFieldWrappers(form).map(fieldKeyOf).join("\0");
6849
+ const storedOrder = stored.map((spec) => spec.key).join("\0");
6850
+ if (currentOrder !== storedOrder) {
6851
+ applyFieldOrder(form, stored.map((spec) => spec.key));
6852
+ }
7177
6853
  }
7178
6854
 
7179
6855
  // src/ui/form-field-toolbar.tsx
@@ -7605,12 +7281,8 @@ function parseSectionsFromHtml(html) {
7605
7281
 
7606
7282
  // src/ui/ai-section/AiSectionOverlay.tsx
7607
7283
  import { Fragment as Fragment5, jsx as jsx17, jsxs as jsxs9 } from "react/jsx-runtime";
7608
- function findSectionElement(instanceId) {
7609
- const escaped = CSS.escape(instanceId);
7610
- return document.querySelector(`[data-ohw-instance="${escaped}"]`) ?? document.querySelector(`[data-ohw-section="${escaped}"]:not([data-ohw-instance])`);
7611
- }
7612
- function readRect(instanceId) {
7613
- const el = findSectionElement(instanceId);
7284
+ function readRect(sectionId) {
7285
+ const el = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
7614
7286
  if (!el) return null;
7615
7287
  const r2 = el.getBoundingClientRect();
7616
7288
  if (r2.width <= 0 || r2.height <= 0) return null;
@@ -7633,7 +7305,7 @@ function useLiveSectionRect(sectionId) {
7633
7305
  const opts = { capture: true, passive: true };
7634
7306
  window.addEventListener("scroll", update, opts);
7635
7307
  window.addEventListener("resize", update);
7636
- const el = findSectionElement(sectionId);
7308
+ const el = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
7637
7309
  const ro = el ? new ResizeObserver(update) : null;
7638
7310
  if (el && ro) ro.observe(el);
7639
7311
  const interval = setInterval(update, 500);
@@ -7646,14 +7318,6 @@ function useLiveSectionRect(sectionId) {
7646
7318
  }, [sectionId]);
7647
7319
  return rect;
7648
7320
  }
7649
- function computeSectionBoundaryFlags(instanceId) {
7650
- const topLevel = Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
7651
- (el) => !el.parentElement?.closest("[data-ohw-section]")
7652
- );
7653
- const index = topLevel.findIndex((el) => (el.dataset.ohwInstance ?? el.dataset.ohwSection) === instanceId);
7654
- if (index === -1) return { isFirst: true, isLast: true };
7655
- return { isFirst: index === 0, isLast: index === topLevel.length - 1 };
7656
- }
7657
7321
  var PRIMARY2 = "#0885FE";
7658
7322
  function edgeAwareRadius(rect) {
7659
7323
  const container = window.innerWidth <= 480 ? 16 : 24;
@@ -7727,7 +7391,6 @@ function AiSectionOverlay({
7727
7391
  }) {
7728
7392
  const [selectedId, setSelectedId] = useState4(null);
7729
7393
  const [reviewId, setReviewId] = useState4(null);
7730
- const [reviewButtonsHidden, setReviewButtonsHidden] = useState4(false);
7731
7394
  const reviewIdRef = useRef4(null);
7732
7395
  reviewIdRef.current = reviewId;
7733
7396
  const selectedIdRef = useRef4(null);
@@ -7736,7 +7399,7 @@ function AiSectionOverlay({
7736
7399
  (el) => {
7737
7400
  postToParent2({
7738
7401
  type: "ow:section-selected",
7739
- sectionId: el ? el.dataset.ohwInstance ?? el.dataset.ohwSection ?? null : null,
7402
+ sectionId: el?.dataset.ohwSection ?? null,
7740
7403
  sectionLabel: el ? el.dataset.ohwSectionLabel ?? titleCaseSectionId(el.dataset.ohwSection ?? "") : null
7741
7404
  });
7742
7405
  },
@@ -7745,7 +7408,7 @@ function AiSectionOverlay({
7745
7408
  const selectFromElement = useCallback2(
7746
7409
  (el, options) => {
7747
7410
  const sectionEl = el?.closest("[data-ohw-section]") ?? null;
7748
- const id = sectionEl ? sectionEl.dataset.ohwInstance ?? sectionEl.dataset.ohwSection ?? null : null;
7411
+ const id = sectionEl?.dataset.ohwSection ?? null;
7749
7412
  if (id === selectedIdRef.current) return;
7750
7413
  setSelectedId(id);
7751
7414
  if (options?.report !== false) report(sectionEl);
@@ -7786,10 +7449,9 @@ function AiSectionOverlay({
7786
7449
  }
7787
7450
  const found = readRect(sectionId) != null;
7788
7451
  setReviewId(found ? sectionId : null);
7789
- setReviewButtonsHidden(e.data.hideButtons === true);
7790
7452
  postToParent2({ type: "ow:ai-review-started", sectionId, found });
7791
7453
  if (found) {
7792
- document.querySelector(`[data-ohw-instance="${CSS.escape(sectionId)}"]`)?.scrollIntoView({ behavior: "smooth", block: "nearest" });
7454
+ document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`)?.scrollIntoView({ behavior: "smooth", block: "nearest" });
7793
7455
  }
7794
7456
  }
7795
7457
  };
@@ -7808,7 +7470,7 @@ function AiSectionOverlay({
7808
7470
  return;
7809
7471
  }
7810
7472
  const sec = t.closest("[data-ohw-section]");
7811
- setHoveredId(sec ? sec.dataset.ohwInstance ?? sec.dataset.ohwSection ?? null : null);
7473
+ setHoveredId(sec?.dataset.ohwSection ?? null);
7812
7474
  };
7813
7475
  const onLeave = () => setHoveredId(null);
7814
7476
  document.addEventListener("mousemove", onMove, { passive: true });
@@ -7840,29 +7502,9 @@ function AiSectionOverlay({
7840
7502
  },
7841
7503
  [postToParent2]
7842
7504
  );
7843
- const activeSelectionId = reviewId ? null : selectedId;
7844
- const selectionRect = useLiveSectionRect(activeSelectionId);
7505
+ const selectionRect = useLiveSectionRect(reviewId ? null : selectedId);
7845
7506
  const reviewRect = useLiveSectionRect(reviewId);
7846
7507
  const hoverRect = useLiveSectionRect(reviewId || hoveredId === selectedId ? null : hoveredId);
7847
- useEffect4(() => {
7848
- if (!activeSelectionId || !selectionRect) {
7849
- postToParent2({ type: "ow:section-rect", instanceId: null, rect: null });
7850
- return;
7851
- }
7852
- const { isFirst, isLast } = computeSectionBoundaryFlags(activeSelectionId);
7853
- postToParent2({
7854
- type: "ow:section-rect",
7855
- instanceId: activeSelectionId,
7856
- rect: {
7857
- top: selectionRect.top + window.scrollY,
7858
- left: selectionRect.left + window.scrollX,
7859
- width: selectionRect.width,
7860
- height: selectionRect.height
7861
- },
7862
- isFirst,
7863
- isLast
7864
- });
7865
- }, [activeSelectionId, selectionRect, postToParent2]);
7866
7508
  return /* @__PURE__ */ jsxs9(Fragment5, { children: [
7867
7509
  hoverRect && /* @__PURE__ */ jsx17(
7868
7510
  "div",
@@ -7913,16 +7555,13 @@ function AiSectionOverlay({
7913
7555
  border: `2px solid ${PRIMARY2}`,
7914
7556
  borderRadius: edgeAwareRadius(reviewRect),
7915
7557
  zIndex: 2147483200,
7916
- // The veil itself: swallows clicks so the section stays locked until decided. This
7917
- // stopPropagation only guards the bubble phase; the bridge's capture-phase click
7918
- // handlers additionally bail on `[data-ohw-ai-review]`, without which a click on
7919
- // Accept/Discard resolves to the media beneath and opens the file picker.
7558
+ // The veil itself: swallows clicks so the section stays locked until decided.
7920
7559
  background: "rgba(8, 133, 254, 0.04)",
7921
7560
  pointerEvents: "auto",
7922
7561
  cursor: "default"
7923
7562
  },
7924
7563
  onClick: (e) => e.stopPropagation(),
7925
- children: !reviewButtonsHidden && /* @__PURE__ */ jsxs9(
7564
+ children: /* @__PURE__ */ jsxs9(
7926
7565
  "div",
7927
7566
  {
7928
7567
  style: {
@@ -11827,296 +11466,42 @@ function deleteFooterColumn(column) {
11827
11466
  };
11828
11467
  }
11829
11468
 
11830
- // src/lib/logo-identity.ts
11831
- var LOGO_TEXT_KEYS = ["nav-logo-text", "footer-logo-text", "logo-text"];
11832
- var LOGO_IMAGE_KEYS = ["nav-logo-image", "footer-logo", "footer-logo-image"];
11833
- var LOGO_HREF_KEYS = ["nav-logo-href", "footer-logo-href", "logo-href"];
11834
- var LOGO_PLACEHOLDER_KEY = "logo-is-placeholder";
11835
- var LOGO_ALT_KEY = "logo-alt";
11836
- var LOGO_IMAGE_URL_KEY = "nav-logo-image";
11837
- var PLACEHOLDER_BUSINESS_NAME = "Business name";
11838
- function resolveLogoDisplayText(text) {
11839
- const trimmed = (text ?? "").trim();
11840
- return trimmed || PLACEHOLDER_BUSINESS_NAME;
11841
- }
11842
- function isFooterLogoRoot(root) {
11843
- return Boolean(root.closest("footer") || root.closest('[data-ohw-section="footer"]'));
11844
- }
11845
- function imageKeyForRoot(root) {
11846
- return isFooterLogoRoot(root) ? "footer-logo" : "nav-logo-image";
11847
- }
11848
- function textKeyForRoot(root) {
11849
- return isFooterLogoRoot(root) ? "footer-logo-text" : "nav-logo-text";
11850
- }
11851
- function ensureLogoHrefKey(root) {
11852
- if (!(root instanceof HTMLAnchorElement)) return;
11853
- if (root.hasAttribute("data-ohw-href-key")) return;
11854
- root.setAttribute("data-ohw-href-key", isFooterLogoRoot(root) ? "footer-logo-href" : "nav-logo-href");
11855
- }
11856
- function applyLogoIdentity(text, isPlaceholder) {
11857
- const display = resolveLogoDisplayText(text);
11858
- const placeholder = isPlaceholder || !text.trim() || display === PLACEHOLDER_BUSINESS_NAME;
11859
- for (const key of LOGO_TEXT_KEYS) {
11860
- document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
11861
- if (el.textContent !== display) el.textContent = display;
11862
- });
11863
- }
11864
- for (const key of LOGO_IMAGE_KEYS) {
11865
- document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
11866
- const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
11867
- if (img) img.alt = display;
11868
- });
11869
- }
11870
- document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((el) => {
11871
- if (placeholder) el.setAttribute("data-ohw-placeholder", "");
11872
- else el.removeAttribute("data-ohw-placeholder");
11873
- });
11874
- return display;
11875
- }
11876
- function applyLogoImage(url, alt) {
11877
- const displayAlt = resolveLogoDisplayText(alt);
11878
- document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((root) => {
11879
- ensureLogoHrefKey(root);
11880
- const imageKey = imageKeyForRoot(root);
11881
- const textKey = textKeyForRoot(root);
11882
- let img = root.querySelector(`img[data-ohw-key="${imageKey}"]`) ?? (root.querySelector(`[data-ohw-key="${imageKey}"]`) instanceof HTMLImageElement ? root.querySelector(`[data-ohw-key="${imageKey}"]`) : null) ?? root.querySelector("img");
11883
- let textEl = root.querySelector(`[data-ohw-key="${textKey}"]`) ?? root.querySelector('[data-ohw-key="logo-text"]');
11884
- if (url) {
11885
- if (!img) {
11886
- img = document.createElement("img");
11887
- img.setAttribute("data-ohw-editable", "image");
11888
- img.setAttribute("data-ohw-key", imageKey);
11889
- img.alt = displayAlt;
11890
- img.style.height = "";
11891
- img.style.maxHeight = "none";
11892
- img.style.width = "auto";
11893
- img.style.display = "block";
11894
- img.style.objectFit = "contain";
11895
- root.insertBefore(img, root.firstChild);
11896
- } else {
11897
- img.setAttribute("data-ohw-editable", "image");
11898
- img.setAttribute("data-ohw-key", imageKey);
11899
- }
11900
- img.removeAttribute("srcset");
11901
- img.removeAttribute("sizes");
11902
- img.src = url;
11903
- img.alt = displayAlt;
11904
- img.style.display = "block";
11905
- if (textEl) textEl.style.display = "none";
11906
- root.removeAttribute("data-ohw-placeholder");
11907
- return;
11908
- }
11909
- if (img) {
11910
- img.removeAttribute("src");
11911
- img.removeAttribute("srcset");
11912
- img.removeAttribute("sizes");
11913
- img.alt = displayAlt;
11914
- img.style.display = "none";
11915
- }
11916
- if (!textEl) {
11917
- textEl = document.createElement("span");
11918
- textEl.setAttribute("data-ohw-editable", "plain");
11919
- textEl.setAttribute("data-ohw-key", textKey);
11920
- root.appendChild(textEl);
11921
- }
11922
- textEl.style.display = "";
11923
- if (textEl.textContent !== displayAlt) textEl.textContent = displayAlt;
11924
- if (!displayAlt.trim() || displayAlt === PLACEHOLDER_BUSINESS_NAME) {
11925
- root.setAttribute("data-ohw-placeholder", "");
11926
- } else {
11927
- root.removeAttribute("data-ohw-placeholder");
11928
- }
11929
- });
11930
- }
11931
- function applyLogoHref(href) {
11932
- const target = href.trim() || "/";
11933
- document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((root) => {
11934
- ensureLogoHrefKey(root);
11935
- if (root instanceof HTMLAnchorElement) {
11936
- root.setAttribute("href", target);
11937
- }
11938
- });
11939
- for (const key of LOGO_HREF_KEYS) setStoredLinkHref(key, target);
11940
- }
11941
- function readLogoIdentityFromDom() {
11942
- let imageUrl = null;
11943
- for (const key of LOGO_IMAGE_KEYS) {
11944
- const el = document.querySelector(`[data-ohw-key="${key}"]`);
11945
- const img = el instanceof HTMLImageElement ? el : el?.querySelector("img");
11946
- const attrSrc = img?.getAttribute("src")?.trim() ?? "";
11947
- if (attrSrc && !attrSrc.startsWith("data:") && img && img.style.display !== "none") {
11948
- imageUrl = img.currentSrc || img.src;
11949
- break;
11950
- }
11951
- }
11952
- let text = PLACEHOLDER_BUSINESS_NAME;
11953
- let isPlaceholder = true;
11954
- for (const key of LOGO_TEXT_KEYS) {
11955
- const el = document.querySelector(`[data-ohw-key="${key}"]`);
11956
- if (el?.textContent?.trim()) {
11957
- text = el.textContent.trim();
11958
- const logoRoot2 = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
11959
- isPlaceholder = logoRoot2?.hasAttribute("data-ohw-placeholder") === true || text === PLACEHOLDER_BUSINESS_NAME;
11960
- break;
11961
- }
11962
- }
11963
- if (imageUrl) {
11964
- const logoImg = document.querySelector(
11965
- '[data-ohw-key="nav-logo-image"], [data-ohw-key="footer-logo"]'
11966
- );
11967
- const alt = logoImg?.alt?.trim() || text;
11968
- isPlaceholder = false;
11969
- const hrefEl = document.querySelector(
11970
- 'a[data-ohw-role="logo"], a[data-ohw-logo], [data-ohw-role="logo"]'
11971
- );
11972
- const href2 = (hrefEl instanceof HTMLAnchorElement ? hrefEl.getAttribute("href") : null) || hrefEl?.closest("a")?.getAttribute("href") || "/";
11973
- return { text, isPlaceholder, imageUrl, href: href2, alt };
11974
- }
11975
- const logoRoot = document.querySelector('[data-ohw-role="logo"], [data-ohw-logo]');
11976
- const href = (logoRoot instanceof HTMLAnchorElement ? logoRoot.getAttribute("href") : null) || logoRoot?.closest("a")?.getAttribute("href") || "/";
11977
- return { text, isPlaceholder, imageUrl: null, href, alt: text };
11978
- }
11979
- function applyLogoFromContent(content) {
11980
- const hasLogoIdentity = LOGO_PLACEHOLDER_KEY in content || LOGO_TEXT_KEYS.some((key) => key in content) || LOGO_IMAGE_KEYS.some((key) => key in content) || LOGO_ALT_KEY in content || LOGO_HREF_KEYS.some((key) => key in content);
11981
- if (!hasLogoIdentity) return false;
11982
- const logoText = content[LOGO_TEXT_KEYS[0]] ?? content[LOGO_TEXT_KEYS[1]] ?? readLogoIdentityFromDom().text;
11983
- const logoAlt = content[LOGO_ALT_KEY] ?? logoText;
11984
- const rawLogoImage = content[LOGO_IMAGE_URL_KEY] ?? content["footer-logo"] ?? content["footer-logo-image"] ?? null;
11985
- const logoImageUrl = typeof rawLogoImage === "string" && rawLogoImage.trim() ? rawLogoImage.trim() : null;
11986
- const imageExplicitlyCleared = LOGO_IMAGE_KEYS.some((key) => key in content) && !logoImageUrl;
11987
- const logoIsPlaceholder = LOGO_PLACEHOLDER_KEY in content ? content[LOGO_PLACEHOLDER_KEY] !== "false" : !logoImageUrl && (!logoText.trim() || logoText === PLACEHOLDER_BUSINESS_NAME);
11988
- if (logoImageUrl) {
11989
- applyLogoImage(logoImageUrl, logoAlt);
11990
- } else {
11991
- if (imageExplicitlyCleared) applyLogoImage(null, logoAlt);
11992
- applyLogoIdentity(logoText, logoIsPlaceholder);
11993
- }
11994
- const logoHref = content["nav-logo-href"] ?? content["footer-logo-href"] ?? content["logo-href"];
11995
- if (typeof logoHref === "string" && logoHref.trim()) {
11996
- applyLogoHref(logoHref);
11997
- }
11998
- return true;
11999
- }
12000
-
12001
- // src/lib/logo-size.ts
12002
- var LOGO_SIZE_DEFAULTS = {
12003
- navbar: 28,
12004
- footer: 32
12005
- };
12006
- var LOGO_SIZE_MIN = 16;
12007
- var LOGO_SIZE_MAX = 80;
12008
- var LOGO_SIZE_DESKTOP_KEYS = {
12009
- navbar: "nav-logo-size",
12010
- footer: "footer-logo-size"
12011
- };
12012
- var LOGO_SIZE_MOBILE_KEYS = {
12013
- navbar: "nav-logo-size-mobile",
12014
- footer: "footer-logo-size-mobile"
12015
- };
12016
- var LOGO_SIZE_KEYS = [
12017
- LOGO_SIZE_DESKTOP_KEYS.navbar,
12018
- LOGO_SIZE_DESKTOP_KEYS.footer,
12019
- LOGO_SIZE_MOBILE_KEYS.navbar,
12020
- LOGO_SIZE_MOBILE_KEYS.footer
12021
- ];
12022
- function isFooterLogoRoot2(root) {
12023
- return Boolean(root.closest("footer") || root.closest('[data-ohw-section="footer"]'));
12024
- }
12025
- function getLogoPlacement(root) {
12026
- return isFooterLogoRoot2(root) ? "footer" : "navbar";
12027
- }
12028
- function parseLogoSizePx(raw, fallback) {
12029
- if (raw == null || raw === "") return fallback;
12030
- const n = Number.parseFloat(raw);
12031
- if (!Number.isFinite(n)) return fallback;
12032
- return Math.min(LOGO_SIZE_MAX, Math.max(LOGO_SIZE_MIN, Math.round(n)));
12033
- }
12034
- function isMobileLogoSizeFollowing(content, placement) {
12035
- const raw = content[LOGO_SIZE_MOBILE_KEYS[placement]];
12036
- return raw == null || raw.trim() === "";
12037
- }
12038
- function resolveDesktopLogoSize(content, placement) {
12039
- return parseLogoSizePx(content[LOGO_SIZE_DESKTOP_KEYS[placement]], LOGO_SIZE_DEFAULTS[placement]);
12040
- }
12041
- function resolveMobileLogoSize(content, placement) {
12042
- if (isMobileLogoSizeFollowing(content, placement)) {
12043
- return resolveDesktopLogoSize(content, placement);
12044
- }
12045
- return parseLogoSizePx(
12046
- content[LOGO_SIZE_MOBILE_KEYS[placement]],
12047
- resolveDesktopLogoSize(content, placement)
12048
- );
11469
+ // src/lib/add-footer-column.ts
11470
+ function buildFooterColumnEditContentPatch(result) {
11471
+ return {
11472
+ [result.headingKey]: result.heading,
11473
+ [result.hrefKey]: result.href,
11474
+ [result.labelKey]: result.label,
11475
+ [FOOTER_ORDER_KEY]: JSON.stringify(result.order)
11476
+ };
12049
11477
  }
12050
- function setRootSizeVars(root, desktopPx, mobilePx, following) {
12051
- root.style.setProperty("--ohw-logo-size", `${desktopPx}px`);
12052
- if (following) {
12053
- root.style.removeProperty("--ohw-logo-size-mobile");
12054
- } else {
12055
- root.style.setProperty("--ohw-logo-size-mobile", `${mobilePx}px`);
11478
+ function addFooterColumnWithPersist({
11479
+ postToParent: postToParent2
11480
+ }) {
11481
+ if (!canAddFooterColumn()) {
11482
+ postToParent2({
11483
+ type: "ow:toast",
11484
+ title: `Maximum ${MAX_FOOTER_COLUMNS} columns`,
11485
+ toastType: "error"
11486
+ });
11487
+ return null;
12056
11488
  }
12057
- root.querySelectorAll("img").forEach((img) => {
12058
- img.style.height = "";
12059
- img.style.maxHeight = "none";
12060
- img.style.width = "auto";
12061
- img.style.objectFit = "contain";
12062
- });
12063
- }
12064
- function applyLogoSizes(content) {
12065
- document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((root) => {
12066
- const placement = getLogoPlacement(root);
12067
- const desktop = resolveDesktopLogoSize(content, placement);
12068
- const following = isMobileLogoSizeFollowing(content, placement);
12069
- const mobile = following ? desktop : resolveMobileLogoSize(content, placement);
12070
- setRootSizeVars(root, desktop, mobile, following);
12071
- });
12072
- }
12073
- function applyLogoSizeToPlacement(placement, desktopPx, mobilePx, following) {
12074
- document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((root) => {
12075
- if (getLogoPlacement(root) !== placement) return;
12076
- setRootSizeVars(root, desktopPx, mobilePx, following);
11489
+ const result = insertFooterColumn();
11490
+ const patch = buildFooterColumnEditContentPatch(result);
11491
+ setStoredLinkHref(result.hrefKey, result.href);
11492
+ postToParent2({
11493
+ type: "ow:change",
11494
+ nodes: Object.entries(patch).map(([key, text]) => ({ key, text }))
12077
11495
  });
12078
- }
12079
- function logoHasUploadedImage(logoEl) {
12080
- if (logoEl.hasAttribute("data-ohw-placeholder")) return false;
12081
- const img = logoEl.querySelector('img[data-ohw-key="nav-logo-image"], img[data-ohw-key="footer-logo"], img[data-ohw-key="footer-logo-image"]') ?? logoEl.querySelector("img");
12082
- if (!img) return false;
12083
- const src = img.getAttribute("src")?.trim() ?? "";
12084
- if (!src || src.startsWith("data:")) return false;
12085
- if (img.style.display === "none") return false;
12086
- return true;
12087
- }
12088
- function getLogoInteractionRect(logoEl) {
12089
- if (logoHasUploadedImage(logoEl)) {
12090
- const img = logoEl.querySelector('img[data-ohw-key="nav-logo-image"], img[data-ohw-key="footer-logo"], img[data-ohw-key="footer-logo-image"]') ?? logoEl.querySelector("img");
12091
- if (img) {
12092
- const r2 = img.getBoundingClientRect();
12093
- if (r2.width > 0 && r2.height > 0) return r2;
12094
- }
12095
- }
12096
- const text = logoEl.querySelector(
12097
- '[data-ohw-key="nav-logo-text"], [data-ohw-key="footer-logo-text"]'
12098
- );
12099
- if (text) {
12100
- const style = window.getComputedStyle(text);
12101
- if (style.display !== "none" && style.visibility !== "hidden") {
12102
- const r2 = text.getBoundingClientRect();
12103
- if (r2.width > 0 && r2.height > 0) return r2;
12104
- }
12105
- }
12106
- return logoEl.getBoundingClientRect();
12107
- }
12108
- function readLogoSizeState(content, placement) {
12109
- const desktopPx = resolveDesktopLogoSize(content, placement);
12110
- const mobileFollowing = isMobileLogoSizeFollowing(content, placement);
12111
- const mobilePx = mobileFollowing ? desktopPx : resolveMobileLogoSize(content, placement);
12112
- return { desktopPx, mobilePx, mobileFollowing };
11496
+ postToParent2({ type: "ow:toast", title: "Item added", toastType: "success" });
11497
+ enforceLinkHrefs();
11498
+ return result;
12113
11499
  }
12114
11500
 
12115
11501
  // src/lib/site-wide-scope.ts
12116
11502
  function getLogoElement(el) {
12117
11503
  const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
12118
11504
  if (marked) return marked;
12119
- if (el.closest('[data-ohw-editable="icon"]')) return null;
12120
11505
  const root = el.closest("nav, [data-ohw-nav-root], footer");
12121
11506
  if (!root) return null;
12122
11507
  const anchor = el.closest("a");
@@ -12150,38 +11535,6 @@ function isSiteWideScopeActive(args) {
12150
11535
  return isSiteWideElement(args.selected) || isSiteWideElement(args.hoveredItem) || isSiteWideElement(args.hoveredNavContainer) || isSiteWideElement(args.active);
12151
11536
  }
12152
11537
 
12153
- // src/lib/add-footer-column.ts
12154
- function buildFooterColumnEditContentPatch(result) {
12155
- return {
12156
- [result.headingKey]: result.heading,
12157
- [result.hrefKey]: result.href,
12158
- [result.labelKey]: result.label,
12159
- [FOOTER_ORDER_KEY]: JSON.stringify(result.order)
12160
- };
12161
- }
12162
- function addFooterColumnWithPersist({
12163
- postToParent: postToParent2
12164
- }) {
12165
- if (!canAddFooterColumn()) {
12166
- postToParent2({
12167
- type: "ow:toast",
12168
- title: `Maximum ${MAX_FOOTER_COLUMNS} columns`,
12169
- toastType: "error"
12170
- });
12171
- return null;
12172
- }
12173
- const result = insertFooterColumn();
12174
- const patch = buildFooterColumnEditContentPatch(result);
12175
- setStoredLinkHref(result.hrefKey, result.href);
12176
- postToParent2({
12177
- type: "ow:change",
12178
- nodes: Object.entries(patch).map(([key, text]) => ({ key, text }))
12179
- });
12180
- postToParent2({ type: "ow:toast", title: "Item added", toastType: "success" });
12181
- enforceLinkHrefs();
12182
- return result;
12183
- }
12184
-
12185
11538
  // src/ui/FloatingPanel.tsx
12186
11539
  import {
12187
11540
  useCallback as useCallback5,
@@ -12371,127 +11724,16 @@ function FloatingPanel({
12371
11724
  );
12372
11725
  }
12373
11726
 
12374
- // src/ui/logo-size-panel.tsx
12375
- import { ArrowUpRight, Link as Link2 } from "lucide-react";
11727
+ // src/ui/socials-display-panel.tsx
12376
11728
  import { jsx as jsx28, jsxs as jsxs18 } from "react/jsx-runtime";
12377
- function SizeSlider({
12378
- value,
11729
+ function DisplaySwitch({
11730
+ label,
11731
+ checked,
11732
+ disabled,
12379
11733
  onChange
12380
11734
  }) {
12381
- const pct = (value - LOGO_SIZE_MIN) / (LOGO_SIZE_MAX - LOGO_SIZE_MIN) * 100;
12382
- return /* @__PURE__ */ jsxs18("div", { className: "flex w-full flex-col gap-3", children: [
12383
- /* @__PURE__ */ jsxs18("div", { className: "flex w-full items-center gap-2 text-sm font-medium leading-5", children: [
12384
- /* @__PURE__ */ jsx28("span", { className: "min-w-0 flex-1 text-foreground", children: "Size" }),
12385
- /* @__PURE__ */ jsxs18("span", { className: "shrink-0 whitespace-nowrap text-muted-foreground", children: [
12386
- value,
12387
- " px"
12388
- ] })
12389
- ] }),
12390
- /* @__PURE__ */ jsxs18("div", { className: "relative h-2 w-full rounded-full bg-primary-50", children: [
12391
- /* @__PURE__ */ jsx28(
12392
- "div",
12393
- {
12394
- className: "absolute inset-y-0 left-0 rounded-full bg-primary",
12395
- style: { width: `${pct}%` }
12396
- }
12397
- ),
12398
- /* @__PURE__ */ jsx28(
12399
- "input",
12400
- {
12401
- type: "range",
12402
- min: LOGO_SIZE_MIN,
12403
- max: LOGO_SIZE_MAX,
12404
- step: 1,
12405
- value,
12406
- "aria-label": "Logo size",
12407
- className: cn(
12408
- "absolute inset-0 h-full w-full cursor-pointer appearance-none bg-transparent",
12409
- "[&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:size-5",
12410
- "[&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border-2",
12411
- "[&::-webkit-slider-thumb]:border-primary [&::-webkit-slider-thumb]:bg-background",
12412
- "[&::-moz-range-thumb]:size-5 [&::-moz-range-thumb]:rounded-full",
12413
- "[&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-primary",
12414
- "[&::-moz-range-thumb]:bg-background"
12415
- ),
12416
- onChange: (e) => onChange(Number(e.target.value))
12417
- }
12418
- )
12419
- ] })
12420
- ] });
12421
- }
12422
- function LogoSizePanel({
12423
- viewport,
12424
- sizePx,
12425
- mobileFollowing = true,
12426
- onSizeChange,
12427
- onCustomizeMobile,
12428
- onResetMobile,
12429
- onUpdateEverywhere,
12430
- className
12431
- }) {
12432
- const showFollowing = viewport === "mobile" && mobileFollowing;
12433
- const showMobileSlider = viewport === "mobile" && !mobileFollowing;
12434
- const showDesktopSlider = viewport === "desktop";
12435
- return /* @__PURE__ */ jsxs18("div", { className: cn("flex w-full flex-col gap-4", className), children: [
12436
- showFollowing ? /* @__PURE__ */ jsxs18("div", { className: "flex w-full flex-col gap-2", children: [
12437
- /* @__PURE__ */ jsxs18("div", { className: "flex items-start gap-1", children: [
12438
- /* @__PURE__ */ jsx28(Link2, { size: 16, className: "mt-0.5 shrink-0 text-foreground", "aria-hidden": true }),
12439
- /* @__PURE__ */ jsx28("p", { className: "text-sm font-semibold leading-5 text-foreground", children: "Following desktop size" })
12440
- ] }),
12441
- /* @__PURE__ */ jsx28("p", { className: "text-sm leading-5 text-muted-foreground", children: "Mobile uses the desktop size until you customize it. Change the desktop size and it follows automatically." }),
12442
- /* @__PURE__ */ jsx28(
12443
- Button,
12444
- {
12445
- type: "button",
12446
- variant: "outline",
12447
- size: "sm",
12448
- className: "h-9 w-full min-w-0 cursor-pointer",
12449
- onClick: onCustomizeMobile,
12450
- children: "Customize for mobile"
12451
- }
12452
- )
12453
- ] }) : null,
12454
- showDesktopSlider || showMobileSlider ? /* @__PURE__ */ jsx28(SizeSlider, { value: sizePx, onChange: onSizeChange }) : null,
12455
- showMobileSlider ? /* @__PURE__ */ jsx28(
12456
- Button,
12457
- {
12458
- type: "button",
12459
- variant: "outline",
12460
- size: "sm",
12461
- className: "h-9 w-full min-w-0 cursor-pointer",
12462
- onClick: onResetMobile,
12463
- children: "Reset to desktop size"
12464
- }
12465
- ) : null,
12466
- /* @__PURE__ */ jsx28("div", { className: "h-px w-full bg-border", role: "separator" }),
12467
- /* @__PURE__ */ jsxs18(
12468
- Button,
12469
- {
12470
- type: "button",
12471
- variant: "outline",
12472
- size: "sm",
12473
- className: "h-9 w-full min-w-0 cursor-pointer gap-1",
12474
- onClick: onUpdateEverywhere,
12475
- children: [
12476
- "Update logo everywhere",
12477
- /* @__PURE__ */ jsx28(ArrowUpRight, { size: 16, "aria-hidden": true })
12478
- ]
12479
- }
12480
- ),
12481
- /* @__PURE__ */ jsx28("p", { className: "text-sm leading-5 text-muted-foreground", children: "Matches the right version to your background." })
12482
- ] });
12483
- }
12484
-
12485
- // src/ui/socials-display-panel.tsx
12486
- import { jsx as jsx29, jsxs as jsxs19 } from "react/jsx-runtime";
12487
- function DisplaySwitch({
12488
- label,
12489
- checked,
12490
- disabled,
12491
- onChange
12492
- }) {
12493
- return /* @__PURE__ */ jsxs19("div", { className: "flex w-full items-center gap-2", children: [
12494
- /* @__PURE__ */ jsx29(
11735
+ return /* @__PURE__ */ jsxs18("div", { className: "flex w-full items-center gap-2", children: [
11736
+ /* @__PURE__ */ jsx28(
12495
11737
  "span",
12496
11738
  {
12497
11739
  className: cn(
@@ -12501,7 +11743,7 @@ function DisplaySwitch({
12501
11743
  children: label
12502
11744
  }
12503
11745
  ),
12504
- /* @__PURE__ */ jsx29(
11746
+ /* @__PURE__ */ jsx28(
12505
11747
  "button",
12506
11748
  {
12507
11749
  type: "button",
@@ -12515,7 +11757,7 @@ function DisplaySwitch({
12515
11757
  checked ? "bg-primary" : "bg-primary-50",
12516
11758
  disabled ? "cursor-default opacity-50" : "cursor-pointer"
12517
11759
  ),
12518
- children: /* @__PURE__ */ jsx29(
11760
+ children: /* @__PURE__ */ jsx28(
12519
11761
  "span",
12520
11762
  {
12521
11763
  className: cn(
@@ -12529,8 +11771,8 @@ function DisplaySwitch({
12529
11771
  ] });
12530
11772
  }
12531
11773
  function SocialsDisplayPanel({ display, onChange, className }) {
12532
- return /* @__PURE__ */ jsxs19("div", { className: cn("flex w-full flex-col gap-3", className), children: [
12533
- /* @__PURE__ */ jsx29(
11774
+ return /* @__PURE__ */ jsxs18("div", { className: cn("flex w-full flex-col gap-3", className), children: [
11775
+ /* @__PURE__ */ jsx28(
12534
11776
  DisplaySwitch,
12535
11777
  {
12536
11778
  label: "Text",
@@ -12539,7 +11781,7 @@ function SocialsDisplayPanel({ display, onChange, className }) {
12539
11781
  onChange: (text) => onChange({ ...display, text })
12540
11782
  }
12541
11783
  ),
12542
- /* @__PURE__ */ jsx29(
11784
+ /* @__PURE__ */ jsx28(
12543
11785
  DisplaySwitch,
12544
11786
  {
12545
11787
  label: "Icon",
@@ -13100,7 +12342,7 @@ function useNavItemDrag({
13100
12342
 
13101
12343
  // src/ui/footer-container-chrome.tsx
13102
12344
  import { Plus as Plus2 } from "lucide-react";
13103
- import { jsx as jsx30, jsxs as jsxs20 } from "react/jsx-runtime";
12345
+ import { jsx as jsx29, jsxs as jsxs19 } from "react/jsx-runtime";
13104
12346
  function FooterContainerChrome({
13105
12347
  rect,
13106
12348
  onAdd,
@@ -13108,7 +12350,7 @@ function FooterContainerChrome({
13108
12350
  }) {
13109
12351
  const chromeGap = 6;
13110
12352
  const buttonMargin = 7;
13111
- return /* @__PURE__ */ jsx30(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsx30(
12353
+ return /* @__PURE__ */ jsx29(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsx29(
13112
12354
  "div",
13113
12355
  {
13114
12356
  "data-ohw-footer-container-chrome": "",
@@ -13120,8 +12362,8 @@ function FooterContainerChrome({
13120
12362
  width: rect.width + chromeGap * 2,
13121
12363
  height: rect.height + chromeGap * 2
13122
12364
  },
13123
- children: /* @__PURE__ */ jsxs20(Tooltip, { children: [
13124
- /* @__PURE__ */ jsx30(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx30(
12365
+ children: /* @__PURE__ */ jsxs19(Tooltip, { children: [
12366
+ /* @__PURE__ */ jsx29(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx29(
13125
12367
  "button",
13126
12368
  {
13127
12369
  type: "button",
@@ -13140,10 +12382,10 @@ function FooterContainerChrome({
13140
12382
  if (addDisabled) return;
13141
12383
  onAdd();
13142
12384
  },
13143
- children: /* @__PURE__ */ jsx30(Plus2, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
12385
+ children: /* @__PURE__ */ jsx29(Plus2, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
13144
12386
  }
13145
12387
  ) }),
13146
- /* @__PURE__ */ jsx30(TooltipContent, { side: "bottom", sideOffset: 9, children: addDisabled ? "Maximum columns reached" : "Add item" })
12388
+ /* @__PURE__ */ jsx29(TooltipContent, { side: "bottom", sideOffset: 9, children: addDisabled ? "Maximum columns reached" : "Add item" })
13147
12389
  ] })
13148
12390
  }
13149
12391
  ) });
@@ -13250,8 +12492,10 @@ function getLinkHref3(el) {
13250
12492
  }
13251
12493
  function collectEditableNodes(extraContent, root = document) {
13252
12494
  const isScoped = root !== document;
13253
- const editableEls = Array.from(root.querySelectorAll("[data-ohw-editable]"));
13254
- if (isScoped && root instanceof HTMLElement && root.matches("[data-ohw-editable]")) {
12495
+ const editableEls = Array.from(
12496
+ root.querySelectorAll('[data-ohw-editable]:not([data-ohw-editable="form"])')
12497
+ );
12498
+ if (isScoped && root instanceof HTMLElement && root.matches('[data-ohw-editable]:not([data-ohw-editable="form"])')) {
13255
12499
  editableEls.unshift(root);
13256
12500
  }
13257
12501
  const nodes = editableEls.map((el) => {
@@ -13326,18 +12570,6 @@ function collectEditableNodes(extraContent, root = document) {
13326
12570
  }
13327
12571
  if (extraContent && !isScoped) {
13328
12572
  applyNavFooterDeleteOverrides(byKey, extraContent);
13329
- for (const key of LOGO_IMAGE_KEYS) {
13330
- if (!(key in extraContent)) continue;
13331
- byKey.set(key, { key, type: "image", text: extraContent[key] ?? "" });
13332
- }
13333
- for (const key of [LOGO_PLACEHOLDER_KEY, LOGO_ALT_KEY]) {
13334
- if (!(key in extraContent)) continue;
13335
- byKey.set(key, { key, type: "meta", text: extraContent[key] ?? "" });
13336
- }
13337
- for (const key of LOGO_SIZE_KEYS) {
13338
- if (!(key in extraContent)) continue;
13339
- byKey.set(key, { key, type: "meta", text: extraContent[key] ?? "" });
13340
- }
13341
12573
  }
13342
12574
  return Array.from(byKey.values());
13343
12575
  }
@@ -13604,13 +12836,13 @@ function deleteSelectedNavFooterItem(deps) {
13604
12836
 
13605
12837
  // src/ui/navbar-container-chrome.tsx
13606
12838
  import { Plus as Plus3 } from "lucide-react";
13607
- import { jsx as jsx31 } from "react/jsx-runtime";
12839
+ import { jsx as jsx30 } from "react/jsx-runtime";
13608
12840
  function NavbarContainerChrome({
13609
12841
  rect,
13610
12842
  onAdd
13611
12843
  }) {
13612
12844
  const chromeGap = 6;
13613
- return /* @__PURE__ */ jsx31(
12845
+ return /* @__PURE__ */ jsx30(
13614
12846
  "div",
13615
12847
  {
13616
12848
  "data-ohw-navbar-container-chrome": "",
@@ -13622,7 +12854,7 @@ function NavbarContainerChrome({
13622
12854
  width: rect.width + chromeGap * 2,
13623
12855
  height: rect.height + chromeGap * 2
13624
12856
  },
13625
- children: /* @__PURE__ */ jsx31(
12857
+ children: /* @__PURE__ */ jsx30(
13626
12858
  "button",
13627
12859
  {
13628
12860
  type: "button",
@@ -13639,7 +12871,7 @@ function NavbarContainerChrome({
13639
12871
  e.stopPropagation();
13640
12872
  onAdd();
13641
12873
  },
13642
- children: /* @__PURE__ */ jsx31(Plus3, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
12874
+ children: /* @__PURE__ */ jsx30(Plus3, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
13643
12875
  }
13644
12876
  )
13645
12877
  }
@@ -13648,7 +12880,7 @@ function NavbarContainerChrome({
13648
12880
 
13649
12881
  // src/ui/drop-indicator.tsx
13650
12882
  import * as React10 from "react";
13651
- import { jsx as jsx32 } from "react/jsx-runtime";
12883
+ import { jsx as jsx31 } from "react/jsx-runtime";
13652
12884
  var dropIndicatorVariants = cva(
13653
12885
  "ov-gap-line pointer-events-none shrink-0 transition-opacity duration-150",
13654
12886
  {
@@ -13672,7 +12904,7 @@ var dropIndicatorVariants = cva(
13672
12904
  );
13673
12905
  var DropIndicator = React10.forwardRef(
13674
12906
  ({ className, direction, state, ...props }, ref) => {
13675
- return /* @__PURE__ */ jsx32(
12907
+ return /* @__PURE__ */ jsx31(
13676
12908
  "div",
13677
12909
  {
13678
12910
  ref,
@@ -13689,7 +12921,7 @@ var DropIndicator = React10.forwardRef(
13689
12921
  DropIndicator.displayName = "DropIndicator";
13690
12922
 
13691
12923
  // src/ui/badge.tsx
13692
- import { jsx as jsx33 } from "react/jsx-runtime";
12924
+ import { jsx as jsx32 } from "react/jsx-runtime";
13693
12925
  var badgeVariants = cva(
13694
12926
  "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
13695
12927
  {
@@ -13707,12 +12939,12 @@ var badgeVariants = cva(
13707
12939
  }
13708
12940
  );
13709
12941
  function Badge({ className, variant, ...props }) {
13710
- return /* @__PURE__ */ jsx33("div", { className: cn(badgeVariants({ variant }), className), ...props });
12942
+ return /* @__PURE__ */ jsx32("div", { className: cn(badgeVariants({ variant }), className), ...props });
13711
12943
  }
13712
12944
 
13713
12945
  // src/OhhwellsBridge.tsx
13714
- import { Link as Link3, Plus as Plus4, Settings, Table } from "lucide-react";
13715
- import { Fragment as Fragment8, jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
12946
+ import { Link as Link2, Plus as Plus4, Settings } from "lucide-react";
12947
+ import { Fragment as Fragment8, jsx as jsx33, jsxs as jsxs20 } from "react/jsx-runtime";
13716
12948
  var PRIMARY3 = "#0885FE";
13717
12949
  var IMAGE_FADE_MS = 300;
13718
12950
  function runOpacityFade(el, onDone) {
@@ -13806,10 +13038,21 @@ function parseSchedulingInsertAfter(insertAfter) {
13806
13038
  insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
13807
13039
  };
13808
13040
  }
13809
- function resolveEntryAnchor(entry) {
13810
- if (entry.anchorId !== void 0) return { anchorId: entry.anchorId, beforeId: entry.beforeId ?? null };
13811
- const parsed = parseSchedulingInsertAfter(entry.insertAfter);
13812
- return { anchorId: parsed.anchor, beforeId: parsed.insertBefore };
13041
+ function resolveSchedulingInsert(insertAfter, explicitInsertBefore) {
13042
+ const parsed = parseSchedulingInsertAfter(insertAfter);
13043
+ const insertBefore = explicitInsertBefore ?? parsed.insertBefore;
13044
+ const effectiveInsertAfter = insertBefore && parsed.insertBefore === null ? `${insertAfter}${INSERT_BEFORE_MARKER}${insertBefore}` : insertAfter;
13045
+ return { effectiveInsertAfter, insertBefore };
13046
+ }
13047
+ function getSchedulingMountPoint(insertAfter) {
13048
+ const { anchor } = parseSchedulingInsertAfter(insertAfter);
13049
+ let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
13050
+ if (!anchorEl && anchor === "scheduling") {
13051
+ const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
13052
+ anchorEl = widgets.at(-1) ?? null;
13053
+ }
13054
+ if (!anchorEl) return null;
13055
+ return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
13813
13056
  }
13814
13057
  function schedulingMountDepth(insertAfter) {
13815
13058
  if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
@@ -13826,7 +13069,8 @@ function getPageSchedulingEntries(raw) {
13826
13069
  }
13827
13070
  }
13828
13071
  function isSchedulingWidgetMissing(entry) {
13829
- return !document.querySelector(`[data-ohw-section="${schedulingSectionId(entry.insertAfter)}"]`);
13072
+ const { effectiveInsertAfter } = resolveSchedulingInsert(entry.insertAfter);
13073
+ return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
13830
13074
  }
13831
13075
  function hasMissingSchedulingWidgets(entries) {
13832
13076
  return entries.some(isSchedulingWidgetMissing);
@@ -13856,17 +13100,16 @@ function initSectionsFromContent(content, removeExisting = false) {
13856
13100
  } catch {
13857
13101
  }
13858
13102
  }
13859
- function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, beforeId, existingWidgetId) {
13860
- const widgetId = existingWidgetId ?? (beforeId ? `${anchorId}${INSERT_BEFORE_MARKER}${beforeId}` : anchorId);
13861
- const sectionId = schedulingSectionId(widgetId);
13103
+ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId, explicitInsertBefore) {
13104
+ const { effectiveInsertAfter, insertBefore } = resolveSchedulingInsert(insertAfter, explicitInsertBefore);
13105
+ const sectionId = schedulingSectionId(effectiveInsertAfter);
13862
13106
  if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
13863
- const anchorEl = document.querySelector(`[data-ohw-section="${anchorId}"]`);
13864
- if (!anchorEl) return false;
13865
- const mountPoint = anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
13107
+ const mountPoint = getSchedulingMountPoint(effectiveInsertAfter);
13108
+ if (!mountPoint) return false;
13866
13109
  const container = document.createElement("div");
13867
13110
  container.dataset.ohwSectionContainer = "scheduling";
13868
- if (beforeId) {
13869
- const beforeAnchor = document.querySelector(`[data-ohw-section="${beforeId}"]`);
13111
+ if (insertBefore) {
13112
+ const beforeAnchor = document.querySelector(`[data-ohw-section="${insertBefore}"]`);
13870
13113
  const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
13871
13114
  if (!beforePoint) return false;
13872
13115
  beforePoint.insertAdjacentElement("beforebegin", container);
@@ -13877,25 +13120,19 @@ function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, be
13877
13120
  }
13878
13121
  tail.insertAdjacentElement("afterend", container);
13879
13122
  }
13880
- try {
13881
- const root = createRoot2(container);
13882
- flushSync2(() => {
13883
- root.render(
13884
- /* @__PURE__ */ jsx34(
13885
- SchedulingWidget,
13886
- {
13887
- notifyOnConnect,
13888
- initialScheduleId: scheduleId,
13889
- insertAfter: widgetId
13890
- }
13891
- )
13892
- );
13893
- });
13894
- } catch (err) {
13895
- console.error("[ow:scheduling] render threw", err);
13896
- container.remove();
13897
- return false;
13898
- }
13123
+ const root = createRoot2(container);
13124
+ flushSync2(() => {
13125
+ root.render(
13126
+ /* @__PURE__ */ jsx33(
13127
+ SchedulingWidget,
13128
+ {
13129
+ notifyOnConnect,
13130
+ initialScheduleId: scheduleId,
13131
+ insertAfter: effectiveInsertAfter
13132
+ }
13133
+ )
13134
+ );
13135
+ });
13899
13136
  const tracker = getSectionsTracker();
13900
13137
  let sections = [];
13901
13138
  try {
@@ -13903,12 +13140,10 @@ function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, be
13903
13140
  } catch {
13904
13141
  }
13905
13142
  const inEditor = typeof window !== "undefined" && window.self !== window.top;
13906
- if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === widgetId)) {
13143
+ if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === effectiveInsertAfter)) {
13907
13144
  sections.push({
13908
13145
  type: "scheduling",
13909
- insertAfter: widgetId,
13910
- anchorId,
13911
- beforeId: beforeId ?? null,
13146
+ insertAfter: effectiveInsertAfter,
13912
13147
  pagePath: window.location.pathname,
13913
13148
  ...scheduleId ? { scheduleId } : {}
13914
13149
  });
@@ -13922,8 +13157,7 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
13922
13157
  for (let i = pending.length - 1; i >= 0; i--) {
13923
13158
  const entry = pending[i];
13924
13159
  const shouldNotify = typeof notifyOnConnect === "function" ? notifyOnConnect(entry) : notifyOnConnect;
13925
- const { anchorId, beforeId } = resolveEntryAnchor(entry);
13926
- if (mountSchedulingWidget(anchorId, shouldNotify, entry.scheduleId ?? null, beforeId, entry.insertAfter)) {
13160
+ if (mountSchedulingWidget(entry.insertAfter, shouldNotify, entry.scheduleId)) {
13927
13161
  pending.splice(i, 1);
13928
13162
  }
13929
13163
  }
@@ -13996,6 +13230,10 @@ function isIconEditable(el) {
13996
13230
  return el.dataset.ohwEditable === "icon";
13997
13231
  }
13998
13232
  var MEDIA_SELECTOR = '[data-ohw-editable="image"], [data-ohw-editable="bg-image"], [data-ohw-editable="video"]';
13233
+ var EDITOR_CHROME_SELECTOR = '[data-ohw-toolbar], [data-ohw-form-toolbar], [data-ohw-field-toolbar], [data-ohw-item-toolbar], [data-ohw-more-menu], [data-ohw-state-toggle], [data-ohw-max-badge], [data-slot="dropdown-menu-content"], [data-slot="dropdown-menu-item"]';
13234
+ function isOverEditorChrome(x, y) {
13235
+ return document.elementsFromPoint(x, y).some((el) => el instanceof HTMLElement && el.matches(EDITOR_CHROME_SELECTOR));
13236
+ }
13999
13237
  var NON_MEDIA_SELECTOR = '[data-ohw-editable]:not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]):not([data-ohw-editable="video"]):not([data-ohw-editable="icon"]):not([data-ohw-editable="form"])';
14000
13238
  function getVideoEl2(el) {
14001
13239
  return el instanceof HTMLVideoElement ? el : el.querySelector("video");
@@ -14067,13 +13305,6 @@ function isInsideLinkEditor(target) {
14067
13305
  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"]')
14068
13306
  );
14069
13307
  }
14070
- function isInsideFloatingPanel(target) {
14071
- return Boolean(target.closest("[data-ohw-floating-panel]"));
14072
- }
14073
- function isPointOverFloatingPanel(clientX, clientY) {
14074
- const el = document.elementFromPoint(clientX, clientY);
14075
- return Boolean(el instanceof Element && el.closest("[data-ohw-floating-panel]"));
14076
- }
14077
13308
  function getHrefKeyFromElement(el) {
14078
13309
  if (!el) return null;
14079
13310
  const anchor = el.closest("[data-ohw-href-key]");
@@ -14121,7 +13352,8 @@ function isNavItemPointerTarget(el) {
14121
13352
  function getNavigationItemAnchor(el) {
14122
13353
  const anchor = el.matches("[data-ohw-href-key]") ? el : el.closest("[data-ohw-href-key]");
14123
13354
  if (!anchor) return null;
14124
- if (!anchor.querySelector('[data-ohw-editable="text"]') && !getSocialItem(anchor)) return null;
13355
+ if (!anchor.matches('[data-ohw-editable="text"], [data-ohw-editable="plain"]') && !anchor.querySelector('[data-ohw-editable="text"], [data-ohw-editable="plain"]') && !getSocialItem(anchor))
13356
+ return null;
14125
13357
  if (!isNavItemPointerTarget(anchor)) return null;
14126
13358
  return anchor;
14127
13359
  }
@@ -14311,7 +13543,7 @@ function getNavigationSelectionParent(el) {
14311
13543
  if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
14312
13544
  return getFooterLinksContainer();
14313
13545
  }
14314
- 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)) {
13546
+ if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
14315
13547
  return getNavigationRoot(el);
14316
13548
  }
14317
13549
  return null;
@@ -14557,7 +13789,7 @@ function EditGlowChrome({
14557
13789
  hideHandle = false
14558
13790
  }) {
14559
13791
  const GAP = SELECTION_CHROME_GAP2;
14560
- return /* @__PURE__ */ jsxs21(
13792
+ return /* @__PURE__ */ jsxs20(
14561
13793
  "div",
14562
13794
  {
14563
13795
  ref: elRef,
@@ -14572,7 +13804,7 @@ function EditGlowChrome({
14572
13804
  zIndex: 2147483646
14573
13805
  },
14574
13806
  children: [
14575
- /* @__PURE__ */ jsx34(
13807
+ /* @__PURE__ */ jsx33(
14576
13808
  "div",
14577
13809
  {
14578
13810
  style: {
@@ -14585,7 +13817,7 @@ function EditGlowChrome({
14585
13817
  }
14586
13818
  }
14587
13819
  ),
14588
- reorderHrefKey && !hideHandle && /* @__PURE__ */ jsx34(
13820
+ reorderHrefKey && !hideHandle && /* @__PURE__ */ jsx33(
14589
13821
  "div",
14590
13822
  {
14591
13823
  "data-ohw-drag-handle-container": "",
@@ -14597,7 +13829,7 @@ function EditGlowChrome({
14597
13829
  transform: "translate(calc(-100% - 7px), -50%)",
14598
13830
  pointerEvents: dragDisabled ? "none" : "auto"
14599
13831
  },
14600
- children: /* @__PURE__ */ jsx34(
13832
+ children: /* @__PURE__ */ jsx33(
14601
13833
  DragHandle,
14602
13834
  {
14603
13835
  "aria-label": `Reorder ${reorderHrefKey}`,
@@ -14807,7 +14039,7 @@ function FloatingToolbar({
14807
14039
  return () => ro.disconnect();
14808
14040
  }, [showEditLink, activeCommands]);
14809
14041
  const { top, left, transform } = calcToolbarPos(rect, parentScroll, measuredW);
14810
- return /* @__PURE__ */ jsx34(
14042
+ return /* @__PURE__ */ jsx33(
14811
14043
  "div",
14812
14044
  {
14813
14045
  ref: setRefs,
@@ -14819,12 +14051,12 @@ function FloatingToolbar({
14819
14051
  zIndex: 2147483647,
14820
14052
  pointerEvents: "auto"
14821
14053
  },
14822
- children: /* @__PURE__ */ jsxs21(CustomToolbar, { children: [
14823
- TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs21(React11.Fragment, { children: [
14824
- gi > 0 && /* @__PURE__ */ jsx34(CustomToolbarDivider, {}),
14054
+ children: /* @__PURE__ */ jsxs20(CustomToolbar, { children: [
14055
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs20(React11.Fragment, { children: [
14056
+ gi > 0 && /* @__PURE__ */ jsx33(CustomToolbarDivider, {}),
14825
14057
  btns.map((btn) => {
14826
14058
  const isActive = activeCommands.has(btn.cmd);
14827
- return /* @__PURE__ */ jsx34(
14059
+ return /* @__PURE__ */ jsx33(
14828
14060
  CustomToolbarButton,
14829
14061
  {
14830
14062
  title: btn.title,
@@ -14833,7 +14065,7 @@ function FloatingToolbar({
14833
14065
  e.preventDefault();
14834
14066
  onCommand(btn.cmd);
14835
14067
  },
14836
- children: /* @__PURE__ */ jsx34(
14068
+ children: /* @__PURE__ */ jsx33(
14837
14069
  "svg",
14838
14070
  {
14839
14071
  width: "16",
@@ -14854,7 +14086,7 @@ function FloatingToolbar({
14854
14086
  );
14855
14087
  })
14856
14088
  ] }, gi)),
14857
- showEditLink ? /* @__PURE__ */ jsx34(
14089
+ showEditLink ? /* @__PURE__ */ jsx33(
14858
14090
  CustomToolbarButton,
14859
14091
  {
14860
14092
  type: "button",
@@ -14868,7 +14100,7 @@ function FloatingToolbar({
14868
14100
  e.preventDefault();
14869
14101
  e.stopPropagation();
14870
14102
  },
14871
- children: /* @__PURE__ */ jsx34(Link3, { className: "size-4 shrink-0", "aria-hidden": true })
14103
+ children: /* @__PURE__ */ jsx33(Link2, { className: "size-4 shrink-0", "aria-hidden": true })
14872
14104
  }
14873
14105
  ) : null
14874
14106
  ] })
@@ -14885,7 +14117,7 @@ function StateToggle({
14885
14117
  states,
14886
14118
  onStateChange
14887
14119
  }) {
14888
- return /* @__PURE__ */ jsx34(
14120
+ return /* @__PURE__ */ jsx33(
14889
14121
  ToggleGroup,
14890
14122
  {
14891
14123
  "data-ohw-state-toggle": "",
@@ -14899,12 +14131,11 @@ function StateToggle({
14899
14131
  left: rect.right - 8,
14900
14132
  transform: "translateX(-100%)"
14901
14133
  },
14902
- children: states.map((state) => /* @__PURE__ */ jsx34(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
14134
+ children: states.map((state) => /* @__PURE__ */ jsx33(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
14903
14135
  }
14904
14136
  );
14905
14137
  }
14906
14138
  var contentCache = /* @__PURE__ */ new Map();
14907
- var fetchedContentPaths = /* @__PURE__ */ new Set();
14908
14139
  function resolveSubdomain(subdomainFromQuery) {
14909
14140
  if (subdomainFromQuery) return subdomainFromQuery;
14910
14141
  if (typeof window !== "undefined") {
@@ -14999,14 +14230,8 @@ function OhhwellsBridge() {
14999
14230
  });
15000
14231
  const selectFrameRef = useRef9(() => {
15001
14232
  });
15002
- const selectLogoRef = useRef9(() => {
15003
- });
15004
- const openLogoSizePanelRef = useRef9(() => {
15005
- });
15006
14233
  const deselectRef = useRef9(() => {
15007
14234
  });
15008
- const closeFloatingPanelOnlyRef = useRef9(() => {
15009
- });
15010
14235
  const reselectNavigationItemRef = useRef9(() => {
15011
14236
  });
15012
14237
  const commitNavigationTextEditRef = useRef9(() => {
@@ -15034,9 +14259,14 @@ function OhhwellsBridge() {
15034
14259
  const [fieldTypePickerOpen, setFieldTypePickerOpen] = useState11(false);
15035
14260
  const clearFormPick = useCallback7(() => {
15036
14261
  const form = formPickElRef.current;
14262
+ const editing = fieldPickElRef.current;
14263
+ if (commitPlaceholderEdit(editing) && editing) {
14264
+ const owner = editing.closest('[data-ohw-editable="form"]');
14265
+ if (owner) persistFieldsRef.current(owner);
14266
+ }
15037
14267
  if (form) {
15038
14268
  const key = formKeyOf(form);
15039
- if (key) setFormViewState(form, key, "default", DEFAULT_SUCCESS_TEXT);
14269
+ if (key) setFormViewState(form, key, "default", successInitialFor(form, key, editContentRef.current));
15040
14270
  }
15041
14271
  setFormViewStateUi("default");
15042
14272
  setFormPickCount(null);
@@ -15090,8 +14320,8 @@ function OhhwellsBridge() {
15090
14320
  if (!wrapper || !form) return;
15091
14321
  commitPlaceholderEdit(wrapper);
15092
14322
  run(form, wrapper);
15093
- beginPlaceholderEdit(wrapper);
15094
14323
  persistFields(form);
14324
+ beginPlaceholderEdit(wrapper);
15095
14325
  setFormPickRect(form.getBoundingClientRect());
15096
14326
  },
15097
14327
  [persistFields]
@@ -15142,17 +14372,37 @@ function OhhwellsBridge() {
15142
14372
  [persistFields, selectField]
15143
14373
  );
15144
14374
  const fieldDragRef = useRef9(null);
14375
+ const buildFieldDropSlots = useCallback7((form, draggedKey) => {
14376
+ const others = listFieldWrappers(form).filter((el) => fieldKeyOf(el) !== draggedKey);
14377
+ const slots = others.map((el) => {
14378
+ const rect = el.getBoundingClientRect();
14379
+ return { top: rect.top, left: rect.left, width: rect.width };
14380
+ });
14381
+ const last = others[others.length - 1];
14382
+ if (last) {
14383
+ const rect = last.getBoundingClientRect();
14384
+ slots.push({ top: rect.bottom, left: rect.left, width: rect.width });
14385
+ }
14386
+ return slots;
14387
+ }, []);
15145
14388
  const handleFieldDragStart = useCallback7(() => {
15146
14389
  const wrapper = fieldPickElRef.current;
15147
14390
  const form = formPickElRef.current;
15148
14391
  if (!wrapper || !form) return;
15149
- fieldDragRef.current = { key: fieldKeyOf(wrapper), form };
15150
- }, []);
14392
+ const key = fieldKeyOf(wrapper);
14393
+ fieldDragRef.current = { key, form };
14394
+ setFieldDragging(true);
14395
+ setFieldDropSlots(buildFieldDropSlots(form, key));
14396
+ }, [buildFieldDropSlots]);
15151
14397
  const handleFieldDragEnd = useCallback7(() => {
15152
14398
  fieldDragRef.current = null;
15153
14399
  setFieldDropIndex(null);
14400
+ setFieldDropSlots([]);
14401
+ setFieldDragging(false);
15154
14402
  }, []);
15155
14403
  const [fieldDropIndex, setFieldDropIndex] = useState11(null);
14404
+ const [fieldDropSlots, setFieldDropSlots] = useState11([]);
14405
+ const [fieldDragging, setFieldDragging] = useState11(false);
15156
14406
  const clearFormPickRef = useRef9(clearFormPick);
15157
14407
  clearFormPickRef.current = clearFormPick;
15158
14408
  useEffect12(() => {
@@ -15218,6 +14468,11 @@ function OhhwellsBridge() {
15218
14468
  const [isItemDragging, setIsItemDragging] = useState11(false);
15219
14469
  const [isFooterFrameSelection, setIsFooterFrameSelection] = useState11(false);
15220
14470
  isFooterFrameSelectionRef.current = isFooterFrameSelection;
14471
+ const [floatingPanel, setFloatingPanel] = useState11(null);
14472
+ const floatingPanelOpenRef = useRef9(false);
14473
+ floatingPanelOpenRef.current = floatingPanel !== null;
14474
+ const [floatingPanelPos, setFloatingPanelPos] = useState11(null);
14475
+ const [parentScrollSnap, setParentScrollSnap] = useState11(null);
15221
14476
  const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = useState11(null);
15222
14477
  const [footerHeadingVisible, setFooterHeadingVisible] = useState11(null);
15223
14478
  const footerDragRef = useRef9(null);
@@ -15232,16 +14487,7 @@ function OhhwellsBridge() {
15232
14487
  const addNavAfterAnchorRef = useRef9(null);
15233
14488
  const editContentRef = useRef9({});
15234
14489
  const aiSectionsRef = useRef9("");
15235
- const brandKitRef = useRef9("");
15236
- const stylesRef = useRef9("");
15237
14490
  const pendingDeleteUndoRef = useRef9(null);
15238
- const [floatingPanel, setFloatingPanel] = useState11(null);
15239
- const floatingPanelOpenRef = useRef9(false);
15240
- const setFloatingPanelRef = useRef9(setFloatingPanel);
15241
- const [floatingPanelPos, setFloatingPanelPos] = useState11(null);
15242
- const [logoSizeDraft, setLogoSizeDraft] = useState11(null);
15243
- const [editorViewport, setEditorViewport] = useState11("desktop");
15244
- const [parentScrollSnap, setParentScrollSnap] = useState11(null);
15245
14491
  const [sitePages, setSitePages] = useState11([]);
15246
14492
  const [sectionsByPath, setSectionsByPath] = useState11({});
15247
14493
  const sectionsPrefetchGenRef = useRef9(0);
@@ -15250,18 +14496,7 @@ function OhhwellsBridge() {
15250
14496
  const linkPopoverOpenRef = useRef9(false);
15251
14497
  const linkPopoverGraceUntilRef = useRef9(0);
15252
14498
  setLinkPopoverRef.current = setLinkPopover;
15253
- setFloatingPanelRef.current = setFloatingPanel;
15254
14499
  linkPopoverSessionRef.current = linkPopover;
15255
- floatingPanelOpenRef.current = Boolean(floatingPanel);
15256
- useEffect12(() => {
15257
- const syncViewport = () => {
15258
- const next = window.innerWidth <= 480 ? "mobile" : "desktop";
15259
- setEditorViewport((prev) => prev === next ? prev : next);
15260
- };
15261
- syncViewport();
15262
- window.addEventListener("resize", syncViewport);
15263
- return () => window.removeEventListener("resize", syncViewport);
15264
- }, []);
15265
14500
  const {
15266
14501
  navDragRef,
15267
14502
  navDropSlots,
@@ -15430,7 +14665,9 @@ function OhhwellsBridge() {
15430
14665
  const deactivate = useCallback7(() => {
15431
14666
  const el = activeElRef.current;
15432
14667
  if (!el) return;
15433
- const key = el.dataset.ohwKey;
14668
+ const isFormBlock = el.dataset.ohwEditable === "form";
14669
+ const isFormControl = el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement;
14670
+ const key = isFormBlock || isFormControl ? void 0 : el.dataset.ohwKey;
15434
14671
  if (key) {
15435
14672
  const timer = autoSaveTimers.current.get(key);
15436
14673
  if (timer !== void 0) {
@@ -15447,6 +14684,12 @@ function OhhwellsBridge() {
15447
14684
  }
15448
14685
  el.removeAttribute("contenteditable");
15449
14686
  el.removeAttribute("data-ohw-editing");
14687
+ const labelField = getFieldWrapper(el);
14688
+ if (labelField) {
14689
+ syncRequiredMark(labelField);
14690
+ const owner = labelField.closest('[data-ohw-editable="form"]');
14691
+ if (owner) persistFieldsRef.current(owner);
14692
+ }
15450
14693
  activeElRef.current = null;
15451
14694
  setNavGroupForceOpen(null, false);
15452
14695
  setReorderHrefKey(null);
@@ -15486,8 +14729,6 @@ function OhhwellsBridge() {
15486
14729
  setHoveredNavContainerRect(null);
15487
14730
  hoveredItemElRef.current = null;
15488
14731
  setHoveredItemRect(null);
15489
- setFloatingPanel(null);
15490
- setLogoSizeDraft(null);
15491
14732
  if (!activeElRef.current) {
15492
14733
  setNavGroupForceOpen(null, false);
15493
14734
  setToolbarRect(null);
@@ -15602,6 +14843,12 @@ function OhhwellsBridge() {
15602
14843
  }
15603
14844
  el.removeAttribute("contenteditable");
15604
14845
  el.removeAttribute("data-ohw-editing");
14846
+ const labelField = getFieldWrapper(el);
14847
+ if (labelField) {
14848
+ syncRequiredMark(labelField);
14849
+ const owner = labelField.closest('[data-ohw-editable="form"]');
14850
+ if (owner) persistFieldsRef.current(owner);
14851
+ }
15605
14852
  activeElRef.current = null;
15606
14853
  setMaxBadge(null);
15607
14854
  setActiveCommands(/* @__PURE__ */ new Set());
@@ -16193,8 +15440,6 @@ function OhhwellsBridge() {
16193
15440
  setToolbarRect(anchor.getBoundingClientRect());
16194
15441
  setToolbarShowEditLink(false);
16195
15442
  setActiveCommands(/* @__PURE__ */ new Set());
16196
- setFloatingPanel(null);
16197
- setLogoSizeDraft(null);
16198
15443
  }, [deactivate, markSelected]);
16199
15444
  const selectFrame = useCallback7((el) => {
16200
15445
  if (!isNavigationContainer(el)) return;
@@ -16244,51 +15489,7 @@ function OhhwellsBridge() {
16244
15489
  setToolbarRect(el.getBoundingClientRect());
16245
15490
  setToolbarShowEditLink(false);
16246
15491
  setActiveCommands(/* @__PURE__ */ new Set());
16247
- setFloatingPanel(null);
16248
- setLogoSizeDraft(null);
16249
15492
  }, [deactivate, markSelected, postToParent2]);
16250
- const selectLogo = useCallback7(
16251
- (logoEl) => {
16252
- if (activeElRef.current) deactivate();
16253
- selectedElRef.current = logoEl;
16254
- selectedHrefKeyRef.current = null;
16255
- selectedFooterColAttrRef.current = null;
16256
- markSelected(logoEl);
16257
- setSelectedIsCta(false);
16258
- setSelectedIsSocial(false);
16259
- setSelectedIsSocialsRow(false);
16260
- clearHrefKeyHover(logoEl);
16261
- hoveredNavContainerRef.current = null;
16262
- setHoveredNavContainerRect(null);
16263
- setHoveredItemRect(null);
16264
- hoveredItemElRef.current = null;
16265
- siblingHintElRef.current = null;
16266
- setSiblingHintRect(null);
16267
- setSiblingHintRects([]);
16268
- setIsItemDragging(false);
16269
- setReorderHrefKey(null);
16270
- setReorderDragDisabled(false);
16271
- setIsFooterFrameSelection(false);
16272
- setToolbarVariant("logo");
16273
- setToolbarRect(getLogoInteractionRect(logoEl));
16274
- setToolbarShowEditLink(false);
16275
- setActiveCommands(/* @__PURE__ */ new Set());
16276
- },
16277
- [deactivate, markSelected]
16278
- );
16279
- const openLogoSizePanel = useCallback7((logoEl) => {
16280
- const placement = getLogoPlacement(logoEl);
16281
- const draft = readLogoSizeState(editContentRef.current, placement);
16282
- setLogoSizeDraft(draft);
16283
- setParentScrollSnap(parentScrollRef.current);
16284
- setFloatingPanel({
16285
- key: `logo-size:${placement}`,
16286
- title: "Logo",
16287
- context: placement === "navbar" ? "Navbar" : "Footer",
16288
- kind: "logo-size",
16289
- placement
16290
- });
16291
- }, []);
16292
15493
  const openSocialsDisplayPanel = useCallback7((row) => {
16293
15494
  setParentScrollSnap(parentScrollRef.current);
16294
15495
  setFloatingPanel({
@@ -16324,54 +15525,13 @@ function OhhwellsBridge() {
16324
15525
  );
16325
15526
  const closeFloatingPanelOnly = useCallback7(() => {
16326
15527
  setFloatingPanel(null);
16327
- setLogoSizeDraft(null);
16328
15528
  }, []);
15529
+ const closeFloatingPanelOnlyRef = useRef9(closeFloatingPanelOnly);
16329
15530
  closeFloatingPanelOnlyRef.current = closeFloatingPanelOnly;
16330
15531
  const closeFloatingPanelAndDeselect = useCallback7(() => {
16331
15532
  setFloatingPanel(null);
16332
- setLogoSizeDraft(null);
16333
15533
  deselectRef.current();
16334
15534
  }, []);
16335
- const persistLogoSizeDraft = useCallback7(
16336
- (placement, draft) => {
16337
- const desktopKey = LOGO_SIZE_DESKTOP_KEYS[placement];
16338
- const mobileKey = LOGO_SIZE_MOBILE_KEYS[placement];
16339
- const nodes = [
16340
- { key: desktopKey, text: String(draft.desktopPx) }
16341
- ];
16342
- if (draft.mobileFollowing) {
16343
- nodes.push({ key: mobileKey, text: "" });
16344
- } else {
16345
- nodes.push({ key: mobileKey, text: String(draft.mobilePx) });
16346
- }
16347
- editContentRef.current = {
16348
- ...editContentRef.current,
16349
- [desktopKey]: String(draft.desktopPx),
16350
- [mobileKey]: draft.mobileFollowing ? "" : String(draft.mobilePx)
16351
- };
16352
- applyLogoSizeToPlacement(
16353
- placement,
16354
- draft.desktopPx,
16355
- draft.mobileFollowing ? draft.desktopPx : draft.mobilePx,
16356
- draft.mobileFollowing
16357
- );
16358
- postToParent2({ type: "ow:change", nodes });
16359
- requestAnimationFrame(() => {
16360
- const selected = selectedElRef.current;
16361
- if (!selected || toolbarVariantRef.current !== "logo") return;
16362
- const rect = getLogoInteractionRect(selected);
16363
- setToolbarRect(rect);
16364
- if (glowElRef.current) {
16365
- const GAP = SELECTION_CHROME_GAP2;
16366
- glowElRef.current.style.top = `${rect.top - GAP}px`;
16367
- glowElRef.current.style.left = `${rect.left - GAP}px`;
16368
- glowElRef.current.style.width = `${rect.width + GAP * 2}px`;
16369
- glowElRef.current.style.height = `${rect.height + GAP * 2}px`;
16370
- }
16371
- });
16372
- },
16373
- [postToParent2]
16374
- );
16375
15535
  const activate = useCallback7((el, options) => {
16376
15536
  if (activeElRef.current === el) return;
16377
15537
  if (isIconEditable(el)) return;
@@ -16452,10 +15612,7 @@ function OhhwellsBridge() {
16452
15612
  deactivateRef.current = deactivate;
16453
15613
  selectRef.current = select;
16454
15614
  selectFrameRef.current = selectFrame;
16455
- selectLogoRef.current = selectLogo;
16456
- openLogoSizePanelRef.current = openLogoSizePanel;
16457
15615
  deselectRef.current = deselect;
16458
- closeFloatingPanelOnlyRef.current = closeFloatingPanelOnly;
16459
15616
  const lastSiteWideScopeRef = useRef9(null);
16460
15617
  useEffect12(() => {
16461
15618
  if (!isEditMode) {
@@ -16494,23 +15651,9 @@ function OhhwellsBridge() {
16494
15651
  aiSectionsRef.current = content[AI_SECTIONS_KEY];
16495
15652
  applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
16496
15653
  }
16497
- if (typeof content[BRAND_KIT_KEY] === "string") {
16498
- brandKitRef.current = content[BRAND_KIT_KEY];
16499
- applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
16500
- }
16501
- if (typeof content[STYLE_STORE_KEY] === "string") {
16502
- stylesRef.current = content[STYLE_STORE_KEY];
16503
- applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
16504
- }
16505
- applyBrandChrome(content);
16506
15654
  for (const [key, val] of Object.entries(content)) {
16507
15655
  if (key === "__ohw_sections") continue;
16508
15656
  if (key === AI_SECTIONS_KEY) continue;
16509
- if (key === LOGO_PLACEHOLDER_KEY) continue;
16510
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
16511
- if (key === BRAND_KIT_KEY) continue;
16512
- if (key === STYLE_STORE_KEY) continue;
16513
- if (BRAND_CHROME_KEYS.has(key)) continue;
16514
15657
  if (applyVideoSettingNode(key, val)) continue;
16515
15658
  if (applyCarouselNode(key, val)) continue;
16516
15659
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -16539,14 +15682,13 @@ function OhhwellsBridge() {
16539
15682
  applyLinkHref(el, val);
16540
15683
  } else if (el.dataset.ohwEditable === "icon") {
16541
15684
  applyIconMarkup(el, val);
15685
+ } else if (el.dataset.ohwEditable === "form") {
16542
15686
  } else if (el.innerHTML !== val) {
16543
15687
  el.innerHTML = val;
16544
15688
  }
16545
15689
  });
16546
15690
  applyLinkByKey(key, val);
16547
15691
  }
16548
- applyLogoFromContent(content);
16549
- applyLogoSizes(content);
16550
15692
  reconcileNavbarItemsFromContent(content);
16551
15693
  reconcileFooterOrderFromContent(content);
16552
15694
  reconcileSocialsFromContent(content);
@@ -16567,9 +15709,7 @@ function OhhwellsBridge() {
16567
15709
  let cancelled = false;
16568
15710
  setFetchState("loading");
16569
15711
  const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
16570
- const initialPath = pathname;
16571
- fetchedContentPaths.add(`${subdomain}::${initialPath}`);
16572
- fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
15712
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
16573
15713
  if (cancelled) return;
16574
15714
  const content = data?.content ?? {};
16575
15715
  contentCache.set(subdomain, content);
@@ -16598,6 +15738,7 @@ function OhhwellsBridge() {
16598
15738
  e.preventDefault();
16599
15739
  e.stopPropagation();
16600
15740
  if (e.dataTransfer) e.dataTransfer.dropEffect = "move";
15741
+ setFieldDropSlots(buildFieldDropSlots(session.form, session.key));
16601
15742
  setFieldDropIndex(resolveIndex(session.form, e.clientY));
16602
15743
  };
16603
15744
  const onDrop = (e) => {
@@ -16612,6 +15753,8 @@ function OhhwellsBridge() {
16612
15753
  setFormPickRect(session.form.getBoundingClientRect());
16613
15754
  fieldDragRef.current = null;
16614
15755
  setFieldDropIndex(null);
15756
+ setFieldDropSlots([]);
15757
+ setFieldDragging(false);
16615
15758
  };
16616
15759
  window.addEventListener("dragover", onDragOver, true);
16617
15760
  window.addEventListener("drop", onDrop, true);
@@ -16619,7 +15762,7 @@ function OhhwellsBridge() {
16619
15762
  window.removeEventListener("dragover", onDragOver, true);
16620
15763
  window.removeEventListener("drop", onDrop, true);
16621
15764
  };
16622
- }, [isEditMode, persistFields, selectField]);
15765
+ }, [buildFieldDropSlots, isEditMode, persistFields, selectField]);
16623
15766
  useEffect12(() => {
16624
15767
  if (!isEditMode) return;
16625
15768
  const mark = () => document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
@@ -16641,8 +15784,9 @@ function OhhwellsBridge() {
16641
15784
  const wrapper = getFieldWrapper(input);
16642
15785
  if (!wrapper || wrapper !== fieldPickElRef.current) return;
16643
15786
  if (saveTimer) clearTimeout(saveTimer);
15787
+ const owner = wrapper.closest('[data-ohw-editable="form"]');
16644
15788
  saveTimer = setTimeout(() => {
16645
- const form = formPickElRef.current;
15789
+ const form = owner ?? formPickElRef.current;
16646
15790
  if (!form) return;
16647
15791
  const previous = input.getAttribute("placeholder");
16648
15792
  input.setAttribute("placeholder", input.value);
@@ -16685,21 +15829,8 @@ function OhhwellsBridge() {
16685
15829
  initSectionInstancesFromContent(content, window.location.pathname);
16686
15830
  observer?.disconnect();
16687
15831
  try {
16688
- applyBrandChrome(content);
16689
- if (typeof content[BRAND_KIT_KEY] === "string") {
16690
- applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
16691
- }
16692
- if (typeof content[STYLE_STORE_KEY] === "string") {
16693
- applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
16694
- }
16695
15832
  for (const [key, val] of Object.entries(content)) {
16696
15833
  if (key === "__ohw_sections") continue;
16697
- if (key === LOGO_PLACEHOLDER_KEY) continue;
16698
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
16699
- if (key === BRAND_KIT_KEY) continue;
16700
- if (key === STYLE_STORE_KEY) continue;
16701
- if (key === STYLE_STORE_KEY) continue;
16702
- if (BRAND_CHROME_KEYS.has(key)) continue;
16703
15834
  if (applyVideoSettingNode(key, val)) continue;
16704
15835
  if (applyCarouselNode(key, val)) continue;
16705
15836
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -16714,17 +15845,23 @@ function OhhwellsBridge() {
16714
15845
  if (video && video.src !== val) applyVideoSrc(video, val);
16715
15846
  } else if (el.dataset.ohwEditable === "link") {
16716
15847
  applyLinkHref(el, val);
15848
+ } else if (el.dataset.ohwEditable === "form") {
16717
15849
  } else if (el.innerHTML !== val) {
16718
15850
  el.innerHTML = val;
16719
15851
  }
16720
15852
  });
16721
15853
  applyLinkByKey(key, val);
16722
15854
  }
16723
- applyLogoFromContent(content);
16724
15855
  reconcileNavbarItemsFromContent(content);
16725
15856
  reconcileFooterOrderFromContent(content);
16726
15857
  reconcileSocialsFromContent(content);
16727
15858
  applySocialsDisplayFromContent(content);
15859
+ document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
15860
+ if (!form.querySelector("[data-ohw-form-success]")) {
15861
+ reconcileFieldsFromContent(form, content);
15862
+ }
15863
+ listFieldWrappers(form).forEach(syncRequiredMark);
15864
+ });
16728
15865
  } finally {
16729
15866
  observer?.observe(document.body, { childList: true, subtree: true });
16730
15867
  }
@@ -16735,17 +15872,6 @@ function OhhwellsBridge() {
16735
15872
  debounceTimer = setTimeout(applyFromCache, 150);
16736
15873
  };
16737
15874
  applyFromCache();
16738
- const pathCacheKey = `${subdomain}::${pathname}`;
16739
- if (!fetchedContentPaths.has(pathCacheKey)) {
16740
- fetchedContentPaths.add(pathCacheKey);
16741
- const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
16742
- fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(pathname)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
16743
- if (!data?.content) return;
16744
- contentCache.set(subdomain, data.content);
16745
- applyFromCache();
16746
- }).catch(() => {
16747
- });
16748
- }
16749
15875
  observer = new MutationObserver(scheduleApply);
16750
15876
  observer.observe(document.body, { childList: true, subtree: true });
16751
15877
  return () => {
@@ -16839,31 +15965,26 @@ function OhhwellsBridge() {
16839
15965
  }, [isEditMode, pathname, subdomain, fetchState, resyncSelectedNavigationItem]);
16840
15966
  useEffect12(() => {
16841
15967
  if (!isEditMode) return;
16842
- let lastPosted = 0;
16843
15968
  const measure = () => {
16844
15969
  const h = document.body.scrollHeight;
16845
- if (h > 50 && Math.abs(h - lastPosted) > 1) {
16846
- lastPosted = h;
16847
- postToParent2({ type: "ow:height", height: h });
16848
- }
16849
- };
16850
- let raf = null;
16851
- const schedule = () => {
16852
- if (raf != null) return;
16853
- raf = requestAnimationFrame(() => {
16854
- raf = null;
16855
- measure();
16856
- });
15970
+ if (h > 50) postToParent2({ type: "ow:height", height: h });
16857
15971
  };
16858
15972
  const t1 = setTimeout(measure, 50);
16859
15973
  const t2 = setTimeout(measure, 500);
16860
- const ro = new ResizeObserver(schedule);
16861
- ro.observe(document.body);
15974
+ let lastWidth = window.innerWidth;
15975
+ let resizeTimer = null;
15976
+ const handleResize = () => {
15977
+ if (window.innerWidth === lastWidth) return;
15978
+ lastWidth = window.innerWidth;
15979
+ if (resizeTimer) clearTimeout(resizeTimer);
15980
+ resizeTimer = setTimeout(measure, 150);
15981
+ };
15982
+ window.addEventListener("resize", handleResize);
16862
15983
  return () => {
16863
15984
  clearTimeout(t1);
16864
15985
  clearTimeout(t2);
16865
- if (raf != null) cancelAnimationFrame(raf);
16866
- ro.disconnect();
15986
+ if (resizeTimer) clearTimeout(resizeTimer);
15987
+ window.removeEventListener("resize", handleResize);
16867
15988
  };
16868
15989
  }, [pathname, isEditMode, postToParent2]);
16869
15990
  useEffect12(() => {
@@ -17044,12 +16165,10 @@ function OhhwellsBridge() {
17044
16165
  return;
17045
16166
  }
17046
16167
  const target = e.target;
17047
- if (target.closest("[data-ohw-ai-review]")) return;
17048
16168
  if (target.closest("[data-ohw-toolbar]")) return;
17049
16169
  if (target.closest("[data-ohw-state-toggle]")) return;
17050
16170
  if (target.closest("[data-ohw-max-badge]")) return;
17051
16171
  if (isInsideLinkEditor(target)) return;
17052
- if (isInsideFloatingPanel(target)) return;
17053
16172
  if (target.closest("[data-ohw-form-toolbar]")) return;
17054
16173
  if (target.closest(
17055
16174
  '[data-ohw-field-toolbar], [data-ohw-field-type-picker], [data-radix-popper-content-wrapper], [role="menu"], [data-slot="dropdown-menu-content"]'
@@ -17160,15 +16279,9 @@ function OhhwellsBridge() {
17160
16279
  if (logoEl) {
17161
16280
  e.preventDefault();
17162
16281
  e.stopPropagation();
17163
- if (!logoHasUploadedImage(logoEl)) {
17164
- deselectRef.current();
17165
- deactivateRef.current();
17166
- const identity = readLogoIdentityFromDom();
17167
- postToParentRef.current({ type: "ow:open-logo-settings", ...identity });
17168
- return;
17169
- }
17170
- selectLogoRef.current(logoEl);
17171
- openLogoSizePanelRef.current(logoEl);
16282
+ deselectRef.current();
16283
+ deactivateRef.current();
16284
+ postToParentRef.current({ type: "ow:open-logo-settings" });
17172
16285
  return;
17173
16286
  }
17174
16287
  const editable = target.closest("[data-ohw-editable]");
@@ -17323,12 +16436,10 @@ function OhhwellsBridge() {
17323
16436
  };
17324
16437
  const handleDblClick = (e) => {
17325
16438
  const target = e.target;
17326
- if (target.closest("[data-ohw-ai-review]")) return;
17327
16439
  if (target.closest("[data-ohw-toolbar]")) return;
17328
16440
  if (target.closest("[data-ohw-state-toggle]")) return;
17329
16441
  if (target.closest("[data-ohw-max-badge]")) return;
17330
16442
  if (isInsideLinkEditor(target)) return;
17331
- if (isInsideFloatingPanel(target)) return;
17332
16443
  if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
17333
16444
  return;
17334
16445
  }
@@ -17356,16 +16467,26 @@ function OhhwellsBridge() {
17356
16467
  setHoveredNavContainerRect(null);
17357
16468
  return;
17358
16469
  }
17359
- if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || isInsideFloatingPanel(target) || isPointOverFloatingPanel(e.clientX, e.clientY)) {
16470
+ if (target.closest(EDITOR_CHROME_SELECTOR) || isOverEditorChrome(e.clientX, e.clientY) || isInsideLinkEditor(target)) {
16471
+ document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
17360
16472
  hoveredItemElRef.current = null;
17361
16473
  setHoveredItemRect(null);
17362
16474
  hoveredNavContainerRef.current = null;
17363
16475
  setHoveredNavContainerRect(null);
16476
+ formHoverElRef.current = null;
16477
+ setFormHoverRect(null);
17364
16478
  siblingHintElRef.current = null;
17365
16479
  setSiblingHintRect(null);
17366
16480
  setSiblingHintRects([]);
17367
16481
  return;
17368
16482
  }
16483
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || target.closest("[data-ohw-floating-panel]")) {
16484
+ hoveredItemElRef.current = null;
16485
+ setHoveredItemRect(null);
16486
+ hoveredNavContainerRef.current = null;
16487
+ setHoveredNavContainerRect(null);
16488
+ return;
16489
+ }
17369
16490
  {
17370
16491
  const selected2 = selectedElRef.current;
17371
16492
  const selectedIsFooterColumn = Boolean(selected2) && !isFooterLinksContainer(selected2) && (selected2.hasAttribute("data-ohw-footer-col") || selected2.hasAttribute("data-ohw-footer-column") || Boolean(selected2.closest("footer") && isInferredFooterGroup2(selected2)));
@@ -17408,7 +16529,7 @@ function OhhwellsBridge() {
17408
16529
  setHoveredNavContainerRect(null);
17409
16530
  if (selectedElRef.current === logoEl) return;
17410
16531
  hoveredItemElRef.current = logoEl;
17411
- setHoveredItemRect(getLogoInteractionRect(logoEl));
16532
+ setHoveredItemRect(logoEl.getBoundingClientRect());
17412
16533
  return;
17413
16534
  }
17414
16535
  const navAnchor = getNavigationItemAnchor(target);
@@ -17472,6 +16593,7 @@ function OhhwellsBridge() {
17472
16593
  hoveredNavContainerRef.current = null;
17473
16594
  setHoveredNavContainerRect(null);
17474
16595
  hoveredItemElRef.current = editable;
16596
+ setHoveredItemRect(editable.getBoundingClientRect());
17475
16597
  }
17476
16598
  }
17477
16599
  }
@@ -17679,7 +16801,7 @@ function OhhwellsBridge() {
17679
16801
  setHoveredNavContainerRect(null);
17680
16802
  if (selectedElRef.current !== logo) {
17681
16803
  hoveredItemElRef.current = logo;
17682
- setHoveredItemRect(getLogoInteractionRect(logo));
16804
+ setHoveredItemRect(logo.getBoundingClientRect());
17683
16805
  }
17684
16806
  return;
17685
16807
  }
@@ -17768,7 +16890,7 @@ function OhhwellsBridge() {
17768
16890
  }
17769
16891
  };
17770
16892
  const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
17771
- if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
16893
+ if (linkPopoverOpenRef.current) {
17772
16894
  if (hoveredImageRef.current) {
17773
16895
  hoveredImageRef.current = null;
17774
16896
  hoveredImageHasTextOverlapRef.current = false;
@@ -18022,7 +17144,7 @@ function OhhwellsBridge() {
18022
17144
  }
18023
17145
  };
18024
17146
  const probeHoverCardsAt = (clientX, clientY, fromParentViewport = false) => {
18025
- if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY) || document.documentElement.hasAttribute("data-ohw-section-picking")) {
17147
+ if (linkPopoverOpenRef.current || document.documentElement.hasAttribute("data-ohw-section-picking")) {
18026
17148
  if (activeStateElRef.current) {
18027
17149
  activeStateElRef.current.removeAttribute("data-ohw-state-hovered");
18028
17150
  activeStateElRef.current = null;
@@ -18097,17 +17219,15 @@ function OhhwellsBridge() {
18097
17219
  };
18098
17220
  const handleMouseMove = (e) => {
18099
17221
  const { clientX, clientY } = e;
18100
- if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
17222
+ if (pointOwnedByFloatingPanel(clientX, clientY)) return;
17223
+ if (isOverEditorChrome(clientX, clientY)) {
17224
+ document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
17225
+ formHoverElRef.current = null;
17226
+ setFormHoverRect(null);
18101
17227
  hoveredItemElRef.current = null;
18102
17228
  setHoveredItemRect(null);
18103
17229
  hoveredNavContainerRef.current = null;
18104
17230
  setHoveredNavContainerRect(null);
18105
- siblingHintElRef.current = null;
18106
- setSiblingHintRect(null);
18107
- setSiblingHintRects([]);
18108
- dismissImageHover();
18109
- clearImageHover();
18110
- setSectionGap(null);
18111
17231
  return;
18112
17232
  }
18113
17233
  probeSectionGapAt(clientX, clientY);
@@ -18118,11 +17238,7 @@ function OhhwellsBridge() {
18118
17238
  if (e.data?.type !== "ow:pointer-sync") return;
18119
17239
  const { clientX, clientY } = e.data;
18120
17240
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
18121
- if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
18122
- dismissImageHover();
18123
- clearImageHover();
18124
- return;
18125
- }
17241
+ if (pointOwnedByFloatingPanel(clientX, clientY)) return;
18126
17242
  probeSectionGapAt(clientX, clientY);
18127
17243
  probeImageAt(clientX, clientY);
18128
17244
  probeHoverCardsAt(clientX, clientY);
@@ -18372,15 +17488,6 @@ function OhhwellsBridge() {
18372
17488
  aiSectionsRef.current = content[AI_SECTIONS_KEY];
18373
17489
  applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
18374
17490
  }
18375
- if (typeof content[BRAND_KIT_KEY] === "string") {
18376
- brandKitRef.current = content[BRAND_KIT_KEY];
18377
- applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
18378
- }
18379
- if (typeof content[STYLE_STORE_KEY] === "string") {
18380
- stylesRef.current = content[STYLE_STORE_KEY];
18381
- applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
18382
- }
18383
- applyBrandChrome(content);
18384
17491
  let sectionsJson = null;
18385
17492
  for (const [key, val] of Object.entries(content)) {
18386
17493
  if (key === "__ohw_sections") {
@@ -18388,11 +17495,6 @@ function OhhwellsBridge() {
18388
17495
  continue;
18389
17496
  }
18390
17497
  if (key === AI_SECTIONS_KEY) continue;
18391
- if (key === LOGO_PLACEHOLDER_KEY) continue;
18392
- if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
18393
- if (key === BRAND_KIT_KEY) continue;
18394
- if (key === STYLE_STORE_KEY) continue;
18395
- if (BRAND_CHROME_KEYS.has(key)) continue;
18396
17498
  if (applyVideoSettingNode(key, val)) continue;
18397
17499
  if (applyCarouselNode(key, val)) continue;
18398
17500
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -18412,8 +17514,6 @@ function OhhwellsBridge() {
18412
17514
  });
18413
17515
  applyLinkByKey(key, val);
18414
17516
  }
18415
- applyLogoFromContent(content);
18416
- applyLogoSizes(content);
18417
17517
  if (sectionsJson) {
18418
17518
  initSectionsFromContent({ __ohw_sections: sectionsJson }, true);
18419
17519
  sectionsLoadedRef.current = true;
@@ -18429,58 +17529,6 @@ function OhhwellsBridge() {
18429
17529
  if (hydratedHeight > 50) postToParentRef.current({ type: "ow:height", height: hydratedHeight });
18430
17530
  postToParentRef.current({ type: "ow:hydrate-done" });
18431
17531
  };
18432
- const handleUpdateLogoIdentity = (e) => {
18433
- if (e.data?.type !== "ow:update-logo-identity") return;
18434
- const rawText = typeof e.data.text === "string" ? e.data.text : "";
18435
- const alt = typeof e.data.alt === "string" ? e.data.alt : rawText;
18436
- const href = typeof e.data.href === "string" ? e.data.href : void 0;
18437
- const imageProvided = "image" in e.data;
18438
- const imageUrl = imageProvided && typeof e.data.image === "string" && e.data.image.trim() ? e.data.image.trim() : imageProvided ? null : void 0;
18439
- let isPlaceholder = e.data.isPlaceholder !== false;
18440
- if (imageUrl) isPlaceholder = false;
18441
- else if (imageProvided && imageUrl === null) {
18442
- isPlaceholder = e.data.isPlaceholder === true || !rawText.trim() || resolveLogoDisplayText(rawText) === PLACEHOLDER_BUSINESS_NAME;
18443
- }
18444
- const display = applyLogoIdentity(rawText, isPlaceholder);
18445
- const displayAlt = resolveLogoDisplayText(alt || display);
18446
- if (imageUrl !== void 0) {
18447
- applyLogoImage(imageUrl, displayAlt);
18448
- } else {
18449
- for (const key of LOGO_IMAGE_KEYS) {
18450
- document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
18451
- const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
18452
- if (img) img.alt = displayAlt;
18453
- });
18454
- }
18455
- }
18456
- if (href !== void 0) {
18457
- applyLogoHref(href);
18458
- applyLinkByKey("nav-logo-href", href);
18459
- applyLinkByKey("footer-logo-href", href);
18460
- applyLinkByKey("logo-href", href);
18461
- }
18462
- const nodes = [
18463
- ...LOGO_TEXT_KEYS.map((key) => ({ key, text: display })),
18464
- { key: LOGO_PLACEHOLDER_KEY, text: isPlaceholder ? "true" : "false" },
18465
- { key: LOGO_ALT_KEY, text: displayAlt }
18466
- ];
18467
- if (imageUrl !== void 0) {
18468
- if (imageUrl) {
18469
- for (const key of LOGO_IMAGE_KEYS) nodes.push({ key, text: imageUrl });
18470
- } else {
18471
- for (const key of LOGO_IMAGE_KEYS) nodes.push({ key, text: "" });
18472
- }
18473
- }
18474
- if (href !== void 0) {
18475
- for (const key of LOGO_HREF_KEYS) nodes.push({ key, text: href.trim() || "/" });
18476
- }
18477
- editContentRef.current = {
18478
- ...editContentRef.current,
18479
- ...Object.fromEntries(nodes.map(({ key, text }) => [key, text]))
18480
- };
18481
- applyLogoSizes(editContentRef.current);
18482
- postToParentRef.current({ type: "ow:change", nodes });
18483
- };
18484
17532
  window.addEventListener("message", handleHydrate);
18485
17533
  const postAiSectionsChanged = () => {
18486
17534
  postToParentRef.current({
@@ -18494,10 +17542,7 @@ function OhhwellsBridge() {
18494
17542
  const payload = e.data.payload;
18495
17543
  if (!payload || typeof payload.id !== "string" || !isRenderableTree(payload.tree)) return;
18496
17544
  const previous = aiSectionsRef.current;
18497
- const nextState = applyTreeToState(parseAiSectionsState(previous), {
18498
- ...payload,
18499
- path: payload.path ?? window.location.pathname
18500
- });
17545
+ const nextState = applyTreeToState(parseAiSectionsState(previous), payload);
18501
17546
  const nextValue = serializeAiSectionsState(nextState);
18502
17547
  aiSectionsRef.current = nextValue;
18503
17548
  applyAiSectionsToDom(nextState);
@@ -18534,42 +17579,12 @@ function OhhwellsBridge() {
18534
17579
  const value = typeof e.data.value === "string" ? e.data.value : "";
18535
17580
  aiSectionsRef.current = value;
18536
17581
  applyAiSectionsToDom(parseAiSectionsState(value));
18537
- applyStylesToDom(parseStyleStore(stylesRef.current));
18538
17582
  const restoredHeight = document.documentElement.scrollHeight;
18539
17583
  if (restoredHeight > 50) postToParentRef.current({ type: "ow:height", height: restoredHeight });
18540
17584
  postToParentRef.current({ type: "ow:change", nodes: [{ key: AI_SECTIONS_KEY, text: value }] });
18541
17585
  postAiSectionsChanged();
18542
17586
  };
18543
17587
  window.addEventListener("message", handleAiSetSections);
18544
- const handleAiSetBrand = (e) => {
18545
- if (e.data?.type !== "ow:ai-set-brand") return;
18546
- const value = typeof e.data.value === "string" ? e.data.value : "";
18547
- const previous = brandKitRef.current;
18548
- brandKitRef.current = value;
18549
- applyBrandToDom(parseBrandKit(value));
18550
- if (aiSectionsRef.current) applyAiSectionsToDom(parseAiSectionsState(aiSectionsRef.current));
18551
- applyStylesToDom(parseStyleStore(stylesRef.current));
18552
- postToParentRef.current({ type: "ow:change", nodes: [{ key: BRAND_KIT_KEY, text: value }] });
18553
- postToParentRef.current({ type: "ow:ai-brand-applied", previous, value });
18554
- };
18555
- window.addEventListener("message", handleAiSetBrand);
18556
- const handleAiSetStyles = (e) => {
18557
- if (e.data?.type !== "ow:ai-set-styles") return;
18558
- const value = typeof e.data.value === "string" ? e.data.value : "";
18559
- const previous = stylesRef.current;
18560
- stylesRef.current = value;
18561
- applyStylesToDom(parseStyleStore(value));
18562
- postToParentRef.current({ type: "ow:change", nodes: [{ key: STYLE_STORE_KEY, text: value }] });
18563
- postToParentRef.current({ type: "ow:ai-styles-applied", previous, value });
18564
- };
18565
- window.addEventListener("message", handleAiSetStyles);
18566
- const handleGetBrand = (e) => {
18567
- if (e.data?.type !== "ow:get-brand") return;
18568
- const template = deriveTemplateBrand();
18569
- const value = brandKitRef.current || (template ? JSON.stringify(template) : "");
18570
- postToParentRef.current({ type: "ow:brand-value", value });
18571
- };
18572
- window.addEventListener("message", handleGetBrand);
18573
17588
  const handleDeactivate = (e) => {
18574
17589
  if (e.data?.type !== "ow:deactivate") return;
18575
17590
  if (Date.now() < linkPopoverGraceUntilRef.current) return;
@@ -18578,12 +17593,6 @@ function OhhwellsBridge() {
18578
17593
  closeLinkPopoverRef.current();
18579
17594
  return;
18580
17595
  }
18581
- if (floatingPanelOpenRef.current) {
18582
- setFloatingPanelRef.current(null);
18583
- deselectRef.current();
18584
- deactivateRef.current();
18585
- return;
18586
- }
18587
17596
  deselectRef.current();
18588
17597
  deactivateRef.current();
18589
17598
  };
@@ -18637,10 +17646,6 @@ function OhhwellsBridge() {
18637
17646
  return;
18638
17647
  }
18639
17648
  if (selectedElRef.current) {
18640
- if (toolbarVariantRef.current === "logo") {
18641
- deselectRef.current();
18642
- return;
18643
- }
18644
17649
  if (toolbarVariantRef.current === "select-frame") {
18645
17650
  const parent2 = getNavigationSelectionParent(selectedElRef.current);
18646
17651
  if (parent2 && isNavigationContainer(parent2) && isFooterLinksContainer(parent2) && !isFooterLinksContainer(selectedElRef.current)) {
@@ -18692,10 +17697,6 @@ function OhhwellsBridge() {
18692
17697
  return;
18693
17698
  }
18694
17699
  if (e.key === "Escape" && selectedElRef.current && !activeElRef.current) {
18695
- if (toolbarVariantRef.current === "logo") {
18696
- deselectRef.current();
18697
- return;
18698
- }
18699
17700
  if (toolbarVariantRef.current === "select-frame") {
18700
17701
  const parent2 = getNavigationSelectionParent(selectedElRef.current);
18701
17702
  if (parent2 && isNavigationContainer(parent2) && isFooterLinksContainer(parent2) && !isFooterLinksContainer(selectedElRef.current)) {
@@ -18773,8 +17774,7 @@ function OhhwellsBridge() {
18773
17774
  const handleScroll = () => {
18774
17775
  const focusEl = activeElRef.current ?? selectedElRef.current;
18775
17776
  if (focusEl) {
18776
- const measureEl = activeElRef.current ? getEditMeasureEl(activeElRef.current) : toolbarVariantRef.current === "logo" ? null : focusEl;
18777
- const r2 = measureEl ? measureEl.getBoundingClientRect() : toolbarVariantRef.current === "logo" ? getLogoInteractionRect(focusEl) : focusEl.getBoundingClientRect();
17777
+ const r2 = activeElRef.current ? getEditMeasureEl(activeElRef.current).getBoundingClientRect() : focusEl.getBoundingClientRect();
18778
17778
  applyToolbarPos(r2);
18779
17779
  setToolbarRect(r2);
18780
17780
  setMaxBadge((prev) => prev && activeElRef.current ? { ...prev, rect: r2 } : prev);
@@ -18784,9 +17784,7 @@ function OhhwellsBridge() {
18784
17784
  setToggleState((prev) => prev ? { ...prev, rect } : null);
18785
17785
  }
18786
17786
  if (hoveredItemElRef.current) {
18787
- const hoverEl = hoveredItemElRef.current;
18788
- const logo = getLogoElement(hoverEl);
18789
- setHoveredItemRect(logo ? getLogoInteractionRect(logo) : hoverEl.getBoundingClientRect());
17787
+ setHoveredItemRect(hoveredItemElRef.current.getBoundingClientRect());
18790
17788
  }
18791
17789
  if (hoveredNavContainerRef.current) {
18792
17790
  setHoveredNavContainerRect(hoveredNavContainerRef.current.getBoundingClientRect());
@@ -18830,12 +17828,13 @@ function OhhwellsBridge() {
18830
17828
  if (aiSectionsRef.current) {
18831
17829
  nodes.push({ key: AI_SECTIONS_KEY, type: "sections", text: aiSectionsRef.current });
18832
17830
  }
18833
- if (stylesRef.current) {
18834
- nodes.push({ key: STYLE_STORE_KEY, type: "styles", text: stylesRef.current });
18835
- }
18836
- if (brandKitRef.current) {
18837
- nodes.push({ key: BRAND_KIT_KEY, type: "brand", text: brandKitRef.current });
18838
- }
17831
+ document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
17832
+ const formKey = formKeyOf(form);
17833
+ if (!formKey) return;
17834
+ const specs = readFieldsFromDom(form);
17835
+ const text = specs.length ? JSON.stringify(specs) : editContentRef.current[fieldsKey(formKey)];
17836
+ if (text) nodes.push({ key: fieldsKey(formKey), type: "text", text });
17837
+ });
18839
17838
  postToParentRef.current({ type: "ow:save-result", nodes });
18840
17839
  };
18841
17840
  const handleInsertSection = (e) => {
@@ -18846,12 +17845,8 @@ function OhhwellsBridge() {
18846
17845
  if (inserted) {
18847
17846
  const tracker = getSectionsTracker();
18848
17847
  postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
18849
- const reportHeight = () => {
18850
- const h = document.body.scrollHeight;
18851
- if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
18852
- };
18853
- reportHeight();
18854
- setTimeout(reportHeight, 500);
17848
+ const h = document.documentElement.scrollHeight;
17849
+ if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
18855
17850
  }
18856
17851
  };
18857
17852
  const handleSwitchSchedule = (e) => {
@@ -19044,17 +18039,13 @@ function OhhwellsBridge() {
19044
18039
  if (e.data?.type !== "ow:parent-scroll") return;
19045
18040
  const { iframeOffsetTop, headerH, canvasH } = e.data;
19046
18041
  parentScrollRef.current = { iframeOffsetTop, headerH, canvasH };
19047
- if (floatingPanelOpenRef.current) {
19048
- setParentScrollSnap({ iframeOffsetTop, headerH, canvasH });
19049
- }
19050
18042
  if (visibleViewportRef.current) {
19051
18043
  applyVisibleViewport(visibleViewportRef.current, parentScrollRef.current);
19052
18044
  }
19053
18045
  const focusEl = activeElRef.current ?? selectedElRef.current;
19054
18046
  if (focusEl) {
19055
- const measureEl = activeElRef.current ? getEditMeasureEl(activeElRef.current) : toolbarVariantRef.current === "logo" ? null : focusEl;
19056
- const r2 = measureEl ? measureEl.getBoundingClientRect() : toolbarVariantRef.current === "logo" ? getLogoInteractionRect(focusEl) : focusEl.getBoundingClientRect();
19057
- applyToolbarPos(r2);
18047
+ const measureEl = activeElRef.current ? getEditMeasureEl(activeElRef.current) : focusEl;
18048
+ applyToolbarPos(measureEl.getBoundingClientRect());
19058
18049
  }
19059
18050
  };
19060
18051
  const handleClickAt = (e) => {
@@ -19087,15 +18078,9 @@ function OhhwellsBridge() {
19087
18078
  return clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom;
19088
18079
  });
19089
18080
  if (logoAtPoint) {
19090
- if (!logoHasUploadedImage(logoAtPoint)) {
19091
- deselectRef.current();
19092
- deactivateRef.current();
19093
- const identity = readLogoIdentityFromDom();
19094
- postToParentRef.current({ type: "ow:open-logo-settings", ...identity });
19095
- return;
19096
- }
19097
- selectLogoRef.current(logoAtPoint);
19098
- openLogoSizePanelRef.current(logoAtPoint);
18081
+ deselectRef.current();
18082
+ deactivateRef.current();
18083
+ postToParentRef.current({ type: "ow:open-logo-settings" });
19099
18084
  return;
19100
18085
  }
19101
18086
  const textEditable = Array.from(
@@ -19169,14 +18154,6 @@ function OhhwellsBridge() {
19169
18154
  window.addEventListener("message", handleParentScroll);
19170
18155
  window.addEventListener("message", handlePointerSync);
19171
18156
  window.addEventListener("message", handleClickAt);
19172
- window.addEventListener("message", handleUpdateLogoIdentity);
19173
- const handleViewMode = (e) => {
19174
- if (e.data?.type !== "ow:view-mode") return;
19175
- const mode = e.data.mode === "Mobile" || e.data.mode === "mobile" ? "mobile" : "desktop";
19176
- setEditorViewport(mode);
19177
- applyLogoSizes(editContentRef.current);
19178
- };
19179
- window.addEventListener("message", handleViewMode);
19180
18157
  const handleViewportResize = () => {
19181
18158
  if (visibleViewportRef.current) {
19182
18159
  applyVisibleViewport(visibleViewportRef.current, parentScrollRef.current);
@@ -19232,15 +18209,10 @@ function OhhwellsBridge() {
19232
18209
  window.removeEventListener("resize", handleViewportResize);
19233
18210
  window.removeEventListener("message", handlePointerSync);
19234
18211
  window.removeEventListener("message", handleClickAt);
19235
- window.removeEventListener("message", handleUpdateLogoIdentity);
19236
- window.removeEventListener("message", handleViewMode);
19237
18212
  window.removeEventListener("message", handleHydrate);
19238
18213
  window.removeEventListener("message", handleAiApplyTree);
19239
18214
  window.removeEventListener("message", handleAiDeleteSection);
19240
18215
  window.removeEventListener("message", handleAiSetSections);
19241
- window.removeEventListener("message", handleAiSetBrand);
19242
- window.removeEventListener("message", handleAiSetStyles);
19243
- window.removeEventListener("message", handleGetBrand);
19244
18216
  window.removeEventListener("message", handleDeactivate);
19245
18217
  window.removeEventListener("message", handleToastAction);
19246
18218
  window.removeEventListener("message", handleFormCount);
@@ -19447,7 +18419,7 @@ function OhhwellsBridge() {
19447
18419
  postToParent2({
19448
18420
  type: "ow:ready",
19449
18421
  version: "1",
19450
- bridgeVersion: "0.1.64",
18422
+ bridgeVersion: "0.1.63",
19451
18423
  path: pathname,
19452
18424
  nodes: collectEditableNodes(editContentRef.current),
19453
18425
  sections
@@ -19842,10 +18814,10 @@ function OhhwellsBridge() {
19842
18814
  [postToParent2]
19843
18815
  );
19844
18816
  return bridgeRoot ? createPortal2(
19845
- /* @__PURE__ */ jsxs21(Fragment8, { children: [
19846
- /* @__PURE__ */ jsx34("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
19847
- isEditMode && /* @__PURE__ */ jsx34(AiSectionOverlay, { postToParent: postToParent2, apiRef: aiSectionApiRef }),
19848
- Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ jsx34(
18817
+ /* @__PURE__ */ jsxs20(Fragment8, { children: [
18818
+ /* @__PURE__ */ jsx33("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
18819
+ isEditMode && /* @__PURE__ */ jsx33(AiSectionOverlay, { postToParent: postToParent2, apiRef: aiSectionApiRef }),
18820
+ Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ jsx33(
19849
18821
  MediaOverlay,
19850
18822
  {
19851
18823
  hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
@@ -19856,7 +18828,7 @@ function OhhwellsBridge() {
19856
18828
  },
19857
18829
  `uploading-${key}`
19858
18830
  )),
19859
- mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ jsx34(
18831
+ mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ jsx33(
19860
18832
  MediaOverlay,
19861
18833
  {
19862
18834
  hover: mediaHover,
@@ -19865,11 +18837,11 @@ function OhhwellsBridge() {
19865
18837
  onVideoSettingsChange: handleVideoSettingsChange
19866
18838
  }
19867
18839
  ),
19868
- carouselHover && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx34(CarouselOverlay, { hover: carouselHover, onEdit: handleEditCarousel }),
19869
- siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ jsx34(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
19870
- siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ jsx34(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
19871
- isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ jsx34(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
19872
- isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ jsx34(
18840
+ carouselHover && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx33(CarouselOverlay, { hover: carouselHover, onEdit: handleEditCarousel }),
18841
+ siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
18842
+ siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
18843
+ isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
18844
+ isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
19873
18845
  "div",
19874
18846
  {
19875
18847
  className: "pointer-events-none fixed z-2147483646",
@@ -19879,7 +18851,7 @@ function OhhwellsBridge() {
19879
18851
  width: slot.width,
19880
18852
  height: slot.height
19881
18853
  },
19882
- children: /* @__PURE__ */ jsx34(
18854
+ children: /* @__PURE__ */ jsx33(
19883
18855
  DropIndicator,
19884
18856
  {
19885
18857
  direction: slot.direction,
@@ -19890,7 +18862,7 @@ function OhhwellsBridge() {
19890
18862
  },
19891
18863
  `footer-drop-${slot.direction}-${slot.columnIndex}-${slot.insertIndex}-${i}`
19892
18864
  )),
19893
- isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ jsx34(
18865
+ isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
19894
18866
  "div",
19895
18867
  {
19896
18868
  className: "pointer-events-none fixed z-2147483646",
@@ -19900,7 +18872,7 @@ function OhhwellsBridge() {
19900
18872
  width: slot.width,
19901
18873
  height: slot.height
19902
18874
  },
19903
- children: /* @__PURE__ */ jsx34(
18875
+ children: /* @__PURE__ */ jsx33(
19904
18876
  DropIndicator,
19905
18877
  {
19906
18878
  direction: slot.direction,
@@ -19911,10 +18883,10 @@ function OhhwellsBridge() {
19911
18883
  },
19912
18884
  `nav-drop-${slot.direction}-${slot.parentId ?? "root"}-${slot.insertIndex}-${i}`
19913
18885
  )),
19914
- hoveredNavContainerRect && (toolbarVariant !== "select-frame" || isFooterFrameSelection) && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx34(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
19915
- hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ jsx34(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
19916
- hoveredTextRect && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ jsx34(ItemInteractionLayer, { rect: hoveredTextRect, state: "hover" }),
19917
- formPickRect && !isItemDragging && /* @__PURE__ */ jsx34(
18886
+ hoveredNavContainerRect && (toolbarVariant !== "select-frame" || isFooterFrameSelection) && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
18887
+ hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
18888
+ hoveredTextRect && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredTextRect, state: "hover" }),
18889
+ formPickRect && !isItemDragging && /* @__PURE__ */ jsx33(
19918
18890
  ItemInteractionLayer,
19919
18891
  {
19920
18892
  rect: formPickRect,
@@ -19922,13 +18894,13 @@ function OhhwellsBridge() {
19922
18894
  itemDragSurface: false,
19923
18895
  toolbarAlign: "left",
19924
18896
  chromeGap: 24,
19925
- toolbar: fieldPickRect ? void 0 : /* @__PURE__ */ jsxs21(
18897
+ toolbar: fieldPickRect ? void 0 : /* @__PURE__ */ jsxs20(
19926
18898
  "div",
19927
18899
  {
19928
18900
  "data-ohw-form-toolbar": "",
19929
18901
  className: "pointer-events-auto flex items-center gap-0.5 whitespace-nowrap rounded-lg border border-border bg-background p-1 shadow-md",
19930
18902
  children: [
19931
- /* @__PURE__ */ jsx34(
18903
+ /* @__PURE__ */ jsx33(
19932
18904
  "button",
19933
18905
  {
19934
18906
  type: "button",
@@ -19936,11 +18908,11 @@ function OhhwellsBridge() {
19936
18908
  className: "flex h-7 w-7 items-center justify-center rounded-md text-foreground transition-colors hover:bg-muted/80",
19937
18909
  onClick: () => setFieldTypePickerOpen((open) => !open),
19938
18910
  "data-ohw-add-field": "",
19939
- children: /* @__PURE__ */ jsx34(Plus4, { size: 15, "aria-hidden": true })
18911
+ children: /* @__PURE__ */ jsx33(Plus4, { size: 15, "aria-hidden": true })
19940
18912
  }
19941
18913
  ),
19942
- /* @__PURE__ */ jsx34("div", { className: "mx-0.5 h-5 w-px bg-border" }),
19943
- /* @__PURE__ */ jsxs21(
18914
+ /* @__PURE__ */ jsx33("div", { className: "mx-0.5 h-5 w-px bg-border" }),
18915
+ /* @__PURE__ */ jsxs20(
19944
18916
  "button",
19945
18917
  {
19946
18918
  type: "button",
@@ -19955,11 +18927,11 @@ function OhhwellsBridge() {
19955
18927
  });
19956
18928
  },
19957
18929
  children: [
19958
- /* @__PURE__ */ jsx34(Settings, { size: 14, "aria-hidden": true }),
18930
+ /* @__PURE__ */ jsx33(Settings, { size: 14, "aria-hidden": true }),
19959
18931
  "Form settings",
19960
18932
  formPickCount ? (
19961
18933
  // Counter pill, per the design — not a text suffix.
19962
- /* @__PURE__ */ jsx34(
18934
+ /* @__PURE__ */ jsx33(
19963
18935
  "span",
19964
18936
  {
19965
18937
  "data-ohw-form-count": "",
@@ -19971,30 +18943,8 @@ function OhhwellsBridge() {
19971
18943
  ]
19972
18944
  }
19973
18945
  ),
19974
- /* @__PURE__ */ jsx34("div", { className: "mx-0.5 h-5 w-px bg-border" }),
19975
- /* @__PURE__ */ jsxs21(
19976
- "button",
19977
- {
19978
- type: "button",
19979
- 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",
19980
- onClick: () => {
19981
- const form = formPickElRef.current;
19982
- if (!form) return;
19983
- postToParent2({
19984
- type: "ow:form-submissions",
19985
- formKey: formKeyOf(form),
19986
- hasLongText: formHasLongText(form)
19987
- });
19988
- },
19989
- "data-ohw-view-submissions": "",
19990
- children: [
19991
- /* @__PURE__ */ jsx34(Table, { size: 14, "aria-hidden": true }),
19992
- "View submissions"
19993
- ]
19994
- }
19995
- ),
19996
- /* @__PURE__ */ jsx34("div", { className: "mx-0.5 h-5 w-px bg-border" }),
19997
- /* @__PURE__ */ jsx34("div", { className: "flex items-center rounded-md bg-muted/70 p-0.5", children: ["default", "success"].map((state) => /* @__PURE__ */ jsx34(
18946
+ /* @__PURE__ */ jsx33("div", { className: "mx-0.5 h-5 w-px bg-border" }),
18947
+ /* @__PURE__ */ jsx33("div", { className: "flex items-center rounded-md bg-muted/70 p-0.5", children: ["default", "success"].map((state) => /* @__PURE__ */ jsx33(
19998
18948
  "button",
19999
18949
  {
20000
18950
  type: "button",
@@ -20004,7 +18954,7 @@ function OhhwellsBridge() {
20004
18954
  const form = formPickElRef.current;
20005
18955
  const key = form ? formKeyOf(form) : null;
20006
18956
  if (!form || !key) return;
20007
- const initial = editContentRef.current[formSuccessKey(key)] ?? DEFAULT_SUCCESS_TEXT;
18957
+ const initial = successInitialFor(form, key, editContentRef.current);
20008
18958
  setFormViewState(form, key, state, initial);
20009
18959
  setFormViewStateUi(state);
20010
18960
  setFormPickRect(form.getBoundingClientRect());
@@ -20024,7 +18974,7 @@ function OhhwellsBridge() {
20024
18974
  )
20025
18975
  }
20026
18976
  ),
20027
- formHoverRect && !isItemDragging && /* @__PURE__ */ jsx34(
18977
+ formHoverRect && !isItemDragging && /* @__PURE__ */ jsx33(
20028
18978
  ItemInteractionLayer,
20029
18979
  {
20030
18980
  rect: formHoverRect,
@@ -20032,11 +18982,11 @@ function OhhwellsBridge() {
20032
18982
  chromeGap: formHoverElRef.current && getFieldWrapper(formHoverElRef.current) ? 8 : 24
20033
18983
  }
20034
18984
  ),
20035
- fieldPickRect && fieldPickState && !isItemDragging && /* @__PURE__ */ jsx34(
18985
+ fieldPickRect && fieldPickState && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ jsx33(
20036
18986
  ItemInteractionLayer,
20037
18987
  {
20038
18988
  rect: fieldPickRect,
20039
- state: "active-top",
18989
+ state: fieldDragging ? "dragging" : "active-top",
20040
18990
  itemDragSurface: false,
20041
18991
  toolbarAlign: "left",
20042
18992
  chromeGap: 10,
@@ -20044,7 +18994,7 @@ function OhhwellsBridge() {
20044
18994
  dragHandleLabel: "Reorder field",
20045
18995
  onDragHandleDragStart: handleFieldDragStart,
20046
18996
  onDragHandleDragEnd: handleFieldDragEnd,
20047
- toolbar: /* @__PURE__ */ jsx34(
18997
+ toolbar: /* @__PURE__ */ jsx33(
20048
18998
  FormFieldToolbar,
20049
18999
  {
20050
19000
  type: fieldPickState.type,
@@ -20057,33 +19007,32 @@ function OhhwellsBridge() {
20057
19007
  )
20058
19008
  }
20059
19009
  ),
20060
- fieldDropIndex !== null && formPickElRef.current ? (() => {
20061
- const wrappers = listFieldWrappers(formPickElRef.current).filter(
20062
- (el) => fieldKeyOf(el) !== fieldDragRef.current?.key
20063
- );
20064
- const anchor = wrappers[Math.min(fieldDropIndex, wrappers.length - 1)];
20065
- if (!anchor) return null;
20066
- const rect = anchor.getBoundingClientRect();
20067
- const atEnd = fieldDropIndex >= wrappers.length;
20068
- return /* @__PURE__ */ jsx34(
20069
- "div",
20070
- {
20071
- className: "pointer-events-none fixed z-[2147483644]",
20072
- style: { top: atEnd ? rect.bottom : rect.top, left: rect.left, width: rect.width },
20073
- children: /* @__PURE__ */ jsx34(DropIndicator, { direction: "horizontal", state: "dragActive" })
20074
- }
20075
- );
20076
- })() : null,
20077
- fieldTypePickerOpen && formPickRect && /* @__PURE__ */ jsx34(
19010
+ fieldDragging ? fieldDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
19011
+ "div",
19012
+ {
19013
+ className: "pointer-events-none fixed z-[2147483644]",
19014
+ style: { top: slot.top, left: slot.left, width: slot.width },
19015
+ children: /* @__PURE__ */ jsx33(
19016
+ DropIndicator,
19017
+ {
19018
+ direction: "horizontal",
19019
+ state: fieldDropIndex === i ? "dragActive" : "dragIdle",
19020
+ className: "!w-full"
19021
+ }
19022
+ )
19023
+ },
19024
+ `field-drop-${i}`
19025
+ )) : null,
19026
+ fieldTypePickerOpen && formPickRect && /* @__PURE__ */ jsx33(
20078
19027
  "div",
20079
19028
  {
20080
19029
  className: "pointer-events-none fixed z-[2147483645]",
20081
19030
  style: { top: formPickRect.top + 16, left: formPickRect.left + 24 },
20082
- children: /* @__PURE__ */ jsx34(FieldTypePicker, { onPick: handleAddField })
19031
+ children: /* @__PURE__ */ jsx33(FieldTypePicker, { onPick: handleAddField })
20083
19032
  }
20084
19033
  ),
20085
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer2(selectedElRef.current) && /* @__PURE__ */ jsx34(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
20086
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isFooterLinksContainer(selectedElRef.current) && /* @__PURE__ */ jsx34(
19034
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer2(selectedElRef.current) && /* @__PURE__ */ jsx33(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
19035
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isFooterLinksContainer(selectedElRef.current) && /* @__PURE__ */ jsx33(
20087
19036
  FooterContainerChrome,
20088
19037
  {
20089
19038
  rect: toolbarRect,
@@ -20091,7 +19040,7 @@ function OhhwellsBridge() {
20091
19040
  addDisabled: !canAddFooterColumn()
20092
19041
  }
20093
19042
  ),
20094
- toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame" || toolbarVariant === "logo") && /* @__PURE__ */ jsx34(
19043
+ toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ jsx33(
20095
19044
  ItemInteractionLayer,
20096
19045
  {
20097
19046
  rect: isItemDragging && draggedItemRect && (footerDragRef.current?.wasSelected || navDragRef.current?.wasSelected) ? draggedItemRect : toolbarRect,
@@ -20103,10 +19052,10 @@ function OhhwellsBridge() {
20103
19052
  dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
20104
19053
  onDragHandleDragStart: handleItemDragStart,
20105
19054
  onDragHandleDragEnd: handleItemDragEnd,
20106
- onItemPointerDown: toolbarVariant === "logo" ? void 0 : handleItemChromePointerDown,
20107
- onItemClick: toolbarVariant === "logo" ? void 0 : handleItemChromeClick,
20108
- itemDragSurface: toolbarVariant !== "logo" && !isFooterFrameSelection,
20109
- toolbar: toolbarVariant === "link-action" && !isItemDragging || toolbarVariant === "select-frame" && (isFooterFrameSelection || selectedIsSocialsRow) && !isItemDragging ? /* @__PURE__ */ jsx34(
19055
+ onItemPointerDown: handleItemChromePointerDown,
19056
+ onItemClick: handleItemChromeClick,
19057
+ itemDragSurface: !isFooterFrameSelection,
19058
+ toolbar: toolbarVariant === "link-action" && !isItemDragging || toolbarVariant === "select-frame" && (isFooterFrameSelection || selectedIsSocialsRow) && !isItemDragging ? /* @__PURE__ */ jsx33(
20110
19059
  ItemActionToolbar,
20111
19060
  {
20112
19061
  onEditLink: openLinkPopoverForSelected,
@@ -20142,8 +19091,8 @@ function OhhwellsBridge() {
20142
19091
  ) : void 0
20143
19092
  }
20144
19093
  ),
20145
- toolbarRect && toolbarVariant === "rich-text" && !linkPopover && /* @__PURE__ */ jsxs21(Fragment8, { children: [
20146
- /* @__PURE__ */ jsx34(
19094
+ toolbarRect && toolbarVariant === "rich-text" && !linkPopover && /* @__PURE__ */ jsxs20(Fragment8, { children: [
19095
+ /* @__PURE__ */ jsx33(
20147
19096
  EditGlowChrome,
20148
19097
  {
20149
19098
  rect: toolbarRect,
@@ -20153,7 +19102,7 @@ function OhhwellsBridge() {
20153
19102
  hideHandle: isItemDragging
20154
19103
  }
20155
19104
  ),
20156
- /* @__PURE__ */ jsx34(
19105
+ /* @__PURE__ */ jsx33(
20157
19106
  FloatingToolbar,
20158
19107
  {
20159
19108
  rect: toolbarRect,
@@ -20166,7 +19115,7 @@ function OhhwellsBridge() {
20166
19115
  }
20167
19116
  )
20168
19117
  ] }),
20169
- maxBadge && /* @__PURE__ */ jsxs21(
19118
+ maxBadge && /* @__PURE__ */ jsxs20(
20170
19119
  "div",
20171
19120
  {
20172
19121
  "data-ohw-max-badge": "",
@@ -20192,7 +19141,7 @@ function OhhwellsBridge() {
20192
19141
  ]
20193
19142
  }
20194
19143
  ),
20195
- toggleState && !linkPopover && /* @__PURE__ */ jsx34(
19144
+ toggleState && !linkPopover && /* @__PURE__ */ jsx33(
20196
19145
  StateToggle,
20197
19146
  {
20198
19147
  rect: toggleState.rect,
@@ -20201,15 +19150,15 @@ function OhhwellsBridge() {
20201
19150
  onStateChange: handleStateChange
20202
19151
  }
20203
19152
  ),
20204
- sectionGap && !linkPopover && /* @__PURE__ */ jsxs21(
19153
+ sectionGap && !linkPopover && /* @__PURE__ */ jsxs20(
20205
19154
  "div",
20206
19155
  {
20207
19156
  "data-ohw-section-insert-line": "",
20208
19157
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
20209
19158
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
20210
19159
  children: [
20211
- /* @__PURE__ */ jsx34("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
20212
- /* @__PURE__ */ jsx34(
19160
+ /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
19161
+ /* @__PURE__ */ jsx33(
20213
19162
  Badge,
20214
19163
  {
20215
19164
  className: "px-8 py-1 bg-primary hover:bg-primary text-primary-foreground text-xs font-medium shrink-0 rounded-full cursor-pointer pointer-events-auto",
@@ -20226,11 +19175,11 @@ function OhhwellsBridge() {
20226
19175
  children: "Add Section"
20227
19176
  }
20228
19177
  ),
20229
- /* @__PURE__ */ jsx34("div", { className: "flex-1 bg-primary", style: { height: 3 } })
19178
+ /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-primary", style: { height: 3 } })
20230
19179
  ]
20231
19180
  }
20232
19181
  ),
20233
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx34(
19182
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx33(
20234
19183
  LinkPopover,
20235
19184
  {
20236
19185
  panelRef: linkPopoverPanelRef,
@@ -20247,7 +19196,7 @@ function OhhwellsBridge() {
20247
19196
  },
20248
19197
  linkPopover.key
20249
19198
  ) : null,
20250
- floatingPanel && floatingPanel.kind === "socials-display" ? /* @__PURE__ */ jsx34(
19199
+ floatingPanel && floatingPanel.kind === "socials-display" ? /* @__PURE__ */ jsx33(
20251
19200
  FloatingPanel,
20252
19201
  {
20253
19202
  open: true,
@@ -20257,7 +19206,7 @@ function OhhwellsBridge() {
20257
19206
  onPositionChange: setFloatingPanelPos,
20258
19207
  parentScroll: parentScrollSnap ?? parentScrollRef.current,
20259
19208
  onClose: closeFloatingPanelOnly,
20260
- children: /* @__PURE__ */ jsx34(
19209
+ children: /* @__PURE__ */ jsx33(
20261
19210
  SocialsDisplayPanel,
20262
19211
  {
20263
19212
  display: socialsDisplayFor(floatingPanel.row, editContentRef.current),
@@ -20268,115 +19217,11 @@ function OhhwellsBridge() {
20268
19217
  }
20269
19218
  )
20270
19219
  }
20271
- ) : null,
20272
- floatingPanel && floatingPanel.kind === "logo-size" && logoSizeDraft ? /* @__PURE__ */ jsx34(
20273
- FloatingPanel,
20274
- {
20275
- open: true,
20276
- title: floatingPanel.title,
20277
- context: floatingPanel.context,
20278
- position: floatingPanelPos,
20279
- onPositionChange: setFloatingPanelPos,
20280
- parentScroll: parentScrollSnap ?? parentScrollRef.current,
20281
- onClose: closeFloatingPanelAndDeselect,
20282
- children: /* @__PURE__ */ jsx34(
20283
- LogoSizePanel,
20284
- {
20285
- viewport: editorViewport,
20286
- sizePx: editorViewport === "mobile" && !logoSizeDraft.mobileFollowing ? logoSizeDraft.mobilePx : logoSizeDraft.desktopPx,
20287
- mobileFollowing: logoSizeDraft.mobileFollowing,
20288
- onSizeChange: (px) => {
20289
- const next = editorViewport === "mobile" ? { ...logoSizeDraft, mobilePx: px, mobileFollowing: false } : {
20290
- ...logoSizeDraft,
20291
- desktopPx: px,
20292
- mobilePx: logoSizeDraft.mobileFollowing ? px : logoSizeDraft.mobilePx
20293
- };
20294
- setLogoSizeDraft(next);
20295
- persistLogoSizeDraft(floatingPanel.placement, next);
20296
- },
20297
- onCustomizeMobile: () => {
20298
- const next = {
20299
- ...logoSizeDraft,
20300
- mobileFollowing: false,
20301
- mobilePx: logoSizeDraft.desktopPx
20302
- };
20303
- setLogoSizeDraft(next);
20304
- persistLogoSizeDraft(floatingPanel.placement, next);
20305
- },
20306
- onResetMobile: () => {
20307
- const next = {
20308
- ...logoSizeDraft,
20309
- mobileFollowing: true,
20310
- mobilePx: logoSizeDraft.desktopPx
20311
- };
20312
- setLogoSizeDraft(next);
20313
- persistLogoSizeDraft(floatingPanel.placement, next);
20314
- },
20315
- onUpdateEverywhere: () => {
20316
- const identity = readLogoIdentityFromDom();
20317
- postToParent2({ type: "ow:open-logo-settings", ...identity });
20318
- }
20319
- }
20320
- )
20321
- }
20322
19220
  ) : null
20323
19221
  ] }),
20324
19222
  bridgeRoot
20325
19223
  ) : null;
20326
19224
  }
20327
-
20328
- // src/ui/EmptySection.tsx
20329
- import Link4 from "next/link";
20330
- import { Fragment as Fragment9, jsx as jsx35, jsxs as jsxs22 } from "react/jsx-runtime";
20331
- function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey }) {
20332
- return /* @__PURE__ */ jsxs22(Fragment9, { children: [
20333
- /* @__PURE__ */ jsx35(
20334
- "p",
20335
- {
20336
- style: {
20337
- fontFamily: "var(--brand-font-body)",
20338
- fontSize: "0.75rem",
20339
- fontWeight: 500,
20340
- letterSpacing: "0.15em",
20341
- textTransform: "uppercase",
20342
- color: "var(--brand-accent)",
20343
- marginBottom: "1.5rem"
20344
- },
20345
- children: /* @__PURE__ */ jsx35(Link4, { href: homeHref, style: { color: "inherit" }, children: /* @__PURE__ */ jsx35("span", { ...eyebrowKey ? { "data-ohw-editable": "text", "data-ohw-key": eyebrowKey } : {}, children: "Home" }) })
20346
- }
20347
- ),
20348
- /* @__PURE__ */ jsx35(
20349
- "h1",
20350
- {
20351
- style: {
20352
- fontFamily: "var(--brand-font-heading)",
20353
- fontSize: "clamp(2rem, 3.5vw, 2.75rem)",
20354
- lineHeight: 1.1,
20355
- letterSpacing: "-0.025em",
20356
- color: "var(--brand-text)",
20357
- marginBottom: "1rem"
20358
- },
20359
- ...titleKey ? { "data-ohw-editable": "text", "data-ohw-key": titleKey, "data-ohw-max-length": 80 } : {},
20360
- children: title
20361
- }
20362
- ),
20363
- /* @__PURE__ */ jsx35(
20364
- "p",
20365
- {
20366
- style: {
20367
- fontFamily: "var(--brand-font-body)",
20368
- fontSize: "1rem",
20369
- lineHeight: 1.7,
20370
- fontWeight: 300,
20371
- color: "var(--brand-text-muted)",
20372
- maxWidth: "340px"
20373
- },
20374
- ...subtitleKey ? { "data-ohw-editable": "text", "data-ohw-key": subtitleKey, "data-ohw-max-length": 160 } : {},
20375
- children: "This page doesn't have any content yet."
20376
- }
20377
- )
20378
- ] });
20379
- }
20380
19225
  export {
20381
19226
  AI_DEFAULT_BRAND,
20382
19227
  AI_TREE_SCHEMA_VERSIONS,
@@ -20393,7 +19238,6 @@ export {
20393
19238
  DropdownMenuItem,
20394
19239
  DropdownMenuSeparator,
20395
19240
  DropdownMenuTrigger,
20396
- EmptySection,
20397
19241
  ItemActionToolbar,
20398
19242
  ItemInteractionLayer,
20399
19243
  LinkEditorPanel,