@ohhwells/bridge 0.1.78 → 0.1.79-next.238
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 +1239 -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 +1238 -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,7 @@ function StateToggle({
|
|
|
15321
16051
|
);
|
|
15322
16052
|
}
|
|
15323
16053
|
var contentCache = /* @__PURE__ */ new Map();
|
|
16054
|
+
var fetchedContentPaths = /* @__PURE__ */ new Set();
|
|
15324
16055
|
var OHW_LOADER_STYLE = {
|
|
15325
16056
|
position: "fixed",
|
|
15326
16057
|
inset: 0,
|
|
@@ -15439,6 +16170,70 @@ function OhhwellsBridge() {
|
|
|
15439
16170
|
const hoveredImageHasTextOverlapRef = (0, import_react17.useRef)(false);
|
|
15440
16171
|
const dragOverElRef = (0, import_react17.useRef)(null);
|
|
15441
16172
|
const [mediaHover, setMediaHover] = (0, import_react17.useState)(null);
|
|
16173
|
+
const [selectedMedia, setSelectedMedia] = (0, import_react17.useState)(null);
|
|
16174
|
+
const selectedMediaElRef = (0, import_react17.useRef)(null);
|
|
16175
|
+
const clearMediaSelection = (0, import_react17.useCallback)(() => {
|
|
16176
|
+
const prev = selectedMediaElRef.current;
|
|
16177
|
+
selectedMediaElRef.current = null;
|
|
16178
|
+
setSelectedMedia(null);
|
|
16179
|
+
const sectionEl = prev?.closest("[data-ohw-section]") ?? null;
|
|
16180
|
+
if (sectionEl) {
|
|
16181
|
+
postToParentRef.current({
|
|
16182
|
+
type: "ow:section-selected",
|
|
16183
|
+
sectionId: sectionEl.dataset.ohwSection ?? null,
|
|
16184
|
+
sectionLabel: sectionEl.dataset.ohwSectionLabel ?? titleCaseSectionId(sectionEl.dataset.ohwSection ?? ""),
|
|
16185
|
+
key: null
|
|
16186
|
+
});
|
|
16187
|
+
}
|
|
16188
|
+
}, []);
|
|
16189
|
+
const clearMediaSelectionRef = (0, import_react17.useRef)(clearMediaSelection);
|
|
16190
|
+
clearMediaSelectionRef.current = clearMediaSelection;
|
|
16191
|
+
const selectMediaElement = (0, import_react17.useCallback)((el) => {
|
|
16192
|
+
const r2 = el.getBoundingClientRect();
|
|
16193
|
+
const video = el.dataset.ohwEditable === "video" ? el.querySelector("video") : null;
|
|
16194
|
+
selectedMediaElRef.current = el;
|
|
16195
|
+
setSelectedMedia({
|
|
16196
|
+
key: el.dataset.ohwKey ?? "",
|
|
16197
|
+
rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height },
|
|
16198
|
+
elementType: el.dataset.ohwEditable ?? "image",
|
|
16199
|
+
hasTextOverlap: false,
|
|
16200
|
+
isDragOver: false,
|
|
16201
|
+
...video ? { videoAutoplay: video.autoplay, videoMuted: video.muted } : {}
|
|
16202
|
+
});
|
|
16203
|
+
const sectionEl = el.closest("[data-ohw-section]");
|
|
16204
|
+
aiSectionApiRef.current?.selectFromElement(el, { report: false });
|
|
16205
|
+
postToParentRef.current({
|
|
16206
|
+
type: "ow:section-selected",
|
|
16207
|
+
sectionId: sectionEl?.dataset.ohwSection ?? null,
|
|
16208
|
+
sectionLabel: sectionEl ? sectionEl.dataset.ohwSectionLabel ?? titleCaseSectionId(sectionEl.dataset.ohwSection ?? "") : null,
|
|
16209
|
+
key: el.dataset.ohwKey ?? null,
|
|
16210
|
+
// Display name for the pill — the raw key prettifies into fragments ("Img"); the
|
|
16211
|
+
// bridge knows what the node IS, so it names it.
|
|
16212
|
+
keyLabel: el.dataset.ohwEditable === "video" ? "Video" : el.dataset.ohwEditable === "bg-image" ? "Background" : "Image"
|
|
16213
|
+
});
|
|
16214
|
+
}, []);
|
|
16215
|
+
const selectMediaElementRef = (0, import_react17.useRef)(selectMediaElement);
|
|
16216
|
+
selectMediaElementRef.current = selectMediaElement;
|
|
16217
|
+
(0, import_react17.useEffect)(() => {
|
|
16218
|
+
if (!selectedMedia) return;
|
|
16219
|
+
const update = () => {
|
|
16220
|
+
const el = selectedMediaElRef.current;
|
|
16221
|
+
if (!el || !el.isConnected) {
|
|
16222
|
+
clearMediaSelection();
|
|
16223
|
+
return;
|
|
16224
|
+
}
|
|
16225
|
+
const r2 = el.getBoundingClientRect();
|
|
16226
|
+
setSelectedMedia(
|
|
16227
|
+
(prev) => prev ? { ...prev, rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height } } : prev
|
|
16228
|
+
);
|
|
16229
|
+
};
|
|
16230
|
+
window.addEventListener("scroll", update, true);
|
|
16231
|
+
window.addEventListener("resize", update);
|
|
16232
|
+
return () => {
|
|
16233
|
+
window.removeEventListener("scroll", update, true);
|
|
16234
|
+
window.removeEventListener("resize", update);
|
|
16235
|
+
};
|
|
16236
|
+
}, [selectedMedia !== null]);
|
|
15442
16237
|
const [carouselHover, setCarouselHover] = (0, import_react17.useState)(null);
|
|
15443
16238
|
const [uploadingRects, setUploadingRects] = (0, import_react17.useState)({});
|
|
15444
16239
|
const hoveredGapRef = (0, import_react17.useRef)(null);
|
|
@@ -15701,13 +16496,6 @@ function OhhwellsBridge() {
|
|
|
15701
16496
|
const [isItemDragging, setIsItemDragging] = (0, import_react17.useState)(false);
|
|
15702
16497
|
const [isFooterFrameSelection, setIsFooterFrameSelection] = (0, import_react17.useState)(false);
|
|
15703
16498
|
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
16499
|
const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = (0, import_react17.useState)(null);
|
|
15712
16500
|
const [footerHeadingVisible, setFooterHeadingVisible] = (0, import_react17.useState)(null);
|
|
15713
16501
|
const footerDragRef = (0, import_react17.useRef)(null);
|
|
@@ -15722,7 +16510,16 @@ function OhhwellsBridge() {
|
|
|
15722
16510
|
const addNavAfterAnchorRef = (0, import_react17.useRef)(null);
|
|
15723
16511
|
const editContentRef = (0, import_react17.useRef)({});
|
|
15724
16512
|
const aiSectionsRef = (0, import_react17.useRef)("");
|
|
16513
|
+
const brandKitRef = (0, import_react17.useRef)("");
|
|
16514
|
+
const stylesRef = (0, import_react17.useRef)("");
|
|
15725
16515
|
const pendingDeleteUndoRef = (0, import_react17.useRef)(null);
|
|
16516
|
+
const [floatingPanel, setFloatingPanel] = (0, import_react17.useState)(null);
|
|
16517
|
+
const floatingPanelOpenRef = (0, import_react17.useRef)(false);
|
|
16518
|
+
const setFloatingPanelRef = (0, import_react17.useRef)(setFloatingPanel);
|
|
16519
|
+
const [floatingPanelPos, setFloatingPanelPos] = (0, import_react17.useState)(null);
|
|
16520
|
+
const [logoSizeDraft, setLogoSizeDraft] = (0, import_react17.useState)(null);
|
|
16521
|
+
const [editorViewport, setEditorViewport] = (0, import_react17.useState)("desktop");
|
|
16522
|
+
const [parentScrollSnap, setParentScrollSnap] = (0, import_react17.useState)(null);
|
|
15726
16523
|
const [sitePages, setSitePages] = (0, import_react17.useState)([]);
|
|
15727
16524
|
const [sectionsByPath, setSectionsByPath] = (0, import_react17.useState)({});
|
|
15728
16525
|
const sectionsPrefetchGenRef = (0, import_react17.useRef)(0);
|
|
@@ -15731,7 +16528,18 @@ function OhhwellsBridge() {
|
|
|
15731
16528
|
const linkPopoverOpenRef = (0, import_react17.useRef)(false);
|
|
15732
16529
|
const linkPopoverGraceUntilRef = (0, import_react17.useRef)(0);
|
|
15733
16530
|
setLinkPopoverRef.current = setLinkPopover;
|
|
16531
|
+
setFloatingPanelRef.current = setFloatingPanel;
|
|
15734
16532
|
linkPopoverSessionRef.current = linkPopover;
|
|
16533
|
+
floatingPanelOpenRef.current = Boolean(floatingPanel);
|
|
16534
|
+
(0, import_react17.useEffect)(() => {
|
|
16535
|
+
const syncViewport = () => {
|
|
16536
|
+
const next = window.innerWidth <= 480 ? "mobile" : "desktop";
|
|
16537
|
+
setEditorViewport((prev) => prev === next ? prev : next);
|
|
16538
|
+
};
|
|
16539
|
+
syncViewport();
|
|
16540
|
+
window.addEventListener("resize", syncViewport);
|
|
16541
|
+
return () => window.removeEventListener("resize", syncViewport);
|
|
16542
|
+
}, []);
|
|
15735
16543
|
const {
|
|
15736
16544
|
navDragRef,
|
|
15737
16545
|
navDropSlots,
|
|
@@ -17042,15 +17850,31 @@ function OhhwellsBridge() {
|
|
|
17042
17850
|
}
|
|
17043
17851
|
const applyContent = (content) => {
|
|
17044
17852
|
const imageLoads = [];
|
|
17853
|
+
if (typeof content[BRAND_KIT_KEY] === "string") {
|
|
17854
|
+
brandKitRef.current = content[BRAND_KIT_KEY];
|
|
17855
|
+
applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
|
|
17856
|
+
} else {
|
|
17857
|
+
brandKitRef.current = "";
|
|
17858
|
+
applyBrandToDom(null);
|
|
17859
|
+
}
|
|
17045
17860
|
if (typeof content[AI_SECTIONS_KEY] === "string") {
|
|
17046
17861
|
aiSectionsRef.current = content[AI_SECTIONS_KEY];
|
|
17862
|
+
setAiSectionOrder(content[SECTION_ORDER_KEY], window.location.pathname);
|
|
17047
17863
|
applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
|
|
17048
17864
|
}
|
|
17865
|
+
if (typeof content[STYLE_STORE_KEY] === "string") {
|
|
17866
|
+
stylesRef.current = content[STYLE_STORE_KEY];
|
|
17867
|
+
applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
|
|
17868
|
+
}
|
|
17869
|
+
applyBrandChrome(content);
|
|
17049
17870
|
for (const [key, val] of Object.entries(content)) {
|
|
17050
17871
|
if (key === "__ohw_sections") continue;
|
|
17051
17872
|
if (key === AI_SECTIONS_KEY) continue;
|
|
17052
17873
|
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
17053
17874
|
if (LOGO_IMAGE_KEYS.includes(key)) continue;
|
|
17875
|
+
if (key === BRAND_KIT_KEY) continue;
|
|
17876
|
+
if (key === STYLE_STORE_KEY) continue;
|
|
17877
|
+
if (BRAND_CHROME_KEYS.has(key)) continue;
|
|
17054
17878
|
if (applyVideoSettingNode(key, val)) continue;
|
|
17055
17879
|
if (applyCarouselNode(key, val)) continue;
|
|
17056
17880
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
@@ -17116,7 +17940,9 @@ function OhhwellsBridge() {
|
|
|
17116
17940
|
let cancelled = false;
|
|
17117
17941
|
setFetchState("loading");
|
|
17118
17942
|
const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
|
|
17119
|
-
|
|
17943
|
+
const initialPath = pathname;
|
|
17944
|
+
fetchedContentPaths.add(`${subdomain}::${initialPath}`);
|
|
17945
|
+
fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
|
|
17120
17946
|
if (cancelled) return;
|
|
17121
17947
|
const content = data?.content ?? {};
|
|
17122
17948
|
contentCache.set(subdomain, content);
|
|
@@ -17236,10 +18062,28 @@ function OhhwellsBridge() {
|
|
|
17236
18062
|
initSectionInstancesFromContent(content, window.location.pathname);
|
|
17237
18063
|
observer?.disconnect();
|
|
17238
18064
|
try {
|
|
18065
|
+
applyBrandChrome(content);
|
|
18066
|
+
if (typeof content[BRAND_KIT_KEY] === "string") {
|
|
18067
|
+
applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
|
|
18068
|
+
} else {
|
|
18069
|
+
applyBrandToDom(null);
|
|
18070
|
+
}
|
|
18071
|
+
if (typeof content[AI_SECTIONS_KEY] === "string") {
|
|
18072
|
+
setAiSectionOrder(content[SECTION_ORDER_KEY], window.location.pathname);
|
|
18073
|
+
applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
|
|
18074
|
+
}
|
|
18075
|
+
if (typeof content[STYLE_STORE_KEY] === "string") {
|
|
18076
|
+
applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
|
|
18077
|
+
}
|
|
17239
18078
|
for (const [key, val] of Object.entries(content)) {
|
|
17240
18079
|
if (key === "__ohw_sections") continue;
|
|
18080
|
+
if (key === AI_SECTIONS_KEY) continue;
|
|
17241
18081
|
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
17242
18082
|
if (LOGO_IMAGE_KEYS.includes(key)) continue;
|
|
18083
|
+
if (key === BRAND_KIT_KEY) continue;
|
|
18084
|
+
if (key === STYLE_STORE_KEY) continue;
|
|
18085
|
+
if (key === STYLE_STORE_KEY) continue;
|
|
18086
|
+
if (BRAND_CHROME_KEYS.has(key)) continue;
|
|
17243
18087
|
if (applyVideoSettingNode(key, val)) continue;
|
|
17244
18088
|
if (applyCarouselNode(key, val)) continue;
|
|
17245
18089
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
@@ -17285,6 +18129,17 @@ function OhhwellsBridge() {
|
|
|
17285
18129
|
debounceTimer = setTimeout(applyFromCache, 150);
|
|
17286
18130
|
};
|
|
17287
18131
|
applyFromCache();
|
|
18132
|
+
const pathCacheKey = `${subdomain}::${pathname}`;
|
|
18133
|
+
if (!fetchedContentPaths.has(pathCacheKey)) {
|
|
18134
|
+
fetchedContentPaths.add(pathCacheKey);
|
|
18135
|
+
const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
|
|
18136
|
+
fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(pathname)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
|
|
18137
|
+
if (!data?.content) return;
|
|
18138
|
+
contentCache.set(subdomain, data.content);
|
|
18139
|
+
applyFromCache();
|
|
18140
|
+
}).catch(() => {
|
|
18141
|
+
});
|
|
18142
|
+
}
|
|
17288
18143
|
observer = new MutationObserver(scheduleApply);
|
|
17289
18144
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
17290
18145
|
return () => {
|
|
@@ -17380,30 +18235,31 @@ function OhhwellsBridge() {
|
|
|
17380
18235
|
}, [isEditMode, pathname, subdomain, fetchState, resyncSelectedNavigationItem]);
|
|
17381
18236
|
(0, import_react17.useEffect)(() => {
|
|
17382
18237
|
if (!isEditMode) return;
|
|
18238
|
+
let lastPosted = 0;
|
|
17383
18239
|
const measure = () => {
|
|
17384
18240
|
const h = document.body.scrollHeight;
|
|
17385
|
-
if (h > 50
|
|
18241
|
+
if (h > 50 && Math.abs(h - lastPosted) > 1) {
|
|
18242
|
+
lastPosted = h;
|
|
18243
|
+
postToParent2({ type: "ow:height", height: h });
|
|
18244
|
+
}
|
|
18245
|
+
};
|
|
18246
|
+
let raf = null;
|
|
18247
|
+
const schedule = () => {
|
|
18248
|
+
if (raf != null) return;
|
|
18249
|
+
raf = requestAnimationFrame(() => {
|
|
18250
|
+
raf = null;
|
|
18251
|
+
measure();
|
|
18252
|
+
});
|
|
17386
18253
|
};
|
|
17387
18254
|
const t1 = setTimeout(measure, 50);
|
|
17388
18255
|
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);
|
|
18256
|
+
const ro = new ResizeObserver(schedule);
|
|
18257
|
+
ro.observe(document.body);
|
|
17402
18258
|
return () => {
|
|
17403
18259
|
clearTimeout(t1);
|
|
17404
18260
|
clearTimeout(t2);
|
|
17405
|
-
|
|
17406
|
-
|
|
18261
|
+
if (raf != null) cancelAnimationFrame(raf);
|
|
18262
|
+
ro.disconnect();
|
|
17407
18263
|
};
|
|
17408
18264
|
}, [pathname, isEditMode, postToParent2]);
|
|
17409
18265
|
(0, import_react17.useEffect)(() => {
|
|
@@ -17644,6 +18500,7 @@ function OhhwellsBridge() {
|
|
|
17644
18500
|
return;
|
|
17645
18501
|
}
|
|
17646
18502
|
const target = e.target;
|
|
18503
|
+
if (target.closest("[data-ohw-ai-review]")) return;
|
|
17647
18504
|
if (target.closest("[data-ohw-toolbar]")) return;
|
|
17648
18505
|
if (target.closest("[data-ohw-state-toggle]")) return;
|
|
17649
18506
|
if (target.closest("[data-ohw-max-badge]")) return;
|
|
@@ -17655,6 +18512,9 @@ function OhhwellsBridge() {
|
|
|
17655
18512
|
)) {
|
|
17656
18513
|
return;
|
|
17657
18514
|
}
|
|
18515
|
+
if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
|
|
18516
|
+
clearMediaSelectionRef.current();
|
|
18517
|
+
}
|
|
17658
18518
|
{
|
|
17659
18519
|
const formEl = getFormElement(target);
|
|
17660
18520
|
const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
|
|
@@ -17806,19 +18666,14 @@ function OhhwellsBridge() {
|
|
|
17806
18666
|
}
|
|
17807
18667
|
const clickedButton = findClosestButtonLike(target);
|
|
17808
18668
|
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
18669
|
if (isMediaEditable(editable) && !buttonOnMedia) {
|
|
17818
18670
|
e.preventDefault();
|
|
17819
18671
|
e.stopPropagation();
|
|
17820
|
-
|
|
17821
|
-
|
|
18672
|
+
if (selectedMediaElRef.current === editable) {
|
|
18673
|
+
postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "", elementType: editable.dataset.ohwEditable ?? "image" });
|
|
18674
|
+
} else {
|
|
18675
|
+
selectMediaElementRef.current(editable);
|
|
18676
|
+
}
|
|
17822
18677
|
return;
|
|
17823
18678
|
}
|
|
17824
18679
|
const socialItem = getSocialItem(editable);
|
|
@@ -17837,11 +18692,6 @@ function OhhwellsBridge() {
|
|
|
17837
18692
|
const hrefLookupTarget = buttonOnMedia ? clickedButton : editable;
|
|
17838
18693
|
const hrefCtx = getHrefKeyFromElement(hrefLookupTarget);
|
|
17839
18694
|
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
18695
|
if (navAnchor) {
|
|
17846
18696
|
e.preventDefault();
|
|
17847
18697
|
e.stopPropagation();
|
|
@@ -17959,6 +18809,7 @@ function OhhwellsBridge() {
|
|
|
17959
18809
|
};
|
|
17960
18810
|
const handleDblClick = (e) => {
|
|
17961
18811
|
const target = e.target;
|
|
18812
|
+
if (target.closest("[data-ohw-ai-review]")) return;
|
|
17962
18813
|
if (target.closest("[data-ohw-toolbar]")) return;
|
|
17963
18814
|
if (target.closest("[data-ohw-state-toggle]")) return;
|
|
17964
18815
|
if (target.closest("[data-ohw-max-badge]")) return;
|
|
@@ -18010,6 +18861,9 @@ function OhhwellsBridge() {
|
|
|
18010
18861
|
setHoveredItemRect(null);
|
|
18011
18862
|
hoveredNavContainerRef.current = null;
|
|
18012
18863
|
setHoveredNavContainerRect(null);
|
|
18864
|
+
siblingHintElRef.current = null;
|
|
18865
|
+
setSiblingHintRect(null);
|
|
18866
|
+
setSiblingHintRects([]);
|
|
18013
18867
|
return;
|
|
18014
18868
|
}
|
|
18015
18869
|
{
|
|
@@ -18128,7 +18982,6 @@ function OhhwellsBridge() {
|
|
|
18128
18982
|
hoveredNavContainerRef.current = null;
|
|
18129
18983
|
setHoveredNavContainerRect(null);
|
|
18130
18984
|
hoveredItemElRef.current = editable;
|
|
18131
|
-
setHoveredItemRect(isSelectedForHoverRef.current(editable) ? null : editable.getBoundingClientRect());
|
|
18132
18985
|
}
|
|
18133
18986
|
}
|
|
18134
18987
|
}
|
|
@@ -18425,7 +19278,7 @@ function OhhwellsBridge() {
|
|
|
18425
19278
|
}
|
|
18426
19279
|
};
|
|
18427
19280
|
const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
|
|
18428
|
-
if (linkPopoverOpenRef.current) {
|
|
19281
|
+
if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
|
|
18429
19282
|
if (hoveredImageRef.current) {
|
|
18430
19283
|
hoveredImageRef.current = null;
|
|
18431
19284
|
hoveredImageHasTextOverlapRef.current = false;
|
|
@@ -18759,7 +19612,9 @@ function OhhwellsBridge() {
|
|
|
18759
19612
|
return;
|
|
18760
19613
|
}
|
|
18761
19614
|
const { y } = toProbeCoords(clientX, clientY, fromParentViewport);
|
|
18762
|
-
const sections = Array.from(document.querySelectorAll("[data-ohw-section]")).
|
|
19615
|
+
const sections = Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
|
|
19616
|
+
(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
|
|
19617
|
+
).sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);
|
|
18763
19618
|
const ZONE = 20;
|
|
18764
19619
|
for (let i = 0; i < sections.length; i++) {
|
|
18765
19620
|
const a = sections[i];
|
|
@@ -18788,8 +19643,7 @@ function OhhwellsBridge() {
|
|
|
18788
19643
|
};
|
|
18789
19644
|
const handleMouseMove = (e) => {
|
|
18790
19645
|
const { clientX, clientY } = e;
|
|
18791
|
-
if (
|
|
18792
|
-
if (isOverEditorChrome(clientX, clientY)) {
|
|
19646
|
+
if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY) || isOverEditorChrome(clientX, clientY)) {
|
|
18793
19647
|
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
18794
19648
|
formHoverElRef.current = null;
|
|
18795
19649
|
setFormHoverRect(null);
|
|
@@ -18797,6 +19651,12 @@ function OhhwellsBridge() {
|
|
|
18797
19651
|
setHoveredItemRect(null);
|
|
18798
19652
|
hoveredNavContainerRef.current = null;
|
|
18799
19653
|
setHoveredNavContainerRect(null);
|
|
19654
|
+
siblingHintElRef.current = null;
|
|
19655
|
+
setSiblingHintRect(null);
|
|
19656
|
+
setSiblingHintRects([]);
|
|
19657
|
+
dismissImageHover();
|
|
19658
|
+
clearImageHover();
|
|
19659
|
+
setSectionGap(null);
|
|
18800
19660
|
return;
|
|
18801
19661
|
}
|
|
18802
19662
|
if (probeSocialsRowAt(clientX, clientY)) return;
|
|
@@ -18808,7 +19668,11 @@ function OhhwellsBridge() {
|
|
|
18808
19668
|
if (e.data?.type !== "ow:pointer-sync") return;
|
|
18809
19669
|
const { clientX, clientY } = e.data;
|
|
18810
19670
|
if (typeof clientX !== "number" || typeof clientY !== "number") return;
|
|
18811
|
-
if (
|
|
19671
|
+
if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
|
|
19672
|
+
dismissImageHover();
|
|
19673
|
+
clearImageHover();
|
|
19674
|
+
return;
|
|
19675
|
+
}
|
|
18812
19676
|
if (probeSocialsRowAt(clientX, clientY)) return;
|
|
18813
19677
|
probeSectionGapAt(clientX, clientY);
|
|
18814
19678
|
probeImageAt(clientX, clientY);
|
|
@@ -19091,10 +19955,23 @@ function OhhwellsBridge() {
|
|
|
19091
19955
|
if (e.data?.type !== "ow:hydrate") return;
|
|
19092
19956
|
const content = e.data.content;
|
|
19093
19957
|
if (!content) return;
|
|
19958
|
+
if (typeof content[BRAND_KIT_KEY] === "string") {
|
|
19959
|
+
brandKitRef.current = content[BRAND_KIT_KEY];
|
|
19960
|
+
applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
|
|
19961
|
+
} else {
|
|
19962
|
+
brandKitRef.current = "";
|
|
19963
|
+
applyBrandToDom(null);
|
|
19964
|
+
}
|
|
19094
19965
|
if (typeof content[AI_SECTIONS_KEY] === "string") {
|
|
19095
19966
|
aiSectionsRef.current = content[AI_SECTIONS_KEY];
|
|
19967
|
+
setAiSectionOrder(content[SECTION_ORDER_KEY], window.location.pathname);
|
|
19096
19968
|
applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
|
|
19097
19969
|
}
|
|
19970
|
+
if (typeof content[STYLE_STORE_KEY] === "string") {
|
|
19971
|
+
stylesRef.current = content[STYLE_STORE_KEY];
|
|
19972
|
+
applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
|
|
19973
|
+
}
|
|
19974
|
+
applyBrandChrome(content);
|
|
19098
19975
|
let sectionsJson = null;
|
|
19099
19976
|
for (const [key, val] of Object.entries(content)) {
|
|
19100
19977
|
if (key === "__ohw_sections") {
|
|
@@ -19104,6 +19981,9 @@ function OhhwellsBridge() {
|
|
|
19104
19981
|
if (key === AI_SECTIONS_KEY) continue;
|
|
19105
19982
|
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
19106
19983
|
if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
|
|
19984
|
+
if (key === BRAND_KIT_KEY) continue;
|
|
19985
|
+
if (key === STYLE_STORE_KEY) continue;
|
|
19986
|
+
if (BRAND_CHROME_KEYS.has(key)) continue;
|
|
19107
19987
|
if (applyVideoSettingNode(key, val)) continue;
|
|
19108
19988
|
if (applyCarouselNode(key, val)) continue;
|
|
19109
19989
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
@@ -19117,6 +19997,8 @@ function OhhwellsBridge() {
|
|
|
19117
19997
|
if (video && video.src !== val) applyVideoSrc(video, val);
|
|
19118
19998
|
} else if (el.dataset.ohwEditable === "link") {
|
|
19119
19999
|
applyLinkHref(el, val);
|
|
20000
|
+
} else if (el.dataset.ohwEditable === "icon") {
|
|
20001
|
+
applyIconMarkup(el, val);
|
|
19120
20002
|
} else if (isIconMarkupValue(val)) {
|
|
19121
20003
|
} else {
|
|
19122
20004
|
el.innerHTML = val;
|
|
@@ -19201,12 +20083,21 @@ function OhhwellsBridge() {
|
|
|
19201
20083
|
nodes: collectEditableNodes(editContentRef.current)
|
|
19202
20084
|
});
|
|
19203
20085
|
};
|
|
20086
|
+
const clearInteractionChrome = () => {
|
|
20087
|
+
deactivateRef.current();
|
|
20088
|
+
deselectRef.current();
|
|
20089
|
+
clearMediaSelectionRef.current();
|
|
20090
|
+
};
|
|
19204
20091
|
const handleAiApplyTree = (e) => {
|
|
19205
20092
|
if (e.data?.type !== "ow:ai-apply-tree") return;
|
|
19206
20093
|
const payload = e.data.payload;
|
|
19207
20094
|
if (!payload || typeof payload.id !== "string" || !isRenderableTree(payload.tree)) return;
|
|
20095
|
+
clearInteractionChrome();
|
|
19208
20096
|
const previous = aiSectionsRef.current;
|
|
19209
|
-
const nextState = applyTreeToState(parseAiSectionsState(previous),
|
|
20097
|
+
const nextState = applyTreeToState(parseAiSectionsState(previous), {
|
|
20098
|
+
...payload,
|
|
20099
|
+
path: payload.path ?? window.location.pathname
|
|
20100
|
+
});
|
|
19210
20101
|
const nextValue = serializeAiSectionsState(nextState);
|
|
19211
20102
|
aiSectionsRef.current = nextValue;
|
|
19212
20103
|
applyAiSectionsToDom(nextState);
|
|
@@ -19227,6 +20118,7 @@ function OhhwellsBridge() {
|
|
|
19227
20118
|
const exists = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
|
|
19228
20119
|
if (!exists) return;
|
|
19229
20120
|
if (isPageFrameSection(exists)) return;
|
|
20121
|
+
clearInteractionChrome();
|
|
19230
20122
|
const previous = aiSectionsRef.current;
|
|
19231
20123
|
const nextState = deleteSectionFromState(parseAiSectionsState(previous), sectionId);
|
|
19232
20124
|
const nextValue = serializeAiSectionsState(nextState);
|
|
@@ -19242,8 +20134,10 @@ function OhhwellsBridge() {
|
|
|
19242
20134
|
const handleAiSetSections = (e) => {
|
|
19243
20135
|
if (e.data?.type !== "ow:ai-set-sections") return;
|
|
19244
20136
|
const value = typeof e.data.value === "string" ? e.data.value : "";
|
|
20137
|
+
clearInteractionChrome();
|
|
19245
20138
|
aiSectionsRef.current = value;
|
|
19246
20139
|
applyAiSectionsToDom(parseAiSectionsState(value));
|
|
20140
|
+
applyStylesToDom(parseStyleStore(stylesRef.current));
|
|
19247
20141
|
const restoredHeight = document.body.scrollHeight;
|
|
19248
20142
|
if (restoredHeight > 50) postToParentRef.current({ type: "ow:height", height: restoredHeight });
|
|
19249
20143
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: AI_SECTIONS_KEY, text: value }] });
|
|
@@ -19259,10 +20153,40 @@ function OhhwellsBridge() {
|
|
|
19259
20153
|
if (!entries) return;
|
|
19260
20154
|
const orderJson = JSON.stringify(entries);
|
|
19261
20155
|
editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
|
|
20156
|
+
setAiSectionOrder(orderJson, window.location.pathname);
|
|
19262
20157
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
|
|
19263
20158
|
window.dispatchEvent(new Event("resize"));
|
|
19264
20159
|
};
|
|
19265
20160
|
window.addEventListener("message", handleMoveSection);
|
|
20161
|
+
const handleAiSetBrand = (e) => {
|
|
20162
|
+
if (e.data?.type !== "ow:ai-set-brand") return;
|
|
20163
|
+
const value = typeof e.data.value === "string" ? e.data.value : "";
|
|
20164
|
+
const previous = brandKitRef.current;
|
|
20165
|
+
brandKitRef.current = value;
|
|
20166
|
+
applyBrandToDom(parseBrandKit(value));
|
|
20167
|
+
if (aiSectionsRef.current) applyAiSectionsToDom(parseAiSectionsState(aiSectionsRef.current));
|
|
20168
|
+
applyStylesToDom(parseStyleStore(stylesRef.current));
|
|
20169
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: BRAND_KIT_KEY, text: value }] });
|
|
20170
|
+
postToParentRef.current({ type: "ow:ai-brand-applied", previous, value });
|
|
20171
|
+
};
|
|
20172
|
+
window.addEventListener("message", handleAiSetBrand);
|
|
20173
|
+
const handleAiSetStyles = (e) => {
|
|
20174
|
+
if (e.data?.type !== "ow:ai-set-styles") return;
|
|
20175
|
+
const value = typeof e.data.value === "string" ? e.data.value : "";
|
|
20176
|
+
const previous = stylesRef.current;
|
|
20177
|
+
stylesRef.current = value;
|
|
20178
|
+
applyStylesToDom(parseStyleStore(value));
|
|
20179
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: STYLE_STORE_KEY, text: value }] });
|
|
20180
|
+
postToParentRef.current({ type: "ow:ai-styles-applied", previous, value });
|
|
20181
|
+
};
|
|
20182
|
+
window.addEventListener("message", handleAiSetStyles);
|
|
20183
|
+
const handleGetBrand = (e) => {
|
|
20184
|
+
if (e.data?.type !== "ow:get-brand") return;
|
|
20185
|
+
const template = deriveTemplateBrand();
|
|
20186
|
+
const value = brandKitRef.current || (template ? JSON.stringify(template) : "");
|
|
20187
|
+
postToParentRef.current({ type: "ow:brand-value", value });
|
|
20188
|
+
};
|
|
20189
|
+
window.addEventListener("message", handleGetBrand);
|
|
19266
20190
|
const handlePanelDragging = (e) => {
|
|
19267
20191
|
if (e.data?.type !== "ow:panel-dragging") return;
|
|
19268
20192
|
if (e.data.dragging) document.documentElement.setAttribute("data-ohw-panel-dragging", "");
|
|
@@ -19320,8 +20244,15 @@ function OhhwellsBridge() {
|
|
|
19320
20244
|
closeLinkPopoverRef.current();
|
|
19321
20245
|
return;
|
|
19322
20246
|
}
|
|
20247
|
+
if (floatingPanelOpenRef.current) {
|
|
20248
|
+
setFloatingPanelRef.current(null);
|
|
20249
|
+
deselectRef.current();
|
|
20250
|
+
deactivateRef.current();
|
|
20251
|
+
return;
|
|
20252
|
+
}
|
|
19323
20253
|
deselectRef.current();
|
|
19324
20254
|
deactivateRef.current();
|
|
20255
|
+
clearMediaSelectionRef.current();
|
|
19325
20256
|
};
|
|
19326
20257
|
window.addEventListener("message", handleDeactivate);
|
|
19327
20258
|
const handleToastAction = (e) => {
|
|
@@ -19407,6 +20338,10 @@ function OhhwellsBridge() {
|
|
|
19407
20338
|
const handleKeyDown = (e) => {
|
|
19408
20339
|
if (e.key === "Escape" && document.querySelector("[data-ohw-section-picker]")) return;
|
|
19409
20340
|
if (e.key === "Escape" && document.querySelector("[data-ohw-more-menu]")) return;
|
|
20341
|
+
if (e.key === "Escape" && selectedMediaElRef.current) {
|
|
20342
|
+
clearMediaSelectionRef.current();
|
|
20343
|
+
return;
|
|
20344
|
+
}
|
|
19410
20345
|
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "a" && activeElRef.current) {
|
|
19411
20346
|
e.preventDefault();
|
|
19412
20347
|
selectAllTextInEditable(activeElRef.current);
|
|
@@ -19566,6 +20501,12 @@ function OhhwellsBridge() {
|
|
|
19566
20501
|
if (aiSectionsRef.current) {
|
|
19567
20502
|
nodes.push({ key: AI_SECTIONS_KEY, type: "sections", text: aiSectionsRef.current });
|
|
19568
20503
|
}
|
|
20504
|
+
if (stylesRef.current) {
|
|
20505
|
+
nodes.push({ key: STYLE_STORE_KEY, type: "styles", text: stylesRef.current });
|
|
20506
|
+
}
|
|
20507
|
+
if (brandKitRef.current) {
|
|
20508
|
+
nodes.push({ key: BRAND_KIT_KEY, type: "brand", text: brandKitRef.current });
|
|
20509
|
+
}
|
|
19569
20510
|
document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
|
|
19570
20511
|
const formKey = formKeyOf(form);
|
|
19571
20512
|
if (!formKey) return;
|
|
@@ -19583,8 +20524,12 @@ function OhhwellsBridge() {
|
|
|
19583
20524
|
if (inserted) {
|
|
19584
20525
|
const tracker = getSectionsTracker();
|
|
19585
20526
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
|
|
19586
|
-
const
|
|
19587
|
-
|
|
20527
|
+
const reportHeight = () => {
|
|
20528
|
+
const h = document.body.scrollHeight;
|
|
20529
|
+
if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
|
|
20530
|
+
};
|
|
20531
|
+
reportHeight();
|
|
20532
|
+
setTimeout(reportHeight, 500);
|
|
19588
20533
|
}
|
|
19589
20534
|
};
|
|
19590
20535
|
const handleSwitchSchedule = (e) => {
|
|
@@ -19981,13 +20926,16 @@ function OhhwellsBridge() {
|
|
|
19981
20926
|
window.removeEventListener("message", handleAiDeleteSection);
|
|
19982
20927
|
window.removeEventListener("message", handleAiSetSections);
|
|
19983
20928
|
window.removeEventListener("message", handleMoveSection);
|
|
20929
|
+
window.removeEventListener("message", handleAiSetBrand);
|
|
20930
|
+
window.removeEventListener("message", handleAiSetStyles);
|
|
20931
|
+
window.removeEventListener("message", handleGetBrand);
|
|
19984
20932
|
window.removeEventListener("message", handlePanelDragging);
|
|
19985
20933
|
window.removeEventListener("message", handleDeleteSection);
|
|
19986
20934
|
window.removeEventListener("message", handleDeactivate);
|
|
19987
|
-
document.documentElement.removeAttribute("data-ohw-panel-dragging");
|
|
19988
20935
|
window.removeEventListener("message", handleToastAction);
|
|
19989
20936
|
window.removeEventListener("message", handleFormCount);
|
|
19990
20937
|
window.removeEventListener("message", handleUiEscape);
|
|
20938
|
+
document.documentElement.removeAttribute("data-ohw-panel-dragging");
|
|
19991
20939
|
autoSaveTimers.current.forEach(clearTimeout);
|
|
19992
20940
|
autoSaveTimers.current.clear();
|
|
19993
20941
|
if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
|
|
@@ -20190,7 +21138,7 @@ function OhhwellsBridge() {
|
|
|
20190
21138
|
postToParent2({
|
|
20191
21139
|
type: "ow:ready",
|
|
20192
21140
|
version: "1",
|
|
20193
|
-
bridgeVersion: "0.1.
|
|
21141
|
+
bridgeVersion: "0.1.79",
|
|
20194
21142
|
path: pathname,
|
|
20195
21143
|
nodes: collectEditableNodes(editContentRef.current),
|
|
20196
21144
|
sections
|
|
@@ -20597,11 +21545,22 @@ function OhhwellsBridge() {
|
|
|
20597
21545
|
const showEditLink = toolbarShowEditLink;
|
|
20598
21546
|
const currentSections = sectionsByPath[pathname] ?? [];
|
|
20599
21547
|
linkPopoverOpenRef.current = linkPopover !== null;
|
|
21548
|
+
const handleMediaSelect = (0, import_react17.useCallback)((key) => {
|
|
21549
|
+
const el = hoveredImageRef.current?.dataset.ohwKey === key ? hoveredImageRef.current : Array.from(document.querySelectorAll(MEDIA_SELECTOR)).find(
|
|
21550
|
+
(m) => (m.dataset.ohwKey ?? "") === key
|
|
21551
|
+
) ?? null;
|
|
21552
|
+
if (!el) return;
|
|
21553
|
+
selectMediaElementRef.current(el);
|
|
21554
|
+
}, []);
|
|
20600
21555
|
const handleMediaReplace = (0, import_react17.useCallback)(
|
|
20601
21556
|
(key) => {
|
|
20602
|
-
postToParent2({
|
|
21557
|
+
postToParent2({
|
|
21558
|
+
type: "ow:image-pick",
|
|
21559
|
+
key,
|
|
21560
|
+
elementType: mediaHover?.elementType ?? selectedMedia?.elementType ?? "image"
|
|
21561
|
+
});
|
|
20603
21562
|
},
|
|
20604
|
-
[postToParent2, mediaHover?.elementType]
|
|
21563
|
+
[postToParent2, mediaHover?.elementType, selectedMedia?.elementType]
|
|
20605
21564
|
);
|
|
20606
21565
|
const handleEditCarousel = (0, import_react17.useCallback)(
|
|
20607
21566
|
(key) => {
|
|
@@ -20673,12 +21632,25 @@ function OhhwellsBridge() {
|
|
|
20673
21632
|
},
|
|
20674
21633
|
`uploading-${key}`
|
|
20675
21634
|
)),
|
|
20676
|
-
mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
21635
|
+
mediaHover && !(mediaHover.key in uploadingRects) && mediaHover.key !== selectedMedia?.key && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
20677
21636
|
MediaOverlay,
|
|
20678
21637
|
{
|
|
20679
21638
|
hover: mediaHover,
|
|
20680
21639
|
isUploading: false,
|
|
20681
21640
|
onReplace: handleMediaReplace,
|
|
21641
|
+
onSelect: handleMediaSelect,
|
|
21642
|
+
onVideoSettingsChange: handleVideoSettingsChange
|
|
21643
|
+
}
|
|
21644
|
+
),
|
|
21645
|
+
selectedMedia && !(selectedMedia.key in uploadingRects) && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
21646
|
+
MediaOverlay,
|
|
21647
|
+
{
|
|
21648
|
+
hover: selectedMedia,
|
|
21649
|
+
selected: true,
|
|
21650
|
+
hovered: mediaHover?.key === selectedMedia.key,
|
|
21651
|
+
isUploading: false,
|
|
21652
|
+
onReplace: handleMediaReplace,
|
|
21653
|
+
onSelect: handleMediaSelect,
|
|
20682
21654
|
onVideoSettingsChange: handleVideoSettingsChange
|
|
20683
21655
|
}
|
|
20684
21656
|
),
|
|
@@ -21084,6 +22056,59 @@ function OhhwellsBridge() {
|
|
|
21084
22056
|
) : null
|
|
21085
22057
|
] });
|
|
21086
22058
|
}
|
|
22059
|
+
|
|
22060
|
+
// src/ui/EmptySection.tsx
|
|
22061
|
+
var import_link = __toESM(require("next/link"), 1);
|
|
22062
|
+
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
22063
|
+
function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey }) {
|
|
22064
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
|
|
22065
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
22066
|
+
"p",
|
|
22067
|
+
{
|
|
22068
|
+
style: {
|
|
22069
|
+
fontFamily: "var(--brand-font-body)",
|
|
22070
|
+
fontSize: "0.75rem",
|
|
22071
|
+
fontWeight: 500,
|
|
22072
|
+
letterSpacing: "0.15em",
|
|
22073
|
+
textTransform: "uppercase",
|
|
22074
|
+
color: "var(--brand-accent)",
|
|
22075
|
+
marginBottom: "1.5rem"
|
|
22076
|
+
},
|
|
22077
|
+
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" }) })
|
|
22078
|
+
}
|
|
22079
|
+
),
|
|
22080
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
22081
|
+
"h1",
|
|
22082
|
+
{
|
|
22083
|
+
style: {
|
|
22084
|
+
fontFamily: "var(--brand-font-heading)",
|
|
22085
|
+
fontSize: "clamp(2rem, 3.5vw, 2.75rem)",
|
|
22086
|
+
lineHeight: 1.1,
|
|
22087
|
+
letterSpacing: "-0.025em",
|
|
22088
|
+
color: "var(--brand-text)",
|
|
22089
|
+
marginBottom: "1rem"
|
|
22090
|
+
},
|
|
22091
|
+
...titleKey ? { "data-ohw-editable": "text", "data-ohw-key": titleKey, "data-ohw-max-length": 80 } : {},
|
|
22092
|
+
children: title
|
|
22093
|
+
}
|
|
22094
|
+
),
|
|
22095
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
22096
|
+
"p",
|
|
22097
|
+
{
|
|
22098
|
+
style: {
|
|
22099
|
+
fontFamily: "var(--brand-font-body)",
|
|
22100
|
+
fontSize: "1rem",
|
|
22101
|
+
lineHeight: 1.7,
|
|
22102
|
+
fontWeight: 300,
|
|
22103
|
+
color: "var(--brand-text-muted)",
|
|
22104
|
+
maxWidth: "340px"
|
|
22105
|
+
},
|
|
22106
|
+
...subtitleKey ? { "data-ohw-editable": "text", "data-ohw-key": subtitleKey, "data-ohw-max-length": 160 } : {},
|
|
22107
|
+
children: "This page doesn't have any content yet."
|
|
22108
|
+
}
|
|
22109
|
+
)
|
|
22110
|
+
] });
|
|
22111
|
+
}
|
|
21087
22112
|
// Annotate the CommonJS export names for ESM import in node:
|
|
21088
22113
|
0 && (module.exports = {
|
|
21089
22114
|
AI_DEFAULT_BRAND,
|
|
@@ -21101,6 +22126,7 @@ function OhhwellsBridge() {
|
|
|
21101
22126
|
DropdownMenuItem,
|
|
21102
22127
|
DropdownMenuSeparator,
|
|
21103
22128
|
DropdownMenuTrigger,
|
|
22129
|
+
EmptySection,
|
|
21104
22130
|
ItemActionToolbar,
|
|
21105
22131
|
ItemInteractionLayer,
|
|
21106
22132
|
LinkEditorPanel,
|