@ohhwells/bridge 0.1.78 → 0.1.79-next.241
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 +1329 -213
- 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 +1328 -213
- 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,320 @@ 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)) return;
|
|
414
|
+
const value = el.style.getPropertyValue(prop) || (prop === "background" ? el.style.getPropertyValue("background-color") : "");
|
|
415
|
+
el.setAttribute(attr, value);
|
|
416
|
+
}
|
|
417
|
+
function restoreInline(el, prop) {
|
|
418
|
+
const attr = `data-ohw-style-prev-${prop}`;
|
|
419
|
+
if (!el.hasAttribute(attr)) return;
|
|
420
|
+
const prev = el.getAttribute(attr) ?? "";
|
|
421
|
+
if (prev) el.style.setProperty(prop, prev);
|
|
422
|
+
else el.style.removeProperty(prop);
|
|
423
|
+
el.removeAttribute(attr);
|
|
424
|
+
}
|
|
425
|
+
function ensureStyleSheet() {
|
|
426
|
+
let el = document.getElementById(STYLE_SHEET_ID);
|
|
427
|
+
if (!el) {
|
|
428
|
+
el = document.createElement("style");
|
|
429
|
+
el.id = STYLE_SHEET_ID;
|
|
430
|
+
document.head.appendChild(el);
|
|
431
|
+
}
|
|
432
|
+
const css = styleSheetCss();
|
|
433
|
+
if (el.textContent !== css) el.textContent = css;
|
|
434
|
+
}
|
|
435
|
+
function clearSectionAttrs(root) {
|
|
436
|
+
for (const attr of Object.values(SECTION_ATTRS)) {
|
|
437
|
+
for (const el of Array.from(root.querySelectorAll(`[${attr}]`))) el.removeAttribute(attr);
|
|
438
|
+
}
|
|
439
|
+
for (const el of Array.from(root.querySelectorAll("[data-ohw-style-bgcolor]"))) {
|
|
440
|
+
restoreInline(el, "background");
|
|
441
|
+
el.removeAttribute("data-ohw-style-bgcolor");
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
function clearNodeProps(root) {
|
|
445
|
+
for (const el of Array.from(root.querySelectorAll(`[${NODE_WROTE_ATTR}]`))) {
|
|
446
|
+
const h = el;
|
|
447
|
+
for (const prop of NODE_PROPS) restoreInline(h, prop);
|
|
448
|
+
h.removeAttribute(NODE_WROTE_ATTR);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
function buttonSurfaceOf(el) {
|
|
452
|
+
return el.closest("a, button") ?? el;
|
|
453
|
+
}
|
|
454
|
+
function applyStylesToDom(store) {
|
|
455
|
+
ensureStyleSheet();
|
|
456
|
+
clearSectionAttrs(document);
|
|
457
|
+
clearNodeProps(document);
|
|
458
|
+
loadStyleFonts(
|
|
459
|
+
store ? Object.values(store.nodes).flatMap((n) => n.fontFamily ? [n.fontFamily] : []) : []
|
|
460
|
+
);
|
|
461
|
+
if (!store) return;
|
|
462
|
+
for (const [sectionId, override] of Object.entries(store.sections)) {
|
|
463
|
+
const sections = document.querySelectorAll(
|
|
464
|
+
`[data-ohw-section="${CSS.escape(sectionId)}"]`
|
|
465
|
+
);
|
|
466
|
+
for (const marker of Array.from(sections)) {
|
|
467
|
+
const section = marker.querySelector(":scope > [data-ai-section]") ?? marker;
|
|
468
|
+
for (const [prop, attr] of Object.entries(SECTION_ATTRS)) {
|
|
469
|
+
const value = override[prop];
|
|
470
|
+
if (value === void 0) continue;
|
|
471
|
+
if (prop === "sectionBackground" && override.sectionBackgroundColor !== void 0) continue;
|
|
472
|
+
section.setAttribute(attr, String(value).replace(":", "-"));
|
|
473
|
+
}
|
|
474
|
+
if (override.sectionBackgroundColor !== void 0) {
|
|
475
|
+
saveInline(section, "background");
|
|
476
|
+
section.style.setProperty("background", override.sectionBackgroundColor, "important");
|
|
477
|
+
section.setAttribute("data-ohw-style-bgcolor", "");
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
for (const [key, override] of Object.entries(store.nodes)) {
|
|
482
|
+
const nodes = document.querySelectorAll(`[data-ohw-key="${CSS.escape(key)}"]`);
|
|
483
|
+
for (const el of Array.from(nodes)) {
|
|
484
|
+
if (override.color !== void 0) {
|
|
485
|
+
saveInline(el, "color");
|
|
486
|
+
el.style.setProperty("color", override.color, "important");
|
|
487
|
+
el.setAttribute(NODE_WROTE_ATTR, "");
|
|
488
|
+
}
|
|
489
|
+
if (override.fontFamily !== void 0) {
|
|
490
|
+
saveInline(el, "font-family");
|
|
491
|
+
el.style.setProperty("font-family", `'${override.fontFamily}'`, "important");
|
|
492
|
+
el.setAttribute(NODE_WROTE_ATTR, "");
|
|
493
|
+
}
|
|
494
|
+
if (override.fontSize !== void 0) {
|
|
495
|
+
saveInline(el, "font-size");
|
|
496
|
+
el.style.setProperty("font-size", `${override.fontSize}px`, "important");
|
|
497
|
+
el.setAttribute(NODE_WROTE_ATTR, "");
|
|
498
|
+
}
|
|
499
|
+
if (override.buttonBackground !== void 0 || override.buttonText !== void 0) {
|
|
500
|
+
const surface = buttonSurfaceOf(el);
|
|
501
|
+
if (override.buttonBackground !== void 0) {
|
|
502
|
+
saveInline(surface, "background");
|
|
503
|
+
surface.style.setProperty("background", override.buttonBackground, "important");
|
|
504
|
+
}
|
|
505
|
+
if (override.buttonText !== void 0) {
|
|
506
|
+
saveInline(surface, "color");
|
|
507
|
+
surface.style.setProperty("color", override.buttonText, "important");
|
|
508
|
+
}
|
|
509
|
+
surface.setAttribute(NODE_WROTE_ATTR, "");
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
194
515
|
// src/ui/ai-tree/aiSectionsManager.tsx
|
|
195
516
|
var import_react_dom = require("react-dom");
|
|
196
517
|
var import_client = require("react-dom/client");
|
|
@@ -205,7 +526,8 @@ function lucideByName(name) {
|
|
|
205
526
|
}
|
|
206
527
|
var typeStyle = (spec, font) => ({
|
|
207
528
|
fontFamily: font,
|
|
208
|
-
|
|
529
|
+
// Headings shrink with the viewport (reaching full size around ~900px wide); body copy stays put.
|
|
530
|
+
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
531
|
lineHeight: spec.line,
|
|
210
532
|
fontWeight: spec.weight
|
|
211
533
|
});
|
|
@@ -214,12 +536,58 @@ var featureLines = (value) => value.split(/\n|<br\s*\/?>/i).map((line) => line.t
|
|
|
214
536
|
var CHECK_MASK = `data:image/svg+xml,${encodeURIComponent(
|
|
215
537
|
'<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
538
|
)}`;
|
|
539
|
+
var AI_MOBILE_CSS = [
|
|
540
|
+
"@media (max-width: 768px){",
|
|
541
|
+
"[data-ai-section]{overflow-x:hidden}",
|
|
542
|
+
"[data-ai-container]{padding:0 20px !important}",
|
|
543
|
+
"[data-ai-row]{display:flex !important;flex-direction:column !important;align-items:stretch !important}",
|
|
544
|
+
"[data-ai-cell]{width:100%;min-width:0}",
|
|
545
|
+
"[data-ai-grid]{grid-template-columns:1fr !important}",
|
|
546
|
+
// Group containers flatten to a column on phones; span placements come along for free.
|
|
547
|
+
"[data-ai-group]{display:flex !important;flex-direction:column !important}",
|
|
548
|
+
"[data-ai-group] > *{grid-column:auto !important}",
|
|
549
|
+
"[data-ai-section] img{max-width:100%}",
|
|
550
|
+
"}",
|
|
551
|
+
"@media (min-width: 769px) and (max-width: 1024px){",
|
|
552
|
+
"[data-ai-grid]{grid-template-columns:repeat(2, 1fr) !important}",
|
|
553
|
+
"}"
|
|
554
|
+
].join("");
|
|
217
555
|
var FEATURE_LINE_CSS = [
|
|
218
556
|
"[data-ai-features]>div{position:relative;padding-left:40px;min-height:24px}",
|
|
219
557
|
'[data-ai-features]>div::before{content:"";position:absolute;left:0;top:1px;width:24px;height:24px;',
|
|
220
558
|
`background-color:currentColor;-webkit-mask:url("${CHECK_MASK}") no-repeat 0 0/24px 24px;`,
|
|
221
559
|
`mask:url("${CHECK_MASK}") no-repeat 0 0/24px 24px}`
|
|
222
560
|
].join("");
|
|
561
|
+
function hexLuminance(color) {
|
|
562
|
+
const m = /^#([0-9a-f]{6})$/i.exec(color.trim());
|
|
563
|
+
if (!m) return null;
|
|
564
|
+
const [r2, g, b] = [0, 2, 4].map((i) => {
|
|
565
|
+
const c = parseInt(m[1].slice(i, i + 2), 16) / 255;
|
|
566
|
+
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
|
567
|
+
});
|
|
568
|
+
return 0.2126 * r2 + 0.7152 * g + 0.0722 * b;
|
|
569
|
+
}
|
|
570
|
+
function hexContrast(a, b) {
|
|
571
|
+
const la = hexLuminance(a);
|
|
572
|
+
const lb = hexLuminance(b);
|
|
573
|
+
if (la === null || lb === null) return null;
|
|
574
|
+
const [hi, lo] = la > lb ? [la, lb] : [lb, la];
|
|
575
|
+
return (hi + 0.05) / (lo + 0.05);
|
|
576
|
+
}
|
|
577
|
+
function accentBandContext(brand) {
|
|
578
|
+
const p = brand.palette;
|
|
579
|
+
const lightWins = (hexContrast(p.light, p.primary) ?? 99) >= (hexContrast(p.dark, p.primary) ?? 0);
|
|
580
|
+
if (lightWins) {
|
|
581
|
+
return {
|
|
582
|
+
brand: { ...brand, palette: { dark: p.light, primary: p.light, accent: p.light, light: p.primary } },
|
|
583
|
+
buttonLabel: p.primary
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
return {
|
|
587
|
+
brand: { ...brand, palette: { dark: p.dark, primary: p.dark, accent: p.dark, light: p.light } },
|
|
588
|
+
buttonLabel: p.light
|
|
589
|
+
};
|
|
590
|
+
}
|
|
223
591
|
function textAttrs(ctx, path) {
|
|
224
592
|
return ctx.keyFor ? { "data-ohw-key": ctx.keyFor(path), "data-ohw-editable": "text" } : {};
|
|
225
593
|
}
|
|
@@ -231,7 +599,12 @@ var AI_RESPONSIVE_CSS = [
|
|
|
231
599
|
"@media (max-width: 640px) {",
|
|
232
600
|
" [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
|
|
233
601
|
" [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
|
|
602
|
+
// Group containers flatten to a column on phones; span placements come along for free.
|
|
603
|
+
" [data-ai-responsive] [data-ai-group] { display: flex !important; flex-direction: column !important; }",
|
|
604
|
+
" [data-ai-responsive] [data-ai-group] > * { grid-column: auto !important; }",
|
|
234
605
|
" [data-ai-responsive] [data-ai-section-inner] { padding-left: 20px !important; padding-right: 20px !important; }",
|
|
606
|
+
" [data-ai-responsive] { overflow-x: hidden; }",
|
|
607
|
+
" [data-ai-responsive] img { max-width: 100%; }",
|
|
235
608
|
"}"
|
|
236
609
|
].join("\n");
|
|
237
610
|
var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
|
|
@@ -309,7 +682,7 @@ function ButtonEl({
|
|
|
309
682
|
}) {
|
|
310
683
|
const secondary = slots.variant === "secondary";
|
|
311
684
|
const href = str(slots.href);
|
|
312
|
-
const linkAttrs = ctx.keyFor && path ? { "data-ohw-href-key": ctx.keyFor(`${path}.href`) } : {};
|
|
685
|
+
const linkAttrs = ctx.keyFor && path ? { "data-ohw-href-key": ctx.keyFor(`${path}.href`), "data-ohw-role": "button" } : {};
|
|
313
686
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
314
687
|
"a",
|
|
315
688
|
{
|
|
@@ -325,7 +698,7 @@ function ButtonEl({
|
|
|
325
698
|
textDecoration: "none",
|
|
326
699
|
cursor: "pointer",
|
|
327
700
|
...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 }
|
|
701
|
+
...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
702
|
},
|
|
330
703
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ...path ? textAttrs(ctx, `${path}.label`) : {}, children: str(slots.label) })
|
|
331
704
|
}
|
|
@@ -831,7 +1204,24 @@ function CardBlock({ node, ctx, path }) {
|
|
|
831
1204
|
minWidth: 0
|
|
832
1205
|
},
|
|
833
1206
|
children: [
|
|
834
|
-
media && (horizontal ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1207
|
+
media && (horizontal ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1208
|
+
"div",
|
|
1209
|
+
{
|
|
1210
|
+
style: (
|
|
1211
|
+
// An icon hugs its glyph — flex:1 gave a 48px icon half the card and pushed the
|
|
1212
|
+
// text to the far side. Photos keep the half-and-half split. The inset has no
|
|
1213
|
+
// inner padding (the photo split absorbed that), so the icon carries its own gap.
|
|
1214
|
+
/^(lucide|simple):/.test(mediaRef) ? {
|
|
1215
|
+
flexShrink: 0,
|
|
1216
|
+
display: "flex",
|
|
1217
|
+
alignItems: "center",
|
|
1218
|
+
padding: mediaInset,
|
|
1219
|
+
[mediaPosition === "right" ? "marginLeft" : "marginRight"]: 20
|
|
1220
|
+
} : { flex: 1, minWidth: 0, display: "flex", alignItems: "center", padding: mediaInset }
|
|
1221
|
+
),
|
|
1222
|
+
children: media
|
|
1223
|
+
}
|
|
1224
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
835
1225
|
"div",
|
|
836
1226
|
{
|
|
837
1227
|
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 +1312,44 @@ function AccordionBlock({ node, ctx, path }) {
|
|
|
922
1312
|
) })
|
|
923
1313
|
] }, i)) });
|
|
924
1314
|
}
|
|
1315
|
+
function useIsMobile() {
|
|
1316
|
+
const [mobile, setMobile] = import_react.default.useState(
|
|
1317
|
+
() => typeof window !== "undefined" && window.matchMedia("(max-width: 768px)").matches
|
|
1318
|
+
);
|
|
1319
|
+
import_react.default.useEffect(() => {
|
|
1320
|
+
const mq = window.matchMedia("(max-width: 768px)");
|
|
1321
|
+
const update = () => setMobile(mq.matches);
|
|
1322
|
+
update();
|
|
1323
|
+
mq.addEventListener("change", update);
|
|
1324
|
+
return () => mq.removeEventListener("change", update);
|
|
1325
|
+
}, []);
|
|
1326
|
+
return mobile;
|
|
1327
|
+
}
|
|
925
1328
|
function Carousel({ items, itemsPerRow, ctx }) {
|
|
1329
|
+
const isMobile = useIsMobile();
|
|
1330
|
+
const perPage = isMobile ? 1 : itemsPerRow;
|
|
1331
|
+
const pages = Math.max(1, Math.ceil(items.length / perPage));
|
|
926
1332
|
const [page, setPage] = import_react.default.useState(0);
|
|
927
|
-
const pages = Math.max(1, Math.ceil(items.length / itemsPerRow));
|
|
928
1333
|
const current = Math.min(page, pages - 1);
|
|
1334
|
+
if (pages <= 1) {
|
|
1335
|
+
const cols = Math.max(1, Math.min(items.length, itemsPerRow));
|
|
1336
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1337
|
+
"div",
|
|
1338
|
+
{
|
|
1339
|
+
"data-ai-grid": String(cols),
|
|
1340
|
+
style: {
|
|
1341
|
+
display: "grid",
|
|
1342
|
+
gridTemplateColumns: `repeat(${cols}, 1fr)`,
|
|
1343
|
+
gap: AI_TREE_TOKENS.spacing8,
|
|
1344
|
+
alignItems: "start"
|
|
1345
|
+
},
|
|
1346
|
+
children: items
|
|
1347
|
+
}
|
|
1348
|
+
);
|
|
1349
|
+
}
|
|
929
1350
|
const pageGroups = Array.from(
|
|
930
1351
|
{ length: pages },
|
|
931
|
-
(_, p) => items.slice(p *
|
|
1352
|
+
(_, p) => items.slice(p * perPage, (p + 1) * perPage)
|
|
932
1353
|
);
|
|
933
1354
|
const chrome = (enabled) => ({
|
|
934
1355
|
border: `1px solid ${ctx.brand.palette.dark}`,
|
|
@@ -953,55 +1374,69 @@ function Carousel({ items, itemsPerRow, ctx }) {
|
|
|
953
1374
|
cursor: "pointer",
|
|
954
1375
|
padding: 0
|
|
955
1376
|
});
|
|
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)(
|
|
1377
|
+
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)(
|
|
1378
|
+
"div",
|
|
1379
|
+
{
|
|
1380
|
+
style: {
|
|
1381
|
+
display: "flex",
|
|
1382
|
+
transform: `translateX(-${current * 100}%)`,
|
|
1383
|
+
transition: "transform 0.4s ease"
|
|
1384
|
+
},
|
|
1385
|
+
children: pageGroups.map((group, p) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
969
1386
|
"div",
|
|
970
1387
|
{
|
|
1388
|
+
"data-ai-grid": String(perPage),
|
|
971
1389
|
style: {
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
1390
|
+
flex: "0 0 100%",
|
|
1391
|
+
display: "grid",
|
|
1392
|
+
gridTemplateColumns: `repeat(${perPage}, 1fr)`,
|
|
1393
|
+
gap: AI_TREE_TOKENS.spacing8,
|
|
1394
|
+
alignItems: "start"
|
|
975
1395
|
},
|
|
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
|
-
|
|
1396
|
+
children: group
|
|
1397
|
+
},
|
|
1398
|
+
p
|
|
1399
|
+
))
|
|
1400
|
+
}
|
|
1401
|
+
) });
|
|
1402
|
+
const prevBtn = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1403
|
+
"button",
|
|
1404
|
+
{
|
|
1405
|
+
type: "button",
|
|
1406
|
+
"aria-label": "Previous",
|
|
1407
|
+
onClick: () => setPage((p) => Math.max(0, p - 1)),
|
|
1408
|
+
style: chrome(current > 0),
|
|
1409
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ArrowLeft, { size: 24, strokeWidth: 1.5, "aria-hidden": true })
|
|
1410
|
+
}
|
|
1411
|
+
);
|
|
1412
|
+
const nextBtn = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1413
|
+
"button",
|
|
1414
|
+
{
|
|
1415
|
+
type: "button",
|
|
1416
|
+
"aria-label": "Next",
|
|
1417
|
+
onClick: () => setPage((p) => Math.min(pages - 1, p + 1)),
|
|
1418
|
+
style: chrome(current < pages - 1),
|
|
1419
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ArrowRight, { size: 24, strokeWidth: 1.5, "aria-hidden": true })
|
|
1420
|
+
}
|
|
1421
|
+
);
|
|
1422
|
+
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)) });
|
|
1423
|
+
if (isMobile) {
|
|
1424
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { "data-ai-carousel": "", style: { display: "flex", flexDirection: "column", gap: AI_TREE_TOKENS.spacing6, alignItems: "center" }, children: [
|
|
1425
|
+
viewport,
|
|
1426
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", gap: AI_TREE_TOKENS.spacing6, justifyContent: "center" }, children: [
|
|
1427
|
+
prevBtn,
|
|
1428
|
+
nextBtn
|
|
1429
|
+
] }),
|
|
1430
|
+
dots
|
|
1431
|
+
] });
|
|
1432
|
+
}
|
|
1433
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { "data-ai-carousel": "", style: { display: "flex", flexDirection: "column", gap: AI_TREE_TOKENS.spacing8, alignItems: "center" }, children: [
|
|
1434
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: AI_TREE_TOKENS.spacing6, width: "100%" }, children: [
|
|
1435
|
+
prevBtn,
|
|
1436
|
+
viewport,
|
|
1437
|
+
nextBtn
|
|
1003
1438
|
] }),
|
|
1004
|
-
|
|
1439
|
+
dots
|
|
1005
1440
|
] });
|
|
1006
1441
|
}
|
|
1007
1442
|
function CollectionBlock({ node, ctx, path }) {
|
|
@@ -1095,6 +1530,49 @@ function renderNode(node, ctx, path) {
|
|
|
1095
1530
|
switch (node.type) {
|
|
1096
1531
|
case "text":
|
|
1097
1532
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TextBlock, { slots, ctx, path });
|
|
1533
|
+
// Layout container: arranges child blocks, contributes no content of its own. `grid` is a
|
|
1534
|
+
// nested 12-column grid the children span (a collage is 3–5 media on it, bottom-aligned so
|
|
1535
|
+
// mixed aspects read as a composition); `split` is exactly two children at a ratio; `stack`
|
|
1536
|
+
// is a column. Children render through this same dispatcher, so edit markers, media
|
|
1537
|
+
// resolution, and copy paths all work unchanged inside a group.
|
|
1538
|
+
case "group": {
|
|
1539
|
+
const layout = str(slots.layout);
|
|
1540
|
+
const gap = slots.spacing === "tight" ? AI_TREE_TOKENS.spacing3 : slots.spacing === "airy" ? AI_TREE_TOKENS.spacing8 : AI_TREE_TOKENS.spacing6;
|
|
1541
|
+
const kids = (node.children ?? []).map((child, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1542
|
+
"div",
|
|
1543
|
+
{
|
|
1544
|
+
style: layout === "grid" ? {
|
|
1545
|
+
gridColumn: `span ${typeof child.span === "number" ? Math.min(12, Math.max(1, child.span)) : 12}`,
|
|
1546
|
+
minWidth: 0
|
|
1547
|
+
} : { minWidth: 0 },
|
|
1548
|
+
children: renderNode(child, ctx, `${path}.c${i}`)
|
|
1549
|
+
},
|
|
1550
|
+
i
|
|
1551
|
+
));
|
|
1552
|
+
if (layout === "grid") {
|
|
1553
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1554
|
+
"div",
|
|
1555
|
+
{
|
|
1556
|
+
"data-ai-group": "grid",
|
|
1557
|
+
style: { display: "grid", gridTemplateColumns: "repeat(12, 1fr)", gap, alignItems: "end" },
|
|
1558
|
+
children: kids
|
|
1559
|
+
}
|
|
1560
|
+
);
|
|
1561
|
+
}
|
|
1562
|
+
if (layout === "split") {
|
|
1563
|
+
const ratio = str(slots.ratio);
|
|
1564
|
+
const cols = ratio === "3:5" ? "3fr 5fr" : ratio === "5:3" ? "5fr 3fr" : "1fr 1fr";
|
|
1565
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1566
|
+
"div",
|
|
1567
|
+
{
|
|
1568
|
+
"data-ai-group": "split",
|
|
1569
|
+
style: { display: "grid", gridTemplateColumns: cols, gap, alignItems: "center" },
|
|
1570
|
+
children: kids
|
|
1571
|
+
}
|
|
1572
|
+
);
|
|
1573
|
+
}
|
|
1574
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { "data-ai-group": "stack", style: { display: "flex", flexDirection: "column", gap }, children: kids });
|
|
1575
|
+
}
|
|
1098
1576
|
case "button":
|
|
1099
1577
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ButtonEl, { slots, ctx, path });
|
|
1100
1578
|
case "button-row":
|
|
@@ -1175,33 +1653,111 @@ function renderNode(node, ctx, path) {
|
|
|
1175
1653
|
}
|
|
1176
1654
|
);
|
|
1177
1655
|
}
|
|
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
|
-
|
|
1656
|
+
case "form": {
|
|
1657
|
+
const formAttrs = ctx.keyFor ? {
|
|
1658
|
+
"data-ohw-editable": "form",
|
|
1659
|
+
"data-ohw-key": ctx.keyFor(`${path}.form`),
|
|
1660
|
+
"data-ohw-success-text": "Thanks \u2014 we'll be in touch shortly."
|
|
1661
|
+
} : {};
|
|
1662
|
+
const fieldStyle = {
|
|
1663
|
+
width: "100%",
|
|
1664
|
+
boxSizing: "border-box",
|
|
1665
|
+
border: `1px solid color-mix(in srgb, ${ctx.brand.palette.dark} 45%, #ffffff)`,
|
|
1666
|
+
borderRadius: 0,
|
|
1667
|
+
padding: 12,
|
|
1668
|
+
background: "#fff",
|
|
1669
|
+
color: ctx.brand.palette.dark,
|
|
1670
|
+
outline: "none",
|
|
1671
|
+
...typeStyle(AI_TREE_TOKENS.type.bodyM, ctx.brand.fonts.body)
|
|
1672
|
+
};
|
|
1673
|
+
const labelStyle = {
|
|
1674
|
+
...typeStyle(AI_TREE_TOKENS.type.bodyMBold, ctx.brand.fonts.body),
|
|
1675
|
+
color: ctx.brand.palette.dark,
|
|
1676
|
+
textAlign: "left",
|
|
1677
|
+
width: "100%"
|
|
1678
|
+
};
|
|
1679
|
+
const centered = ctx.sectionAlignment === "center";
|
|
1680
|
+
const submitAlign = centered ? "center" : "flex-start";
|
|
1681
|
+
const children = node.children ?? [];
|
|
1682
|
+
return (
|
|
1683
|
+
// 32px between the field group and the submit. In a stacked (centered) section the form is
|
|
1684
|
+
// capped at 780px and centered — the section's 12-col grid would otherwise leave it hugging
|
|
1685
|
+
// the left edge; a split section lets it fill its own column.
|
|
1686
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1687
|
+
"form",
|
|
1688
|
+
{
|
|
1689
|
+
...formAttrs,
|
|
1690
|
+
"data-ai-form": "",
|
|
1691
|
+
style: {
|
|
1692
|
+
display: "flex",
|
|
1693
|
+
flexDirection: "column",
|
|
1694
|
+
gap: 32,
|
|
1695
|
+
width: "100%",
|
|
1696
|
+
...centered ? { maxWidth: 780, marginLeft: "auto", marginRight: "auto" } : {}
|
|
1697
|
+
},
|
|
1698
|
+
children: [
|
|
1699
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: 24, width: "100%", alignItems: "flex-start" }, children: children.map((child, i) => {
|
|
1700
|
+
if (child.type !== "input") return null;
|
|
1701
|
+
const cs = child.slots ?? {};
|
|
1702
|
+
const kind = str(cs.kind);
|
|
1703
|
+
const label = str(cs.label);
|
|
1704
|
+
const placeholder = str(cs.placeholder);
|
|
1705
|
+
const required = cs.required === true;
|
|
1706
|
+
const name = label.toLowerCase().replace(/[^a-z0-9]+/gu, "-").replace(/^-+|-+$/gu, "") || `field-${i}`;
|
|
1707
|
+
const isTextarea = kind === "textarea";
|
|
1708
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 8, width: "100%" }, children: [
|
|
1709
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { ...textAttrs(ctx, `${path}.c${i}.label`), style: labelStyle, children: label }),
|
|
1710
|
+
isTextarea ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1711
|
+
"textarea",
|
|
1712
|
+
{
|
|
1713
|
+
name,
|
|
1714
|
+
placeholder,
|
|
1715
|
+
required,
|
|
1716
|
+
style: { ...fieldStyle, height: 180, resize: "vertical" }
|
|
1717
|
+
}
|
|
1718
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1719
|
+
"input",
|
|
1720
|
+
{
|
|
1721
|
+
name,
|
|
1722
|
+
type: kind === "email" ? "email" : "text",
|
|
1723
|
+
placeholder,
|
|
1724
|
+
required,
|
|
1725
|
+
style: { ...fieldStyle, height: 48 }
|
|
1726
|
+
}
|
|
1727
|
+
)
|
|
1728
|
+
] }, i);
|
|
1729
|
+
}) }),
|
|
1730
|
+
children.map((child, i) => {
|
|
1731
|
+
if (child.type === "input") return null;
|
|
1732
|
+
const cs = child.slots ?? {};
|
|
1733
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1734
|
+
"button",
|
|
1735
|
+
{
|
|
1736
|
+
type: "submit",
|
|
1737
|
+
style: {
|
|
1738
|
+
alignSelf: submitAlign,
|
|
1739
|
+
border: "none",
|
|
1740
|
+
cursor: "pointer",
|
|
1741
|
+
padding: "12px 24px",
|
|
1742
|
+
// Corner radius follows the host template's own buttons (measured from a template
|
|
1743
|
+
// CTA); 8px only when the page has no template button to match.
|
|
1744
|
+
borderRadius: ctx.buttonRadius ?? 8,
|
|
1745
|
+
// Brand-styled: primary fill, brand-derived label colour (not a fixed token) so it
|
|
1746
|
+
// reads correctly on custom palettes.
|
|
1747
|
+
background: ctx.brand.palette.primary,
|
|
1748
|
+
color: ctx.buttonLabel ?? ctx.brand.palette.light,
|
|
1749
|
+
...typeStyle(AI_TREE_TOKENS.type.button, ctx.brand.fonts.body)
|
|
1750
|
+
},
|
|
1751
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ...textAttrs(ctx, `${path}.c${i}.label`), children: str(cs.label) })
|
|
1752
|
+
},
|
|
1753
|
+
i
|
|
1754
|
+
);
|
|
1755
|
+
})
|
|
1756
|
+
]
|
|
1757
|
+
}
|
|
1758
|
+
)
|
|
1759
|
+
);
|
|
1760
|
+
}
|
|
1205
1761
|
case "schedule-widget":
|
|
1206
1762
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1207
1763
|
"div",
|
|
@@ -1222,16 +1778,27 @@ function renderNode(node, ctx, path) {
|
|
|
1222
1778
|
return null;
|
|
1223
1779
|
}
|
|
1224
1780
|
}
|
|
1225
|
-
function AiTreeRenderer({
|
|
1781
|
+
function AiTreeRenderer({
|
|
1782
|
+
tree,
|
|
1783
|
+
brand,
|
|
1784
|
+
buttonRadius,
|
|
1785
|
+
resolveMedia,
|
|
1786
|
+
editKeyPrefix
|
|
1787
|
+
}) {
|
|
1226
1788
|
if (!isRenderableTree(tree)) {
|
|
1227
1789
|
return null;
|
|
1228
1790
|
}
|
|
1229
1791
|
const resolvedBrand = brand ?? AI_DEFAULT_BRAND;
|
|
1792
|
+
const band = (tree.settings ?? {}).sectionBackground === "accent" ? accentBandContext(resolvedBrand) : null;
|
|
1793
|
+
const blockBrand = band?.brand ?? resolvedBrand;
|
|
1230
1794
|
const ctx = {
|
|
1231
|
-
brand:
|
|
1795
|
+
brand: blockBrand,
|
|
1232
1796
|
resolveMedia: resolveMedia ?? (() => null),
|
|
1233
|
-
cardSurface:
|
|
1234
|
-
keyFor: editKeyPrefix ? (path) => `${editKeyPrefix}.${path}` : null
|
|
1797
|
+
cardSurface: blockBrand.palette.light.toUpperCase() === AI_DEFAULT_BRAND.palette.light.toUpperCase() ? "#ECECEB" : `color-mix(in srgb, ${blockBrand.palette.light} 90%, ${blockBrand.palette.dark})`,
|
|
1798
|
+
keyFor: editKeyPrefix ? (path) => `${editKeyPrefix}.${path}` : null,
|
|
1799
|
+
sectionAlignment: (tree.settings ?? {}).alignment === "center" ? "center" : "left",
|
|
1800
|
+
buttonRadius,
|
|
1801
|
+
...band ? { buttonLabel: band.buttonLabel } : {}
|
|
1235
1802
|
};
|
|
1236
1803
|
const settings = tree.settings ?? {};
|
|
1237
1804
|
const pad = AI_TREE_TOKENS.sectionPadding[settings.spacing ?? "balanced"];
|
|
@@ -1239,6 +1806,20 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
|
|
|
1239
1806
|
const overlayAlpha = { low: 0.25, medium: 0.45, high: 0.65 }[settings.overlayIntensity ?? "medium"];
|
|
1240
1807
|
const backgroundUrl = isOverlay && settings.backgroundMedia ? ctx.resolveMedia(settings.backgroundMedia) : null;
|
|
1241
1808
|
const bgAttrs = ctx.keyFor && isOverlay && settings.backgroundMedia ? { "data-ohw-key": ctx.keyFor("settings.backgroundMedia"), "data-ohw-editable": "bg-image" } : {};
|
|
1809
|
+
const toneBackground = (() => {
|
|
1810
|
+
const { dark, primary, light } = resolvedBrand.palette;
|
|
1811
|
+
switch (settings.sectionBackground) {
|
|
1812
|
+
case "surface":
|
|
1813
|
+
return `color-mix(in srgb, ${light} 94%, ${dark})`;
|
|
1814
|
+
case "accent":
|
|
1815
|
+
return primary;
|
|
1816
|
+
case "accent-soft":
|
|
1817
|
+
return `color-mix(in srgb, ${primary} 12%, ${light})`;
|
|
1818
|
+
default:
|
|
1819
|
+
return void 0;
|
|
1820
|
+
}
|
|
1821
|
+
})();
|
|
1822
|
+
const distributed = !isOverlay && settings.textDistribution;
|
|
1242
1823
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1243
1824
|
"section",
|
|
1244
1825
|
{
|
|
@@ -1248,13 +1829,15 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
|
|
|
1248
1829
|
style: {
|
|
1249
1830
|
position: "relative",
|
|
1250
1831
|
padding: `${pad}px 0`,
|
|
1251
|
-
background: isOverlay && !backgroundUrl ? "#DBEAFE" : void 0,
|
|
1832
|
+
background: toneBackground ?? (isOverlay && !backgroundUrl ? "#DBEAFE" : void 0),
|
|
1252
1833
|
backgroundImage: backgroundUrl ? `url(${backgroundUrl})` : void 0,
|
|
1253
1834
|
backgroundSize: "cover",
|
|
1254
|
-
backgroundPosition: "center"
|
|
1835
|
+
backgroundPosition: "center",
|
|
1836
|
+
color: settings.sectionBackground === "accent" ? blockBrand.palette.dark : void 0
|
|
1255
1837
|
},
|
|
1256
1838
|
children: [
|
|
1257
1839
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_RESPONSIVE_CSS }),
|
|
1840
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_MOBILE_CSS }),
|
|
1258
1841
|
isOverlay && backgroundUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
|
|
1259
1842
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1260
1843
|
"div",
|
|
@@ -1275,10 +1858,24 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
|
|
|
1275
1858
|
display: "grid",
|
|
1276
1859
|
gridTemplateColumns: "repeat(12, 1fr)",
|
|
1277
1860
|
gap: AI_TREE_TOKENS.spacing6,
|
|
1278
|
-
alignItems: settings.verticalPosition === "top" ? "start" : "center",
|
|
1861
|
+
alignItems: distributed === "space-between" ? "stretch" : (distributed ?? settings.verticalPosition) === "top" ? "start" : "center",
|
|
1279
1862
|
marginTop: r2 > 0 ? AI_TREE_TOKENS.spacing8 : 0
|
|
1280
1863
|
},
|
|
1281
|
-
children: (row.blocks ?? []).map((block, b) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1864
|
+
children: (row.blocks ?? []).map((block, b) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1865
|
+
"div",
|
|
1866
|
+
{
|
|
1867
|
+
"data-ai-cell": "",
|
|
1868
|
+
style: {
|
|
1869
|
+
gridColumn: `span ${Math.min(12, Math.max(1, block.span))}`,
|
|
1870
|
+
minWidth: 0,
|
|
1871
|
+
// space-between: each column becomes a flex column whose content spreads over
|
|
1872
|
+
// the full row height instead of clumping at the top.
|
|
1873
|
+
...distributed === "space-between" ? { display: "flex", flexDirection: "column", justifyContent: "space-between" } : {}
|
|
1874
|
+
},
|
|
1875
|
+
children: renderNode(block, ctx, `r${r2}.b${b}`)
|
|
1876
|
+
},
|
|
1877
|
+
b
|
|
1878
|
+
))
|
|
1282
1879
|
},
|
|
1283
1880
|
r2
|
|
1284
1881
|
))
|
|
@@ -1294,17 +1891,36 @@ var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
|
1294
1891
|
var CONTAINER_ATTR = "data-ohw-ai-generated";
|
|
1295
1892
|
var REPLACED_ATTR = "data-ohw-ai-replaced-by";
|
|
1296
1893
|
var REMOVED_ATTR = "data-ohw-ai-removed";
|
|
1894
|
+
var TEMPLATE_HIDDEN_ATTR = "data-ohw-ai-template-hidden";
|
|
1895
|
+
var CHROME_SECTION_IDS = /* @__PURE__ */ new Set(["navbar", "footer"]);
|
|
1896
|
+
function readRootVar(name) {
|
|
1897
|
+
if (typeof document === "undefined") return "";
|
|
1898
|
+
return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
|
|
1899
|
+
}
|
|
1900
|
+
function deriveBrandOverride() {
|
|
1901
|
+
const dark = readRootVar("--ohw-brand-dark");
|
|
1902
|
+
const primary = readRootVar("--ohw-brand-primary");
|
|
1903
|
+
const light = readRootVar("--ohw-brand-light");
|
|
1904
|
+
if (!dark || !primary || !light) return null;
|
|
1905
|
+
const accent = readRootVar("--ohw-brand-accent");
|
|
1906
|
+
const heading = readRootVar("--font-heading") || readRootVar("--font-display");
|
|
1907
|
+
const body = readRootVar("--font-body");
|
|
1908
|
+
return {
|
|
1909
|
+
palette: { dark, primary, accent: accent || dark, light },
|
|
1910
|
+
fonts: {
|
|
1911
|
+
heading: heading || AI_DEFAULT_BRAND.fonts.heading,
|
|
1912
|
+
body: body || AI_DEFAULT_BRAND.fonts.body
|
|
1913
|
+
}
|
|
1914
|
+
};
|
|
1915
|
+
}
|
|
1297
1916
|
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");
|
|
1917
|
+
const dark = readRootVar("--color-dark");
|
|
1918
|
+
const primary = readRootVar("--color-primary");
|
|
1919
|
+
const light = readRootVar("--color-light");
|
|
1304
1920
|
if (!dark || !primary || !light) return null;
|
|
1305
|
-
const accent =
|
|
1306
|
-
const heading =
|
|
1307
|
-
const body =
|
|
1921
|
+
const accent = readRootVar("--color-accent");
|
|
1922
|
+
const heading = readRootVar("--font-heading") || readRootVar("--font-display");
|
|
1923
|
+
const body = readRootVar("--font-body");
|
|
1308
1924
|
return {
|
|
1309
1925
|
palette: { dark, primary, accent: accent || dark, light },
|
|
1310
1926
|
fonts: {
|
|
@@ -1313,6 +1929,13 @@ function deriveTemplateBrand() {
|
|
|
1313
1929
|
}
|
|
1314
1930
|
};
|
|
1315
1931
|
}
|
|
1932
|
+
function deriveTemplateButtonRadius() {
|
|
1933
|
+
if (typeof document === "undefined") return null;
|
|
1934
|
+
const btn = document.querySelector('[data-ohw-role="button"]');
|
|
1935
|
+
if (!btn) return null;
|
|
1936
|
+
const radius = getComputedStyle(btn).borderTopLeftRadius;
|
|
1937
|
+
return radius || null;
|
|
1938
|
+
}
|
|
1316
1939
|
var mounted = /* @__PURE__ */ new Map();
|
|
1317
1940
|
function findTemplateSection(id) {
|
|
1318
1941
|
for (const el of document.querySelectorAll(`[data-ohw-section="${CSS.escape(id)}"]`)) {
|
|
@@ -1376,6 +1999,24 @@ function syncRemovedSections(state) {
|
|
|
1376
1999
|
}
|
|
1377
2000
|
}
|
|
1378
2001
|
}
|
|
2002
|
+
function syncTemplateHidden(state, pageHasSections) {
|
|
2003
|
+
const hide = state.hideTemplate === true && pageHasSections;
|
|
2004
|
+
for (const el of Array.from(document.querySelectorAll(`[${TEMPLATE_HIDDEN_ATTR}]`))) {
|
|
2005
|
+
if (!hide) {
|
|
2006
|
+
el.style.removeProperty("display");
|
|
2007
|
+
el.removeAttribute(TEMPLATE_HIDDEN_ATTR);
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
if (!hide) return;
|
|
2011
|
+
for (const el of Array.from(document.querySelectorAll("[data-ohw-section]"))) {
|
|
2012
|
+
if (el.hasAttribute(CONTAINER_ATTR)) continue;
|
|
2013
|
+
if (CHROME_SECTION_IDS.has(el.getAttribute("data-ohw-section") ?? "")) continue;
|
|
2014
|
+
if (el.parentElement?.closest("[data-ohw-section]")) continue;
|
|
2015
|
+
if (el.hasAttribute(REPLACED_ATTR) || el.hasAttribute(REMOVED_ATTR)) continue;
|
|
2016
|
+
el.style.display = "none";
|
|
2017
|
+
el.setAttribute(TEMPLATE_HIDDEN_ATTR, "");
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
1379
2020
|
function syncReplacedOriginals(state) {
|
|
1380
2021
|
for (const el of document.querySelectorAll(`[${REPLACED_ATTR}]`)) {
|
|
1381
2022
|
const byId = el.getAttribute(REPLACED_ATTR) ?? "";
|
|
@@ -1394,10 +2035,64 @@ function syncReplacedOriginals(state) {
|
|
|
1394
2035
|
}
|
|
1395
2036
|
}
|
|
1396
2037
|
}
|
|
2038
|
+
var sectionOrderIndex = /* @__PURE__ */ new Map();
|
|
2039
|
+
function setAiSectionOrder(raw, currentPath) {
|
|
2040
|
+
const next = /* @__PURE__ */ new Map();
|
|
2041
|
+
if (raw) {
|
|
2042
|
+
try {
|
|
2043
|
+
const entries = JSON.parse(raw);
|
|
2044
|
+
for (const entry of entries) {
|
|
2045
|
+
if (!entry.pagePath || entry.pagePath === currentPath) next.set(entry.instanceId, entry.order);
|
|
2046
|
+
}
|
|
2047
|
+
} catch {
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
sectionOrderIndex = next;
|
|
2051
|
+
}
|
|
2052
|
+
function applyExplicitOrder(entries) {
|
|
2053
|
+
if (sectionOrderIndex.size === 0) return entries;
|
|
2054
|
+
return entries.map((entry, index) => ({ entry, index, order: sectionOrderIndex.get(entry.id) })).sort((a, b) => {
|
|
2055
|
+
if (a.order === void 0 && b.order === void 0) return a.index - b.index;
|
|
2056
|
+
if (a.order === void 0) return 1;
|
|
2057
|
+
if (b.order === void 0) return -1;
|
|
2058
|
+
return a.order - b.order;
|
|
2059
|
+
}).map((item) => item.entry);
|
|
2060
|
+
}
|
|
2061
|
+
function orderByChain(sections) {
|
|
2062
|
+
const ids = new Set(sections.map((entry) => entry.id));
|
|
2063
|
+
const after = /* @__PURE__ */ new Map();
|
|
2064
|
+
const roots = [];
|
|
2065
|
+
for (const entry of sections) {
|
|
2066
|
+
const anchor = entry.replaces ?? entry.beforeSection ?? entry.afterSection ?? null;
|
|
2067
|
+
if (anchor && ids.has(anchor)) {
|
|
2068
|
+
const bucket = after.get(anchor);
|
|
2069
|
+
if (bucket) bucket.push(entry);
|
|
2070
|
+
else after.set(anchor, [entry]);
|
|
2071
|
+
} else {
|
|
2072
|
+
roots.push(entry);
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
const out = [];
|
|
2076
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2077
|
+
const visit = (entry) => {
|
|
2078
|
+
if (seen.has(entry.id)) return;
|
|
2079
|
+
seen.add(entry.id);
|
|
2080
|
+
out.push(entry);
|
|
2081
|
+
for (const child of after.get(entry.id) ?? []) visit(child);
|
|
2082
|
+
};
|
|
2083
|
+
for (const root of roots) visit(root);
|
|
2084
|
+
return out.length === sections.length ? out : sections;
|
|
2085
|
+
}
|
|
1397
2086
|
function applyAiSectionsToDom(state, options) {
|
|
1398
2087
|
if (typeof document === "undefined") return;
|
|
2088
|
+
const brandOverride = deriveBrandOverride();
|
|
1399
2089
|
const templateBrand = deriveTemplateBrand();
|
|
1400
|
-
const
|
|
2090
|
+
const templateButtonRadius = deriveTemplateButtonRadius();
|
|
2091
|
+
const brandKey = brandOverride ? JSON.stringify(brandOverride) : "";
|
|
2092
|
+
const pagePath = window.location.pathname;
|
|
2093
|
+
const pageSections = state.sections.filter((entry) => (entry.path ?? "/") === pagePath);
|
|
2094
|
+
const activeIds = new Set(pageSections.map((entry) => entry.id));
|
|
2095
|
+
const ordered = state.hideTemplate === true ? applyExplicitOrder(orderByChain(pageSections)) : pageSections;
|
|
1401
2096
|
for (const [id, section] of mounted) {
|
|
1402
2097
|
if (!activeIds.has(id)) {
|
|
1403
2098
|
section.root.unmount();
|
|
@@ -1405,8 +2100,8 @@ function applyAiSectionsToDom(state, options) {
|
|
|
1405
2100
|
mounted.delete(id);
|
|
1406
2101
|
}
|
|
1407
2102
|
}
|
|
1408
|
-
for (const entry of
|
|
1409
|
-
const serialized = JSON.stringify(entry);
|
|
2103
|
+
for (const entry of ordered) {
|
|
2104
|
+
const serialized = JSON.stringify(entry) + brandKey;
|
|
1410
2105
|
const existing = mounted.get(entry.id);
|
|
1411
2106
|
if (existing && existing.serialized === serialized && existing.container.isConnected) {
|
|
1412
2107
|
continue;
|
|
@@ -1431,7 +2126,8 @@ function applyAiSectionsToDom(state, options) {
|
|
|
1431
2126
|
AiTreeRenderer,
|
|
1432
2127
|
{
|
|
1433
2128
|
tree: entry.tree,
|
|
1434
|
-
brand: entry.brand ?? options?.brand ?? templateBrand ?? void 0,
|
|
2129
|
+
brand: brandOverride ?? entry.brand ?? options?.brand ?? templateBrand ?? void 0,
|
|
2130
|
+
buttonRadius: templateButtonRadius,
|
|
1435
2131
|
resolveMedia,
|
|
1436
2132
|
editKeyPrefix: `ai.${entry.id}`
|
|
1437
2133
|
}
|
|
@@ -1440,8 +2136,20 @@ function applyAiSectionsToDom(state, options) {
|
|
|
1440
2136
|
});
|
|
1441
2137
|
mounted.set(entry.id, { root, container, serialized });
|
|
1442
2138
|
}
|
|
2139
|
+
if (state.hideTemplate === true) {
|
|
2140
|
+
let prev = null;
|
|
2141
|
+
for (const entry of ordered) {
|
|
2142
|
+
const el = mounted.get(entry.id)?.container;
|
|
2143
|
+
if (!el) continue;
|
|
2144
|
+
if (prev && !(prev.compareDocumentPosition(el) & Node.DOCUMENT_POSITION_FOLLOWING)) {
|
|
2145
|
+
prev.insertAdjacentElement("afterend", el);
|
|
2146
|
+
}
|
|
2147
|
+
prev = el;
|
|
2148
|
+
}
|
|
2149
|
+
}
|
|
1443
2150
|
syncReplacedOriginals(state);
|
|
1444
2151
|
syncRemovedSections(state);
|
|
2152
|
+
syncTemplateHidden(state, pageSections.length > 0);
|
|
1445
2153
|
}
|
|
1446
2154
|
|
|
1447
2155
|
// src/useLinkHrefGuardian.ts
|
|
@@ -1568,6 +2276,7 @@ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
|
|
|
1568
2276
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1569
2277
|
import_radix_ui.Dialog.Overlay,
|
|
1570
2278
|
{
|
|
2279
|
+
"data-ohw-scheduling-modal": "",
|
|
1571
2280
|
className: "fixed inset-0 z-50",
|
|
1572
2281
|
style: { background: "rgba(0,0,0,0.45)" }
|
|
1573
2282
|
}
|
|
@@ -1575,6 +2284,7 @@ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
|
|
|
1575
2284
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1576
2285
|
import_radix_ui.Dialog.Content,
|
|
1577
2286
|
{
|
|
2287
|
+
"data-ohw-scheduling-modal": "",
|
|
1578
2288
|
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
2289
|
style: { maxWidth: 400 },
|
|
1580
2290
|
children: [
|
|
@@ -2048,7 +2758,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
|
|
|
2048
2758
|
const autoId = (0, import_react5.useId)();
|
|
2049
2759
|
const insertAfter = insertAfterProp ?? autoId;
|
|
2050
2760
|
const [schedule, setSchedule] = (0, import_react5.useState)(null);
|
|
2051
|
-
const [loading, setLoading] = (0, import_react5.useState)(
|
|
2761
|
+
const [loading, setLoading] = (0, import_react5.useState)(initialScheduleId !== null);
|
|
2052
2762
|
const [inEditor, setInEditor] = (0, import_react5.useState)(false);
|
|
2053
2763
|
const [isHovered, setIsHovered] = (0, import_react5.useState)(false);
|
|
2054
2764
|
const [modalState, setModalState] = (0, import_react5.useState)(null);
|
|
@@ -2222,8 +2932,10 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
|
|
|
2222
2932
|
"*"
|
|
2223
2933
|
);
|
|
2224
2934
|
};
|
|
2225
|
-
if (!inEditor && !loading && !schedule) return null;
|
|
2226
2935
|
const sectionId = `scheduling-${insertAfter}`;
|
|
2936
|
+
if (!inEditor && !loading && !schedule) {
|
|
2937
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("section", { "data-ohw-section": sectionId, "data-ohw-scheduling-anchor": insertAfter, style: { display: "none" } });
|
|
2938
|
+
}
|
|
2227
2939
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2228
2940
|
"section",
|
|
2229
2941
|
{
|
|
@@ -7140,13 +7852,17 @@ function MediaOverlay({
|
|
|
7140
7852
|
hover,
|
|
7141
7853
|
isUploading,
|
|
7142
7854
|
fadingOut = false,
|
|
7855
|
+
selected = false,
|
|
7856
|
+
hovered = false,
|
|
7143
7857
|
onFadeOutComplete,
|
|
7144
7858
|
onReplace,
|
|
7859
|
+
onSelect,
|
|
7145
7860
|
onVideoSettingsChange
|
|
7146
7861
|
}) {
|
|
7147
7862
|
const { rect } = hover;
|
|
7148
7863
|
const skeletonRef = React8.useRef(null);
|
|
7149
7864
|
const isVideo = hover.elementType === "video";
|
|
7865
|
+
const showChrome = !selected || hovered;
|
|
7150
7866
|
const autoplay = hover.videoAutoplay ?? true;
|
|
7151
7867
|
const muted = hover.videoMuted ?? true;
|
|
7152
7868
|
const probeRef = React8.useRef(null);
|
|
@@ -7160,6 +7876,7 @@ function MediaOverlay({
|
|
|
7160
7876
|
(prev) => prev && prev.fullWidth === width && prev.height === height ? prev : { fullWidth: width, height }
|
|
7161
7877
|
);
|
|
7162
7878
|
}, [isVideo]);
|
|
7879
|
+
const replaceLabel = isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image";
|
|
7163
7880
|
const replaceMode = buttonSize ? replaceButtonMode({ width: rect.width, height: rect.height }, buttonSize) : "none";
|
|
7164
7881
|
const box = {
|
|
7165
7882
|
position: "fixed",
|
|
@@ -7193,7 +7910,7 @@ function MediaOverlay({
|
|
|
7193
7910
|
}
|
|
7194
7911
|
);
|
|
7195
7912
|
}
|
|
7196
|
-
const settingsBar = isVideo && !hover.isDragOver ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
7913
|
+
const settingsBar = isVideo && !hover.isDragOver && showChrome ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
7197
7914
|
"div",
|
|
7198
7915
|
{
|
|
7199
7916
|
"data-ohw-bridge": "",
|
|
@@ -7263,10 +7980,12 @@ function MediaOverlay({
|
|
|
7263
7980
|
// in-document, pointer-events does it natively. The button below opts back in, so
|
|
7264
7981
|
// Replace still works.
|
|
7265
7982
|
pointerEvents: hover.hasTextOverlap ? "none" : "auto",
|
|
7266
|
-
|
|
7267
|
-
|
|
7983
|
+
// Selected: a firm component ring with no wash, so the image reads as chosen rather
|
|
7984
|
+
// than hovered. Hover keeps the existing tinted preview.
|
|
7985
|
+
boxShadow: selected ? "inset 0 0 0 2px var(--color-primary)" : "inset 0 0 0 1.5px var(--color-primary)",
|
|
7986
|
+
background: selected ? "transparent" : "color-mix(in srgb, var(--color-primary) 20%, transparent)"
|
|
7268
7987
|
},
|
|
7269
|
-
onClick: () => onReplace(hover.key),
|
|
7988
|
+
onClick: () => onSelect && !selected ? onSelect(hover.key) : onReplace(hover.key),
|
|
7270
7989
|
children: [
|
|
7271
7990
|
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
7272
7991
|
Button,
|
|
@@ -7287,17 +8006,17 @@ function MediaOverlay({
|
|
|
7287
8006
|
},
|
|
7288
8007
|
children: [
|
|
7289
8008
|
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
|
-
|
|
8009
|
+
replaceLabel
|
|
7291
8010
|
]
|
|
7292
8011
|
}
|
|
7293
8012
|
),
|
|
7294
|
-
replaceMode
|
|
8013
|
+
showChrome && replaceMode !== "none" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
7295
8014
|
Button,
|
|
7296
8015
|
{
|
|
7297
8016
|
"data-ohw-media-overlay": "",
|
|
7298
8017
|
variant: "outline",
|
|
7299
8018
|
size: "sm",
|
|
7300
|
-
"aria-label":
|
|
8019
|
+
"aria-label": replaceLabel,
|
|
7301
8020
|
className: "gap-1.5 cursor-pointer hover:bg-background",
|
|
7302
8021
|
style: {
|
|
7303
8022
|
...OVERLAY_BUTTON_STYLE,
|
|
@@ -7320,7 +8039,7 @@ function MediaOverlay({
|
|
|
7320
8039
|
},
|
|
7321
8040
|
children: [
|
|
7322
8041
|
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" ?
|
|
8042
|
+
replaceMode === "full" ? replaceLabel : null
|
|
7324
8043
|
]
|
|
7325
8044
|
}
|
|
7326
8045
|
)
|
|
@@ -7404,6 +8123,8 @@ function parseSectionsFromRoot(root) {
|
|
|
7404
8123
|
const id = el.getAttribute("data-ohw-section") ?? "";
|
|
7405
8124
|
if (!id || seen.has(id) || LINK_PICKER_EXCLUDED_IDS.has(id)) continue;
|
|
7406
8125
|
if (el.parentElement?.closest("[data-ohw-section]")) continue;
|
|
8126
|
+
if (el.hasAttribute("data-ohw-ai-template-hidden") || el.hasAttribute("data-ohw-ai-removed") || el.hasAttribute("data-ohw-ai-replaced-by"))
|
|
8127
|
+
continue;
|
|
7407
8128
|
seen.add(id);
|
|
7408
8129
|
const label = el.getAttribute("data-ohw-section-label") ?? titleCaseSectionId(id);
|
|
7409
8130
|
sections.push({ id, label });
|
|
@@ -7689,6 +8410,7 @@ function AiSectionOverlay({
|
|
|
7689
8410
|
}) {
|
|
7690
8411
|
const [selectedId, setSelectedId] = (0, import_react8.useState)(null);
|
|
7691
8412
|
const [reviewId, setReviewId] = (0, import_react8.useState)(null);
|
|
8413
|
+
const [reviewButtonsHidden, setReviewButtonsHidden] = (0, import_react8.useState)(false);
|
|
7692
8414
|
const reviewIdRef = (0, import_react8.useRef)(null);
|
|
7693
8415
|
reviewIdRef.current = reviewId;
|
|
7694
8416
|
const selectedIdRef = (0, import_react8.useRef)(null);
|
|
@@ -7750,6 +8472,7 @@ function AiSectionOverlay({
|
|
|
7750
8472
|
}
|
|
7751
8473
|
const found = readRect(sectionId) != null;
|
|
7752
8474
|
setReviewId(found ? sectionId : null);
|
|
8475
|
+
setReviewButtonsHidden(e.data.hideButtons === true);
|
|
7753
8476
|
postToParent2({ type: "ow:ai-review-started", sectionId, found });
|
|
7754
8477
|
if (found) {
|
|
7755
8478
|
document.querySelector(`[data-ohw-instance="${CSS.escape(sectionId)}"]`)?.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
|
@@ -7877,13 +8600,16 @@ function AiSectionOverlay({
|
|
|
7877
8600
|
border: `2px solid ${PRIMARY2}`,
|
|
7878
8601
|
borderRadius: edgeAwareRadius(reviewRect),
|
|
7879
8602
|
zIndex: 2147483200,
|
|
7880
|
-
// The veil itself: swallows clicks so the section stays locked until decided.
|
|
8603
|
+
// The veil itself: swallows clicks so the section stays locked until decided. This
|
|
8604
|
+
// stopPropagation only guards the bubble phase; the bridge's capture-phase click
|
|
8605
|
+
// handlers additionally bail on `[data-ohw-ai-review]`, without which a click on
|
|
8606
|
+
// Accept/Discard resolves to the media beneath and opens the file picker.
|
|
7881
8607
|
background: "rgba(8, 133, 254, 0.04)",
|
|
7882
8608
|
pointerEvents: "auto",
|
|
7883
8609
|
cursor: "default"
|
|
7884
8610
|
},
|
|
7885
8611
|
onClick: (e) => e.stopPropagation(),
|
|
7886
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
|
|
8612
|
+
children: !reviewButtonsHidden && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
|
|
7887
8613
|
"div",
|
|
7888
8614
|
{
|
|
7889
8615
|
style: {
|
|
@@ -10450,8 +11176,13 @@ function referenceBox(slot) {
|
|
|
10450
11176
|
const neighbour = row ? Array.from(row.querySelectorAll('[data-ohw-editable="icon"]')).find(
|
|
10451
11177
|
(el) => el !== slot && !el.hasAttribute("data-ohw-social-icon-placeholder")
|
|
10452
11178
|
) : null;
|
|
10453
|
-
|
|
10454
|
-
|
|
11179
|
+
if (neighbour) {
|
|
11180
|
+
const box2 = neighbour.querySelector(GLYPH_SELECTOR)?.getBoundingClientRect();
|
|
11181
|
+
if (box2?.width && box2.height) return box2;
|
|
11182
|
+
}
|
|
11183
|
+
const own = slot.getBoundingClientRect();
|
|
11184
|
+
if (own.width && own.height) return own;
|
|
11185
|
+
const box = slot.querySelector(GLYPH_SELECTOR)?.getBoundingClientRect() ?? null;
|
|
10455
11186
|
return box?.width && box.height ? box : null;
|
|
10456
11187
|
}
|
|
10457
11188
|
function iconMarkupSizedFor(slot, markup) {
|
|
@@ -12252,6 +12983,7 @@ function readLogoSizeState(content, placement) {
|
|
|
12252
12983
|
function getLogoElement(el) {
|
|
12253
12984
|
const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
|
|
12254
12985
|
if (marked) return marked;
|
|
12986
|
+
if (el.closest('[data-ohw-editable="icon"]')) return null;
|
|
12255
12987
|
const root = el.closest("nav, [data-ohw-nav-root], footer");
|
|
12256
12988
|
if (!root) return null;
|
|
12257
12989
|
const anchor = el.closest("a");
|
|
@@ -13320,6 +14052,7 @@ function useSectionDrag({
|
|
|
13320
14052
|
}
|
|
13321
14053
|
const orderJson = JSON.stringify(entries);
|
|
13322
14054
|
editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
|
|
14055
|
+
setAiSectionOrder(orderJson, window.location.pathname);
|
|
13323
14056
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
|
|
13324
14057
|
applyPersistedOrder(entries);
|
|
13325
14058
|
clearSectionDragVisuals();
|
|
@@ -14179,21 +14912,10 @@ function parseSchedulingInsertAfter(insertAfter) {
|
|
|
14179
14912
|
insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
|
|
14180
14913
|
};
|
|
14181
14914
|
}
|
|
14182
|
-
function
|
|
14183
|
-
|
|
14184
|
-
const
|
|
14185
|
-
|
|
14186
|
-
return { effectiveInsertAfter, insertBefore };
|
|
14187
|
-
}
|
|
14188
|
-
function getSchedulingMountPoint(insertAfter) {
|
|
14189
|
-
const { anchor } = parseSchedulingInsertAfter(insertAfter);
|
|
14190
|
-
let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
|
|
14191
|
-
if (!anchorEl && anchor === "scheduling") {
|
|
14192
|
-
const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
|
|
14193
|
-
anchorEl = widgets.at(-1) ?? null;
|
|
14194
|
-
}
|
|
14195
|
-
if (!anchorEl) return null;
|
|
14196
|
-
return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
|
|
14915
|
+
function resolveEntryAnchor(entry) {
|
|
14916
|
+
if (entry.anchorId !== void 0) return { anchorId: entry.anchorId, beforeId: entry.beforeId ?? null };
|
|
14917
|
+
const parsed = parseSchedulingInsertAfter(entry.insertAfter);
|
|
14918
|
+
return { anchorId: parsed.anchor, beforeId: parsed.insertBefore };
|
|
14197
14919
|
}
|
|
14198
14920
|
function schedulingMountDepth(insertAfter) {
|
|
14199
14921
|
if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
|
|
@@ -14210,8 +14932,7 @@ function getPageSchedulingEntries(raw) {
|
|
|
14210
14932
|
}
|
|
14211
14933
|
}
|
|
14212
14934
|
function isSchedulingWidgetMissing(entry) {
|
|
14213
|
-
|
|
14214
|
-
return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
|
|
14935
|
+
return !document.querySelector(`[data-ohw-section="${schedulingSectionId(entry.insertAfter)}"]`);
|
|
14215
14936
|
}
|
|
14216
14937
|
function hasMissingSchedulingWidgets(entries) {
|
|
14217
14938
|
return entries.some(isSchedulingWidgetMissing);
|
|
@@ -14241,16 +14962,17 @@ function initSectionsFromContent(content, removeExisting = false) {
|
|
|
14241
14962
|
} catch {
|
|
14242
14963
|
}
|
|
14243
14964
|
}
|
|
14244
|
-
function mountSchedulingWidget(
|
|
14245
|
-
const
|
|
14246
|
-
const sectionId = schedulingSectionId(
|
|
14965
|
+
function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, beforeId, existingWidgetId) {
|
|
14966
|
+
const widgetId = existingWidgetId ?? (beforeId ? `${anchorId}${INSERT_BEFORE_MARKER}${beforeId}` : anchorId);
|
|
14967
|
+
const sectionId = schedulingSectionId(widgetId);
|
|
14247
14968
|
if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
|
|
14248
|
-
const
|
|
14249
|
-
if (!
|
|
14969
|
+
const anchorEl = document.querySelector(`[data-ohw-section="${anchorId}"]`);
|
|
14970
|
+
if (!anchorEl) return false;
|
|
14971
|
+
const mountPoint = anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
|
|
14250
14972
|
const container = document.createElement("div");
|
|
14251
14973
|
container.dataset.ohwSectionContainer = "scheduling";
|
|
14252
|
-
if (
|
|
14253
|
-
const beforeAnchor = document.querySelector(`[data-ohw-section="${
|
|
14974
|
+
if (beforeId) {
|
|
14975
|
+
const beforeAnchor = document.querySelector(`[data-ohw-section="${beforeId}"]`);
|
|
14254
14976
|
const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
|
|
14255
14977
|
if (!beforePoint) return false;
|
|
14256
14978
|
beforePoint.insertAdjacentElement("beforebegin", container);
|
|
@@ -14261,19 +14983,25 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
|
|
|
14261
14983
|
}
|
|
14262
14984
|
tail.insertAdjacentElement("afterend", container);
|
|
14263
14985
|
}
|
|
14264
|
-
|
|
14265
|
-
|
|
14266
|
-
|
|
14267
|
-
|
|
14268
|
-
|
|
14269
|
-
|
|
14270
|
-
|
|
14271
|
-
|
|
14272
|
-
|
|
14273
|
-
|
|
14274
|
-
|
|
14275
|
-
|
|
14276
|
-
|
|
14986
|
+
try {
|
|
14987
|
+
const root = (0, import_client2.createRoot)(container);
|
|
14988
|
+
(0, import_react_dom3.flushSync)(() => {
|
|
14989
|
+
root.render(
|
|
14990
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
14991
|
+
SchedulingWidget,
|
|
14992
|
+
{
|
|
14993
|
+
notifyOnConnect,
|
|
14994
|
+
initialScheduleId: scheduleId,
|
|
14995
|
+
insertAfter: widgetId
|
|
14996
|
+
}
|
|
14997
|
+
)
|
|
14998
|
+
);
|
|
14999
|
+
});
|
|
15000
|
+
} catch (err) {
|
|
15001
|
+
console.error("[ow:scheduling] render threw", err);
|
|
15002
|
+
container.remove();
|
|
15003
|
+
return false;
|
|
15004
|
+
}
|
|
14277
15005
|
const tracker = getSectionsTracker();
|
|
14278
15006
|
let sections = [];
|
|
14279
15007
|
try {
|
|
@@ -14281,10 +15009,12 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
|
|
|
14281
15009
|
} catch {
|
|
14282
15010
|
}
|
|
14283
15011
|
const inEditor = typeof window !== "undefined" && window.self !== window.top;
|
|
14284
|
-
if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter ===
|
|
15012
|
+
if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === widgetId)) {
|
|
14285
15013
|
sections.push({
|
|
14286
15014
|
type: "scheduling",
|
|
14287
|
-
insertAfter:
|
|
15015
|
+
insertAfter: widgetId,
|
|
15016
|
+
anchorId,
|
|
15017
|
+
beforeId: beforeId ?? null,
|
|
14288
15018
|
pagePath: window.location.pathname,
|
|
14289
15019
|
...scheduleId ? { scheduleId } : {}
|
|
14290
15020
|
});
|
|
@@ -14298,7 +15028,8 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
|
|
|
14298
15028
|
for (let i = pending.length - 1; i >= 0; i--) {
|
|
14299
15029
|
const entry = pending[i];
|
|
14300
15030
|
const shouldNotify = typeof notifyOnConnect === "function" ? notifyOnConnect(entry) : notifyOnConnect;
|
|
14301
|
-
|
|
15031
|
+
const { anchorId, beforeId } = resolveEntryAnchor(entry);
|
|
15032
|
+
if (mountSchedulingWidget(anchorId, shouldNotify, entry.scheduleId ?? null, beforeId, entry.insertAfter)) {
|
|
14302
15033
|
pending.splice(i, 1);
|
|
14303
15034
|
}
|
|
14304
15035
|
}
|
|
@@ -14456,6 +15187,11 @@ function applyLinkByKey(key, val) {
|
|
|
14456
15187
|
hrefAnchors.forEach((el) => applyLinkHref(el, val));
|
|
14457
15188
|
}
|
|
14458
15189
|
}
|
|
15190
|
+
function isInsideLinkEditor(target) {
|
|
15191
|
+
return Boolean(
|
|
15192
|
+
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"]')
|
|
15193
|
+
);
|
|
15194
|
+
}
|
|
14459
15195
|
function isInsideFloatingPanel(target) {
|
|
14460
15196
|
return Boolean(target.closest("[data-ohw-floating-panel]"));
|
|
14461
15197
|
}
|
|
@@ -14463,11 +15199,6 @@ function isPointOverFloatingPanel(clientX, clientY) {
|
|
|
14463
15199
|
const el = document.elementFromPoint(clientX, clientY);
|
|
14464
15200
|
return Boolean(el instanceof Element && el.closest("[data-ohw-floating-panel]"));
|
|
14465
15201
|
}
|
|
14466
|
-
function isInsideLinkEditor(target) {
|
|
14467
|
-
return Boolean(
|
|
14468
|
-
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"]')
|
|
14469
|
-
);
|
|
14470
|
-
}
|
|
14471
15202
|
function getHrefKeyFromElement(el) {
|
|
14472
15203
|
if (!el) return null;
|
|
14473
15204
|
const anchor = el.closest("[data-ohw-href-key]");
|
|
@@ -14726,7 +15457,7 @@ function getNavigationSelectionParent(el) {
|
|
|
14726
15457
|
if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
|
|
14727
15458
|
return getFooterLinksContainer();
|
|
14728
15459
|
}
|
|
14729
|
-
if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
|
|
15460
|
+
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)) {
|
|
14730
15461
|
return getNavigationRoot(el);
|
|
14731
15462
|
}
|
|
14732
15463
|
return null;
|
|
@@ -14941,7 +15672,6 @@ var ICONS = {
|
|
|
14941
15672
|
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"/>',
|
|
14942
15673
|
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"/>'
|
|
14943
15674
|
};
|
|
14944
|
-
var HEIGHT_SETTLE_DELAYS = [150, 400, 800];
|
|
14945
15675
|
var SELECTION_CHROME_GAP2 = 4;
|
|
14946
15676
|
var TOOLBAR_STROKE_GAP2 = 4;
|
|
14947
15677
|
var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
|
|
@@ -15321,6 +16051,8 @@ function StateToggle({
|
|
|
15321
16051
|
);
|
|
15322
16052
|
}
|
|
15323
16053
|
var contentCache = /* @__PURE__ */ new Map();
|
|
16054
|
+
var fetchedContentPaths = /* @__PURE__ */ new Set();
|
|
16055
|
+
var brandingCache = /* @__PURE__ */ new Map();
|
|
15324
16056
|
var OHW_LOADER_STYLE = {
|
|
15325
16057
|
position: "fixed",
|
|
15326
16058
|
inset: 0,
|
|
@@ -15358,6 +16090,89 @@ function OhwLoaderSpinner() {
|
|
|
15358
16090
|
)
|
|
15359
16091
|
] });
|
|
15360
16092
|
}
|
|
16093
|
+
function OhwBrandMark() {
|
|
16094
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
|
|
16095
|
+
"svg",
|
|
16096
|
+
{
|
|
16097
|
+
width: "16",
|
|
16098
|
+
height: "16",
|
|
16099
|
+
viewBox: "0 0 48 48",
|
|
16100
|
+
fill: "none",
|
|
16101
|
+
"aria-hidden": true,
|
|
16102
|
+
style: { display: "block", flexShrink: 0 },
|
|
16103
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
16104
|
+
children: [
|
|
16105
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
16106
|
+
"mask",
|
|
16107
|
+
{
|
|
16108
|
+
id: "ohw-badge-mark",
|
|
16109
|
+
style: { maskType: "luminance" },
|
|
16110
|
+
maskUnits: "userSpaceOnUse",
|
|
16111
|
+
x: "0",
|
|
16112
|
+
y: "0",
|
|
16113
|
+
width: "48",
|
|
16114
|
+
height: "48",
|
|
16115
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("path", { d: "M23.8741 48C37.0594 48 47.7481 37.2548 47.7481 24C47.7481 10.7452 37.0594 0 23.8741 0C10.6888 0 0 10.7452 0 24C0 37.2548 10.6888 48 23.8741 48Z", fill: "white" })
|
|
16116
|
+
}
|
|
16117
|
+
),
|
|
16118
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("g", { mask: "url(#ohw-badge-mark)", children: [
|
|
16119
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("path", { d: "M23.8731 48.0497C37.0584 48.0497 47.7472 37.3046 47.7472 24.0497C47.7472 10.7949 37.0584 0.0497208 23.8731 0.0497208C10.6878 0.0497208 -0.000976562 10.7949 -0.000976562 24.0497C-0.000976562 37.3046 10.6878 48.0497 23.8731 48.0497Z", fill: "#0078E5" }),
|
|
16120
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("path", { d: "M17.1307 14.7172C13.1687 14.7172 9.38102 18.1154 8.65114 22.34C8.38885 23.8488 8.5598 25.2581 9.06929 26.4451C6.20005 29.1677 1.77216 27.8721 -1.40212 26.1536C-2.73618 25.4317 -3.92695 27.4745 -2.59037 28.1981C1.33389 30.3226 6.86037 31.6621 10.4402 28.4188C11.4718 29.3859 12.867 29.9621 14.4894 29.9621C18.4161 29.9621 22.2038 26.5318 22.9337 22.34C23.6636 18.1162 21.0566 14.7172 17.1298 14.7172H17.1307ZM19.9798 22.34C19.5281 25.0399 17.2689 27.231 14.9754 27.231C12.6466 27.231 11.1877 25.0399 11.6394 22.34C12.1262 19.6401 14.3151 17.4482 16.6438 17.4482C18.9374 17.4482 20.4667 19.6401 19.9798 22.34Z", fill: "white" }),
|
|
16121
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("path", { d: "M40.0017 27.0262C39.1797 27.081 38.2721 26.995 37.4668 26.7415C37.3344 26.6993 37.28 26.5401 37.3529 26.4205C37.5959 26.0212 37.8255 25.6152 38.009 25.1889C38.1071 24.9918 38.2018 24.793 38.2897 24.5908C38.3274 24.5041 38.4163 24.451 38.5101 24.4619C38.63 24.4754 38.7054 24.4821 38.8881 24.4821L39.1529 24.4796L39.8283 24.4543C45.9229 24.0492 50.4765 20.4319 54.8466 16.9014C56.0172 15.9554 57.6932 17.6208 56.5116 18.5752C51.7687 22.4065 47.1966 26.5081 40.9319 26.9689", fill: "white" }),
|
|
16122
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("path", { d: "M37.9687 24.27C38.4472 23.1319 38.7656 21.9045 38.9609 20.6991C39.5927 16.76 38.5058 14.2193 36.1553 14.2193C34.1835 14.2193 33.1469 17.2427 32.9199 18.8694C32.743 19.9872 32.5914 22.1219 33.6028 23.9524C33.7553 24.259 34.15 24.7712 34.471 25.1259C34.5447 25.2067 34.6746 25.2 34.7349 25.1082C34.9528 24.7788 35.1615 24.4039 35.3584 24.0257C35.5444 23.6677 35.5888 23.6138 35.8587 23.0207C35.8838 22.966 35.8813 22.9002 35.8478 22.8505C35.5888 22.4597 35.2168 21.9787 35.1204 21.4614C34.9184 20.4455 34.9436 19.2257 35.2218 18.1078C35.4413 17.3118 35.7195 16.7844 35.9039 16.5106C35.9466 16.4466 36.0279 16.4129 36.0975 16.4449C36.369 16.5671 36.5827 16.8838 36.7385 17.396C37.0167 18.2089 37.0167 19.3782 36.814 20.6999C36.6991 21.5271 36.4771 22.3729 36.1746 23.1673C36.1293 23.308 36.0757 23.4461 36.0187 23.5826C36.0187 23.5868 36.0187 23.591 36.0187 23.5961C35.9911 23.6946 35.9207 23.8067 35.8846 23.901C35.5536 24.5497 35.2344 25.1697 34.8439 25.7838C34.8388 25.7863 34.8346 25.7914 34.8296 25.7931C34.6528 26.0525 34.4718 26.2901 34.2866 26.4965C34.2774 26.5099 34.2682 26.5234 34.2589 26.5369C34.2405 26.5638 34.2179 26.5815 34.1944 26.595C33.5064 27.3212 32.7665 27.6893 32.0123 27.6893C31.8606 27.6893 31.6838 27.664 31.507 27.4349C31.3042 27.1299 30.85 26.0879 31.2539 22.9112C31.4785 21.3949 31.8011 20.0268 31.931 19.5408C31.9579 19.4413 31.8908 19.3419 31.7886 19.3293L30.0062 19.1162C29.9241 19.1061 29.8478 19.1566 29.8252 19.2366C29.2704 21.1615 27.0305 27.6885 24.9599 27.6885C24.328 27.6885 24.1512 26.6211 24.1001 26.2909C23.775 23.6264 25.528 18.5492 29.5302 16.2267C29.6048 16.1838 29.635 16.0928 29.5998 16.0136L28.9042 14.467C28.8632 14.3752 28.7501 14.3389 28.6638 14.3886C25.8079 16.0414 24.1847 18.5231 23.2915 20.3436C22.2046 22.5793 21.6993 25.0189 21.9515 26.8738C22.1795 28.7794 23.1398 29.872 24.6054 29.872C26.0543 29.872 27.4369 28.9066 28.7098 27.0187C28.7953 26.8915 28.9889 26.9311 29.0149 27.0819C29.2646 28.5283 29.9811 29.872 31.6579 29.872C33.5282 29.872 35.3232 28.7288 36.7134 26.6447C36.7712 26.5874 36.7972 26.5411 36.8181 26.4906C36.8232 26.4931 36.8282 26.4948 36.8332 26.4973C37.01 26.2025 37.181 25.9051 37.3444 25.6027C37.4508 25.4005 37.5572 25.1992 37.6586 24.9945C37.7181 24.874 37.7743 24.7527 37.8262 24.6289C37.838 24.6002 37.8547 24.5665 37.8706 24.5337", fill: "white" }),
|
|
16123
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("path", { d: "M30.5839 31.6397C25.7546 34.8577 19.4773 34.7853 14.6907 31.5243C13.5368 30.7384 12.5044 32.6498 13.6474 33.4281C19.034 37.0985 26.3077 37.096 31.7218 33.488C32.8791 32.7172 31.7478 30.8639 30.5839 31.6397Z", fill: "white" })
|
|
16124
|
+
] })
|
|
16125
|
+
]
|
|
16126
|
+
}
|
|
16127
|
+
);
|
|
16128
|
+
}
|
|
16129
|
+
var OHW_BADGE_STYLE = {
|
|
16130
|
+
position: "fixed",
|
|
16131
|
+
left: 20,
|
|
16132
|
+
bottom: 20,
|
|
16133
|
+
zIndex: 2147483e3,
|
|
16134
|
+
boxSizing: "border-box",
|
|
16135
|
+
display: "inline-flex",
|
|
16136
|
+
alignItems: "center",
|
|
16137
|
+
gap: 0,
|
|
16138
|
+
padding: "6px 8px",
|
|
16139
|
+
margin: 0,
|
|
16140
|
+
background: "#ffffff",
|
|
16141
|
+
border: "1px solid #e7e5e4",
|
|
16142
|
+
borderRadius: 9999,
|
|
16143
|
+
boxShadow: "0 1px 3px rgba(0, 0, 0, 0.1)",
|
|
16144
|
+
color: "#0c0a09",
|
|
16145
|
+
textDecoration: "none",
|
|
16146
|
+
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif"
|
|
16147
|
+
};
|
|
16148
|
+
var OHW_BADGE_LABEL_STYLE = {
|
|
16149
|
+
padding: "0 4px",
|
|
16150
|
+
fontSize: 14,
|
|
16151
|
+
lineHeight: "24px",
|
|
16152
|
+
fontWeight: 500,
|
|
16153
|
+
fontStyle: "normal",
|
|
16154
|
+
letterSpacing: "normal",
|
|
16155
|
+
textTransform: "none",
|
|
16156
|
+
color: "#0c0a09",
|
|
16157
|
+
whiteSpace: "nowrap"
|
|
16158
|
+
};
|
|
16159
|
+
function MadeWithOhhWells() {
|
|
16160
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
|
|
16161
|
+
"a",
|
|
16162
|
+
{
|
|
16163
|
+
href: "https://ohhwells.com",
|
|
16164
|
+
target: "_blank",
|
|
16165
|
+
rel: "noopener noreferrer",
|
|
16166
|
+
"aria-label": "Made with OhhWells",
|
|
16167
|
+
"data-ohw-badge": "",
|
|
16168
|
+
style: OHW_BADGE_STYLE,
|
|
16169
|
+
children: [
|
|
16170
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(OhwBrandMark, {}),
|
|
16171
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { style: OHW_BADGE_LABEL_STYLE, children: "Made with OhhWells" })
|
|
16172
|
+
]
|
|
16173
|
+
}
|
|
16174
|
+
);
|
|
16175
|
+
}
|
|
15361
16176
|
var OHW_LOADER_PREHYDRATE_SCRIPT = `(function(){try{var p=location.hostname.split(".");var fromHost=p.length>=3&&p[0]!=="www"?p[0]:"";var fromQuery=new URLSearchParams(location.search).get("subdomain")||"";if(!fromHost&&!fromQuery)return;var e=document.getElementById("ohw-loader");if(e)e.style.display="flex"}catch(e){}})();`;
|
|
15362
16177
|
function resolveSubdomain(subdomainFromQuery) {
|
|
15363
16178
|
if (subdomainFromQuery) return subdomainFromQuery;
|
|
@@ -15417,6 +16232,7 @@ function OhhwellsBridge() {
|
|
|
15417
16232
|
}
|
|
15418
16233
|
}, []);
|
|
15419
16234
|
const [fetchState, setFetchState] = (0, import_react17.useState)("idle");
|
|
16235
|
+
const [showBranding, setShowBranding] = (0, import_react17.useState)(false);
|
|
15420
16236
|
const autoSaveTimers = (0, import_react17.useRef)(/* @__PURE__ */ new Map());
|
|
15421
16237
|
const activeElRef = (0, import_react17.useRef)(null);
|
|
15422
16238
|
const pointerHeldRef = (0, import_react17.useRef)(false);
|
|
@@ -15439,6 +16255,70 @@ function OhhwellsBridge() {
|
|
|
15439
16255
|
const hoveredImageHasTextOverlapRef = (0, import_react17.useRef)(false);
|
|
15440
16256
|
const dragOverElRef = (0, import_react17.useRef)(null);
|
|
15441
16257
|
const [mediaHover, setMediaHover] = (0, import_react17.useState)(null);
|
|
16258
|
+
const [selectedMedia, setSelectedMedia] = (0, import_react17.useState)(null);
|
|
16259
|
+
const selectedMediaElRef = (0, import_react17.useRef)(null);
|
|
16260
|
+
const clearMediaSelection = (0, import_react17.useCallback)(() => {
|
|
16261
|
+
const prev = selectedMediaElRef.current;
|
|
16262
|
+
selectedMediaElRef.current = null;
|
|
16263
|
+
setSelectedMedia(null);
|
|
16264
|
+
const sectionEl = prev?.closest("[data-ohw-section]") ?? null;
|
|
16265
|
+
if (sectionEl) {
|
|
16266
|
+
postToParentRef.current({
|
|
16267
|
+
type: "ow:section-selected",
|
|
16268
|
+
sectionId: sectionEl.dataset.ohwSection ?? null,
|
|
16269
|
+
sectionLabel: sectionEl.dataset.ohwSectionLabel ?? titleCaseSectionId(sectionEl.dataset.ohwSection ?? ""),
|
|
16270
|
+
key: null
|
|
16271
|
+
});
|
|
16272
|
+
}
|
|
16273
|
+
}, []);
|
|
16274
|
+
const clearMediaSelectionRef = (0, import_react17.useRef)(clearMediaSelection);
|
|
16275
|
+
clearMediaSelectionRef.current = clearMediaSelection;
|
|
16276
|
+
const selectMediaElement = (0, import_react17.useCallback)((el) => {
|
|
16277
|
+
const r2 = el.getBoundingClientRect();
|
|
16278
|
+
const video = el.dataset.ohwEditable === "video" ? el.querySelector("video") : null;
|
|
16279
|
+
selectedMediaElRef.current = el;
|
|
16280
|
+
setSelectedMedia({
|
|
16281
|
+
key: el.dataset.ohwKey ?? "",
|
|
16282
|
+
rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height },
|
|
16283
|
+
elementType: el.dataset.ohwEditable ?? "image",
|
|
16284
|
+
hasTextOverlap: false,
|
|
16285
|
+
isDragOver: false,
|
|
16286
|
+
...video ? { videoAutoplay: video.autoplay, videoMuted: video.muted } : {}
|
|
16287
|
+
});
|
|
16288
|
+
const sectionEl = el.closest("[data-ohw-section]");
|
|
16289
|
+
aiSectionApiRef.current?.selectFromElement(el, { report: false });
|
|
16290
|
+
postToParentRef.current({
|
|
16291
|
+
type: "ow:section-selected",
|
|
16292
|
+
sectionId: sectionEl?.dataset.ohwSection ?? null,
|
|
16293
|
+
sectionLabel: sectionEl ? sectionEl.dataset.ohwSectionLabel ?? titleCaseSectionId(sectionEl.dataset.ohwSection ?? "") : null,
|
|
16294
|
+
key: el.dataset.ohwKey ?? null,
|
|
16295
|
+
// Display name for the pill — the raw key prettifies into fragments ("Img"); the
|
|
16296
|
+
// bridge knows what the node IS, so it names it.
|
|
16297
|
+
keyLabel: el.dataset.ohwEditable === "video" ? "Video" : el.dataset.ohwEditable === "bg-image" ? "Background" : "Image"
|
|
16298
|
+
});
|
|
16299
|
+
}, []);
|
|
16300
|
+
const selectMediaElementRef = (0, import_react17.useRef)(selectMediaElement);
|
|
16301
|
+
selectMediaElementRef.current = selectMediaElement;
|
|
16302
|
+
(0, import_react17.useEffect)(() => {
|
|
16303
|
+
if (!selectedMedia) return;
|
|
16304
|
+
const update = () => {
|
|
16305
|
+
const el = selectedMediaElRef.current;
|
|
16306
|
+
if (!el || !el.isConnected) {
|
|
16307
|
+
clearMediaSelection();
|
|
16308
|
+
return;
|
|
16309
|
+
}
|
|
16310
|
+
const r2 = el.getBoundingClientRect();
|
|
16311
|
+
setSelectedMedia(
|
|
16312
|
+
(prev) => prev ? { ...prev, rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height } } : prev
|
|
16313
|
+
);
|
|
16314
|
+
};
|
|
16315
|
+
window.addEventListener("scroll", update, true);
|
|
16316
|
+
window.addEventListener("resize", update);
|
|
16317
|
+
return () => {
|
|
16318
|
+
window.removeEventListener("scroll", update, true);
|
|
16319
|
+
window.removeEventListener("resize", update);
|
|
16320
|
+
};
|
|
16321
|
+
}, [selectedMedia !== null]);
|
|
15442
16322
|
const [carouselHover, setCarouselHover] = (0, import_react17.useState)(null);
|
|
15443
16323
|
const [uploadingRects, setUploadingRects] = (0, import_react17.useState)({});
|
|
15444
16324
|
const hoveredGapRef = (0, import_react17.useRef)(null);
|
|
@@ -15701,13 +16581,6 @@ function OhhwellsBridge() {
|
|
|
15701
16581
|
const [isItemDragging, setIsItemDragging] = (0, import_react17.useState)(false);
|
|
15702
16582
|
const [isFooterFrameSelection, setIsFooterFrameSelection] = (0, import_react17.useState)(false);
|
|
15703
16583
|
isFooterFrameSelectionRef.current = isFooterFrameSelection;
|
|
15704
|
-
const [floatingPanel, setFloatingPanel] = (0, import_react17.useState)(null);
|
|
15705
|
-
const floatingPanelOpenRef = (0, import_react17.useRef)(false);
|
|
15706
|
-
floatingPanelOpenRef.current = floatingPanel !== null;
|
|
15707
|
-
const [floatingPanelPos, setFloatingPanelPos] = (0, import_react17.useState)(null);
|
|
15708
|
-
const [logoSizeDraft, setLogoSizeDraft] = (0, import_react17.useState)(null);
|
|
15709
|
-
const [editorViewport, setEditorViewport] = (0, import_react17.useState)("desktop");
|
|
15710
|
-
const [parentScrollSnap, setParentScrollSnap] = (0, import_react17.useState)(null);
|
|
15711
16584
|
const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = (0, import_react17.useState)(null);
|
|
15712
16585
|
const [footerHeadingVisible, setFooterHeadingVisible] = (0, import_react17.useState)(null);
|
|
15713
16586
|
const footerDragRef = (0, import_react17.useRef)(null);
|
|
@@ -15722,7 +16595,16 @@ function OhhwellsBridge() {
|
|
|
15722
16595
|
const addNavAfterAnchorRef = (0, import_react17.useRef)(null);
|
|
15723
16596
|
const editContentRef = (0, import_react17.useRef)({});
|
|
15724
16597
|
const aiSectionsRef = (0, import_react17.useRef)("");
|
|
16598
|
+
const brandKitRef = (0, import_react17.useRef)("");
|
|
16599
|
+
const stylesRef = (0, import_react17.useRef)("");
|
|
15725
16600
|
const pendingDeleteUndoRef = (0, import_react17.useRef)(null);
|
|
16601
|
+
const [floatingPanel, setFloatingPanel] = (0, import_react17.useState)(null);
|
|
16602
|
+
const floatingPanelOpenRef = (0, import_react17.useRef)(false);
|
|
16603
|
+
const setFloatingPanelRef = (0, import_react17.useRef)(setFloatingPanel);
|
|
16604
|
+
const [floatingPanelPos, setFloatingPanelPos] = (0, import_react17.useState)(null);
|
|
16605
|
+
const [logoSizeDraft, setLogoSizeDraft] = (0, import_react17.useState)(null);
|
|
16606
|
+
const [editorViewport, setEditorViewport] = (0, import_react17.useState)("desktop");
|
|
16607
|
+
const [parentScrollSnap, setParentScrollSnap] = (0, import_react17.useState)(null);
|
|
15726
16608
|
const [sitePages, setSitePages] = (0, import_react17.useState)([]);
|
|
15727
16609
|
const [sectionsByPath, setSectionsByPath] = (0, import_react17.useState)({});
|
|
15728
16610
|
const sectionsPrefetchGenRef = (0, import_react17.useRef)(0);
|
|
@@ -15731,7 +16613,18 @@ function OhhwellsBridge() {
|
|
|
15731
16613
|
const linkPopoverOpenRef = (0, import_react17.useRef)(false);
|
|
15732
16614
|
const linkPopoverGraceUntilRef = (0, import_react17.useRef)(0);
|
|
15733
16615
|
setLinkPopoverRef.current = setLinkPopover;
|
|
16616
|
+
setFloatingPanelRef.current = setFloatingPanel;
|
|
15734
16617
|
linkPopoverSessionRef.current = linkPopover;
|
|
16618
|
+
floatingPanelOpenRef.current = Boolean(floatingPanel);
|
|
16619
|
+
(0, import_react17.useEffect)(() => {
|
|
16620
|
+
const syncViewport = () => {
|
|
16621
|
+
const next = window.innerWidth <= 480 ? "mobile" : "desktop";
|
|
16622
|
+
setEditorViewport((prev) => prev === next ? prev : next);
|
|
16623
|
+
};
|
|
16624
|
+
syncViewport();
|
|
16625
|
+
window.addEventListener("resize", syncViewport);
|
|
16626
|
+
return () => window.removeEventListener("resize", syncViewport);
|
|
16627
|
+
}, []);
|
|
15735
16628
|
const {
|
|
15736
16629
|
navDragRef,
|
|
15737
16630
|
navDropSlots,
|
|
@@ -17042,15 +17935,31 @@ function OhhwellsBridge() {
|
|
|
17042
17935
|
}
|
|
17043
17936
|
const applyContent = (content) => {
|
|
17044
17937
|
const imageLoads = [];
|
|
17938
|
+
if (typeof content[BRAND_KIT_KEY] === "string") {
|
|
17939
|
+
brandKitRef.current = content[BRAND_KIT_KEY];
|
|
17940
|
+
applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
|
|
17941
|
+
} else {
|
|
17942
|
+
brandKitRef.current = "";
|
|
17943
|
+
applyBrandToDom(null);
|
|
17944
|
+
}
|
|
17045
17945
|
if (typeof content[AI_SECTIONS_KEY] === "string") {
|
|
17046
17946
|
aiSectionsRef.current = content[AI_SECTIONS_KEY];
|
|
17947
|
+
setAiSectionOrder(content[SECTION_ORDER_KEY], window.location.pathname);
|
|
17047
17948
|
applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
|
|
17048
17949
|
}
|
|
17950
|
+
if (typeof content[STYLE_STORE_KEY] === "string") {
|
|
17951
|
+
stylesRef.current = content[STYLE_STORE_KEY];
|
|
17952
|
+
applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
|
|
17953
|
+
}
|
|
17954
|
+
applyBrandChrome(content);
|
|
17049
17955
|
for (const [key, val] of Object.entries(content)) {
|
|
17050
17956
|
if (key === "__ohw_sections") continue;
|
|
17051
17957
|
if (key === AI_SECTIONS_KEY) continue;
|
|
17052
17958
|
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
17053
17959
|
if (LOGO_IMAGE_KEYS.includes(key)) continue;
|
|
17960
|
+
if (key === BRAND_KIT_KEY) continue;
|
|
17961
|
+
if (key === STYLE_STORE_KEY) continue;
|
|
17962
|
+
if (BRAND_CHROME_KEYS.has(key)) continue;
|
|
17054
17963
|
if (applyVideoSettingNode(key, val)) continue;
|
|
17055
17964
|
if (applyCarouselNode(key, val)) continue;
|
|
17056
17965
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
@@ -17110,16 +18019,22 @@ function OhhwellsBridge() {
|
|
|
17110
18019
|
};
|
|
17111
18020
|
const cached = contentCache.get(subdomain);
|
|
17112
18021
|
if (cached) {
|
|
18022
|
+
setShowBranding(brandingCache.get(subdomain) ?? false);
|
|
17113
18023
|
applyContent(cached).finally(() => setFetchState("done"));
|
|
17114
18024
|
return;
|
|
17115
18025
|
}
|
|
17116
18026
|
let cancelled = false;
|
|
17117
18027
|
setFetchState("loading");
|
|
17118
18028
|
const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
|
|
17119
|
-
|
|
18029
|
+
const initialPath = pathname;
|
|
18030
|
+
fetchedContentPaths.add(`${subdomain}::${initialPath}`);
|
|
18031
|
+
fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
|
|
17120
18032
|
if (cancelled) return;
|
|
17121
18033
|
const content = data?.content ?? {};
|
|
18034
|
+
const branding = Boolean(data?.showBranding);
|
|
17122
18035
|
contentCache.set(subdomain, content);
|
|
18036
|
+
brandingCache.set(subdomain, branding);
|
|
18037
|
+
setShowBranding(branding);
|
|
17123
18038
|
return applyContent(content);
|
|
17124
18039
|
}).catch(() => {
|
|
17125
18040
|
}).finally(() => {
|
|
@@ -17236,10 +18151,28 @@ function OhhwellsBridge() {
|
|
|
17236
18151
|
initSectionInstancesFromContent(content, window.location.pathname);
|
|
17237
18152
|
observer?.disconnect();
|
|
17238
18153
|
try {
|
|
18154
|
+
applyBrandChrome(content);
|
|
18155
|
+
if (typeof content[BRAND_KIT_KEY] === "string") {
|
|
18156
|
+
applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
|
|
18157
|
+
} else {
|
|
18158
|
+
applyBrandToDom(null);
|
|
18159
|
+
}
|
|
18160
|
+
if (typeof content[AI_SECTIONS_KEY] === "string") {
|
|
18161
|
+
setAiSectionOrder(content[SECTION_ORDER_KEY], window.location.pathname);
|
|
18162
|
+
applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
|
|
18163
|
+
}
|
|
18164
|
+
if (typeof content[STYLE_STORE_KEY] === "string") {
|
|
18165
|
+
applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
|
|
18166
|
+
}
|
|
17239
18167
|
for (const [key, val] of Object.entries(content)) {
|
|
17240
18168
|
if (key === "__ohw_sections") continue;
|
|
18169
|
+
if (key === AI_SECTIONS_KEY) continue;
|
|
17241
18170
|
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
17242
18171
|
if (LOGO_IMAGE_KEYS.includes(key)) continue;
|
|
18172
|
+
if (key === BRAND_KIT_KEY) continue;
|
|
18173
|
+
if (key === STYLE_STORE_KEY) continue;
|
|
18174
|
+
if (key === STYLE_STORE_KEY) continue;
|
|
18175
|
+
if (BRAND_CHROME_KEYS.has(key)) continue;
|
|
17243
18176
|
if (applyVideoSettingNode(key, val)) continue;
|
|
17244
18177
|
if (applyCarouselNode(key, val)) continue;
|
|
17245
18178
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
@@ -17285,6 +18218,17 @@ function OhhwellsBridge() {
|
|
|
17285
18218
|
debounceTimer = setTimeout(applyFromCache, 150);
|
|
17286
18219
|
};
|
|
17287
18220
|
applyFromCache();
|
|
18221
|
+
const pathCacheKey = `${subdomain}::${pathname}`;
|
|
18222
|
+
if (!fetchedContentPaths.has(pathCacheKey)) {
|
|
18223
|
+
fetchedContentPaths.add(pathCacheKey);
|
|
18224
|
+
const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
|
|
18225
|
+
fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(pathname)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
|
|
18226
|
+
if (!data?.content) return;
|
|
18227
|
+
contentCache.set(subdomain, data.content);
|
|
18228
|
+
applyFromCache();
|
|
18229
|
+
}).catch(() => {
|
|
18230
|
+
});
|
|
18231
|
+
}
|
|
17288
18232
|
observer = new MutationObserver(scheduleApply);
|
|
17289
18233
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
17290
18234
|
return () => {
|
|
@@ -17380,30 +18324,31 @@ function OhhwellsBridge() {
|
|
|
17380
18324
|
}, [isEditMode, pathname, subdomain, fetchState, resyncSelectedNavigationItem]);
|
|
17381
18325
|
(0, import_react17.useEffect)(() => {
|
|
17382
18326
|
if (!isEditMode) return;
|
|
18327
|
+
let lastPosted = 0;
|
|
17383
18328
|
const measure = () => {
|
|
17384
18329
|
const h = document.body.scrollHeight;
|
|
17385
|
-
if (h > 50
|
|
18330
|
+
if (h > 50 && Math.abs(h - lastPosted) > 1) {
|
|
18331
|
+
lastPosted = h;
|
|
18332
|
+
postToParent2({ type: "ow:height", height: h });
|
|
18333
|
+
}
|
|
18334
|
+
};
|
|
18335
|
+
let raf = null;
|
|
18336
|
+
const schedule = () => {
|
|
18337
|
+
if (raf != null) return;
|
|
18338
|
+
raf = requestAnimationFrame(() => {
|
|
18339
|
+
raf = null;
|
|
18340
|
+
measure();
|
|
18341
|
+
});
|
|
17386
18342
|
};
|
|
17387
18343
|
const t1 = setTimeout(measure, 50);
|
|
17388
18344
|
const t2 = setTimeout(measure, 500);
|
|
17389
|
-
|
|
17390
|
-
|
|
17391
|
-
const clearResizeTimers = () => {
|
|
17392
|
-
resizeTimers.forEach(clearTimeout);
|
|
17393
|
-
resizeTimers = [];
|
|
17394
|
-
};
|
|
17395
|
-
const handleResize = () => {
|
|
17396
|
-
if (window.innerWidth === lastWidth) return;
|
|
17397
|
-
lastWidth = window.innerWidth;
|
|
17398
|
-
clearResizeTimers();
|
|
17399
|
-
resizeTimers = HEIGHT_SETTLE_DELAYS.map((delay) => setTimeout(measure, delay));
|
|
17400
|
-
};
|
|
17401
|
-
window.addEventListener("resize", handleResize);
|
|
18345
|
+
const ro = new ResizeObserver(schedule);
|
|
18346
|
+
ro.observe(document.body);
|
|
17402
18347
|
return () => {
|
|
17403
18348
|
clearTimeout(t1);
|
|
17404
18349
|
clearTimeout(t2);
|
|
17405
|
-
|
|
17406
|
-
|
|
18350
|
+
if (raf != null) cancelAnimationFrame(raf);
|
|
18351
|
+
ro.disconnect();
|
|
17407
18352
|
};
|
|
17408
18353
|
}, [pathname, isEditMode, postToParent2]);
|
|
17409
18354
|
(0, import_react17.useEffect)(() => {
|
|
@@ -17644,6 +18589,7 @@ function OhhwellsBridge() {
|
|
|
17644
18589
|
return;
|
|
17645
18590
|
}
|
|
17646
18591
|
const target = e.target;
|
|
18592
|
+
if (target.closest("[data-ohw-ai-review]")) return;
|
|
17647
18593
|
if (target.closest("[data-ohw-toolbar]")) return;
|
|
17648
18594
|
if (target.closest("[data-ohw-state-toggle]")) return;
|
|
17649
18595
|
if (target.closest("[data-ohw-max-badge]")) return;
|
|
@@ -17655,6 +18601,9 @@ function OhhwellsBridge() {
|
|
|
17655
18601
|
)) {
|
|
17656
18602
|
return;
|
|
17657
18603
|
}
|
|
18604
|
+
if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
|
|
18605
|
+
clearMediaSelectionRef.current();
|
|
18606
|
+
}
|
|
17658
18607
|
{
|
|
17659
18608
|
const formEl = getFormElement(target);
|
|
17660
18609
|
const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
|
|
@@ -17806,19 +18755,14 @@ function OhhwellsBridge() {
|
|
|
17806
18755
|
}
|
|
17807
18756
|
const clickedButton = findClosestButtonLike(target);
|
|
17808
18757
|
const buttonOnMedia = Boolean(clickedButton && isMediaEditable(editable) && editable.contains(clickedButton));
|
|
17809
|
-
console.log("[click-debug]", {
|
|
17810
|
-
editableType: editable.dataset.ohwEditable,
|
|
17811
|
-
editableTag: editable.tagName,
|
|
17812
|
-
targetTag: target.tagName,
|
|
17813
|
-
clickedButtonTag: clickedButton?.tagName ?? null,
|
|
17814
|
-
buttonOnMedia,
|
|
17815
|
-
isMediaEditableEditable: isMediaEditable(editable)
|
|
17816
|
-
});
|
|
17817
18758
|
if (isMediaEditable(editable) && !buttonOnMedia) {
|
|
17818
18759
|
e.preventDefault();
|
|
17819
18760
|
e.stopPropagation();
|
|
17820
|
-
|
|
17821
|
-
|
|
18761
|
+
if (selectedMediaElRef.current === editable) {
|
|
18762
|
+
postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "", elementType: editable.dataset.ohwEditable ?? "image" });
|
|
18763
|
+
} else {
|
|
18764
|
+
selectMediaElementRef.current(editable);
|
|
18765
|
+
}
|
|
17822
18766
|
return;
|
|
17823
18767
|
}
|
|
17824
18768
|
const socialItem = getSocialItem(editable);
|
|
@@ -17837,11 +18781,6 @@ function OhhwellsBridge() {
|
|
|
17837
18781
|
const hrefLookupTarget = buttonOnMedia ? clickedButton : editable;
|
|
17838
18782
|
const hrefCtx = getHrefKeyFromElement(hrefLookupTarget);
|
|
17839
18783
|
const navAnchor = hrefCtx ? getNavigationItemAnchor(hrefCtx.anchor) : null;
|
|
17840
|
-
console.log("[click-debug 2]", {
|
|
17841
|
-
hrefLookupTargetTag: hrefLookupTarget.tagName,
|
|
17842
|
-
hrefCtx: hrefCtx ? { key: hrefCtx.key } : null,
|
|
17843
|
-
navAnchorTag: navAnchor?.tagName ?? null
|
|
17844
|
-
});
|
|
17845
18784
|
if (navAnchor) {
|
|
17846
18785
|
e.preventDefault();
|
|
17847
18786
|
e.stopPropagation();
|
|
@@ -17959,6 +18898,7 @@ function OhhwellsBridge() {
|
|
|
17959
18898
|
};
|
|
17960
18899
|
const handleDblClick = (e) => {
|
|
17961
18900
|
const target = e.target;
|
|
18901
|
+
if (target.closest("[data-ohw-ai-review]")) return;
|
|
17962
18902
|
if (target.closest("[data-ohw-toolbar]")) return;
|
|
17963
18903
|
if (target.closest("[data-ohw-state-toggle]")) return;
|
|
17964
18904
|
if (target.closest("[data-ohw-max-badge]")) return;
|
|
@@ -18010,6 +18950,9 @@ function OhhwellsBridge() {
|
|
|
18010
18950
|
setHoveredItemRect(null);
|
|
18011
18951
|
hoveredNavContainerRef.current = null;
|
|
18012
18952
|
setHoveredNavContainerRect(null);
|
|
18953
|
+
siblingHintElRef.current = null;
|
|
18954
|
+
setSiblingHintRect(null);
|
|
18955
|
+
setSiblingHintRects([]);
|
|
18013
18956
|
return;
|
|
18014
18957
|
}
|
|
18015
18958
|
{
|
|
@@ -18128,7 +19071,6 @@ function OhhwellsBridge() {
|
|
|
18128
19071
|
hoveredNavContainerRef.current = null;
|
|
18129
19072
|
setHoveredNavContainerRect(null);
|
|
18130
19073
|
hoveredItemElRef.current = editable;
|
|
18131
|
-
setHoveredItemRect(isSelectedForHoverRef.current(editable) ? null : editable.getBoundingClientRect());
|
|
18132
19074
|
}
|
|
18133
19075
|
}
|
|
18134
19076
|
}
|
|
@@ -18425,7 +19367,7 @@ function OhhwellsBridge() {
|
|
|
18425
19367
|
}
|
|
18426
19368
|
};
|
|
18427
19369
|
const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
|
|
18428
|
-
if (linkPopoverOpenRef.current) {
|
|
19370
|
+
if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
|
|
18429
19371
|
if (hoveredImageRef.current) {
|
|
18430
19372
|
hoveredImageRef.current = null;
|
|
18431
19373
|
hoveredImageHasTextOverlapRef.current = false;
|
|
@@ -18759,7 +19701,9 @@ function OhhwellsBridge() {
|
|
|
18759
19701
|
return;
|
|
18760
19702
|
}
|
|
18761
19703
|
const { y } = toProbeCoords(clientX, clientY, fromParentViewport);
|
|
18762
|
-
const sections = Array.from(document.querySelectorAll("[data-ohw-section]")).
|
|
19704
|
+
const sections = Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
|
|
19705
|
+
(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
|
|
19706
|
+
).sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);
|
|
18763
19707
|
const ZONE = 20;
|
|
18764
19708
|
for (let i = 0; i < sections.length; i++) {
|
|
18765
19709
|
const a = sections[i];
|
|
@@ -18788,8 +19732,7 @@ function OhhwellsBridge() {
|
|
|
18788
19732
|
};
|
|
18789
19733
|
const handleMouseMove = (e) => {
|
|
18790
19734
|
const { clientX, clientY } = e;
|
|
18791
|
-
if (
|
|
18792
|
-
if (isOverEditorChrome(clientX, clientY)) {
|
|
19735
|
+
if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY) || isOverEditorChrome(clientX, clientY)) {
|
|
18793
19736
|
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
18794
19737
|
formHoverElRef.current = null;
|
|
18795
19738
|
setFormHoverRect(null);
|
|
@@ -18797,6 +19740,12 @@ function OhhwellsBridge() {
|
|
|
18797
19740
|
setHoveredItemRect(null);
|
|
18798
19741
|
hoveredNavContainerRef.current = null;
|
|
18799
19742
|
setHoveredNavContainerRect(null);
|
|
19743
|
+
siblingHintElRef.current = null;
|
|
19744
|
+
setSiblingHintRect(null);
|
|
19745
|
+
setSiblingHintRects([]);
|
|
19746
|
+
dismissImageHover();
|
|
19747
|
+
clearImageHover();
|
|
19748
|
+
setSectionGap(null);
|
|
18800
19749
|
return;
|
|
18801
19750
|
}
|
|
18802
19751
|
if (probeSocialsRowAt(clientX, clientY)) return;
|
|
@@ -18808,7 +19757,11 @@ function OhhwellsBridge() {
|
|
|
18808
19757
|
if (e.data?.type !== "ow:pointer-sync") return;
|
|
18809
19758
|
const { clientX, clientY } = e.data;
|
|
18810
19759
|
if (typeof clientX !== "number" || typeof clientY !== "number") return;
|
|
18811
|
-
if (
|
|
19760
|
+
if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
|
|
19761
|
+
dismissImageHover();
|
|
19762
|
+
clearImageHover();
|
|
19763
|
+
return;
|
|
19764
|
+
}
|
|
18812
19765
|
if (probeSocialsRowAt(clientX, clientY)) return;
|
|
18813
19766
|
probeSectionGapAt(clientX, clientY);
|
|
18814
19767
|
probeImageAt(clientX, clientY);
|
|
@@ -19091,10 +20044,23 @@ function OhhwellsBridge() {
|
|
|
19091
20044
|
if (e.data?.type !== "ow:hydrate") return;
|
|
19092
20045
|
const content = e.data.content;
|
|
19093
20046
|
if (!content) return;
|
|
20047
|
+
if (typeof content[BRAND_KIT_KEY] === "string") {
|
|
20048
|
+
brandKitRef.current = content[BRAND_KIT_KEY];
|
|
20049
|
+
applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
|
|
20050
|
+
} else {
|
|
20051
|
+
brandKitRef.current = "";
|
|
20052
|
+
applyBrandToDom(null);
|
|
20053
|
+
}
|
|
19094
20054
|
if (typeof content[AI_SECTIONS_KEY] === "string") {
|
|
19095
20055
|
aiSectionsRef.current = content[AI_SECTIONS_KEY];
|
|
20056
|
+
setAiSectionOrder(content[SECTION_ORDER_KEY], window.location.pathname);
|
|
19096
20057
|
applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
|
|
19097
20058
|
}
|
|
20059
|
+
if (typeof content[STYLE_STORE_KEY] === "string") {
|
|
20060
|
+
stylesRef.current = content[STYLE_STORE_KEY];
|
|
20061
|
+
applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
|
|
20062
|
+
}
|
|
20063
|
+
applyBrandChrome(content);
|
|
19098
20064
|
let sectionsJson = null;
|
|
19099
20065
|
for (const [key, val] of Object.entries(content)) {
|
|
19100
20066
|
if (key === "__ohw_sections") {
|
|
@@ -19104,6 +20070,9 @@ function OhhwellsBridge() {
|
|
|
19104
20070
|
if (key === AI_SECTIONS_KEY) continue;
|
|
19105
20071
|
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
19106
20072
|
if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
|
|
20073
|
+
if (key === BRAND_KIT_KEY) continue;
|
|
20074
|
+
if (key === STYLE_STORE_KEY) continue;
|
|
20075
|
+
if (BRAND_CHROME_KEYS.has(key)) continue;
|
|
19107
20076
|
if (applyVideoSettingNode(key, val)) continue;
|
|
19108
20077
|
if (applyCarouselNode(key, val)) continue;
|
|
19109
20078
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
@@ -19117,6 +20086,8 @@ function OhhwellsBridge() {
|
|
|
19117
20086
|
if (video && video.src !== val) applyVideoSrc(video, val);
|
|
19118
20087
|
} else if (el.dataset.ohwEditable === "link") {
|
|
19119
20088
|
applyLinkHref(el, val);
|
|
20089
|
+
} else if (el.dataset.ohwEditable === "icon") {
|
|
20090
|
+
applyIconMarkup(el, val);
|
|
19120
20091
|
} else if (isIconMarkupValue(val)) {
|
|
19121
20092
|
} else {
|
|
19122
20093
|
el.innerHTML = val;
|
|
@@ -19201,12 +20172,21 @@ function OhhwellsBridge() {
|
|
|
19201
20172
|
nodes: collectEditableNodes(editContentRef.current)
|
|
19202
20173
|
});
|
|
19203
20174
|
};
|
|
20175
|
+
const clearInteractionChrome = () => {
|
|
20176
|
+
deactivateRef.current();
|
|
20177
|
+
deselectRef.current();
|
|
20178
|
+
clearMediaSelectionRef.current();
|
|
20179
|
+
};
|
|
19204
20180
|
const handleAiApplyTree = (e) => {
|
|
19205
20181
|
if (e.data?.type !== "ow:ai-apply-tree") return;
|
|
19206
20182
|
const payload = e.data.payload;
|
|
19207
20183
|
if (!payload || typeof payload.id !== "string" || !isRenderableTree(payload.tree)) return;
|
|
20184
|
+
clearInteractionChrome();
|
|
19208
20185
|
const previous = aiSectionsRef.current;
|
|
19209
|
-
const nextState = applyTreeToState(parseAiSectionsState(previous),
|
|
20186
|
+
const nextState = applyTreeToState(parseAiSectionsState(previous), {
|
|
20187
|
+
...payload,
|
|
20188
|
+
path: payload.path ?? window.location.pathname
|
|
20189
|
+
});
|
|
19210
20190
|
const nextValue = serializeAiSectionsState(nextState);
|
|
19211
20191
|
aiSectionsRef.current = nextValue;
|
|
19212
20192
|
applyAiSectionsToDom(nextState);
|
|
@@ -19227,6 +20207,7 @@ function OhhwellsBridge() {
|
|
|
19227
20207
|
const exists = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
|
|
19228
20208
|
if (!exists) return;
|
|
19229
20209
|
if (isPageFrameSection(exists)) return;
|
|
20210
|
+
clearInteractionChrome();
|
|
19230
20211
|
const previous = aiSectionsRef.current;
|
|
19231
20212
|
const nextState = deleteSectionFromState(parseAiSectionsState(previous), sectionId);
|
|
19232
20213
|
const nextValue = serializeAiSectionsState(nextState);
|
|
@@ -19242,8 +20223,10 @@ function OhhwellsBridge() {
|
|
|
19242
20223
|
const handleAiSetSections = (e) => {
|
|
19243
20224
|
if (e.data?.type !== "ow:ai-set-sections") return;
|
|
19244
20225
|
const value = typeof e.data.value === "string" ? e.data.value : "";
|
|
20226
|
+
clearInteractionChrome();
|
|
19245
20227
|
aiSectionsRef.current = value;
|
|
19246
20228
|
applyAiSectionsToDom(parseAiSectionsState(value));
|
|
20229
|
+
applyStylesToDom(parseStyleStore(stylesRef.current));
|
|
19247
20230
|
const restoredHeight = document.body.scrollHeight;
|
|
19248
20231
|
if (restoredHeight > 50) postToParentRef.current({ type: "ow:height", height: restoredHeight });
|
|
19249
20232
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: AI_SECTIONS_KEY, text: value }] });
|
|
@@ -19259,10 +20242,40 @@ function OhhwellsBridge() {
|
|
|
19259
20242
|
if (!entries) return;
|
|
19260
20243
|
const orderJson = JSON.stringify(entries);
|
|
19261
20244
|
editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
|
|
20245
|
+
setAiSectionOrder(orderJson, window.location.pathname);
|
|
19262
20246
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
|
|
19263
20247
|
window.dispatchEvent(new Event("resize"));
|
|
19264
20248
|
};
|
|
19265
20249
|
window.addEventListener("message", handleMoveSection);
|
|
20250
|
+
const handleAiSetBrand = (e) => {
|
|
20251
|
+
if (e.data?.type !== "ow:ai-set-brand") return;
|
|
20252
|
+
const value = typeof e.data.value === "string" ? e.data.value : "";
|
|
20253
|
+
const previous = brandKitRef.current;
|
|
20254
|
+
brandKitRef.current = value;
|
|
20255
|
+
applyBrandToDom(parseBrandKit(value));
|
|
20256
|
+
if (aiSectionsRef.current) applyAiSectionsToDom(parseAiSectionsState(aiSectionsRef.current));
|
|
20257
|
+
applyStylesToDom(parseStyleStore(stylesRef.current));
|
|
20258
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: BRAND_KIT_KEY, text: value }] });
|
|
20259
|
+
postToParentRef.current({ type: "ow:ai-brand-applied", previous, value });
|
|
20260
|
+
};
|
|
20261
|
+
window.addEventListener("message", handleAiSetBrand);
|
|
20262
|
+
const handleAiSetStyles = (e) => {
|
|
20263
|
+
if (e.data?.type !== "ow:ai-set-styles") return;
|
|
20264
|
+
const value = typeof e.data.value === "string" ? e.data.value : "";
|
|
20265
|
+
const previous = stylesRef.current;
|
|
20266
|
+
stylesRef.current = value;
|
|
20267
|
+
applyStylesToDom(parseStyleStore(value));
|
|
20268
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: STYLE_STORE_KEY, text: value }] });
|
|
20269
|
+
postToParentRef.current({ type: "ow:ai-styles-applied", previous, value });
|
|
20270
|
+
};
|
|
20271
|
+
window.addEventListener("message", handleAiSetStyles);
|
|
20272
|
+
const handleGetBrand = (e) => {
|
|
20273
|
+
if (e.data?.type !== "ow:get-brand") return;
|
|
20274
|
+
const template = deriveTemplateBrand();
|
|
20275
|
+
const value = brandKitRef.current || (template ? JSON.stringify(template) : "");
|
|
20276
|
+
postToParentRef.current({ type: "ow:brand-value", value });
|
|
20277
|
+
};
|
|
20278
|
+
window.addEventListener("message", handleGetBrand);
|
|
19266
20279
|
const handlePanelDragging = (e) => {
|
|
19267
20280
|
if (e.data?.type !== "ow:panel-dragging") return;
|
|
19268
20281
|
if (e.data.dragging) document.documentElement.setAttribute("data-ohw-panel-dragging", "");
|
|
@@ -19320,8 +20333,15 @@ function OhhwellsBridge() {
|
|
|
19320
20333
|
closeLinkPopoverRef.current();
|
|
19321
20334
|
return;
|
|
19322
20335
|
}
|
|
20336
|
+
if (floatingPanelOpenRef.current) {
|
|
20337
|
+
setFloatingPanelRef.current(null);
|
|
20338
|
+
deselectRef.current();
|
|
20339
|
+
deactivateRef.current();
|
|
20340
|
+
return;
|
|
20341
|
+
}
|
|
19323
20342
|
deselectRef.current();
|
|
19324
20343
|
deactivateRef.current();
|
|
20344
|
+
clearMediaSelectionRef.current();
|
|
19325
20345
|
};
|
|
19326
20346
|
window.addEventListener("message", handleDeactivate);
|
|
19327
20347
|
const handleToastAction = (e) => {
|
|
@@ -19407,6 +20427,10 @@ function OhhwellsBridge() {
|
|
|
19407
20427
|
const handleKeyDown = (e) => {
|
|
19408
20428
|
if (e.key === "Escape" && document.querySelector("[data-ohw-section-picker]")) return;
|
|
19409
20429
|
if (e.key === "Escape" && document.querySelector("[data-ohw-more-menu]")) return;
|
|
20430
|
+
if (e.key === "Escape" && selectedMediaElRef.current) {
|
|
20431
|
+
clearMediaSelectionRef.current();
|
|
20432
|
+
return;
|
|
20433
|
+
}
|
|
19410
20434
|
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "a" && activeElRef.current) {
|
|
19411
20435
|
e.preventDefault();
|
|
19412
20436
|
selectAllTextInEditable(activeElRef.current);
|
|
@@ -19566,6 +20590,12 @@ function OhhwellsBridge() {
|
|
|
19566
20590
|
if (aiSectionsRef.current) {
|
|
19567
20591
|
nodes.push({ key: AI_SECTIONS_KEY, type: "sections", text: aiSectionsRef.current });
|
|
19568
20592
|
}
|
|
20593
|
+
if (stylesRef.current) {
|
|
20594
|
+
nodes.push({ key: STYLE_STORE_KEY, type: "styles", text: stylesRef.current });
|
|
20595
|
+
}
|
|
20596
|
+
if (brandKitRef.current) {
|
|
20597
|
+
nodes.push({ key: BRAND_KIT_KEY, type: "brand", text: brandKitRef.current });
|
|
20598
|
+
}
|
|
19569
20599
|
document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
|
|
19570
20600
|
const formKey = formKeyOf(form);
|
|
19571
20601
|
if (!formKey) return;
|
|
@@ -19583,8 +20613,12 @@ function OhhwellsBridge() {
|
|
|
19583
20613
|
if (inserted) {
|
|
19584
20614
|
const tracker = getSectionsTracker();
|
|
19585
20615
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
|
|
19586
|
-
const
|
|
19587
|
-
|
|
20616
|
+
const reportHeight = () => {
|
|
20617
|
+
const h = document.body.scrollHeight;
|
|
20618
|
+
if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
|
|
20619
|
+
};
|
|
20620
|
+
reportHeight();
|
|
20621
|
+
setTimeout(reportHeight, 500);
|
|
19588
20622
|
}
|
|
19589
20623
|
};
|
|
19590
20624
|
const handleSwitchSchedule = (e) => {
|
|
@@ -19981,13 +21015,16 @@ function OhhwellsBridge() {
|
|
|
19981
21015
|
window.removeEventListener("message", handleAiDeleteSection);
|
|
19982
21016
|
window.removeEventListener("message", handleAiSetSections);
|
|
19983
21017
|
window.removeEventListener("message", handleMoveSection);
|
|
21018
|
+
window.removeEventListener("message", handleAiSetBrand);
|
|
21019
|
+
window.removeEventListener("message", handleAiSetStyles);
|
|
21020
|
+
window.removeEventListener("message", handleGetBrand);
|
|
19984
21021
|
window.removeEventListener("message", handlePanelDragging);
|
|
19985
21022
|
window.removeEventListener("message", handleDeleteSection);
|
|
19986
21023
|
window.removeEventListener("message", handleDeactivate);
|
|
19987
|
-
document.documentElement.removeAttribute("data-ohw-panel-dragging");
|
|
19988
21024
|
window.removeEventListener("message", handleToastAction);
|
|
19989
21025
|
window.removeEventListener("message", handleFormCount);
|
|
19990
21026
|
window.removeEventListener("message", handleUiEscape);
|
|
21027
|
+
document.documentElement.removeAttribute("data-ohw-panel-dragging");
|
|
19991
21028
|
autoSaveTimers.current.forEach(clearTimeout);
|
|
19992
21029
|
autoSaveTimers.current.clear();
|
|
19993
21030
|
if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
|
|
@@ -20190,7 +21227,7 @@ function OhhwellsBridge() {
|
|
|
20190
21227
|
postToParent2({
|
|
20191
21228
|
type: "ow:ready",
|
|
20192
21229
|
version: "1",
|
|
20193
|
-
bridgeVersion: "0.1.
|
|
21230
|
+
bridgeVersion: "0.1.79",
|
|
20194
21231
|
path: pathname,
|
|
20195
21232
|
nodes: collectEditableNodes(editContentRef.current),
|
|
20196
21233
|
sections
|
|
@@ -20597,11 +21634,22 @@ function OhhwellsBridge() {
|
|
|
20597
21634
|
const showEditLink = toolbarShowEditLink;
|
|
20598
21635
|
const currentSections = sectionsByPath[pathname] ?? [];
|
|
20599
21636
|
linkPopoverOpenRef.current = linkPopover !== null;
|
|
21637
|
+
const handleMediaSelect = (0, import_react17.useCallback)((key) => {
|
|
21638
|
+
const el = hoveredImageRef.current?.dataset.ohwKey === key ? hoveredImageRef.current : Array.from(document.querySelectorAll(MEDIA_SELECTOR)).find(
|
|
21639
|
+
(m) => (m.dataset.ohwKey ?? "") === key
|
|
21640
|
+
) ?? null;
|
|
21641
|
+
if (!el) return;
|
|
21642
|
+
selectMediaElementRef.current(el);
|
|
21643
|
+
}, []);
|
|
20600
21644
|
const handleMediaReplace = (0, import_react17.useCallback)(
|
|
20601
21645
|
(key) => {
|
|
20602
|
-
postToParent2({
|
|
21646
|
+
postToParent2({
|
|
21647
|
+
type: "ow:image-pick",
|
|
21648
|
+
key,
|
|
21649
|
+
elementType: mediaHover?.elementType ?? selectedMedia?.elementType ?? "image"
|
|
21650
|
+
});
|
|
20603
21651
|
},
|
|
20604
|
-
[postToParent2, mediaHover?.elementType]
|
|
21652
|
+
[postToParent2, mediaHover?.elementType, selectedMedia?.elementType]
|
|
20605
21653
|
);
|
|
20606
21654
|
const handleEditCarousel = (0, import_react17.useCallback)(
|
|
20607
21655
|
(key) => {
|
|
@@ -20642,6 +21690,7 @@ function OhhwellsBridge() {
|
|
|
20642
21690
|
return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
|
|
20643
21691
|
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { id: "ohw-loader", suppressHydrationWarning: true, style: { ...OHW_LOADER_STYLE, display: "none" }, children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(OhwLoaderSpinner, {}) }),
|
|
20644
21692
|
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("script", { suppressHydrationWarning: true, dangerouslySetInnerHTML: { __html: OHW_LOADER_PREHYDRATE_SCRIPT } }),
|
|
21693
|
+
subdomain && !isEditMode && showBranding && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(MadeWithOhhWells, {}),
|
|
20645
21694
|
bridgeRoot ? (0, import_react_dom4.createPortal)(
|
|
20646
21695
|
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
|
|
20647
21696
|
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
|
|
@@ -20673,12 +21722,25 @@ function OhhwellsBridge() {
|
|
|
20673
21722
|
},
|
|
20674
21723
|
`uploading-${key}`
|
|
20675
21724
|
)),
|
|
20676
|
-
mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
21725
|
+
mediaHover && !(mediaHover.key in uploadingRects) && mediaHover.key !== selectedMedia?.key && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
20677
21726
|
MediaOverlay,
|
|
20678
21727
|
{
|
|
20679
21728
|
hover: mediaHover,
|
|
20680
21729
|
isUploading: false,
|
|
20681
21730
|
onReplace: handleMediaReplace,
|
|
21731
|
+
onSelect: handleMediaSelect,
|
|
21732
|
+
onVideoSettingsChange: handleVideoSettingsChange
|
|
21733
|
+
}
|
|
21734
|
+
),
|
|
21735
|
+
selectedMedia && !(selectedMedia.key in uploadingRects) && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
21736
|
+
MediaOverlay,
|
|
21737
|
+
{
|
|
21738
|
+
hover: selectedMedia,
|
|
21739
|
+
selected: true,
|
|
21740
|
+
hovered: mediaHover?.key === selectedMedia.key,
|
|
21741
|
+
isUploading: false,
|
|
21742
|
+
onReplace: handleMediaReplace,
|
|
21743
|
+
onSelect: handleMediaSelect,
|
|
20682
21744
|
onVideoSettingsChange: handleVideoSettingsChange
|
|
20683
21745
|
}
|
|
20684
21746
|
),
|
|
@@ -21084,6 +22146,59 @@ function OhhwellsBridge() {
|
|
|
21084
22146
|
) : null
|
|
21085
22147
|
] });
|
|
21086
22148
|
}
|
|
22149
|
+
|
|
22150
|
+
// src/ui/EmptySection.tsx
|
|
22151
|
+
var import_link = __toESM(require("next/link"), 1);
|
|
22152
|
+
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
22153
|
+
function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey }) {
|
|
22154
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
|
|
22155
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
22156
|
+
"p",
|
|
22157
|
+
{
|
|
22158
|
+
style: {
|
|
22159
|
+
fontFamily: "var(--brand-font-body)",
|
|
22160
|
+
fontSize: "0.75rem",
|
|
22161
|
+
fontWeight: 500,
|
|
22162
|
+
letterSpacing: "0.15em",
|
|
22163
|
+
textTransform: "uppercase",
|
|
22164
|
+
color: "var(--brand-accent)",
|
|
22165
|
+
marginBottom: "1.5rem"
|
|
22166
|
+
},
|
|
22167
|
+
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" }) })
|
|
22168
|
+
}
|
|
22169
|
+
),
|
|
22170
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
22171
|
+
"h1",
|
|
22172
|
+
{
|
|
22173
|
+
style: {
|
|
22174
|
+
fontFamily: "var(--brand-font-heading)",
|
|
22175
|
+
fontSize: "clamp(2rem, 3.5vw, 2.75rem)",
|
|
22176
|
+
lineHeight: 1.1,
|
|
22177
|
+
letterSpacing: "-0.025em",
|
|
22178
|
+
color: "var(--brand-text)",
|
|
22179
|
+
marginBottom: "1rem"
|
|
22180
|
+
},
|
|
22181
|
+
...titleKey ? { "data-ohw-editable": "text", "data-ohw-key": titleKey, "data-ohw-max-length": 80 } : {},
|
|
22182
|
+
children: title
|
|
22183
|
+
}
|
|
22184
|
+
),
|
|
22185
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
22186
|
+
"p",
|
|
22187
|
+
{
|
|
22188
|
+
style: {
|
|
22189
|
+
fontFamily: "var(--brand-font-body)",
|
|
22190
|
+
fontSize: "1rem",
|
|
22191
|
+
lineHeight: 1.7,
|
|
22192
|
+
fontWeight: 300,
|
|
22193
|
+
color: "var(--brand-text-muted)",
|
|
22194
|
+
maxWidth: "340px"
|
|
22195
|
+
},
|
|
22196
|
+
...subtitleKey ? { "data-ohw-editable": "text", "data-ohw-key": subtitleKey, "data-ohw-max-length": 160 } : {},
|
|
22197
|
+
children: "This page doesn't have any content yet."
|
|
22198
|
+
}
|
|
22199
|
+
)
|
|
22200
|
+
] });
|
|
22201
|
+
}
|
|
21087
22202
|
// Annotate the CommonJS export names for ESM import in node:
|
|
21088
22203
|
0 && (module.exports = {
|
|
21089
22204
|
AI_DEFAULT_BRAND,
|
|
@@ -21101,6 +22216,7 @@ function OhhwellsBridge() {
|
|
|
21101
22216
|
DropdownMenuItem,
|
|
21102
22217
|
DropdownMenuSeparator,
|
|
21103
22218
|
DropdownMenuTrigger,
|
|
22219
|
+
EmptySection,
|
|
21104
22220
|
ItemActionToolbar,
|
|
21105
22221
|
ItemInteractionLayer,
|
|
21106
22222
|
LinkEditorPanel,
|