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