@ohhwells/bridge 0.1.77 → 0.1.78-next.234
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1242 -217
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -5
- package/dist/index.d.ts +21 -5
- package/dist/index.js +1241 -217
- package/dist/index.js.map +1 -1
- package/dist/pages.cjs +141 -0
- package/dist/pages.cjs.map +1 -0
- package/dist/pages.d.cts +45 -0
- package/dist/pages.d.ts +45 -0
- package/dist/pages.js +107 -0
- package/dist/pages.js.map +1 -0
- package/dist/styles.css +510 -452
- package/package.json +8 -3
package/dist/index.cjs
CHANGED
|
@@ -46,6 +46,7 @@ __export(index_exports, {
|
|
|
46
46
|
DropdownMenuItem: () => DropdownMenuItem,
|
|
47
47
|
DropdownMenuSeparator: () => DropdownMenuSeparator,
|
|
48
48
|
DropdownMenuTrigger: () => DropdownMenuTrigger,
|
|
49
|
+
EmptySection: () => EmptySection,
|
|
49
50
|
ItemActionToolbar: () => ItemActionToolbar,
|
|
50
51
|
ItemInteractionLayer: () => ItemInteractionLayer,
|
|
51
52
|
LinkEditorPanel: () => LinkEditorPanel,
|
|
@@ -156,7 +157,12 @@ function parseAiSectionsState(raw) {
|
|
|
156
157
|
media: entry.media && typeof entry.media === "object" ? entry.media : {}
|
|
157
158
|
}));
|
|
158
159
|
const removed = Array.isArray(parsed.removed) ? parsed.removed.filter((id) => typeof id === "string" && id.length > 0) : [];
|
|
159
|
-
return {
|
|
160
|
+
return {
|
|
161
|
+
v: 1,
|
|
162
|
+
sections,
|
|
163
|
+
...removed.length ? { removed } : {},
|
|
164
|
+
...parsed.hideTemplate === true ? { hideTemplate: true } : {}
|
|
165
|
+
};
|
|
160
166
|
} catch {
|
|
161
167
|
return EMPTY_AI_SECTIONS;
|
|
162
168
|
}
|
|
@@ -169,6 +175,7 @@ function applyTreeToState(state, payload) {
|
|
|
169
175
|
const entry = {
|
|
170
176
|
id: payload.id,
|
|
171
177
|
label: payload.label ?? "Generated section",
|
|
178
|
+
...typeof payload.path === "string" && payload.path ? { path: payload.path } : {},
|
|
172
179
|
afterSection: payload.mode === "insert" && !insertBefore ? payload.targetSectionId ?? null : null,
|
|
173
180
|
...insertBefore && payload.targetSectionId ? { beforeSection: payload.targetSectionId } : {},
|
|
174
181
|
...payload.mode === "replace" && payload.targetSectionId ? { replaces: payload.targetSectionId } : {},
|
|
@@ -191,6 +198,317 @@ function deleteSectionFromState(state, sectionId) {
|
|
|
191
198
|
return { ...state, v: 1, sections: state.sections, removed: [...removed, sectionId] };
|
|
192
199
|
}
|
|
193
200
|
|
|
201
|
+
// src/lib/brand-chrome.ts
|
|
202
|
+
var BRAND_NAME_KEY = "__ohw_brand_name";
|
|
203
|
+
var BRAND_TITLE_KEY = "__ohw_site_title";
|
|
204
|
+
var BRAND_FAVICON_LETTER_KEY = "__ohw_favicon_letter";
|
|
205
|
+
var BRAND_CHROME_KEYS = /* @__PURE__ */ new Set([
|
|
206
|
+
BRAND_NAME_KEY,
|
|
207
|
+
BRAND_TITLE_KEY,
|
|
208
|
+
BRAND_FAVICON_LETTER_KEY
|
|
209
|
+
]);
|
|
210
|
+
function upsertMeta(selector, attr, token, value) {
|
|
211
|
+
let el = document.head.querySelector(selector);
|
|
212
|
+
if (!el) {
|
|
213
|
+
el = document.createElement("meta");
|
|
214
|
+
el.setAttribute(attr, token);
|
|
215
|
+
document.head.appendChild(el);
|
|
216
|
+
}
|
|
217
|
+
if (el.getAttribute("content") !== value) el.setAttribute("content", value);
|
|
218
|
+
}
|
|
219
|
+
function escapeXml(value) {
|
|
220
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
221
|
+
}
|
|
222
|
+
function applyLetterFavicon(letter) {
|
|
223
|
+
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" rx="12" fill="#111827"/><text x="32" y="46" font-family="system-ui,-apple-system,Segoe UI,sans-serif" font-size="40" font-weight="700" text-anchor="middle" fill="#ffffff">${escapeXml(letter)}</text></svg>`;
|
|
224
|
+
const href = `data:image/svg+xml,${encodeURIComponent(svg)}`;
|
|
225
|
+
let link = document.head.querySelector('link[rel="icon"]');
|
|
226
|
+
if (!link) {
|
|
227
|
+
link = document.createElement("link");
|
|
228
|
+
link.rel = "icon";
|
|
229
|
+
document.head.appendChild(link);
|
|
230
|
+
}
|
|
231
|
+
link.type = "image/svg+xml";
|
|
232
|
+
if (link.href !== href) link.href = href;
|
|
233
|
+
}
|
|
234
|
+
function applyBrandChrome(content) {
|
|
235
|
+
const name = content[BRAND_NAME_KEY];
|
|
236
|
+
if (typeof name === "string" && name.length > 0) {
|
|
237
|
+
document.querySelectorAll("[data-ohw-wordmark]").forEach((el) => {
|
|
238
|
+
if (el.textContent !== name) el.textContent = name;
|
|
239
|
+
if (el.getAttribute("title") !== name) el.setAttribute("title", name);
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
const title = content[BRAND_TITLE_KEY];
|
|
243
|
+
if (typeof title === "string" && title.length > 0) {
|
|
244
|
+
if (document.title !== title) document.title = title;
|
|
245
|
+
upsertMeta('meta[property="og:title"]', "property", "og:title", title);
|
|
246
|
+
upsertMeta('meta[name="twitter:title"]', "name", "twitter:title", title);
|
|
247
|
+
}
|
|
248
|
+
const letter = content[BRAND_FAVICON_LETTER_KEY];
|
|
249
|
+
if (typeof letter === "string" && letter.length > 0) {
|
|
250
|
+
applyLetterFavicon(letter);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// src/lib/brand-kit.ts
|
|
255
|
+
var BRAND_KIT_KEY = "__ohw_brand";
|
|
256
|
+
var BRAND_VAR_PREFIX = "--ohw-brand-";
|
|
257
|
+
var BRAND_VAR_NAMES = ["primary", "accent", "light", "dark", "surface", "border", "muted"].map(
|
|
258
|
+
(role) => `${BRAND_VAR_PREFIX}${role}`
|
|
259
|
+
);
|
|
260
|
+
var FONT_VARS = {
|
|
261
|
+
heading: ["--font-heading", "--font-display", "--brand-font-heading"],
|
|
262
|
+
body: ["--font-body", "--brand-font-body"]
|
|
263
|
+
};
|
|
264
|
+
var BRAND_FONT_LINK_ID = "ohw-brand-fonts";
|
|
265
|
+
function brandColorVars(kit) {
|
|
266
|
+
const { dark, primary, accent, light } = kit.palette;
|
|
267
|
+
const mix = (a, aPct, b) => `color-mix(in srgb, ${a} ${aPct}%, ${b})`;
|
|
268
|
+
return {
|
|
269
|
+
[`${BRAND_VAR_PREFIX}primary`]: primary,
|
|
270
|
+
[`${BRAND_VAR_PREFIX}accent`]: accent,
|
|
271
|
+
[`${BRAND_VAR_PREFIX}light`]: light,
|
|
272
|
+
[`${BRAND_VAR_PREFIX}dark`]: dark,
|
|
273
|
+
[`${BRAND_VAR_PREFIX}surface`]: mix(light, 95, dark),
|
|
274
|
+
[`${BRAND_VAR_PREFIX}border`]: mix(light, 85, dark),
|
|
275
|
+
[`${BRAND_VAR_PREFIX}muted`]: mix(dark, 62, light)
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
function parseBrandKit(raw) {
|
|
279
|
+
if (!raw) return null;
|
|
280
|
+
try {
|
|
281
|
+
const parsed = JSON.parse(raw);
|
|
282
|
+
const p = parsed?.palette;
|
|
283
|
+
const f = parsed?.fonts;
|
|
284
|
+
if (!p || !f || typeof p.dark !== "string" || typeof p.primary !== "string" || typeof p.accent !== "string" || typeof p.light !== "string" || typeof f.heading !== "string" || typeof f.body !== "string") {
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
return {
|
|
288
|
+
palette: { dark: p.dark, primary: p.primary, accent: p.accent, light: p.light },
|
|
289
|
+
fonts: { heading: f.heading, body: f.body }
|
|
290
|
+
};
|
|
291
|
+
} catch {
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
function familyOf(stack) {
|
|
296
|
+
const first = stack.split(",")[0]?.trim() ?? "";
|
|
297
|
+
return first.replace(/^['"]|['"]$/g, "");
|
|
298
|
+
}
|
|
299
|
+
function loadBrandFonts(families) {
|
|
300
|
+
const unique = [...new Set(families.map((f) => f.trim()).filter(Boolean))];
|
|
301
|
+
if (unique.length === 0) return;
|
|
302
|
+
const spec = unique.map((f) => `${f.replace(/ /g, "+")}:400,500,600,700`).join("|");
|
|
303
|
+
const href = `https://fonts.googleapis.com/css?family=${spec}&display=swap`;
|
|
304
|
+
let link = document.getElementById(BRAND_FONT_LINK_ID);
|
|
305
|
+
if (!link) {
|
|
306
|
+
link = document.createElement("link");
|
|
307
|
+
link.id = BRAND_FONT_LINK_ID;
|
|
308
|
+
link.rel = "stylesheet";
|
|
309
|
+
document.head.appendChild(link);
|
|
310
|
+
}
|
|
311
|
+
if (link.href !== href) link.href = href;
|
|
312
|
+
}
|
|
313
|
+
function applyBrandToDom(kit) {
|
|
314
|
+
const root = document.documentElement;
|
|
315
|
+
if (!kit) {
|
|
316
|
+
for (const name of BRAND_VAR_NAMES) root.style.removeProperty(name);
|
|
317
|
+
for (const name of [...FONT_VARS.heading, ...FONT_VARS.body]) root.style.removeProperty(name);
|
|
318
|
+
document.getElementById(BRAND_FONT_LINK_ID)?.remove();
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
for (const [name, value] of Object.entries(brandColorVars(kit))) root.style.setProperty(name, value);
|
|
322
|
+
for (const name of FONT_VARS.heading) root.style.setProperty(name, kit.fonts.heading);
|
|
323
|
+
for (const name of FONT_VARS.body) root.style.setProperty(name, kit.fonts.body);
|
|
324
|
+
loadBrandFonts([familyOf(kit.fonts.heading), familyOf(kit.fonts.body)]);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// src/lib/section-styles.ts
|
|
328
|
+
var STYLE_STORE_KEY = "__ohw_styles";
|
|
329
|
+
var STYLE_SHEET_ID = "ohw-section-styles";
|
|
330
|
+
function parseStyleStore(raw) {
|
|
331
|
+
if (!raw) return null;
|
|
332
|
+
try {
|
|
333
|
+
const parsed = JSON.parse(raw);
|
|
334
|
+
if (parsed?.v !== 1) return null;
|
|
335
|
+
return {
|
|
336
|
+
v: 1,
|
|
337
|
+
sections: typeof parsed.sections === "object" && parsed.sections ? parsed.sections : {},
|
|
338
|
+
nodes: typeof parsed.nodes === "object" && parsed.nodes ? parsed.nodes : {}
|
|
339
|
+
};
|
|
340
|
+
} catch {
|
|
341
|
+
return null;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
var BG_VALUES = {
|
|
345
|
+
surface: "color-mix(in srgb, var(--ohw-brand-light, var(--color-light, #ECECEB)) 94%, var(--ohw-brand-dark, var(--color-dark, #0C0A09)))",
|
|
346
|
+
accent: "var(--ohw-brand-primary, var(--color-primary, #0078E5))",
|
|
347
|
+
"accent-soft": "color-mix(in srgb, var(--ohw-brand-primary, var(--color-primary, #0078E5)) 12%, var(--ohw-brand-light, var(--color-light, #FFFFFF)))"
|
|
348
|
+
};
|
|
349
|
+
var HEADLINE_SIZES = { md: 24, lg: 32, xl: 40, display: 56 };
|
|
350
|
+
function styleSheetCss() {
|
|
351
|
+
const rules = [];
|
|
352
|
+
for (const [tone, value] of Object.entries(BG_VALUES)) {
|
|
353
|
+
rules.push(`[data-ohw-style-bg="${tone}"] { background: ${value} !important; }`);
|
|
354
|
+
}
|
|
355
|
+
rules.push(
|
|
356
|
+
`[data-ohw-style-bg="accent"] { color: var(--ohw-brand-light, var(--color-light, #FFFFFF)) !important; }`
|
|
357
|
+
);
|
|
358
|
+
rules.push(
|
|
359
|
+
`[data-ohw-style-distribution="space-between"] { display: flex !important; flex-direction: column; justify-content: space-between; }`,
|
|
360
|
+
`[data-ohw-style-distribution="center"] { display: flex !important; flex-direction: column; justify-content: center; }`
|
|
361
|
+
);
|
|
362
|
+
for (const [scale, size] of Object.entries(HEADLINE_SIZES)) {
|
|
363
|
+
rules.push(
|
|
364
|
+
`[data-ohw-style-headline="${scale}"] :is(h1, h2, h3) { font-size: ${size}px !important; line-height: 1.15 !important; }`
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
rules.push(
|
|
368
|
+
`[data-ohw-style-aspect="fill-height"] img { height: 100% !important; aspect-ratio: auto !important; object-fit: cover; }`
|
|
369
|
+
);
|
|
370
|
+
for (const aspect of ["1:1", "4:5", "3:4", "3:2", "16:9", "2:1", "3:1"]) {
|
|
371
|
+
rules.push(
|
|
372
|
+
`[data-ohw-style-aspect="${aspect.replace(":", "-")}"] img { aspect-ratio: ${aspect.replace(":", " / ")} !important; height: auto !important; object-fit: cover; }`
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
const pad = { tight: 40, balanced: 64, airy: 96 };
|
|
376
|
+
for (const [spacing, px] of Object.entries(pad)) {
|
|
377
|
+
rules.push(
|
|
378
|
+
`[data-ohw-style-spacing="${spacing}"] { padding-top: ${px}px !important; padding-bottom: ${px}px !important; }`
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
return rules.join("\n");
|
|
382
|
+
}
|
|
383
|
+
var STYLE_FONT_LINK_ID = "ohw-style-fonts";
|
|
384
|
+
function loadStyleFonts(families) {
|
|
385
|
+
const unique = [...new Set(families.map((f) => f.trim()).filter(Boolean))];
|
|
386
|
+
const existing = document.getElementById(STYLE_FONT_LINK_ID);
|
|
387
|
+
if (unique.length === 0) {
|
|
388
|
+
existing?.remove();
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
const spec = unique.map((f) => `${f.replace(/ /g, "+")}:400,500,600,700`).join("|");
|
|
392
|
+
const href = `https://fonts.googleapis.com/css?family=${spec}&display=swap`;
|
|
393
|
+
let link = existing;
|
|
394
|
+
if (!link) {
|
|
395
|
+
link = document.createElement("link");
|
|
396
|
+
link.id = STYLE_FONT_LINK_ID;
|
|
397
|
+
link.rel = "stylesheet";
|
|
398
|
+
document.head.appendChild(link);
|
|
399
|
+
}
|
|
400
|
+
if (link.href !== href) link.href = href;
|
|
401
|
+
}
|
|
402
|
+
var SECTION_ATTRS = {
|
|
403
|
+
sectionBackground: "data-ohw-style-bg",
|
|
404
|
+
textDistribution: "data-ohw-style-distribution",
|
|
405
|
+
headlineScale: "data-ohw-style-headline",
|
|
406
|
+
imageAspect: "data-ohw-style-aspect",
|
|
407
|
+
spacing: "data-ohw-style-spacing"
|
|
408
|
+
};
|
|
409
|
+
var NODE_WROTE_ATTR = "data-ohw-style-node";
|
|
410
|
+
var NODE_PROPS = ["color", "font-family", "font-size", "background"];
|
|
411
|
+
function saveInline(el, prop) {
|
|
412
|
+
const attr = `data-ohw-style-prev-${prop}`;
|
|
413
|
+
if (!el.hasAttribute(attr)) el.setAttribute(attr, el.style.getPropertyValue(prop));
|
|
414
|
+
}
|
|
415
|
+
function restoreInline(el, prop) {
|
|
416
|
+
const attr = `data-ohw-style-prev-${prop}`;
|
|
417
|
+
if (!el.hasAttribute(attr)) return;
|
|
418
|
+
const prev = el.getAttribute(attr) ?? "";
|
|
419
|
+
if (prev) el.style.setProperty(prop, prev);
|
|
420
|
+
else el.style.removeProperty(prop);
|
|
421
|
+
el.removeAttribute(attr);
|
|
422
|
+
}
|
|
423
|
+
function ensureStyleSheet() {
|
|
424
|
+
let el = document.getElementById(STYLE_SHEET_ID);
|
|
425
|
+
if (!el) {
|
|
426
|
+
el = document.createElement("style");
|
|
427
|
+
el.id = STYLE_SHEET_ID;
|
|
428
|
+
document.head.appendChild(el);
|
|
429
|
+
}
|
|
430
|
+
const css = styleSheetCss();
|
|
431
|
+
if (el.textContent !== css) el.textContent = css;
|
|
432
|
+
}
|
|
433
|
+
function clearSectionAttrs(root) {
|
|
434
|
+
for (const attr of Object.values(SECTION_ATTRS)) {
|
|
435
|
+
for (const el of Array.from(root.querySelectorAll(`[${attr}]`))) el.removeAttribute(attr);
|
|
436
|
+
}
|
|
437
|
+
for (const el of Array.from(root.querySelectorAll("[data-ohw-style-bgcolor]"))) {
|
|
438
|
+
restoreInline(el, "background");
|
|
439
|
+
el.removeAttribute("data-ohw-style-bgcolor");
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
function clearNodeProps(root) {
|
|
443
|
+
for (const el of Array.from(root.querySelectorAll(`[${NODE_WROTE_ATTR}]`))) {
|
|
444
|
+
const h = el;
|
|
445
|
+
for (const prop of NODE_PROPS) restoreInline(h, prop);
|
|
446
|
+
h.removeAttribute(NODE_WROTE_ATTR);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
function buttonSurfaceOf(el) {
|
|
450
|
+
return el.closest("a, button") ?? el;
|
|
451
|
+
}
|
|
452
|
+
function applyStylesToDom(store) {
|
|
453
|
+
ensureStyleSheet();
|
|
454
|
+
clearSectionAttrs(document);
|
|
455
|
+
clearNodeProps(document);
|
|
456
|
+
loadStyleFonts(
|
|
457
|
+
store ? Object.values(store.nodes).flatMap((n) => n.fontFamily ? [n.fontFamily] : []) : []
|
|
458
|
+
);
|
|
459
|
+
if (!store) return;
|
|
460
|
+
for (const [sectionId, override] of Object.entries(store.sections)) {
|
|
461
|
+
const sections = document.querySelectorAll(
|
|
462
|
+
`[data-ohw-section="${CSS.escape(sectionId)}"]`
|
|
463
|
+
);
|
|
464
|
+
for (const section of Array.from(sections)) {
|
|
465
|
+
for (const [prop, attr] of Object.entries(SECTION_ATTRS)) {
|
|
466
|
+
const value = override[prop];
|
|
467
|
+
if (value === void 0) continue;
|
|
468
|
+
if (prop === "sectionBackground" && override.sectionBackgroundColor !== void 0) continue;
|
|
469
|
+
section.setAttribute(attr, String(value).replace(":", "-"));
|
|
470
|
+
}
|
|
471
|
+
if (override.sectionBackgroundColor !== void 0) {
|
|
472
|
+
saveInline(section, "background");
|
|
473
|
+
section.style.setProperty("background", override.sectionBackgroundColor, "important");
|
|
474
|
+
section.setAttribute("data-ohw-style-bgcolor", "");
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
for (const [key, override] of Object.entries(store.nodes)) {
|
|
479
|
+
const nodes = document.querySelectorAll(`[data-ohw-key="${CSS.escape(key)}"]`);
|
|
480
|
+
for (const el of Array.from(nodes)) {
|
|
481
|
+
if (override.color !== void 0) {
|
|
482
|
+
saveInline(el, "color");
|
|
483
|
+
el.style.setProperty("color", override.color, "important");
|
|
484
|
+
el.setAttribute(NODE_WROTE_ATTR, "");
|
|
485
|
+
}
|
|
486
|
+
if (override.fontFamily !== void 0) {
|
|
487
|
+
saveInline(el, "font-family");
|
|
488
|
+
el.style.setProperty("font-family", `'${override.fontFamily}'`, "important");
|
|
489
|
+
el.setAttribute(NODE_WROTE_ATTR, "");
|
|
490
|
+
}
|
|
491
|
+
if (override.fontSize !== void 0) {
|
|
492
|
+
saveInline(el, "font-size");
|
|
493
|
+
el.style.setProperty("font-size", `${override.fontSize}px`, "important");
|
|
494
|
+
el.setAttribute(NODE_WROTE_ATTR, "");
|
|
495
|
+
}
|
|
496
|
+
if (override.buttonBackground !== void 0 || override.buttonText !== void 0) {
|
|
497
|
+
const surface = buttonSurfaceOf(el);
|
|
498
|
+
if (override.buttonBackground !== void 0) {
|
|
499
|
+
saveInline(surface, "background");
|
|
500
|
+
surface.style.setProperty("background", override.buttonBackground, "important");
|
|
501
|
+
}
|
|
502
|
+
if (override.buttonText !== void 0) {
|
|
503
|
+
saveInline(surface, "color");
|
|
504
|
+
surface.style.setProperty("color", override.buttonText, "important");
|
|
505
|
+
}
|
|
506
|
+
surface.setAttribute(NODE_WROTE_ATTR, "");
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
194
512
|
// src/ui/ai-tree/aiSectionsManager.tsx
|
|
195
513
|
var import_react_dom = require("react-dom");
|
|
196
514
|
var import_client = require("react-dom/client");
|
|
@@ -205,7 +523,8 @@ function lucideByName(name) {
|
|
|
205
523
|
}
|
|
206
524
|
var typeStyle = (spec, font) => ({
|
|
207
525
|
fontFamily: font,
|
|
208
|
-
|
|
526
|
+
// Headings shrink with the viewport (reaching full size around ~900px wide); body copy stays put.
|
|
527
|
+
fontSize: spec.size >= 24 ? `clamp(${Math.max(18, Math.round(spec.size * 0.6))}px, ${(spec.size / 9).toFixed(2)}vw, ${spec.size}px)` : spec.size,
|
|
209
528
|
lineHeight: spec.line,
|
|
210
529
|
fontWeight: spec.weight
|
|
211
530
|
});
|
|
@@ -214,12 +533,58 @@ var featureLines = (value) => value.split(/\n|<br\s*\/?>/i).map((line) => line.t
|
|
|
214
533
|
var CHECK_MASK = `data:image/svg+xml,${encodeURIComponent(
|
|
215
534
|
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>'
|
|
216
535
|
)}`;
|
|
536
|
+
var AI_MOBILE_CSS = [
|
|
537
|
+
"@media (max-width: 768px){",
|
|
538
|
+
"[data-ai-section]{overflow-x:hidden}",
|
|
539
|
+
"[data-ai-container]{padding:0 20px !important}",
|
|
540
|
+
"[data-ai-row]{display:flex !important;flex-direction:column !important;align-items:stretch !important}",
|
|
541
|
+
"[data-ai-cell]{width:100%;min-width:0}",
|
|
542
|
+
"[data-ai-grid]{grid-template-columns:1fr !important}",
|
|
543
|
+
// Group containers flatten to a column on phones; span placements come along for free.
|
|
544
|
+
"[data-ai-group]{display:flex !important;flex-direction:column !important}",
|
|
545
|
+
"[data-ai-group] > *{grid-column:auto !important}",
|
|
546
|
+
"[data-ai-section] img{max-width:100%}",
|
|
547
|
+
"}",
|
|
548
|
+
"@media (min-width: 769px) and (max-width: 1024px){",
|
|
549
|
+
"[data-ai-grid]{grid-template-columns:repeat(2, 1fr) !important}",
|
|
550
|
+
"}"
|
|
551
|
+
].join("");
|
|
217
552
|
var FEATURE_LINE_CSS = [
|
|
218
553
|
"[data-ai-features]>div{position:relative;padding-left:40px;min-height:24px}",
|
|
219
554
|
'[data-ai-features]>div::before{content:"";position:absolute;left:0;top:1px;width:24px;height:24px;',
|
|
220
555
|
`background-color:currentColor;-webkit-mask:url("${CHECK_MASK}") no-repeat 0 0/24px 24px;`,
|
|
221
556
|
`mask:url("${CHECK_MASK}") no-repeat 0 0/24px 24px}`
|
|
222
557
|
].join("");
|
|
558
|
+
function hexLuminance(color) {
|
|
559
|
+
const m = /^#([0-9a-f]{6})$/i.exec(color.trim());
|
|
560
|
+
if (!m) return null;
|
|
561
|
+
const [r2, g, b] = [0, 2, 4].map((i) => {
|
|
562
|
+
const c = parseInt(m[1].slice(i, i + 2), 16) / 255;
|
|
563
|
+
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
|
564
|
+
});
|
|
565
|
+
return 0.2126 * r2 + 0.7152 * g + 0.0722 * b;
|
|
566
|
+
}
|
|
567
|
+
function hexContrast(a, b) {
|
|
568
|
+
const la = hexLuminance(a);
|
|
569
|
+
const lb = hexLuminance(b);
|
|
570
|
+
if (la === null || lb === null) return null;
|
|
571
|
+
const [hi, lo] = la > lb ? [la, lb] : [lb, la];
|
|
572
|
+
return (hi + 0.05) / (lo + 0.05);
|
|
573
|
+
}
|
|
574
|
+
function accentBandContext(brand) {
|
|
575
|
+
const p = brand.palette;
|
|
576
|
+
const lightWins = (hexContrast(p.light, p.primary) ?? 99) >= (hexContrast(p.dark, p.primary) ?? 0);
|
|
577
|
+
if (lightWins) {
|
|
578
|
+
return {
|
|
579
|
+
brand: { ...brand, palette: { dark: p.light, primary: p.light, accent: p.light, light: p.primary } },
|
|
580
|
+
buttonLabel: p.primary
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
return {
|
|
584
|
+
brand: { ...brand, palette: { dark: p.dark, primary: p.dark, accent: p.dark, light: p.light } },
|
|
585
|
+
buttonLabel: p.light
|
|
586
|
+
};
|
|
587
|
+
}
|
|
223
588
|
function textAttrs(ctx, path) {
|
|
224
589
|
return ctx.keyFor ? { "data-ohw-key": ctx.keyFor(path), "data-ohw-editable": "text" } : {};
|
|
225
590
|
}
|
|
@@ -231,7 +596,12 @@ var AI_RESPONSIVE_CSS = [
|
|
|
231
596
|
"@media (max-width: 640px) {",
|
|
232
597
|
" [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
|
|
233
598
|
" [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
|
|
599
|
+
// Group containers flatten to a column on phones; span placements come along for free.
|
|
600
|
+
" [data-ai-responsive] [data-ai-group] { display: flex !important; flex-direction: column !important; }",
|
|
601
|
+
" [data-ai-responsive] [data-ai-group] > * { grid-column: auto !important; }",
|
|
234
602
|
" [data-ai-responsive] [data-ai-section-inner] { padding-left: 20px !important; padding-right: 20px !important; }",
|
|
603
|
+
" [data-ai-responsive] { overflow-x: hidden; }",
|
|
604
|
+
" [data-ai-responsive] img { max-width: 100%; }",
|
|
235
605
|
"}"
|
|
236
606
|
].join("\n");
|
|
237
607
|
var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
|
|
@@ -309,7 +679,7 @@ function ButtonEl({
|
|
|
309
679
|
}) {
|
|
310
680
|
const secondary = slots.variant === "secondary";
|
|
311
681
|
const href = str(slots.href);
|
|
312
|
-
const linkAttrs = ctx.keyFor && path ? { "data-ohw-href-key": ctx.keyFor(`${path}.href`) } : {};
|
|
682
|
+
const linkAttrs = ctx.keyFor && path ? { "data-ohw-href-key": ctx.keyFor(`${path}.href`), "data-ohw-role": "button" } : {};
|
|
313
683
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
314
684
|
"a",
|
|
315
685
|
{
|
|
@@ -325,7 +695,7 @@ function ButtonEl({
|
|
|
325
695
|
textDecoration: "none",
|
|
326
696
|
cursor: "pointer",
|
|
327
697
|
...typeStyle(AI_TREE_TOKENS.type.button, ctx.brand.fonts.body),
|
|
328
|
-
...secondary ? { border: `1px solid ${ctx.brand.palette.dark}`, color: ctx.brand.palette.dark } : { background: ctx.brand.palette.primary, color: AI_TREE_TOKENS.textPrimaryForeground }
|
|
698
|
+
...secondary ? { border: `1px solid ${ctx.brand.palette.dark}`, color: ctx.brand.palette.dark } : { background: ctx.brand.palette.primary, color: ctx.buttonLabel ?? AI_TREE_TOKENS.textPrimaryForeground }
|
|
329
699
|
},
|
|
330
700
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ...path ? textAttrs(ctx, `${path}.label`) : {}, children: str(slots.label) })
|
|
331
701
|
}
|
|
@@ -831,7 +1201,24 @@ function CardBlock({ node, ctx, path }) {
|
|
|
831
1201
|
minWidth: 0
|
|
832
1202
|
},
|
|
833
1203
|
children: [
|
|
834
|
-
media && (horizontal ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1204
|
+
media && (horizontal ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1205
|
+
"div",
|
|
1206
|
+
{
|
|
1207
|
+
style: (
|
|
1208
|
+
// An icon hugs its glyph — flex:1 gave a 48px icon half the card and pushed the
|
|
1209
|
+
// text to the far side. Photos keep the half-and-half split. The inset has no
|
|
1210
|
+
// inner padding (the photo split absorbed that), so the icon carries its own gap.
|
|
1211
|
+
/^(lucide|simple):/.test(mediaRef) ? {
|
|
1212
|
+
flexShrink: 0,
|
|
1213
|
+
display: "flex",
|
|
1214
|
+
alignItems: "center",
|
|
1215
|
+
padding: mediaInset,
|
|
1216
|
+
[mediaPosition === "right" ? "marginLeft" : "marginRight"]: 20
|
|
1217
|
+
} : { flex: 1, minWidth: 0, display: "flex", alignItems: "center", padding: mediaInset }
|
|
1218
|
+
),
|
|
1219
|
+
children: media
|
|
1220
|
+
}
|
|
1221
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
835
1222
|
"div",
|
|
836
1223
|
{
|
|
837
1224
|
style: /^(lucide|simple):/.test(mediaRef) ? { padding: `${AI_TREE_TOKENS.paddingBlock}px ${AI_TREE_TOKENS.paddingBlock}px 0` } : hasBg ? void 0 : { borderRadius: AI_TREE_TOKENS.radiusCard, overflow: "hidden" },
|
|
@@ -922,13 +1309,44 @@ function AccordionBlock({ node, ctx, path }) {
|
|
|
922
1309
|
) })
|
|
923
1310
|
] }, i)) });
|
|
924
1311
|
}
|
|
1312
|
+
function useIsMobile() {
|
|
1313
|
+
const [mobile, setMobile] = import_react.default.useState(
|
|
1314
|
+
() => typeof window !== "undefined" && window.matchMedia("(max-width: 768px)").matches
|
|
1315
|
+
);
|
|
1316
|
+
import_react.default.useEffect(() => {
|
|
1317
|
+
const mq = window.matchMedia("(max-width: 768px)");
|
|
1318
|
+
const update = () => setMobile(mq.matches);
|
|
1319
|
+
update();
|
|
1320
|
+
mq.addEventListener("change", update);
|
|
1321
|
+
return () => mq.removeEventListener("change", update);
|
|
1322
|
+
}, []);
|
|
1323
|
+
return mobile;
|
|
1324
|
+
}
|
|
925
1325
|
function Carousel({ items, itemsPerRow, ctx }) {
|
|
1326
|
+
const isMobile = useIsMobile();
|
|
1327
|
+
const perPage = isMobile ? 1 : itemsPerRow;
|
|
1328
|
+
const pages = Math.max(1, Math.ceil(items.length / perPage));
|
|
926
1329
|
const [page, setPage] = import_react.default.useState(0);
|
|
927
|
-
const pages = Math.max(1, Math.ceil(items.length / itemsPerRow));
|
|
928
1330
|
const current = Math.min(page, pages - 1);
|
|
1331
|
+
if (pages <= 1) {
|
|
1332
|
+
const cols = Math.max(1, Math.min(items.length, itemsPerRow));
|
|
1333
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1334
|
+
"div",
|
|
1335
|
+
{
|
|
1336
|
+
"data-ai-grid": String(cols),
|
|
1337
|
+
style: {
|
|
1338
|
+
display: "grid",
|
|
1339
|
+
gridTemplateColumns: `repeat(${cols}, 1fr)`,
|
|
1340
|
+
gap: AI_TREE_TOKENS.spacing8,
|
|
1341
|
+
alignItems: "start"
|
|
1342
|
+
},
|
|
1343
|
+
children: items
|
|
1344
|
+
}
|
|
1345
|
+
);
|
|
1346
|
+
}
|
|
929
1347
|
const pageGroups = Array.from(
|
|
930
1348
|
{ length: pages },
|
|
931
|
-
(_, p) => items.slice(p *
|
|
1349
|
+
(_, p) => items.slice(p * perPage, (p + 1) * perPage)
|
|
932
1350
|
);
|
|
933
1351
|
const chrome = (enabled) => ({
|
|
934
1352
|
border: `1px solid ${ctx.brand.palette.dark}`,
|
|
@@ -953,55 +1371,69 @@ function Carousel({ items, itemsPerRow, ctx }) {
|
|
|
953
1371
|
cursor: "pointer",
|
|
954
1372
|
padding: 0
|
|
955
1373
|
});
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ArrowLeft, { size: 24, strokeWidth: 1.5, "aria-hidden": true })
|
|
966
|
-
}
|
|
967
|
-
),
|
|
968
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { flex: 1, minWidth: 0, overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1374
|
+
const viewport = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { flex: isMobile ? "0 0 auto" : 1, minWidth: 0, width: "100%", overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1375
|
+
"div",
|
|
1376
|
+
{
|
|
1377
|
+
style: {
|
|
1378
|
+
display: "flex",
|
|
1379
|
+
transform: `translateX(-${current * 100}%)`,
|
|
1380
|
+
transition: "transform 0.4s ease"
|
|
1381
|
+
},
|
|
1382
|
+
children: pageGroups.map((group, p) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
969
1383
|
"div",
|
|
970
1384
|
{
|
|
1385
|
+
"data-ai-grid": String(perPage),
|
|
971
1386
|
style: {
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
1387
|
+
flex: "0 0 100%",
|
|
1388
|
+
display: "grid",
|
|
1389
|
+
gridTemplateColumns: `repeat(${perPage}, 1fr)`,
|
|
1390
|
+
gap: AI_TREE_TOKENS.spacing8,
|
|
1391
|
+
alignItems: "start"
|
|
975
1392
|
},
|
|
976
|
-
children:
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1393
|
+
children: group
|
|
1394
|
+
},
|
|
1395
|
+
p
|
|
1396
|
+
))
|
|
1397
|
+
}
|
|
1398
|
+
) });
|
|
1399
|
+
const prevBtn = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1400
|
+
"button",
|
|
1401
|
+
{
|
|
1402
|
+
type: "button",
|
|
1403
|
+
"aria-label": "Previous",
|
|
1404
|
+
onClick: () => setPage((p) => Math.max(0, p - 1)),
|
|
1405
|
+
style: chrome(current > 0),
|
|
1406
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ArrowLeft, { size: 24, strokeWidth: 1.5, "aria-hidden": true })
|
|
1407
|
+
}
|
|
1408
|
+
);
|
|
1409
|
+
const nextBtn = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1410
|
+
"button",
|
|
1411
|
+
{
|
|
1412
|
+
type: "button",
|
|
1413
|
+
"aria-label": "Next",
|
|
1414
|
+
onClick: () => setPage((p) => Math.min(pages - 1, p + 1)),
|
|
1415
|
+
style: chrome(current < pages - 1),
|
|
1416
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ArrowRight, { size: 24, strokeWidth: 1.5, "aria-hidden": true })
|
|
1417
|
+
}
|
|
1418
|
+
);
|
|
1419
|
+
const dots = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { display: "flex", gap: 9, justifyContent: "center" }, children: pageGroups.map((_, p) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", "aria-label": `Page ${p + 1}`, onClick: () => setPage(p), style: dot(p === current) }, p)) });
|
|
1420
|
+
if (isMobile) {
|
|
1421
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { "data-ai-carousel": "", style: { display: "flex", flexDirection: "column", gap: AI_TREE_TOKENS.spacing6, alignItems: "center" }, children: [
|
|
1422
|
+
viewport,
|
|
1423
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", gap: AI_TREE_TOKENS.spacing6, justifyContent: "center" }, children: [
|
|
1424
|
+
prevBtn,
|
|
1425
|
+
nextBtn
|
|
1426
|
+
] }),
|
|
1427
|
+
dots
|
|
1428
|
+
] });
|
|
1429
|
+
}
|
|
1430
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { "data-ai-carousel": "", style: { display: "flex", flexDirection: "column", gap: AI_TREE_TOKENS.spacing8, alignItems: "center" }, children: [
|
|
1431
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: AI_TREE_TOKENS.spacing6, width: "100%" }, children: [
|
|
1432
|
+
prevBtn,
|
|
1433
|
+
viewport,
|
|
1434
|
+
nextBtn
|
|
1003
1435
|
] }),
|
|
1004
|
-
|
|
1436
|
+
dots
|
|
1005
1437
|
] });
|
|
1006
1438
|
}
|
|
1007
1439
|
function CollectionBlock({ node, ctx, path }) {
|
|
@@ -1095,6 +1527,49 @@ function renderNode(node, ctx, path) {
|
|
|
1095
1527
|
switch (node.type) {
|
|
1096
1528
|
case "text":
|
|
1097
1529
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TextBlock, { slots, ctx, path });
|
|
1530
|
+
// Layout container: arranges child blocks, contributes no content of its own. `grid` is a
|
|
1531
|
+
// nested 12-column grid the children span (a collage is 3–5 media on it, bottom-aligned so
|
|
1532
|
+
// mixed aspects read as a composition); `split` is exactly two children at a ratio; `stack`
|
|
1533
|
+
// is a column. Children render through this same dispatcher, so edit markers, media
|
|
1534
|
+
// resolution, and copy paths all work unchanged inside a group.
|
|
1535
|
+
case "group": {
|
|
1536
|
+
const layout = str(slots.layout);
|
|
1537
|
+
const gap = slots.spacing === "tight" ? AI_TREE_TOKENS.spacing3 : slots.spacing === "airy" ? AI_TREE_TOKENS.spacing8 : AI_TREE_TOKENS.spacing6;
|
|
1538
|
+
const kids = (node.children ?? []).map((child, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1539
|
+
"div",
|
|
1540
|
+
{
|
|
1541
|
+
style: layout === "grid" ? {
|
|
1542
|
+
gridColumn: `span ${typeof child.span === "number" ? Math.min(12, Math.max(1, child.span)) : 12}`,
|
|
1543
|
+
minWidth: 0
|
|
1544
|
+
} : { minWidth: 0 },
|
|
1545
|
+
children: renderNode(child, ctx, `${path}.c${i}`)
|
|
1546
|
+
},
|
|
1547
|
+
i
|
|
1548
|
+
));
|
|
1549
|
+
if (layout === "grid") {
|
|
1550
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1551
|
+
"div",
|
|
1552
|
+
{
|
|
1553
|
+
"data-ai-group": "grid",
|
|
1554
|
+
style: { display: "grid", gridTemplateColumns: "repeat(12, 1fr)", gap, alignItems: "end" },
|
|
1555
|
+
children: kids
|
|
1556
|
+
}
|
|
1557
|
+
);
|
|
1558
|
+
}
|
|
1559
|
+
if (layout === "split") {
|
|
1560
|
+
const ratio = str(slots.ratio);
|
|
1561
|
+
const cols = ratio === "3:5" ? "3fr 5fr" : ratio === "5:3" ? "5fr 3fr" : "1fr 1fr";
|
|
1562
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1563
|
+
"div",
|
|
1564
|
+
{
|
|
1565
|
+
"data-ai-group": "split",
|
|
1566
|
+
style: { display: "grid", gridTemplateColumns: cols, gap, alignItems: "center" },
|
|
1567
|
+
children: kids
|
|
1568
|
+
}
|
|
1569
|
+
);
|
|
1570
|
+
}
|
|
1571
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { "data-ai-group": "stack", style: { display: "flex", flexDirection: "column", gap }, children: kids });
|
|
1572
|
+
}
|
|
1098
1573
|
case "button":
|
|
1099
1574
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ButtonEl, { slots, ctx, path });
|
|
1100
1575
|
case "button-row":
|
|
@@ -1175,33 +1650,111 @@ function renderNode(node, ctx, path) {
|
|
|
1175
1650
|
}
|
|
1176
1651
|
);
|
|
1177
1652
|
}
|
|
1178
|
-
case "form":
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1653
|
+
case "form": {
|
|
1654
|
+
const formAttrs = ctx.keyFor ? {
|
|
1655
|
+
"data-ohw-editable": "form",
|
|
1656
|
+
"data-ohw-key": ctx.keyFor(`${path}.form`),
|
|
1657
|
+
"data-ohw-success-text": "Thanks \u2014 we'll be in touch shortly."
|
|
1658
|
+
} : {};
|
|
1659
|
+
const fieldStyle = {
|
|
1660
|
+
width: "100%",
|
|
1661
|
+
boxSizing: "border-box",
|
|
1662
|
+
border: `1px solid color-mix(in srgb, ${ctx.brand.palette.dark} 45%, #ffffff)`,
|
|
1663
|
+
borderRadius: 0,
|
|
1664
|
+
padding: 12,
|
|
1665
|
+
background: "#fff",
|
|
1666
|
+
color: ctx.brand.palette.dark,
|
|
1667
|
+
outline: "none",
|
|
1668
|
+
...typeStyle(AI_TREE_TOKENS.type.bodyM, ctx.brand.fonts.body)
|
|
1669
|
+
};
|
|
1670
|
+
const labelStyle = {
|
|
1671
|
+
...typeStyle(AI_TREE_TOKENS.type.bodyMBold, ctx.brand.fonts.body),
|
|
1672
|
+
color: ctx.brand.palette.dark,
|
|
1673
|
+
textAlign: "left",
|
|
1674
|
+
width: "100%"
|
|
1675
|
+
};
|
|
1676
|
+
const centered = ctx.sectionAlignment === "center";
|
|
1677
|
+
const submitAlign = centered ? "center" : "flex-start";
|
|
1678
|
+
const children = node.children ?? [];
|
|
1679
|
+
return (
|
|
1680
|
+
// 32px between the field group and the submit. In a stacked (centered) section the form is
|
|
1681
|
+
// capped at 780px and centered — the section's 12-col grid would otherwise leave it hugging
|
|
1682
|
+
// the left edge; a split section lets it fill its own column.
|
|
1683
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1684
|
+
"form",
|
|
1685
|
+
{
|
|
1686
|
+
...formAttrs,
|
|
1687
|
+
"data-ai-form": "",
|
|
1688
|
+
style: {
|
|
1689
|
+
display: "flex",
|
|
1690
|
+
flexDirection: "column",
|
|
1691
|
+
gap: 32,
|
|
1692
|
+
width: "100%",
|
|
1693
|
+
...centered ? { maxWidth: 780, marginLeft: "auto", marginRight: "auto" } : {}
|
|
1694
|
+
},
|
|
1695
|
+
children: [
|
|
1696
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: 24, width: "100%", alignItems: "flex-start" }, children: children.map((child, i) => {
|
|
1697
|
+
if (child.type !== "input") return null;
|
|
1698
|
+
const cs = child.slots ?? {};
|
|
1699
|
+
const kind = str(cs.kind);
|
|
1700
|
+
const label = str(cs.label);
|
|
1701
|
+
const placeholder = str(cs.placeholder);
|
|
1702
|
+
const required = cs.required === true;
|
|
1703
|
+
const name = label.toLowerCase().replace(/[^a-z0-9]+/gu, "-").replace(/^-+|-+$/gu, "") || `field-${i}`;
|
|
1704
|
+
const isTextarea = kind === "textarea";
|
|
1705
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 8, width: "100%" }, children: [
|
|
1706
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { ...textAttrs(ctx, `${path}.c${i}.label`), style: labelStyle, children: label }),
|
|
1707
|
+
isTextarea ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1708
|
+
"textarea",
|
|
1709
|
+
{
|
|
1710
|
+
name,
|
|
1711
|
+
placeholder,
|
|
1712
|
+
required,
|
|
1713
|
+
style: { ...fieldStyle, height: 180, resize: "vertical" }
|
|
1714
|
+
}
|
|
1715
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1716
|
+
"input",
|
|
1717
|
+
{
|
|
1718
|
+
name,
|
|
1719
|
+
type: kind === "email" ? "email" : "text",
|
|
1720
|
+
placeholder,
|
|
1721
|
+
required,
|
|
1722
|
+
style: { ...fieldStyle, height: 48 }
|
|
1723
|
+
}
|
|
1724
|
+
)
|
|
1725
|
+
] }, i);
|
|
1726
|
+
}) }),
|
|
1727
|
+
children.map((child, i) => {
|
|
1728
|
+
if (child.type === "input") return null;
|
|
1729
|
+
const cs = child.slots ?? {};
|
|
1730
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1731
|
+
"button",
|
|
1732
|
+
{
|
|
1733
|
+
type: "submit",
|
|
1734
|
+
style: {
|
|
1735
|
+
alignSelf: submitAlign,
|
|
1736
|
+
border: "none",
|
|
1737
|
+
cursor: "pointer",
|
|
1738
|
+
padding: "12px 24px",
|
|
1739
|
+
// Corner radius follows the host template's own buttons (measured from a template
|
|
1740
|
+
// CTA); 8px only when the page has no template button to match.
|
|
1741
|
+
borderRadius: ctx.buttonRadius ?? 8,
|
|
1742
|
+
// Brand-styled: primary fill, brand-derived label colour (not a fixed token) so it
|
|
1743
|
+
// reads correctly on custom palettes.
|
|
1744
|
+
background: ctx.brand.palette.primary,
|
|
1745
|
+
color: ctx.buttonLabel ?? ctx.brand.palette.light,
|
|
1746
|
+
...typeStyle(AI_TREE_TOKENS.type.button, ctx.brand.fonts.body)
|
|
1747
|
+
},
|
|
1748
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ...textAttrs(ctx, `${path}.c${i}.label`), children: str(cs.label) })
|
|
1749
|
+
},
|
|
1750
|
+
i
|
|
1751
|
+
);
|
|
1752
|
+
})
|
|
1753
|
+
]
|
|
1754
|
+
}
|
|
1755
|
+
)
|
|
1756
|
+
);
|
|
1757
|
+
}
|
|
1205
1758
|
case "schedule-widget":
|
|
1206
1759
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1207
1760
|
"div",
|
|
@@ -1222,16 +1775,27 @@ function renderNode(node, ctx, path) {
|
|
|
1222
1775
|
return null;
|
|
1223
1776
|
}
|
|
1224
1777
|
}
|
|
1225
|
-
function AiTreeRenderer({
|
|
1778
|
+
function AiTreeRenderer({
|
|
1779
|
+
tree,
|
|
1780
|
+
brand,
|
|
1781
|
+
buttonRadius,
|
|
1782
|
+
resolveMedia,
|
|
1783
|
+
editKeyPrefix
|
|
1784
|
+
}) {
|
|
1226
1785
|
if (!isRenderableTree(tree)) {
|
|
1227
1786
|
return null;
|
|
1228
1787
|
}
|
|
1229
1788
|
const resolvedBrand = brand ?? AI_DEFAULT_BRAND;
|
|
1789
|
+
const band = (tree.settings ?? {}).sectionBackground === "accent" ? accentBandContext(resolvedBrand) : null;
|
|
1790
|
+
const blockBrand = band?.brand ?? resolvedBrand;
|
|
1230
1791
|
const ctx = {
|
|
1231
|
-
brand:
|
|
1792
|
+
brand: blockBrand,
|
|
1232
1793
|
resolveMedia: resolveMedia ?? (() => null),
|
|
1233
|
-
cardSurface:
|
|
1234
|
-
keyFor: editKeyPrefix ? (path) => `${editKeyPrefix}.${path}` : null
|
|
1794
|
+
cardSurface: blockBrand.palette.light.toUpperCase() === AI_DEFAULT_BRAND.palette.light.toUpperCase() ? "#ECECEB" : `color-mix(in srgb, ${blockBrand.palette.light} 90%, ${blockBrand.palette.dark})`,
|
|
1795
|
+
keyFor: editKeyPrefix ? (path) => `${editKeyPrefix}.${path}` : null,
|
|
1796
|
+
sectionAlignment: (tree.settings ?? {}).alignment === "center" ? "center" : "left",
|
|
1797
|
+
buttonRadius,
|
|
1798
|
+
...band ? { buttonLabel: band.buttonLabel } : {}
|
|
1235
1799
|
};
|
|
1236
1800
|
const settings = tree.settings ?? {};
|
|
1237
1801
|
const pad = AI_TREE_TOKENS.sectionPadding[settings.spacing ?? "balanced"];
|
|
@@ -1239,6 +1803,20 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
|
|
|
1239
1803
|
const overlayAlpha = { low: 0.25, medium: 0.45, high: 0.65 }[settings.overlayIntensity ?? "medium"];
|
|
1240
1804
|
const backgroundUrl = isOverlay && settings.backgroundMedia ? ctx.resolveMedia(settings.backgroundMedia) : null;
|
|
1241
1805
|
const bgAttrs = ctx.keyFor && isOverlay && settings.backgroundMedia ? { "data-ohw-key": ctx.keyFor("settings.backgroundMedia"), "data-ohw-editable": "bg-image" } : {};
|
|
1806
|
+
const toneBackground = (() => {
|
|
1807
|
+
const { dark, primary, light } = resolvedBrand.palette;
|
|
1808
|
+
switch (settings.sectionBackground) {
|
|
1809
|
+
case "surface":
|
|
1810
|
+
return `color-mix(in srgb, ${light} 94%, ${dark})`;
|
|
1811
|
+
case "accent":
|
|
1812
|
+
return primary;
|
|
1813
|
+
case "accent-soft":
|
|
1814
|
+
return `color-mix(in srgb, ${primary} 12%, ${light})`;
|
|
1815
|
+
default:
|
|
1816
|
+
return void 0;
|
|
1817
|
+
}
|
|
1818
|
+
})();
|
|
1819
|
+
const distributed = !isOverlay && settings.textDistribution;
|
|
1242
1820
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1243
1821
|
"section",
|
|
1244
1822
|
{
|
|
@@ -1248,13 +1826,15 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
|
|
|
1248
1826
|
style: {
|
|
1249
1827
|
position: "relative",
|
|
1250
1828
|
padding: `${pad}px 0`,
|
|
1251
|
-
background: isOverlay && !backgroundUrl ? "#DBEAFE" : void 0,
|
|
1829
|
+
background: toneBackground ?? (isOverlay && !backgroundUrl ? "#DBEAFE" : void 0),
|
|
1252
1830
|
backgroundImage: backgroundUrl ? `url(${backgroundUrl})` : void 0,
|
|
1253
1831
|
backgroundSize: "cover",
|
|
1254
|
-
backgroundPosition: "center"
|
|
1832
|
+
backgroundPosition: "center",
|
|
1833
|
+
color: settings.sectionBackground === "accent" ? blockBrand.palette.dark : void 0
|
|
1255
1834
|
},
|
|
1256
1835
|
children: [
|
|
1257
1836
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_RESPONSIVE_CSS }),
|
|
1837
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_MOBILE_CSS }),
|
|
1258
1838
|
isOverlay && backgroundUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
|
|
1259
1839
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1260
1840
|
"div",
|
|
@@ -1275,10 +1855,24 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
|
|
|
1275
1855
|
display: "grid",
|
|
1276
1856
|
gridTemplateColumns: "repeat(12, 1fr)",
|
|
1277
1857
|
gap: AI_TREE_TOKENS.spacing6,
|
|
1278
|
-
alignItems: settings.verticalPosition === "top" ? "start" : "center",
|
|
1858
|
+
alignItems: distributed === "space-between" ? "stretch" : (distributed ?? settings.verticalPosition) === "top" ? "start" : "center",
|
|
1279
1859
|
marginTop: r2 > 0 ? AI_TREE_TOKENS.spacing8 : 0
|
|
1280
1860
|
},
|
|
1281
|
-
children: (row.blocks ?? []).map((block, b) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1861
|
+
children: (row.blocks ?? []).map((block, b) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1862
|
+
"div",
|
|
1863
|
+
{
|
|
1864
|
+
"data-ai-cell": "",
|
|
1865
|
+
style: {
|
|
1866
|
+
gridColumn: `span ${Math.min(12, Math.max(1, block.span))}`,
|
|
1867
|
+
minWidth: 0,
|
|
1868
|
+
// space-between: each column becomes a flex column whose content spreads over
|
|
1869
|
+
// the full row height instead of clumping at the top.
|
|
1870
|
+
...distributed === "space-between" ? { display: "flex", flexDirection: "column", justifyContent: "space-between" } : {}
|
|
1871
|
+
},
|
|
1872
|
+
children: renderNode(block, ctx, `r${r2}.b${b}`)
|
|
1873
|
+
},
|
|
1874
|
+
b
|
|
1875
|
+
))
|
|
1282
1876
|
},
|
|
1283
1877
|
r2
|
|
1284
1878
|
))
|
|
@@ -1294,17 +1888,36 @@ var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
|
1294
1888
|
var CONTAINER_ATTR = "data-ohw-ai-generated";
|
|
1295
1889
|
var REPLACED_ATTR = "data-ohw-ai-replaced-by";
|
|
1296
1890
|
var REMOVED_ATTR = "data-ohw-ai-removed";
|
|
1891
|
+
var TEMPLATE_HIDDEN_ATTR = "data-ohw-ai-template-hidden";
|
|
1892
|
+
var CHROME_SECTION_IDS = /* @__PURE__ */ new Set(["navbar", "footer"]);
|
|
1893
|
+
function readRootVar(name) {
|
|
1894
|
+
if (typeof document === "undefined") return "";
|
|
1895
|
+
return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
|
|
1896
|
+
}
|
|
1897
|
+
function deriveBrandOverride() {
|
|
1898
|
+
const dark = readRootVar("--ohw-brand-dark");
|
|
1899
|
+
const primary = readRootVar("--ohw-brand-primary");
|
|
1900
|
+
const light = readRootVar("--ohw-brand-light");
|
|
1901
|
+
if (!dark || !primary || !light) return null;
|
|
1902
|
+
const accent = readRootVar("--ohw-brand-accent");
|
|
1903
|
+
const heading = readRootVar("--font-heading") || readRootVar("--font-display");
|
|
1904
|
+
const body = readRootVar("--font-body");
|
|
1905
|
+
return {
|
|
1906
|
+
palette: { dark, primary, accent: accent || dark, light },
|
|
1907
|
+
fonts: {
|
|
1908
|
+
heading: heading || AI_DEFAULT_BRAND.fonts.heading,
|
|
1909
|
+
body: body || AI_DEFAULT_BRAND.fonts.body
|
|
1910
|
+
}
|
|
1911
|
+
};
|
|
1912
|
+
}
|
|
1297
1913
|
function deriveTemplateBrand() {
|
|
1298
|
-
|
|
1299
|
-
const
|
|
1300
|
-
const
|
|
1301
|
-
const dark = read("--color-dark");
|
|
1302
|
-
const primary = read("--color-primary");
|
|
1303
|
-
const light = read("--color-light");
|
|
1914
|
+
const dark = readRootVar("--color-dark");
|
|
1915
|
+
const primary = readRootVar("--color-primary");
|
|
1916
|
+
const light = readRootVar("--color-light");
|
|
1304
1917
|
if (!dark || !primary || !light) return null;
|
|
1305
|
-
const accent =
|
|
1306
|
-
const heading =
|
|
1307
|
-
const body =
|
|
1918
|
+
const accent = readRootVar("--color-accent");
|
|
1919
|
+
const heading = readRootVar("--font-heading") || readRootVar("--font-display");
|
|
1920
|
+
const body = readRootVar("--font-body");
|
|
1308
1921
|
return {
|
|
1309
1922
|
palette: { dark, primary, accent: accent || dark, light },
|
|
1310
1923
|
fonts: {
|
|
@@ -1313,6 +1926,13 @@ function deriveTemplateBrand() {
|
|
|
1313
1926
|
}
|
|
1314
1927
|
};
|
|
1315
1928
|
}
|
|
1929
|
+
function deriveTemplateButtonRadius() {
|
|
1930
|
+
if (typeof document === "undefined") return null;
|
|
1931
|
+
const btn = document.querySelector('[data-ohw-role="button"]');
|
|
1932
|
+
if (!btn) return null;
|
|
1933
|
+
const radius = getComputedStyle(btn).borderTopLeftRadius;
|
|
1934
|
+
return radius || null;
|
|
1935
|
+
}
|
|
1316
1936
|
var mounted = /* @__PURE__ */ new Map();
|
|
1317
1937
|
function findTemplateSection(id) {
|
|
1318
1938
|
for (const el of document.querySelectorAll(`[data-ohw-section="${CSS.escape(id)}"]`)) {
|
|
@@ -1376,6 +1996,24 @@ function syncRemovedSections(state) {
|
|
|
1376
1996
|
}
|
|
1377
1997
|
}
|
|
1378
1998
|
}
|
|
1999
|
+
function syncTemplateHidden(state, pageHasSections) {
|
|
2000
|
+
const hide = state.hideTemplate === true && pageHasSections;
|
|
2001
|
+
for (const el of Array.from(document.querySelectorAll(`[${TEMPLATE_HIDDEN_ATTR}]`))) {
|
|
2002
|
+
if (!hide) {
|
|
2003
|
+
el.style.removeProperty("display");
|
|
2004
|
+
el.removeAttribute(TEMPLATE_HIDDEN_ATTR);
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
if (!hide) return;
|
|
2008
|
+
for (const el of Array.from(document.querySelectorAll("[data-ohw-section]"))) {
|
|
2009
|
+
if (el.hasAttribute(CONTAINER_ATTR)) continue;
|
|
2010
|
+
if (CHROME_SECTION_IDS.has(el.getAttribute("data-ohw-section") ?? "")) continue;
|
|
2011
|
+
if (el.parentElement?.closest("[data-ohw-section]")) continue;
|
|
2012
|
+
if (el.hasAttribute(REPLACED_ATTR) || el.hasAttribute(REMOVED_ATTR)) continue;
|
|
2013
|
+
el.style.display = "none";
|
|
2014
|
+
el.setAttribute(TEMPLATE_HIDDEN_ATTR, "");
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
1379
2017
|
function syncReplacedOriginals(state) {
|
|
1380
2018
|
for (const el of document.querySelectorAll(`[${REPLACED_ATTR}]`)) {
|
|
1381
2019
|
const byId = el.getAttribute(REPLACED_ATTR) ?? "";
|
|
@@ -1394,10 +2032,64 @@ function syncReplacedOriginals(state) {
|
|
|
1394
2032
|
}
|
|
1395
2033
|
}
|
|
1396
2034
|
}
|
|
2035
|
+
var sectionOrderIndex = /* @__PURE__ */ new Map();
|
|
2036
|
+
function setAiSectionOrder(raw, currentPath) {
|
|
2037
|
+
const next = /* @__PURE__ */ new Map();
|
|
2038
|
+
if (raw) {
|
|
2039
|
+
try {
|
|
2040
|
+
const entries = JSON.parse(raw);
|
|
2041
|
+
for (const entry of entries) {
|
|
2042
|
+
if (!entry.pagePath || entry.pagePath === currentPath) next.set(entry.instanceId, entry.order);
|
|
2043
|
+
}
|
|
2044
|
+
} catch {
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
sectionOrderIndex = next;
|
|
2048
|
+
}
|
|
2049
|
+
function applyExplicitOrder(entries) {
|
|
2050
|
+
if (sectionOrderIndex.size === 0) return entries;
|
|
2051
|
+
return entries.map((entry, index) => ({ entry, index, order: sectionOrderIndex.get(entry.id) })).sort((a, b) => {
|
|
2052
|
+
if (a.order === void 0 && b.order === void 0) return a.index - b.index;
|
|
2053
|
+
if (a.order === void 0) return 1;
|
|
2054
|
+
if (b.order === void 0) return -1;
|
|
2055
|
+
return a.order - b.order;
|
|
2056
|
+
}).map((item) => item.entry);
|
|
2057
|
+
}
|
|
2058
|
+
function orderByChain(sections) {
|
|
2059
|
+
const ids = new Set(sections.map((entry) => entry.id));
|
|
2060
|
+
const after = /* @__PURE__ */ new Map();
|
|
2061
|
+
const roots = [];
|
|
2062
|
+
for (const entry of sections) {
|
|
2063
|
+
const anchor = entry.replaces ?? entry.beforeSection ?? entry.afterSection ?? null;
|
|
2064
|
+
if (anchor && ids.has(anchor)) {
|
|
2065
|
+
const bucket = after.get(anchor);
|
|
2066
|
+
if (bucket) bucket.push(entry);
|
|
2067
|
+
else after.set(anchor, [entry]);
|
|
2068
|
+
} else {
|
|
2069
|
+
roots.push(entry);
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
const out = [];
|
|
2073
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2074
|
+
const visit = (entry) => {
|
|
2075
|
+
if (seen.has(entry.id)) return;
|
|
2076
|
+
seen.add(entry.id);
|
|
2077
|
+
out.push(entry);
|
|
2078
|
+
for (const child of after.get(entry.id) ?? []) visit(child);
|
|
2079
|
+
};
|
|
2080
|
+
for (const root of roots) visit(root);
|
|
2081
|
+
return out.length === sections.length ? out : sections;
|
|
2082
|
+
}
|
|
1397
2083
|
function applyAiSectionsToDom(state, options) {
|
|
1398
2084
|
if (typeof document === "undefined") return;
|
|
2085
|
+
const brandOverride = deriveBrandOverride();
|
|
1399
2086
|
const templateBrand = deriveTemplateBrand();
|
|
1400
|
-
const
|
|
2087
|
+
const templateButtonRadius = deriveTemplateButtonRadius();
|
|
2088
|
+
const brandKey = brandOverride ? JSON.stringify(brandOverride) : "";
|
|
2089
|
+
const pagePath = window.location.pathname;
|
|
2090
|
+
const pageSections = state.sections.filter((entry) => (entry.path ?? "/") === pagePath);
|
|
2091
|
+
const activeIds = new Set(pageSections.map((entry) => entry.id));
|
|
2092
|
+
const ordered = state.hideTemplate === true ? applyExplicitOrder(orderByChain(pageSections)) : pageSections;
|
|
1401
2093
|
for (const [id, section] of mounted) {
|
|
1402
2094
|
if (!activeIds.has(id)) {
|
|
1403
2095
|
section.root.unmount();
|
|
@@ -1405,8 +2097,8 @@ function applyAiSectionsToDom(state, options) {
|
|
|
1405
2097
|
mounted.delete(id);
|
|
1406
2098
|
}
|
|
1407
2099
|
}
|
|
1408
|
-
for (const entry of
|
|
1409
|
-
const serialized = JSON.stringify(entry);
|
|
2100
|
+
for (const entry of ordered) {
|
|
2101
|
+
const serialized = JSON.stringify(entry) + brandKey;
|
|
1410
2102
|
const existing = mounted.get(entry.id);
|
|
1411
2103
|
if (existing && existing.serialized === serialized && existing.container.isConnected) {
|
|
1412
2104
|
continue;
|
|
@@ -1431,7 +2123,8 @@ function applyAiSectionsToDom(state, options) {
|
|
|
1431
2123
|
AiTreeRenderer,
|
|
1432
2124
|
{
|
|
1433
2125
|
tree: entry.tree,
|
|
1434
|
-
brand: entry.brand ?? options?.brand ?? templateBrand ?? void 0,
|
|
2126
|
+
brand: brandOverride ?? entry.brand ?? options?.brand ?? templateBrand ?? void 0,
|
|
2127
|
+
buttonRadius: templateButtonRadius,
|
|
1435
2128
|
resolveMedia,
|
|
1436
2129
|
editKeyPrefix: `ai.${entry.id}`
|
|
1437
2130
|
}
|
|
@@ -1440,8 +2133,20 @@ function applyAiSectionsToDom(state, options) {
|
|
|
1440
2133
|
});
|
|
1441
2134
|
mounted.set(entry.id, { root, container, serialized });
|
|
1442
2135
|
}
|
|
2136
|
+
if (state.hideTemplate === true) {
|
|
2137
|
+
let prev = null;
|
|
2138
|
+
for (const entry of ordered) {
|
|
2139
|
+
const el = mounted.get(entry.id)?.container;
|
|
2140
|
+
if (!el) continue;
|
|
2141
|
+
if (prev && !(prev.compareDocumentPosition(el) & Node.DOCUMENT_POSITION_FOLLOWING)) {
|
|
2142
|
+
prev.insertAdjacentElement("afterend", el);
|
|
2143
|
+
}
|
|
2144
|
+
prev = el;
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
1443
2147
|
syncReplacedOriginals(state);
|
|
1444
2148
|
syncRemovedSections(state);
|
|
2149
|
+
syncTemplateHidden(state, pageSections.length > 0);
|
|
1445
2150
|
}
|
|
1446
2151
|
|
|
1447
2152
|
// src/useLinkHrefGuardian.ts
|
|
@@ -1568,6 +2273,7 @@ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
|
|
|
1568
2273
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1569
2274
|
import_radix_ui.Dialog.Overlay,
|
|
1570
2275
|
{
|
|
2276
|
+
"data-ohw-scheduling-modal": "",
|
|
1571
2277
|
className: "fixed inset-0 z-50",
|
|
1572
2278
|
style: { background: "rgba(0,0,0,0.45)" }
|
|
1573
2279
|
}
|
|
@@ -1575,6 +2281,7 @@ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
|
|
|
1575
2281
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1576
2282
|
import_radix_ui.Dialog.Content,
|
|
1577
2283
|
{
|
|
2284
|
+
"data-ohw-scheduling-modal": "",
|
|
1578
2285
|
className: "fixed left-1/2 top-1/2 z-50 w-full -translate-x-1/2 -translate-y-1/2 bg-white rounded-xl shadow-xl outline-none font-body box-border overflow-hidden",
|
|
1579
2286
|
style: { maxWidth: 400 },
|
|
1580
2287
|
children: [
|
|
@@ -2048,7 +2755,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
|
|
|
2048
2755
|
const autoId = (0, import_react5.useId)();
|
|
2049
2756
|
const insertAfter = insertAfterProp ?? autoId;
|
|
2050
2757
|
const [schedule, setSchedule] = (0, import_react5.useState)(null);
|
|
2051
|
-
const [loading, setLoading] = (0, import_react5.useState)(
|
|
2758
|
+
const [loading, setLoading] = (0, import_react5.useState)(initialScheduleId !== null);
|
|
2052
2759
|
const [inEditor, setInEditor] = (0, import_react5.useState)(false);
|
|
2053
2760
|
const [isHovered, setIsHovered] = (0, import_react5.useState)(false);
|
|
2054
2761
|
const [modalState, setModalState] = (0, import_react5.useState)(null);
|
|
@@ -2222,8 +2929,10 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
|
|
|
2222
2929
|
"*"
|
|
2223
2930
|
);
|
|
2224
2931
|
};
|
|
2225
|
-
if (!inEditor && !loading && !schedule) return null;
|
|
2226
2932
|
const sectionId = `scheduling-${insertAfter}`;
|
|
2933
|
+
if (!inEditor && !loading && !schedule) {
|
|
2934
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("section", { "data-ohw-section": sectionId, "data-ohw-scheduling-anchor": insertAfter, style: { display: "none" } });
|
|
2935
|
+
}
|
|
2227
2936
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2228
2937
|
"section",
|
|
2229
2938
|
{
|
|
@@ -7140,13 +7849,17 @@ function MediaOverlay({
|
|
|
7140
7849
|
hover,
|
|
7141
7850
|
isUploading,
|
|
7142
7851
|
fadingOut = false,
|
|
7852
|
+
selected = false,
|
|
7853
|
+
hovered = false,
|
|
7143
7854
|
onFadeOutComplete,
|
|
7144
7855
|
onReplace,
|
|
7856
|
+
onSelect,
|
|
7145
7857
|
onVideoSettingsChange
|
|
7146
7858
|
}) {
|
|
7147
7859
|
const { rect } = hover;
|
|
7148
7860
|
const skeletonRef = React8.useRef(null);
|
|
7149
7861
|
const isVideo = hover.elementType === "video";
|
|
7862
|
+
const showChrome = !selected || hovered;
|
|
7150
7863
|
const autoplay = hover.videoAutoplay ?? true;
|
|
7151
7864
|
const muted = hover.videoMuted ?? true;
|
|
7152
7865
|
const probeRef = React8.useRef(null);
|
|
@@ -7160,6 +7873,7 @@ function MediaOverlay({
|
|
|
7160
7873
|
(prev) => prev && prev.fullWidth === width && prev.height === height ? prev : { fullWidth: width, height }
|
|
7161
7874
|
);
|
|
7162
7875
|
}, [isVideo]);
|
|
7876
|
+
const replaceLabel = isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image";
|
|
7163
7877
|
const replaceMode = buttonSize ? replaceButtonMode({ width: rect.width, height: rect.height }, buttonSize) : "none";
|
|
7164
7878
|
const box = {
|
|
7165
7879
|
position: "fixed",
|
|
@@ -7193,7 +7907,7 @@ function MediaOverlay({
|
|
|
7193
7907
|
}
|
|
7194
7908
|
);
|
|
7195
7909
|
}
|
|
7196
|
-
const settingsBar = isVideo && !hover.isDragOver ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
7910
|
+
const settingsBar = isVideo && !hover.isDragOver && showChrome ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
7197
7911
|
"div",
|
|
7198
7912
|
{
|
|
7199
7913
|
"data-ohw-bridge": "",
|
|
@@ -7263,10 +7977,12 @@ function MediaOverlay({
|
|
|
7263
7977
|
// in-document, pointer-events does it natively. The button below opts back in, so
|
|
7264
7978
|
// Replace still works.
|
|
7265
7979
|
pointerEvents: hover.hasTextOverlap ? "none" : "auto",
|
|
7266
|
-
|
|
7267
|
-
|
|
7980
|
+
// Selected: a firm component ring with no wash, so the image reads as chosen rather
|
|
7981
|
+
// than hovered. Hover keeps the existing tinted preview.
|
|
7982
|
+
boxShadow: selected ? "inset 0 0 0 2px var(--color-primary)" : "inset 0 0 0 1.5px var(--color-primary)",
|
|
7983
|
+
background: selected ? "transparent" : "color-mix(in srgb, var(--color-primary) 20%, transparent)"
|
|
7268
7984
|
},
|
|
7269
|
-
onClick: () => onReplace(hover.key),
|
|
7985
|
+
onClick: () => onSelect && !selected ? onSelect(hover.key) : onReplace(hover.key),
|
|
7270
7986
|
children: [
|
|
7271
7987
|
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
7272
7988
|
Button,
|
|
@@ -7287,17 +8003,17 @@ function MediaOverlay({
|
|
|
7287
8003
|
},
|
|
7288
8004
|
children: [
|
|
7289
8005
|
isVideo ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.Film, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.ImageIcon, { size: 14 }),
|
|
7290
|
-
|
|
8006
|
+
replaceLabel
|
|
7291
8007
|
]
|
|
7292
8008
|
}
|
|
7293
8009
|
),
|
|
7294
|
-
replaceMode
|
|
8010
|
+
showChrome && replaceMode !== "none" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
7295
8011
|
Button,
|
|
7296
8012
|
{
|
|
7297
8013
|
"data-ohw-media-overlay": "",
|
|
7298
8014
|
variant: "outline",
|
|
7299
8015
|
size: "sm",
|
|
7300
|
-
"aria-label":
|
|
8016
|
+
"aria-label": replaceLabel,
|
|
7301
8017
|
className: "gap-1.5 cursor-pointer hover:bg-background",
|
|
7302
8018
|
style: {
|
|
7303
8019
|
...OVERLAY_BUTTON_STYLE,
|
|
@@ -7320,7 +8036,7 @@ function MediaOverlay({
|
|
|
7320
8036
|
},
|
|
7321
8037
|
children: [
|
|
7322
8038
|
isVideo ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.Film, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.ImageIcon, { size: 14 }),
|
|
7323
|
-
replaceMode === "full" ?
|
|
8039
|
+
replaceMode === "full" ? replaceLabel : null
|
|
7324
8040
|
]
|
|
7325
8041
|
}
|
|
7326
8042
|
)
|
|
@@ -7404,6 +8120,8 @@ function parseSectionsFromRoot(root) {
|
|
|
7404
8120
|
const id = el.getAttribute("data-ohw-section") ?? "";
|
|
7405
8121
|
if (!id || seen.has(id) || LINK_PICKER_EXCLUDED_IDS.has(id)) continue;
|
|
7406
8122
|
if (el.parentElement?.closest("[data-ohw-section]")) continue;
|
|
8123
|
+
if (el.hasAttribute("data-ohw-ai-template-hidden") || el.hasAttribute("data-ohw-ai-removed") || el.hasAttribute("data-ohw-ai-replaced-by"))
|
|
8124
|
+
continue;
|
|
7407
8125
|
seen.add(id);
|
|
7408
8126
|
const label = el.getAttribute("data-ohw-section-label") ?? titleCaseSectionId(id);
|
|
7409
8127
|
sections.push({ id, label });
|
|
@@ -7433,6 +8151,10 @@ function topLevelSections() {
|
|
|
7433
8151
|
function instanceIdOf(el) {
|
|
7434
8152
|
return el.dataset.ohwInstance ?? el.dataset.ohwSection ?? "";
|
|
7435
8153
|
}
|
|
8154
|
+
function findByInstanceId(instanceId) {
|
|
8155
|
+
const escapedId = CSS.escape(instanceId);
|
|
8156
|
+
return document.querySelector(`[data-ohw-instance="${escapedId}"]`) ?? document.querySelector(`[data-ohw-section="${escapedId}"]:not([data-ohw-instance])`);
|
|
8157
|
+
}
|
|
7436
8158
|
function planSectionMove(instanceId, targetIndex, currentPath) {
|
|
7437
8159
|
const sections = topLevelSections();
|
|
7438
8160
|
const index = sections.findIndex((el) => instanceIdOf(el) === instanceId);
|
|
@@ -7468,7 +8190,7 @@ function syncRemovedFlags(entries) {
|
|
|
7468
8190
|
}
|
|
7469
8191
|
});
|
|
7470
8192
|
for (const id of removedIds) {
|
|
7471
|
-
const el =
|
|
8193
|
+
const el = findByInstanceId(id);
|
|
7472
8194
|
if (el) {
|
|
7473
8195
|
el.style.display = "none";
|
|
7474
8196
|
el.setAttribute(REMOVED_ATTR2, "");
|
|
@@ -7496,9 +8218,7 @@ function applyPersistedOrder(entries) {
|
|
|
7496
8218
|
}
|
|
7497
8219
|
}
|
|
7498
8220
|
function setSectionRemoved(instanceId, currentPath, existingEntries, removed) {
|
|
7499
|
-
|
|
7500
|
-
if (!document.querySelector(`[data-ohw-instance="${escapedId}"]`) && !document.querySelector(`[data-ohw-section="${escapedId}"]:not([data-ohw-instance])`))
|
|
7501
|
-
return null;
|
|
8221
|
+
if (!findByInstanceId(instanceId)) return null;
|
|
7502
8222
|
const byId = new Map(existingEntries.map((e) => [e.instanceId, e]));
|
|
7503
8223
|
const allSections = Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
|
|
7504
8224
|
(el) => !el.parentElement?.closest("[data-ohw-section]") && !isChromeSection(el)
|
|
@@ -7687,6 +8407,7 @@ function AiSectionOverlay({
|
|
|
7687
8407
|
}) {
|
|
7688
8408
|
const [selectedId, setSelectedId] = (0, import_react8.useState)(null);
|
|
7689
8409
|
const [reviewId, setReviewId] = (0, import_react8.useState)(null);
|
|
8410
|
+
const [reviewButtonsHidden, setReviewButtonsHidden] = (0, import_react8.useState)(false);
|
|
7690
8411
|
const reviewIdRef = (0, import_react8.useRef)(null);
|
|
7691
8412
|
reviewIdRef.current = reviewId;
|
|
7692
8413
|
const selectedIdRef = (0, import_react8.useRef)(null);
|
|
@@ -7748,6 +8469,7 @@ function AiSectionOverlay({
|
|
|
7748
8469
|
}
|
|
7749
8470
|
const found = readRect(sectionId) != null;
|
|
7750
8471
|
setReviewId(found ? sectionId : null);
|
|
8472
|
+
setReviewButtonsHidden(e.data.hideButtons === true);
|
|
7751
8473
|
postToParent2({ type: "ow:ai-review-started", sectionId, found });
|
|
7752
8474
|
if (found) {
|
|
7753
8475
|
document.querySelector(`[data-ohw-instance="${CSS.escape(sectionId)}"]`)?.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
|
@@ -7875,13 +8597,16 @@ function AiSectionOverlay({
|
|
|
7875
8597
|
border: `2px solid ${PRIMARY2}`,
|
|
7876
8598
|
borderRadius: edgeAwareRadius(reviewRect),
|
|
7877
8599
|
zIndex: 2147483200,
|
|
7878
|
-
// The veil itself: swallows clicks so the section stays locked until decided.
|
|
8600
|
+
// The veil itself: swallows clicks so the section stays locked until decided. This
|
|
8601
|
+
// stopPropagation only guards the bubble phase; the bridge's capture-phase click
|
|
8602
|
+
// handlers additionally bail on `[data-ohw-ai-review]`, without which a click on
|
|
8603
|
+
// Accept/Discard resolves to the media beneath and opens the file picker.
|
|
7879
8604
|
background: "rgba(8, 133, 254, 0.04)",
|
|
7880
8605
|
pointerEvents: "auto",
|
|
7881
8606
|
cursor: "default"
|
|
7882
8607
|
},
|
|
7883
8608
|
onClick: (e) => e.stopPropagation(),
|
|
7884
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
|
|
8609
|
+
children: !reviewButtonsHidden && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
|
|
7885
8610
|
"div",
|
|
7886
8611
|
{
|
|
7887
8612
|
style: {
|
|
@@ -10448,8 +11173,13 @@ function referenceBox(slot) {
|
|
|
10448
11173
|
const neighbour = row ? Array.from(row.querySelectorAll('[data-ohw-editable="icon"]')).find(
|
|
10449
11174
|
(el) => el !== slot && !el.hasAttribute("data-ohw-social-icon-placeholder")
|
|
10450
11175
|
) : null;
|
|
10451
|
-
|
|
10452
|
-
|
|
11176
|
+
if (neighbour) {
|
|
11177
|
+
const box2 = neighbour.querySelector(GLYPH_SELECTOR)?.getBoundingClientRect();
|
|
11178
|
+
if (box2?.width && box2.height) return box2;
|
|
11179
|
+
}
|
|
11180
|
+
const own = slot.getBoundingClientRect();
|
|
11181
|
+
if (own.width && own.height) return own;
|
|
11182
|
+
const box = slot.querySelector(GLYPH_SELECTOR)?.getBoundingClientRect() ?? null;
|
|
10453
11183
|
return box?.width && box.height ? box : null;
|
|
10454
11184
|
}
|
|
10455
11185
|
function iconMarkupSizedFor(slot, markup) {
|
|
@@ -12250,6 +12980,7 @@ function readLogoSizeState(content, placement) {
|
|
|
12250
12980
|
function getLogoElement(el) {
|
|
12251
12981
|
const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
|
|
12252
12982
|
if (marked) return marked;
|
|
12983
|
+
if (el.closest('[data-ohw-editable="icon"]')) return null;
|
|
12253
12984
|
const root = el.closest("nav, [data-ohw-nav-root], footer");
|
|
12254
12985
|
if (!root) return null;
|
|
12255
12986
|
const anchor = el.closest("a");
|
|
@@ -13318,6 +14049,7 @@ function useSectionDrag({
|
|
|
13318
14049
|
}
|
|
13319
14050
|
const orderJson = JSON.stringify(entries);
|
|
13320
14051
|
editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
|
|
14052
|
+
setAiSectionOrder(orderJson, window.location.pathname);
|
|
13321
14053
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
|
|
13322
14054
|
applyPersistedOrder(entries);
|
|
13323
14055
|
clearSectionDragVisuals();
|
|
@@ -14177,21 +14909,10 @@ function parseSchedulingInsertAfter(insertAfter) {
|
|
|
14177
14909
|
insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
|
|
14178
14910
|
};
|
|
14179
14911
|
}
|
|
14180
|
-
function
|
|
14181
|
-
|
|
14182
|
-
const
|
|
14183
|
-
|
|
14184
|
-
return { effectiveInsertAfter, insertBefore };
|
|
14185
|
-
}
|
|
14186
|
-
function getSchedulingMountPoint(insertAfter) {
|
|
14187
|
-
const { anchor } = parseSchedulingInsertAfter(insertAfter);
|
|
14188
|
-
let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
|
|
14189
|
-
if (!anchorEl && anchor === "scheduling") {
|
|
14190
|
-
const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
|
|
14191
|
-
anchorEl = widgets.at(-1) ?? null;
|
|
14192
|
-
}
|
|
14193
|
-
if (!anchorEl) return null;
|
|
14194
|
-
return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
|
|
14912
|
+
function resolveEntryAnchor(entry) {
|
|
14913
|
+
if (entry.anchorId !== void 0) return { anchorId: entry.anchorId, beforeId: entry.beforeId ?? null };
|
|
14914
|
+
const parsed = parseSchedulingInsertAfter(entry.insertAfter);
|
|
14915
|
+
return { anchorId: parsed.anchor, beforeId: parsed.insertBefore };
|
|
14195
14916
|
}
|
|
14196
14917
|
function schedulingMountDepth(insertAfter) {
|
|
14197
14918
|
if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
|
|
@@ -14208,8 +14929,7 @@ function getPageSchedulingEntries(raw) {
|
|
|
14208
14929
|
}
|
|
14209
14930
|
}
|
|
14210
14931
|
function isSchedulingWidgetMissing(entry) {
|
|
14211
|
-
|
|
14212
|
-
return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
|
|
14932
|
+
return !document.querySelector(`[data-ohw-section="${schedulingSectionId(entry.insertAfter)}"]`);
|
|
14213
14933
|
}
|
|
14214
14934
|
function hasMissingSchedulingWidgets(entries) {
|
|
14215
14935
|
return entries.some(isSchedulingWidgetMissing);
|
|
@@ -14239,16 +14959,17 @@ function initSectionsFromContent(content, removeExisting = false) {
|
|
|
14239
14959
|
} catch {
|
|
14240
14960
|
}
|
|
14241
14961
|
}
|
|
14242
|
-
function mountSchedulingWidget(
|
|
14243
|
-
const
|
|
14244
|
-
const sectionId = schedulingSectionId(
|
|
14962
|
+
function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, beforeId, existingWidgetId) {
|
|
14963
|
+
const widgetId = existingWidgetId ?? (beforeId ? `${anchorId}${INSERT_BEFORE_MARKER}${beforeId}` : anchorId);
|
|
14964
|
+
const sectionId = schedulingSectionId(widgetId);
|
|
14245
14965
|
if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
|
|
14246
|
-
const
|
|
14247
|
-
if (!
|
|
14966
|
+
const anchorEl = document.querySelector(`[data-ohw-section="${anchorId}"]`);
|
|
14967
|
+
if (!anchorEl) return false;
|
|
14968
|
+
const mountPoint = anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
|
|
14248
14969
|
const container = document.createElement("div");
|
|
14249
14970
|
container.dataset.ohwSectionContainer = "scheduling";
|
|
14250
|
-
if (
|
|
14251
|
-
const beforeAnchor = document.querySelector(`[data-ohw-section="${
|
|
14971
|
+
if (beforeId) {
|
|
14972
|
+
const beforeAnchor = document.querySelector(`[data-ohw-section="${beforeId}"]`);
|
|
14252
14973
|
const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
|
|
14253
14974
|
if (!beforePoint) return false;
|
|
14254
14975
|
beforePoint.insertAdjacentElement("beforebegin", container);
|
|
@@ -14259,19 +14980,25 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
|
|
|
14259
14980
|
}
|
|
14260
14981
|
tail.insertAdjacentElement("afterend", container);
|
|
14261
14982
|
}
|
|
14262
|
-
|
|
14263
|
-
|
|
14264
|
-
|
|
14265
|
-
|
|
14266
|
-
|
|
14267
|
-
|
|
14268
|
-
|
|
14269
|
-
|
|
14270
|
-
|
|
14271
|
-
|
|
14272
|
-
|
|
14273
|
-
|
|
14274
|
-
|
|
14983
|
+
try {
|
|
14984
|
+
const root = (0, import_client2.createRoot)(container);
|
|
14985
|
+
(0, import_react_dom3.flushSync)(() => {
|
|
14986
|
+
root.render(
|
|
14987
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
14988
|
+
SchedulingWidget,
|
|
14989
|
+
{
|
|
14990
|
+
notifyOnConnect,
|
|
14991
|
+
initialScheduleId: scheduleId,
|
|
14992
|
+
insertAfter: widgetId
|
|
14993
|
+
}
|
|
14994
|
+
)
|
|
14995
|
+
);
|
|
14996
|
+
});
|
|
14997
|
+
} catch (err) {
|
|
14998
|
+
console.error("[ow:scheduling] render threw", err);
|
|
14999
|
+
container.remove();
|
|
15000
|
+
return false;
|
|
15001
|
+
}
|
|
14275
15002
|
const tracker = getSectionsTracker();
|
|
14276
15003
|
let sections = [];
|
|
14277
15004
|
try {
|
|
@@ -14279,10 +15006,12 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
|
|
|
14279
15006
|
} catch {
|
|
14280
15007
|
}
|
|
14281
15008
|
const inEditor = typeof window !== "undefined" && window.self !== window.top;
|
|
14282
|
-
if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter ===
|
|
15009
|
+
if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === widgetId)) {
|
|
14283
15010
|
sections.push({
|
|
14284
15011
|
type: "scheduling",
|
|
14285
|
-
insertAfter:
|
|
15012
|
+
insertAfter: widgetId,
|
|
15013
|
+
anchorId,
|
|
15014
|
+
beforeId: beforeId ?? null,
|
|
14286
15015
|
pagePath: window.location.pathname,
|
|
14287
15016
|
...scheduleId ? { scheduleId } : {}
|
|
14288
15017
|
});
|
|
@@ -14296,7 +15025,8 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
|
|
|
14296
15025
|
for (let i = pending.length - 1; i >= 0; i--) {
|
|
14297
15026
|
const entry = pending[i];
|
|
14298
15027
|
const shouldNotify = typeof notifyOnConnect === "function" ? notifyOnConnect(entry) : notifyOnConnect;
|
|
14299
|
-
|
|
15028
|
+
const { anchorId, beforeId } = resolveEntryAnchor(entry);
|
|
15029
|
+
if (mountSchedulingWidget(anchorId, shouldNotify, entry.scheduleId ?? null, beforeId, entry.insertAfter)) {
|
|
14300
15030
|
pending.splice(i, 1);
|
|
14301
15031
|
}
|
|
14302
15032
|
}
|
|
@@ -14454,6 +15184,11 @@ function applyLinkByKey(key, val) {
|
|
|
14454
15184
|
hrefAnchors.forEach((el) => applyLinkHref(el, val));
|
|
14455
15185
|
}
|
|
14456
15186
|
}
|
|
15187
|
+
function isInsideLinkEditor(target) {
|
|
15188
|
+
return Boolean(
|
|
15189
|
+
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"]')
|
|
15190
|
+
);
|
|
15191
|
+
}
|
|
14457
15192
|
function isInsideFloatingPanel(target) {
|
|
14458
15193
|
return Boolean(target.closest("[data-ohw-floating-panel]"));
|
|
14459
15194
|
}
|
|
@@ -14461,11 +15196,6 @@ function isPointOverFloatingPanel(clientX, clientY) {
|
|
|
14461
15196
|
const el = document.elementFromPoint(clientX, clientY);
|
|
14462
15197
|
return Boolean(el instanceof Element && el.closest("[data-ohw-floating-panel]"));
|
|
14463
15198
|
}
|
|
14464
|
-
function isInsideLinkEditor(target) {
|
|
14465
|
-
return Boolean(
|
|
14466
|
-
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"]')
|
|
14467
|
-
);
|
|
14468
|
-
}
|
|
14469
15199
|
function getHrefKeyFromElement(el) {
|
|
14470
15200
|
if (!el) return null;
|
|
14471
15201
|
const anchor = el.closest("[data-ohw-href-key]");
|
|
@@ -14724,7 +15454,7 @@ function getNavigationSelectionParent(el) {
|
|
|
14724
15454
|
if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
|
|
14725
15455
|
return getFooterLinksContainer();
|
|
14726
15456
|
}
|
|
14727
|
-
if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
|
|
15457
|
+
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)) {
|
|
14728
15458
|
return getNavigationRoot(el);
|
|
14729
15459
|
}
|
|
14730
15460
|
return null;
|
|
@@ -14939,7 +15669,6 @@ var ICONS = {
|
|
|
14939
15669
|
insertUnorderedList: '<line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/>',
|
|
14940
15670
|
insertOrderedList: '<line x1="10" y1="6" x2="21" y2="6"/><line x1="10" y1="12" x2="21" y2="12"/><line x1="10" y1="18" x2="21" y2="18"/><path d="M4 6h1v4"/><path d="M4 10h2"/><path d="M6 18H4c0-1 2-2 2-3s-1-1.5-2-1"/>'
|
|
14941
15671
|
};
|
|
14942
|
-
var HEIGHT_SETTLE_DELAYS = [150, 400, 800];
|
|
14943
15672
|
var SELECTION_CHROME_GAP2 = 4;
|
|
14944
15673
|
var TOOLBAR_STROKE_GAP2 = 4;
|
|
14945
15674
|
var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
|
|
@@ -15319,6 +16048,7 @@ function StateToggle({
|
|
|
15319
16048
|
);
|
|
15320
16049
|
}
|
|
15321
16050
|
var contentCache = /* @__PURE__ */ new Map();
|
|
16051
|
+
var fetchedContentPaths = /* @__PURE__ */ new Set();
|
|
15322
16052
|
var OHW_LOADER_STYLE = {
|
|
15323
16053
|
position: "fixed",
|
|
15324
16054
|
inset: 0,
|
|
@@ -15437,6 +16167,70 @@ function OhhwellsBridge() {
|
|
|
15437
16167
|
const hoveredImageHasTextOverlapRef = (0, import_react17.useRef)(false);
|
|
15438
16168
|
const dragOverElRef = (0, import_react17.useRef)(null);
|
|
15439
16169
|
const [mediaHover, setMediaHover] = (0, import_react17.useState)(null);
|
|
16170
|
+
const [selectedMedia, setSelectedMedia] = (0, import_react17.useState)(null);
|
|
16171
|
+
const selectedMediaElRef = (0, import_react17.useRef)(null);
|
|
16172
|
+
const clearMediaSelection = (0, import_react17.useCallback)(() => {
|
|
16173
|
+
const prev = selectedMediaElRef.current;
|
|
16174
|
+
selectedMediaElRef.current = null;
|
|
16175
|
+
setSelectedMedia(null);
|
|
16176
|
+
const sectionEl = prev?.closest("[data-ohw-section]") ?? null;
|
|
16177
|
+
if (sectionEl) {
|
|
16178
|
+
postToParentRef.current({
|
|
16179
|
+
type: "ow:section-selected",
|
|
16180
|
+
sectionId: sectionEl.dataset.ohwSection ?? null,
|
|
16181
|
+
sectionLabel: sectionEl.dataset.ohwSectionLabel ?? titleCaseSectionId(sectionEl.dataset.ohwSection ?? ""),
|
|
16182
|
+
key: null
|
|
16183
|
+
});
|
|
16184
|
+
}
|
|
16185
|
+
}, []);
|
|
16186
|
+
const clearMediaSelectionRef = (0, import_react17.useRef)(clearMediaSelection);
|
|
16187
|
+
clearMediaSelectionRef.current = clearMediaSelection;
|
|
16188
|
+
const selectMediaElement = (0, import_react17.useCallback)((el) => {
|
|
16189
|
+
const r2 = el.getBoundingClientRect();
|
|
16190
|
+
const video = el.dataset.ohwEditable === "video" ? el.querySelector("video") : null;
|
|
16191
|
+
selectedMediaElRef.current = el;
|
|
16192
|
+
setSelectedMedia({
|
|
16193
|
+
key: el.dataset.ohwKey ?? "",
|
|
16194
|
+
rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height },
|
|
16195
|
+
elementType: el.dataset.ohwEditable ?? "image",
|
|
16196
|
+
hasTextOverlap: false,
|
|
16197
|
+
isDragOver: false,
|
|
16198
|
+
...video ? { videoAutoplay: video.autoplay, videoMuted: video.muted } : {}
|
|
16199
|
+
});
|
|
16200
|
+
const sectionEl = el.closest("[data-ohw-section]");
|
|
16201
|
+
aiSectionApiRef.current?.selectFromElement(el, { report: false });
|
|
16202
|
+
postToParentRef.current({
|
|
16203
|
+
type: "ow:section-selected",
|
|
16204
|
+
sectionId: sectionEl?.dataset.ohwSection ?? null,
|
|
16205
|
+
sectionLabel: sectionEl ? sectionEl.dataset.ohwSectionLabel ?? titleCaseSectionId(sectionEl.dataset.ohwSection ?? "") : null,
|
|
16206
|
+
key: el.dataset.ohwKey ?? null,
|
|
16207
|
+
// Display name for the pill — the raw key prettifies into fragments ("Img"); the
|
|
16208
|
+
// bridge knows what the node IS, so it names it.
|
|
16209
|
+
keyLabel: el.dataset.ohwEditable === "video" ? "Video" : el.dataset.ohwEditable === "bg-image" ? "Background" : "Image"
|
|
16210
|
+
});
|
|
16211
|
+
}, []);
|
|
16212
|
+
const selectMediaElementRef = (0, import_react17.useRef)(selectMediaElement);
|
|
16213
|
+
selectMediaElementRef.current = selectMediaElement;
|
|
16214
|
+
(0, import_react17.useEffect)(() => {
|
|
16215
|
+
if (!selectedMedia) return;
|
|
16216
|
+
const update = () => {
|
|
16217
|
+
const el = selectedMediaElRef.current;
|
|
16218
|
+
if (!el || !el.isConnected) {
|
|
16219
|
+
clearMediaSelection();
|
|
16220
|
+
return;
|
|
16221
|
+
}
|
|
16222
|
+
const r2 = el.getBoundingClientRect();
|
|
16223
|
+
setSelectedMedia(
|
|
16224
|
+
(prev) => prev ? { ...prev, rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height } } : prev
|
|
16225
|
+
);
|
|
16226
|
+
};
|
|
16227
|
+
window.addEventListener("scroll", update, true);
|
|
16228
|
+
window.addEventListener("resize", update);
|
|
16229
|
+
return () => {
|
|
16230
|
+
window.removeEventListener("scroll", update, true);
|
|
16231
|
+
window.removeEventListener("resize", update);
|
|
16232
|
+
};
|
|
16233
|
+
}, [selectedMedia !== null]);
|
|
15440
16234
|
const [carouselHover, setCarouselHover] = (0, import_react17.useState)(null);
|
|
15441
16235
|
const [uploadingRects, setUploadingRects] = (0, import_react17.useState)({});
|
|
15442
16236
|
const hoveredGapRef = (0, import_react17.useRef)(null);
|
|
@@ -15699,13 +16493,6 @@ function OhhwellsBridge() {
|
|
|
15699
16493
|
const [isItemDragging, setIsItemDragging] = (0, import_react17.useState)(false);
|
|
15700
16494
|
const [isFooterFrameSelection, setIsFooterFrameSelection] = (0, import_react17.useState)(false);
|
|
15701
16495
|
isFooterFrameSelectionRef.current = isFooterFrameSelection;
|
|
15702
|
-
const [floatingPanel, setFloatingPanel] = (0, import_react17.useState)(null);
|
|
15703
|
-
const floatingPanelOpenRef = (0, import_react17.useRef)(false);
|
|
15704
|
-
floatingPanelOpenRef.current = floatingPanel !== null;
|
|
15705
|
-
const [floatingPanelPos, setFloatingPanelPos] = (0, import_react17.useState)(null);
|
|
15706
|
-
const [logoSizeDraft, setLogoSizeDraft] = (0, import_react17.useState)(null);
|
|
15707
|
-
const [editorViewport, setEditorViewport] = (0, import_react17.useState)("desktop");
|
|
15708
|
-
const [parentScrollSnap, setParentScrollSnap] = (0, import_react17.useState)(null);
|
|
15709
16496
|
const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = (0, import_react17.useState)(null);
|
|
15710
16497
|
const [footerHeadingVisible, setFooterHeadingVisible] = (0, import_react17.useState)(null);
|
|
15711
16498
|
const footerDragRef = (0, import_react17.useRef)(null);
|
|
@@ -15720,7 +16507,16 @@ function OhhwellsBridge() {
|
|
|
15720
16507
|
const addNavAfterAnchorRef = (0, import_react17.useRef)(null);
|
|
15721
16508
|
const editContentRef = (0, import_react17.useRef)({});
|
|
15722
16509
|
const aiSectionsRef = (0, import_react17.useRef)("");
|
|
16510
|
+
const brandKitRef = (0, import_react17.useRef)("");
|
|
16511
|
+
const stylesRef = (0, import_react17.useRef)("");
|
|
15723
16512
|
const pendingDeleteUndoRef = (0, import_react17.useRef)(null);
|
|
16513
|
+
const [floatingPanel, setFloatingPanel] = (0, import_react17.useState)(null);
|
|
16514
|
+
const floatingPanelOpenRef = (0, import_react17.useRef)(false);
|
|
16515
|
+
const setFloatingPanelRef = (0, import_react17.useRef)(setFloatingPanel);
|
|
16516
|
+
const [floatingPanelPos, setFloatingPanelPos] = (0, import_react17.useState)(null);
|
|
16517
|
+
const [logoSizeDraft, setLogoSizeDraft] = (0, import_react17.useState)(null);
|
|
16518
|
+
const [editorViewport, setEditorViewport] = (0, import_react17.useState)("desktop");
|
|
16519
|
+
const [parentScrollSnap, setParentScrollSnap] = (0, import_react17.useState)(null);
|
|
15724
16520
|
const [sitePages, setSitePages] = (0, import_react17.useState)([]);
|
|
15725
16521
|
const [sectionsByPath, setSectionsByPath] = (0, import_react17.useState)({});
|
|
15726
16522
|
const sectionsPrefetchGenRef = (0, import_react17.useRef)(0);
|
|
@@ -15729,7 +16525,18 @@ function OhhwellsBridge() {
|
|
|
15729
16525
|
const linkPopoverOpenRef = (0, import_react17.useRef)(false);
|
|
15730
16526
|
const linkPopoverGraceUntilRef = (0, import_react17.useRef)(0);
|
|
15731
16527
|
setLinkPopoverRef.current = setLinkPopover;
|
|
16528
|
+
setFloatingPanelRef.current = setFloatingPanel;
|
|
15732
16529
|
linkPopoverSessionRef.current = linkPopover;
|
|
16530
|
+
floatingPanelOpenRef.current = Boolean(floatingPanel);
|
|
16531
|
+
(0, import_react17.useEffect)(() => {
|
|
16532
|
+
const syncViewport = () => {
|
|
16533
|
+
const next = window.innerWidth <= 480 ? "mobile" : "desktop";
|
|
16534
|
+
setEditorViewport((prev) => prev === next ? prev : next);
|
|
16535
|
+
};
|
|
16536
|
+
syncViewport();
|
|
16537
|
+
window.addEventListener("resize", syncViewport);
|
|
16538
|
+
return () => window.removeEventListener("resize", syncViewport);
|
|
16539
|
+
}, []);
|
|
15733
16540
|
const {
|
|
15734
16541
|
navDragRef,
|
|
15735
16542
|
navDropSlots,
|
|
@@ -17040,15 +17847,31 @@ function OhhwellsBridge() {
|
|
|
17040
17847
|
}
|
|
17041
17848
|
const applyContent = (content) => {
|
|
17042
17849
|
const imageLoads = [];
|
|
17850
|
+
if (typeof content[BRAND_KIT_KEY] === "string") {
|
|
17851
|
+
brandKitRef.current = content[BRAND_KIT_KEY];
|
|
17852
|
+
applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
|
|
17853
|
+
} else {
|
|
17854
|
+
brandKitRef.current = "";
|
|
17855
|
+
applyBrandToDom(null);
|
|
17856
|
+
}
|
|
17043
17857
|
if (typeof content[AI_SECTIONS_KEY] === "string") {
|
|
17044
17858
|
aiSectionsRef.current = content[AI_SECTIONS_KEY];
|
|
17859
|
+
setAiSectionOrder(content[SECTION_ORDER_KEY], window.location.pathname);
|
|
17045
17860
|
applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
|
|
17046
17861
|
}
|
|
17862
|
+
if (typeof content[STYLE_STORE_KEY] === "string") {
|
|
17863
|
+
stylesRef.current = content[STYLE_STORE_KEY];
|
|
17864
|
+
applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
|
|
17865
|
+
}
|
|
17866
|
+
applyBrandChrome(content);
|
|
17047
17867
|
for (const [key, val] of Object.entries(content)) {
|
|
17048
17868
|
if (key === "__ohw_sections") continue;
|
|
17049
17869
|
if (key === AI_SECTIONS_KEY) continue;
|
|
17050
17870
|
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
17051
17871
|
if (LOGO_IMAGE_KEYS.includes(key)) continue;
|
|
17872
|
+
if (key === BRAND_KIT_KEY) continue;
|
|
17873
|
+
if (key === STYLE_STORE_KEY) continue;
|
|
17874
|
+
if (BRAND_CHROME_KEYS.has(key)) continue;
|
|
17052
17875
|
if (applyVideoSettingNode(key, val)) continue;
|
|
17053
17876
|
if (applyCarouselNode(key, val)) continue;
|
|
17054
17877
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
@@ -17114,7 +17937,9 @@ function OhhwellsBridge() {
|
|
|
17114
17937
|
let cancelled = false;
|
|
17115
17938
|
setFetchState("loading");
|
|
17116
17939
|
const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
|
|
17117
|
-
|
|
17940
|
+
const initialPath = pathname;
|
|
17941
|
+
fetchedContentPaths.add(`${subdomain}::${initialPath}`);
|
|
17942
|
+
fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
|
|
17118
17943
|
if (cancelled) return;
|
|
17119
17944
|
const content = data?.content ?? {};
|
|
17120
17945
|
contentCache.set(subdomain, content);
|
|
@@ -17234,10 +18059,28 @@ function OhhwellsBridge() {
|
|
|
17234
18059
|
initSectionInstancesFromContent(content, window.location.pathname);
|
|
17235
18060
|
observer?.disconnect();
|
|
17236
18061
|
try {
|
|
18062
|
+
applyBrandChrome(content);
|
|
18063
|
+
if (typeof content[BRAND_KIT_KEY] === "string") {
|
|
18064
|
+
applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
|
|
18065
|
+
} else {
|
|
18066
|
+
applyBrandToDom(null);
|
|
18067
|
+
}
|
|
18068
|
+
if (typeof content[AI_SECTIONS_KEY] === "string") {
|
|
18069
|
+
setAiSectionOrder(content[SECTION_ORDER_KEY], window.location.pathname);
|
|
18070
|
+
applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
|
|
18071
|
+
}
|
|
18072
|
+
if (typeof content[STYLE_STORE_KEY] === "string") {
|
|
18073
|
+
applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
|
|
18074
|
+
}
|
|
17237
18075
|
for (const [key, val] of Object.entries(content)) {
|
|
17238
18076
|
if (key === "__ohw_sections") continue;
|
|
18077
|
+
if (key === AI_SECTIONS_KEY) continue;
|
|
17239
18078
|
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
17240
18079
|
if (LOGO_IMAGE_KEYS.includes(key)) continue;
|
|
18080
|
+
if (key === BRAND_KIT_KEY) continue;
|
|
18081
|
+
if (key === STYLE_STORE_KEY) continue;
|
|
18082
|
+
if (key === STYLE_STORE_KEY) continue;
|
|
18083
|
+
if (BRAND_CHROME_KEYS.has(key)) continue;
|
|
17241
18084
|
if (applyVideoSettingNode(key, val)) continue;
|
|
17242
18085
|
if (applyCarouselNode(key, val)) continue;
|
|
17243
18086
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
@@ -17283,6 +18126,17 @@ function OhhwellsBridge() {
|
|
|
17283
18126
|
debounceTimer = setTimeout(applyFromCache, 150);
|
|
17284
18127
|
};
|
|
17285
18128
|
applyFromCache();
|
|
18129
|
+
const pathCacheKey = `${subdomain}::${pathname}`;
|
|
18130
|
+
if (!fetchedContentPaths.has(pathCacheKey)) {
|
|
18131
|
+
fetchedContentPaths.add(pathCacheKey);
|
|
18132
|
+
const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
|
|
18133
|
+
fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(pathname)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
|
|
18134
|
+
if (!data?.content) return;
|
|
18135
|
+
contentCache.set(subdomain, data.content);
|
|
18136
|
+
applyFromCache();
|
|
18137
|
+
}).catch(() => {
|
|
18138
|
+
});
|
|
18139
|
+
}
|
|
17286
18140
|
observer = new MutationObserver(scheduleApply);
|
|
17287
18141
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
17288
18142
|
return () => {
|
|
@@ -17378,30 +18232,31 @@ function OhhwellsBridge() {
|
|
|
17378
18232
|
}, [isEditMode, pathname, subdomain, fetchState, resyncSelectedNavigationItem]);
|
|
17379
18233
|
(0, import_react17.useEffect)(() => {
|
|
17380
18234
|
if (!isEditMode) return;
|
|
18235
|
+
let lastPosted = 0;
|
|
17381
18236
|
const measure = () => {
|
|
17382
18237
|
const h = document.body.scrollHeight;
|
|
17383
|
-
if (h > 50
|
|
18238
|
+
if (h > 50 && Math.abs(h - lastPosted) > 1) {
|
|
18239
|
+
lastPosted = h;
|
|
18240
|
+
postToParent2({ type: "ow:height", height: h });
|
|
18241
|
+
}
|
|
18242
|
+
};
|
|
18243
|
+
let raf = null;
|
|
18244
|
+
const schedule = () => {
|
|
18245
|
+
if (raf != null) return;
|
|
18246
|
+
raf = requestAnimationFrame(() => {
|
|
18247
|
+
raf = null;
|
|
18248
|
+
measure();
|
|
18249
|
+
});
|
|
17384
18250
|
};
|
|
17385
18251
|
const t1 = setTimeout(measure, 50);
|
|
17386
18252
|
const t2 = setTimeout(measure, 500);
|
|
17387
|
-
|
|
17388
|
-
|
|
17389
|
-
const clearResizeTimers = () => {
|
|
17390
|
-
resizeTimers.forEach(clearTimeout);
|
|
17391
|
-
resizeTimers = [];
|
|
17392
|
-
};
|
|
17393
|
-
const handleResize = () => {
|
|
17394
|
-
if (window.innerWidth === lastWidth) return;
|
|
17395
|
-
lastWidth = window.innerWidth;
|
|
17396
|
-
clearResizeTimers();
|
|
17397
|
-
resizeTimers = HEIGHT_SETTLE_DELAYS.map((delay) => setTimeout(measure, delay));
|
|
17398
|
-
};
|
|
17399
|
-
window.addEventListener("resize", handleResize);
|
|
18253
|
+
const ro = new ResizeObserver(schedule);
|
|
18254
|
+
ro.observe(document.body);
|
|
17400
18255
|
return () => {
|
|
17401
18256
|
clearTimeout(t1);
|
|
17402
18257
|
clearTimeout(t2);
|
|
17403
|
-
|
|
17404
|
-
|
|
18258
|
+
if (raf != null) cancelAnimationFrame(raf);
|
|
18259
|
+
ro.disconnect();
|
|
17405
18260
|
};
|
|
17406
18261
|
}, [pathname, isEditMode, postToParent2]);
|
|
17407
18262
|
(0, import_react17.useEffect)(() => {
|
|
@@ -17642,6 +18497,7 @@ function OhhwellsBridge() {
|
|
|
17642
18497
|
return;
|
|
17643
18498
|
}
|
|
17644
18499
|
const target = e.target;
|
|
18500
|
+
if (target.closest("[data-ohw-ai-review]")) return;
|
|
17645
18501
|
if (target.closest("[data-ohw-toolbar]")) return;
|
|
17646
18502
|
if (target.closest("[data-ohw-state-toggle]")) return;
|
|
17647
18503
|
if (target.closest("[data-ohw-max-badge]")) return;
|
|
@@ -17653,6 +18509,9 @@ function OhhwellsBridge() {
|
|
|
17653
18509
|
)) {
|
|
17654
18510
|
return;
|
|
17655
18511
|
}
|
|
18512
|
+
if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
|
|
18513
|
+
clearMediaSelectionRef.current();
|
|
18514
|
+
}
|
|
17656
18515
|
{
|
|
17657
18516
|
const formEl = getFormElement(target);
|
|
17658
18517
|
const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
|
|
@@ -17804,19 +18663,14 @@ function OhhwellsBridge() {
|
|
|
17804
18663
|
}
|
|
17805
18664
|
const clickedButton = findClosestButtonLike(target);
|
|
17806
18665
|
const buttonOnMedia = Boolean(clickedButton && isMediaEditable(editable) && editable.contains(clickedButton));
|
|
17807
|
-
console.log("[click-debug]", {
|
|
17808
|
-
editableType: editable.dataset.ohwEditable,
|
|
17809
|
-
editableTag: editable.tagName,
|
|
17810
|
-
targetTag: target.tagName,
|
|
17811
|
-
clickedButtonTag: clickedButton?.tagName ?? null,
|
|
17812
|
-
buttonOnMedia,
|
|
17813
|
-
isMediaEditableEditable: isMediaEditable(editable)
|
|
17814
|
-
});
|
|
17815
18666
|
if (isMediaEditable(editable) && !buttonOnMedia) {
|
|
17816
18667
|
e.preventDefault();
|
|
17817
18668
|
e.stopPropagation();
|
|
17818
|
-
|
|
17819
|
-
|
|
18669
|
+
if (selectedMediaElRef.current === editable) {
|
|
18670
|
+
postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "", elementType: editable.dataset.ohwEditable ?? "image" });
|
|
18671
|
+
} else {
|
|
18672
|
+
selectMediaElementRef.current(editable);
|
|
18673
|
+
}
|
|
17820
18674
|
return;
|
|
17821
18675
|
}
|
|
17822
18676
|
const socialItem = getSocialItem(editable);
|
|
@@ -17835,11 +18689,6 @@ function OhhwellsBridge() {
|
|
|
17835
18689
|
const hrefLookupTarget = buttonOnMedia ? clickedButton : editable;
|
|
17836
18690
|
const hrefCtx = getHrefKeyFromElement(hrefLookupTarget);
|
|
17837
18691
|
const navAnchor = hrefCtx ? getNavigationItemAnchor(hrefCtx.anchor) : null;
|
|
17838
|
-
console.log("[click-debug 2]", {
|
|
17839
|
-
hrefLookupTargetTag: hrefLookupTarget.tagName,
|
|
17840
|
-
hrefCtx: hrefCtx ? { key: hrefCtx.key } : null,
|
|
17841
|
-
navAnchorTag: navAnchor?.tagName ?? null
|
|
17842
|
-
});
|
|
17843
18692
|
if (navAnchor) {
|
|
17844
18693
|
e.preventDefault();
|
|
17845
18694
|
e.stopPropagation();
|
|
@@ -17957,6 +18806,7 @@ function OhhwellsBridge() {
|
|
|
17957
18806
|
};
|
|
17958
18807
|
const handleDblClick = (e) => {
|
|
17959
18808
|
const target = e.target;
|
|
18809
|
+
if (target.closest("[data-ohw-ai-review]")) return;
|
|
17960
18810
|
if (target.closest("[data-ohw-toolbar]")) return;
|
|
17961
18811
|
if (target.closest("[data-ohw-state-toggle]")) return;
|
|
17962
18812
|
if (target.closest("[data-ohw-max-badge]")) return;
|
|
@@ -18008,6 +18858,9 @@ function OhhwellsBridge() {
|
|
|
18008
18858
|
setHoveredItemRect(null);
|
|
18009
18859
|
hoveredNavContainerRef.current = null;
|
|
18010
18860
|
setHoveredNavContainerRect(null);
|
|
18861
|
+
siblingHintElRef.current = null;
|
|
18862
|
+
setSiblingHintRect(null);
|
|
18863
|
+
setSiblingHintRects([]);
|
|
18011
18864
|
return;
|
|
18012
18865
|
}
|
|
18013
18866
|
{
|
|
@@ -18126,7 +18979,6 @@ function OhhwellsBridge() {
|
|
|
18126
18979
|
hoveredNavContainerRef.current = null;
|
|
18127
18980
|
setHoveredNavContainerRect(null);
|
|
18128
18981
|
hoveredItemElRef.current = editable;
|
|
18129
|
-
setHoveredItemRect(isSelectedForHoverRef.current(editable) ? null : editable.getBoundingClientRect());
|
|
18130
18982
|
}
|
|
18131
18983
|
}
|
|
18132
18984
|
}
|
|
@@ -18423,7 +19275,7 @@ function OhhwellsBridge() {
|
|
|
18423
19275
|
}
|
|
18424
19276
|
};
|
|
18425
19277
|
const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
|
|
18426
|
-
if (linkPopoverOpenRef.current) {
|
|
19278
|
+
if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
|
|
18427
19279
|
if (hoveredImageRef.current) {
|
|
18428
19280
|
hoveredImageRef.current = null;
|
|
18429
19281
|
hoveredImageHasTextOverlapRef.current = false;
|
|
@@ -18757,7 +19609,9 @@ function OhhwellsBridge() {
|
|
|
18757
19609
|
return;
|
|
18758
19610
|
}
|
|
18759
19611
|
const { y } = toProbeCoords(clientX, clientY, fromParentViewport);
|
|
18760
|
-
const sections = Array.from(document.querySelectorAll("[data-ohw-section]")).
|
|
19612
|
+
const sections = Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
|
|
19613
|
+
(el) => !el.hasAttribute("data-ohw-ai-template-hidden") && !el.hasAttribute("data-ohw-ai-removed") && !el.hasAttribute("data-ohw-ai-replaced-by") && el.getBoundingClientRect().height > 0
|
|
19614
|
+
).sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);
|
|
18761
19615
|
const ZONE = 20;
|
|
18762
19616
|
for (let i = 0; i < sections.length; i++) {
|
|
18763
19617
|
const a = sections[i];
|
|
@@ -18786,8 +19640,7 @@ function OhhwellsBridge() {
|
|
|
18786
19640
|
};
|
|
18787
19641
|
const handleMouseMove = (e) => {
|
|
18788
19642
|
const { clientX, clientY } = e;
|
|
18789
|
-
if (
|
|
18790
|
-
if (isOverEditorChrome(clientX, clientY)) {
|
|
19643
|
+
if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY) || isOverEditorChrome(clientX, clientY)) {
|
|
18791
19644
|
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
18792
19645
|
formHoverElRef.current = null;
|
|
18793
19646
|
setFormHoverRect(null);
|
|
@@ -18795,6 +19648,12 @@ function OhhwellsBridge() {
|
|
|
18795
19648
|
setHoveredItemRect(null);
|
|
18796
19649
|
hoveredNavContainerRef.current = null;
|
|
18797
19650
|
setHoveredNavContainerRect(null);
|
|
19651
|
+
siblingHintElRef.current = null;
|
|
19652
|
+
setSiblingHintRect(null);
|
|
19653
|
+
setSiblingHintRects([]);
|
|
19654
|
+
dismissImageHover();
|
|
19655
|
+
clearImageHover();
|
|
19656
|
+
setSectionGap(null);
|
|
18798
19657
|
return;
|
|
18799
19658
|
}
|
|
18800
19659
|
if (probeSocialsRowAt(clientX, clientY)) return;
|
|
@@ -18806,7 +19665,11 @@ function OhhwellsBridge() {
|
|
|
18806
19665
|
if (e.data?.type !== "ow:pointer-sync") return;
|
|
18807
19666
|
const { clientX, clientY } = e.data;
|
|
18808
19667
|
if (typeof clientX !== "number" || typeof clientY !== "number") return;
|
|
18809
|
-
if (
|
|
19668
|
+
if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
|
|
19669
|
+
dismissImageHover();
|
|
19670
|
+
clearImageHover();
|
|
19671
|
+
return;
|
|
19672
|
+
}
|
|
18810
19673
|
if (probeSocialsRowAt(clientX, clientY)) return;
|
|
18811
19674
|
probeSectionGapAt(clientX, clientY);
|
|
18812
19675
|
probeImageAt(clientX, clientY);
|
|
@@ -19089,10 +19952,23 @@ function OhhwellsBridge() {
|
|
|
19089
19952
|
if (e.data?.type !== "ow:hydrate") return;
|
|
19090
19953
|
const content = e.data.content;
|
|
19091
19954
|
if (!content) return;
|
|
19955
|
+
if (typeof content[BRAND_KIT_KEY] === "string") {
|
|
19956
|
+
brandKitRef.current = content[BRAND_KIT_KEY];
|
|
19957
|
+
applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
|
|
19958
|
+
} else {
|
|
19959
|
+
brandKitRef.current = "";
|
|
19960
|
+
applyBrandToDom(null);
|
|
19961
|
+
}
|
|
19092
19962
|
if (typeof content[AI_SECTIONS_KEY] === "string") {
|
|
19093
19963
|
aiSectionsRef.current = content[AI_SECTIONS_KEY];
|
|
19964
|
+
setAiSectionOrder(content[SECTION_ORDER_KEY], window.location.pathname);
|
|
19094
19965
|
applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
|
|
19095
19966
|
}
|
|
19967
|
+
if (typeof content[STYLE_STORE_KEY] === "string") {
|
|
19968
|
+
stylesRef.current = content[STYLE_STORE_KEY];
|
|
19969
|
+
applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
|
|
19970
|
+
}
|
|
19971
|
+
applyBrandChrome(content);
|
|
19096
19972
|
let sectionsJson = null;
|
|
19097
19973
|
for (const [key, val] of Object.entries(content)) {
|
|
19098
19974
|
if (key === "__ohw_sections") {
|
|
@@ -19102,6 +19978,9 @@ function OhhwellsBridge() {
|
|
|
19102
19978
|
if (key === AI_SECTIONS_KEY) continue;
|
|
19103
19979
|
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
19104
19980
|
if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
|
|
19981
|
+
if (key === BRAND_KIT_KEY) continue;
|
|
19982
|
+
if (key === STYLE_STORE_KEY) continue;
|
|
19983
|
+
if (BRAND_CHROME_KEYS.has(key)) continue;
|
|
19105
19984
|
if (applyVideoSettingNode(key, val)) continue;
|
|
19106
19985
|
if (applyCarouselNode(key, val)) continue;
|
|
19107
19986
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
@@ -19115,6 +19994,8 @@ function OhhwellsBridge() {
|
|
|
19115
19994
|
if (video && video.src !== val) applyVideoSrc(video, val);
|
|
19116
19995
|
} else if (el.dataset.ohwEditable === "link") {
|
|
19117
19996
|
applyLinkHref(el, val);
|
|
19997
|
+
} else if (el.dataset.ohwEditable === "icon") {
|
|
19998
|
+
applyIconMarkup(el, val);
|
|
19118
19999
|
} else if (isIconMarkupValue(val)) {
|
|
19119
20000
|
} else {
|
|
19120
20001
|
el.innerHTML = val;
|
|
@@ -19199,12 +20080,21 @@ function OhhwellsBridge() {
|
|
|
19199
20080
|
nodes: collectEditableNodes(editContentRef.current)
|
|
19200
20081
|
});
|
|
19201
20082
|
};
|
|
20083
|
+
const clearInteractionChrome = () => {
|
|
20084
|
+
deactivateRef.current();
|
|
20085
|
+
deselectRef.current();
|
|
20086
|
+
clearMediaSelectionRef.current();
|
|
20087
|
+
};
|
|
19202
20088
|
const handleAiApplyTree = (e) => {
|
|
19203
20089
|
if (e.data?.type !== "ow:ai-apply-tree") return;
|
|
19204
20090
|
const payload = e.data.payload;
|
|
19205
20091
|
if (!payload || typeof payload.id !== "string" || !isRenderableTree(payload.tree)) return;
|
|
20092
|
+
clearInteractionChrome();
|
|
19206
20093
|
const previous = aiSectionsRef.current;
|
|
19207
|
-
const nextState = applyTreeToState(parseAiSectionsState(previous),
|
|
20094
|
+
const nextState = applyTreeToState(parseAiSectionsState(previous), {
|
|
20095
|
+
...payload,
|
|
20096
|
+
path: payload.path ?? window.location.pathname
|
|
20097
|
+
});
|
|
19208
20098
|
const nextValue = serializeAiSectionsState(nextState);
|
|
19209
20099
|
aiSectionsRef.current = nextValue;
|
|
19210
20100
|
applyAiSectionsToDom(nextState);
|
|
@@ -19225,6 +20115,7 @@ function OhhwellsBridge() {
|
|
|
19225
20115
|
const exists = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
|
|
19226
20116
|
if (!exists) return;
|
|
19227
20117
|
if (isPageFrameSection(exists)) return;
|
|
20118
|
+
clearInteractionChrome();
|
|
19228
20119
|
const previous = aiSectionsRef.current;
|
|
19229
20120
|
const nextState = deleteSectionFromState(parseAiSectionsState(previous), sectionId);
|
|
19230
20121
|
const nextValue = serializeAiSectionsState(nextState);
|
|
@@ -19240,8 +20131,10 @@ function OhhwellsBridge() {
|
|
|
19240
20131
|
const handleAiSetSections = (e) => {
|
|
19241
20132
|
if (e.data?.type !== "ow:ai-set-sections") return;
|
|
19242
20133
|
const value = typeof e.data.value === "string" ? e.data.value : "";
|
|
20134
|
+
clearInteractionChrome();
|
|
19243
20135
|
aiSectionsRef.current = value;
|
|
19244
20136
|
applyAiSectionsToDom(parseAiSectionsState(value));
|
|
20137
|
+
applyStylesToDom(parseStyleStore(stylesRef.current));
|
|
19245
20138
|
const restoredHeight = document.body.scrollHeight;
|
|
19246
20139
|
if (restoredHeight > 50) postToParentRef.current({ type: "ow:height", height: restoredHeight });
|
|
19247
20140
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: AI_SECTIONS_KEY, text: value }] });
|
|
@@ -19257,10 +20150,40 @@ function OhhwellsBridge() {
|
|
|
19257
20150
|
if (!entries) return;
|
|
19258
20151
|
const orderJson = JSON.stringify(entries);
|
|
19259
20152
|
editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
|
|
20153
|
+
setAiSectionOrder(orderJson, window.location.pathname);
|
|
19260
20154
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
|
|
19261
20155
|
window.dispatchEvent(new Event("resize"));
|
|
19262
20156
|
};
|
|
19263
20157
|
window.addEventListener("message", handleMoveSection);
|
|
20158
|
+
const handleAiSetBrand = (e) => {
|
|
20159
|
+
if (e.data?.type !== "ow:ai-set-brand") return;
|
|
20160
|
+
const value = typeof e.data.value === "string" ? e.data.value : "";
|
|
20161
|
+
const previous = brandKitRef.current;
|
|
20162
|
+
brandKitRef.current = value;
|
|
20163
|
+
applyBrandToDom(parseBrandKit(value));
|
|
20164
|
+
if (aiSectionsRef.current) applyAiSectionsToDom(parseAiSectionsState(aiSectionsRef.current));
|
|
20165
|
+
applyStylesToDom(parseStyleStore(stylesRef.current));
|
|
20166
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: BRAND_KIT_KEY, text: value }] });
|
|
20167
|
+
postToParentRef.current({ type: "ow:ai-brand-applied", previous, value });
|
|
20168
|
+
};
|
|
20169
|
+
window.addEventListener("message", handleAiSetBrand);
|
|
20170
|
+
const handleAiSetStyles = (e) => {
|
|
20171
|
+
if (e.data?.type !== "ow:ai-set-styles") return;
|
|
20172
|
+
const value = typeof e.data.value === "string" ? e.data.value : "";
|
|
20173
|
+
const previous = stylesRef.current;
|
|
20174
|
+
stylesRef.current = value;
|
|
20175
|
+
applyStylesToDom(parseStyleStore(value));
|
|
20176
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: STYLE_STORE_KEY, text: value }] });
|
|
20177
|
+
postToParentRef.current({ type: "ow:ai-styles-applied", previous, value });
|
|
20178
|
+
};
|
|
20179
|
+
window.addEventListener("message", handleAiSetStyles);
|
|
20180
|
+
const handleGetBrand = (e) => {
|
|
20181
|
+
if (e.data?.type !== "ow:get-brand") return;
|
|
20182
|
+
const template = deriveTemplateBrand();
|
|
20183
|
+
const value = brandKitRef.current || (template ? JSON.stringify(template) : "");
|
|
20184
|
+
postToParentRef.current({ type: "ow:brand-value", value });
|
|
20185
|
+
};
|
|
20186
|
+
window.addEventListener("message", handleGetBrand);
|
|
19264
20187
|
const handlePanelDragging = (e) => {
|
|
19265
20188
|
if (e.data?.type !== "ow:panel-dragging") return;
|
|
19266
20189
|
if (e.data.dragging) document.documentElement.setAttribute("data-ohw-panel-dragging", "");
|
|
@@ -19318,8 +20241,15 @@ function OhhwellsBridge() {
|
|
|
19318
20241
|
closeLinkPopoverRef.current();
|
|
19319
20242
|
return;
|
|
19320
20243
|
}
|
|
20244
|
+
if (floatingPanelOpenRef.current) {
|
|
20245
|
+
setFloatingPanelRef.current(null);
|
|
20246
|
+
deselectRef.current();
|
|
20247
|
+
deactivateRef.current();
|
|
20248
|
+
return;
|
|
20249
|
+
}
|
|
19321
20250
|
deselectRef.current();
|
|
19322
20251
|
deactivateRef.current();
|
|
20252
|
+
clearMediaSelectionRef.current();
|
|
19323
20253
|
};
|
|
19324
20254
|
window.addEventListener("message", handleDeactivate);
|
|
19325
20255
|
const handleToastAction = (e) => {
|
|
@@ -19405,6 +20335,10 @@ function OhhwellsBridge() {
|
|
|
19405
20335
|
const handleKeyDown = (e) => {
|
|
19406
20336
|
if (e.key === "Escape" && document.querySelector("[data-ohw-section-picker]")) return;
|
|
19407
20337
|
if (e.key === "Escape" && document.querySelector("[data-ohw-more-menu]")) return;
|
|
20338
|
+
if (e.key === "Escape" && selectedMediaElRef.current) {
|
|
20339
|
+
clearMediaSelectionRef.current();
|
|
20340
|
+
return;
|
|
20341
|
+
}
|
|
19408
20342
|
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "a" && activeElRef.current) {
|
|
19409
20343
|
e.preventDefault();
|
|
19410
20344
|
selectAllTextInEditable(activeElRef.current);
|
|
@@ -19564,6 +20498,12 @@ function OhhwellsBridge() {
|
|
|
19564
20498
|
if (aiSectionsRef.current) {
|
|
19565
20499
|
nodes.push({ key: AI_SECTIONS_KEY, type: "sections", text: aiSectionsRef.current });
|
|
19566
20500
|
}
|
|
20501
|
+
if (stylesRef.current) {
|
|
20502
|
+
nodes.push({ key: STYLE_STORE_KEY, type: "styles", text: stylesRef.current });
|
|
20503
|
+
}
|
|
20504
|
+
if (brandKitRef.current) {
|
|
20505
|
+
nodes.push({ key: BRAND_KIT_KEY, type: "brand", text: brandKitRef.current });
|
|
20506
|
+
}
|
|
19567
20507
|
document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
|
|
19568
20508
|
const formKey = formKeyOf(form);
|
|
19569
20509
|
if (!formKey) return;
|
|
@@ -19581,8 +20521,12 @@ function OhhwellsBridge() {
|
|
|
19581
20521
|
if (inserted) {
|
|
19582
20522
|
const tracker = getSectionsTracker();
|
|
19583
20523
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
|
|
19584
|
-
const
|
|
19585
|
-
|
|
20524
|
+
const reportHeight = () => {
|
|
20525
|
+
const h = document.body.scrollHeight;
|
|
20526
|
+
if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
|
|
20527
|
+
};
|
|
20528
|
+
reportHeight();
|
|
20529
|
+
setTimeout(reportHeight, 500);
|
|
19586
20530
|
}
|
|
19587
20531
|
};
|
|
19588
20532
|
const handleSwitchSchedule = (e) => {
|
|
@@ -19979,13 +20923,16 @@ function OhhwellsBridge() {
|
|
|
19979
20923
|
window.removeEventListener("message", handleAiDeleteSection);
|
|
19980
20924
|
window.removeEventListener("message", handleAiSetSections);
|
|
19981
20925
|
window.removeEventListener("message", handleMoveSection);
|
|
20926
|
+
window.removeEventListener("message", handleAiSetBrand);
|
|
20927
|
+
window.removeEventListener("message", handleAiSetStyles);
|
|
20928
|
+
window.removeEventListener("message", handleGetBrand);
|
|
19982
20929
|
window.removeEventListener("message", handlePanelDragging);
|
|
19983
20930
|
window.removeEventListener("message", handleDeleteSection);
|
|
19984
20931
|
window.removeEventListener("message", handleDeactivate);
|
|
19985
|
-
document.documentElement.removeAttribute("data-ohw-panel-dragging");
|
|
19986
20932
|
window.removeEventListener("message", handleToastAction);
|
|
19987
20933
|
window.removeEventListener("message", handleFormCount);
|
|
19988
20934
|
window.removeEventListener("message", handleUiEscape);
|
|
20935
|
+
document.documentElement.removeAttribute("data-ohw-panel-dragging");
|
|
19989
20936
|
autoSaveTimers.current.forEach(clearTimeout);
|
|
19990
20937
|
autoSaveTimers.current.clear();
|
|
19991
20938
|
if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
|
|
@@ -20188,7 +21135,7 @@ function OhhwellsBridge() {
|
|
|
20188
21135
|
postToParent2({
|
|
20189
21136
|
type: "ow:ready",
|
|
20190
21137
|
version: "1",
|
|
20191
|
-
bridgeVersion: "0.1.
|
|
21138
|
+
bridgeVersion: "0.1.78",
|
|
20192
21139
|
path: pathname,
|
|
20193
21140
|
nodes: collectEditableNodes(editContentRef.current),
|
|
20194
21141
|
sections
|
|
@@ -20595,11 +21542,22 @@ function OhhwellsBridge() {
|
|
|
20595
21542
|
const showEditLink = toolbarShowEditLink;
|
|
20596
21543
|
const currentSections = sectionsByPath[pathname] ?? [];
|
|
20597
21544
|
linkPopoverOpenRef.current = linkPopover !== null;
|
|
21545
|
+
const handleMediaSelect = (0, import_react17.useCallback)((key) => {
|
|
21546
|
+
const el = hoveredImageRef.current?.dataset.ohwKey === key ? hoveredImageRef.current : Array.from(document.querySelectorAll(MEDIA_SELECTOR)).find(
|
|
21547
|
+
(m) => (m.dataset.ohwKey ?? "") === key
|
|
21548
|
+
) ?? null;
|
|
21549
|
+
if (!el) return;
|
|
21550
|
+
selectMediaElementRef.current(el);
|
|
21551
|
+
}, []);
|
|
20598
21552
|
const handleMediaReplace = (0, import_react17.useCallback)(
|
|
20599
21553
|
(key) => {
|
|
20600
|
-
postToParent2({
|
|
21554
|
+
postToParent2({
|
|
21555
|
+
type: "ow:image-pick",
|
|
21556
|
+
key,
|
|
21557
|
+
elementType: mediaHover?.elementType ?? selectedMedia?.elementType ?? "image"
|
|
21558
|
+
});
|
|
20601
21559
|
},
|
|
20602
|
-
[postToParent2, mediaHover?.elementType]
|
|
21560
|
+
[postToParent2, mediaHover?.elementType, selectedMedia?.elementType]
|
|
20603
21561
|
);
|
|
20604
21562
|
const handleEditCarousel = (0, import_react17.useCallback)(
|
|
20605
21563
|
(key) => {
|
|
@@ -20671,12 +21629,25 @@ function OhhwellsBridge() {
|
|
|
20671
21629
|
},
|
|
20672
21630
|
`uploading-${key}`
|
|
20673
21631
|
)),
|
|
20674
|
-
mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
21632
|
+
mediaHover && !(mediaHover.key in uploadingRects) && mediaHover.key !== selectedMedia?.key && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
20675
21633
|
MediaOverlay,
|
|
20676
21634
|
{
|
|
20677
21635
|
hover: mediaHover,
|
|
20678
21636
|
isUploading: false,
|
|
20679
21637
|
onReplace: handleMediaReplace,
|
|
21638
|
+
onSelect: handleMediaSelect,
|
|
21639
|
+
onVideoSettingsChange: handleVideoSettingsChange
|
|
21640
|
+
}
|
|
21641
|
+
),
|
|
21642
|
+
selectedMedia && !(selectedMedia.key in uploadingRects) && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
21643
|
+
MediaOverlay,
|
|
21644
|
+
{
|
|
21645
|
+
hover: selectedMedia,
|
|
21646
|
+
selected: true,
|
|
21647
|
+
hovered: mediaHover?.key === selectedMedia.key,
|
|
21648
|
+
isUploading: false,
|
|
21649
|
+
onReplace: handleMediaReplace,
|
|
21650
|
+
onSelect: handleMediaSelect,
|
|
20680
21651
|
onVideoSettingsChange: handleVideoSettingsChange
|
|
20681
21652
|
}
|
|
20682
21653
|
),
|
|
@@ -21082,6 +22053,59 @@ function OhhwellsBridge() {
|
|
|
21082
22053
|
) : null
|
|
21083
22054
|
] });
|
|
21084
22055
|
}
|
|
22056
|
+
|
|
22057
|
+
// src/ui/EmptySection.tsx
|
|
22058
|
+
var import_link = __toESM(require("next/link"), 1);
|
|
22059
|
+
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
22060
|
+
function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey }) {
|
|
22061
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
|
|
22062
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
22063
|
+
"p",
|
|
22064
|
+
{
|
|
22065
|
+
style: {
|
|
22066
|
+
fontFamily: "var(--brand-font-body)",
|
|
22067
|
+
fontSize: "0.75rem",
|
|
22068
|
+
fontWeight: 500,
|
|
22069
|
+
letterSpacing: "0.15em",
|
|
22070
|
+
textTransform: "uppercase",
|
|
22071
|
+
color: "var(--brand-accent)",
|
|
22072
|
+
marginBottom: "1.5rem"
|
|
22073
|
+
},
|
|
22074
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_link.default, { href: homeHref, style: { color: "inherit" }, children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { ...eyebrowKey ? { "data-ohw-editable": "text", "data-ohw-key": eyebrowKey } : {}, children: "Home" }) })
|
|
22075
|
+
}
|
|
22076
|
+
),
|
|
22077
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
22078
|
+
"h1",
|
|
22079
|
+
{
|
|
22080
|
+
style: {
|
|
22081
|
+
fontFamily: "var(--brand-font-heading)",
|
|
22082
|
+
fontSize: "clamp(2rem, 3.5vw, 2.75rem)",
|
|
22083
|
+
lineHeight: 1.1,
|
|
22084
|
+
letterSpacing: "-0.025em",
|
|
22085
|
+
color: "var(--brand-text)",
|
|
22086
|
+
marginBottom: "1rem"
|
|
22087
|
+
},
|
|
22088
|
+
...titleKey ? { "data-ohw-editable": "text", "data-ohw-key": titleKey, "data-ohw-max-length": 80 } : {},
|
|
22089
|
+
children: title
|
|
22090
|
+
}
|
|
22091
|
+
),
|
|
22092
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
22093
|
+
"p",
|
|
22094
|
+
{
|
|
22095
|
+
style: {
|
|
22096
|
+
fontFamily: "var(--brand-font-body)",
|
|
22097
|
+
fontSize: "1rem",
|
|
22098
|
+
lineHeight: 1.7,
|
|
22099
|
+
fontWeight: 300,
|
|
22100
|
+
color: "var(--brand-text-muted)",
|
|
22101
|
+
maxWidth: "340px"
|
|
22102
|
+
},
|
|
22103
|
+
...subtitleKey ? { "data-ohw-editable": "text", "data-ohw-key": subtitleKey, "data-ohw-max-length": 160 } : {},
|
|
22104
|
+
children: "This page doesn't have any content yet."
|
|
22105
|
+
}
|
|
22106
|
+
)
|
|
22107
|
+
] });
|
|
22108
|
+
}
|
|
21085
22109
|
// Annotate the CommonJS export names for ESM import in node:
|
|
21086
22110
|
0 && (module.exports = {
|
|
21087
22111
|
AI_DEFAULT_BRAND,
|
|
@@ -21099,6 +22123,7 @@ function OhhwellsBridge() {
|
|
|
21099
22123
|
DropdownMenuItem,
|
|
21100
22124
|
DropdownMenuSeparator,
|
|
21101
22125
|
DropdownMenuTrigger,
|
|
22126
|
+
EmptySection,
|
|
21102
22127
|
ItemActionToolbar,
|
|
21103
22128
|
ItemInteractionLayer,
|
|
21104
22129
|
LinkEditorPanel,
|