@ohhwells/bridge 0.1.78 → 0.1.79-next.241
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1329 -213
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -5
- package/dist/index.d.ts +21 -5
- package/dist/index.js +1328 -213
- package/dist/index.js.map +1 -1
- package/dist/pages.cjs +141 -0
- package/dist/pages.cjs.map +1 -0
- package/dist/pages.d.cts +45 -0
- package/dist/pages.d.ts +45 -0
- package/dist/pages.js +107 -0
- package/dist/pages.js.map +1 -0
- package/dist/styles.css +510 -452
- package/package.json +8 -3
package/dist/index.js
CHANGED
|
@@ -83,7 +83,12 @@ function parseAiSectionsState(raw) {
|
|
|
83
83
|
media: entry.media && typeof entry.media === "object" ? entry.media : {}
|
|
84
84
|
}));
|
|
85
85
|
const removed = Array.isArray(parsed.removed) ? parsed.removed.filter((id) => typeof id === "string" && id.length > 0) : [];
|
|
86
|
-
return {
|
|
86
|
+
return {
|
|
87
|
+
v: 1,
|
|
88
|
+
sections,
|
|
89
|
+
...removed.length ? { removed } : {},
|
|
90
|
+
...parsed.hideTemplate === true ? { hideTemplate: true } : {}
|
|
91
|
+
};
|
|
87
92
|
} catch {
|
|
88
93
|
return EMPTY_AI_SECTIONS;
|
|
89
94
|
}
|
|
@@ -96,6 +101,7 @@ function applyTreeToState(state, payload) {
|
|
|
96
101
|
const entry = {
|
|
97
102
|
id: payload.id,
|
|
98
103
|
label: payload.label ?? "Generated section",
|
|
104
|
+
...typeof payload.path === "string" && payload.path ? { path: payload.path } : {},
|
|
99
105
|
afterSection: payload.mode === "insert" && !insertBefore ? payload.targetSectionId ?? null : null,
|
|
100
106
|
...insertBefore && payload.targetSectionId ? { beforeSection: payload.targetSectionId } : {},
|
|
101
107
|
...payload.mode === "replace" && payload.targetSectionId ? { replaces: payload.targetSectionId } : {},
|
|
@@ -118,6 +124,320 @@ function deleteSectionFromState(state, sectionId) {
|
|
|
118
124
|
return { ...state, v: 1, sections: state.sections, removed: [...removed, sectionId] };
|
|
119
125
|
}
|
|
120
126
|
|
|
127
|
+
// src/lib/brand-chrome.ts
|
|
128
|
+
var BRAND_NAME_KEY = "__ohw_brand_name";
|
|
129
|
+
var BRAND_TITLE_KEY = "__ohw_site_title";
|
|
130
|
+
var BRAND_FAVICON_LETTER_KEY = "__ohw_favicon_letter";
|
|
131
|
+
var BRAND_CHROME_KEYS = /* @__PURE__ */ new Set([
|
|
132
|
+
BRAND_NAME_KEY,
|
|
133
|
+
BRAND_TITLE_KEY,
|
|
134
|
+
BRAND_FAVICON_LETTER_KEY
|
|
135
|
+
]);
|
|
136
|
+
function upsertMeta(selector, attr, token, value) {
|
|
137
|
+
let el = document.head.querySelector(selector);
|
|
138
|
+
if (!el) {
|
|
139
|
+
el = document.createElement("meta");
|
|
140
|
+
el.setAttribute(attr, token);
|
|
141
|
+
document.head.appendChild(el);
|
|
142
|
+
}
|
|
143
|
+
if (el.getAttribute("content") !== value) el.setAttribute("content", value);
|
|
144
|
+
}
|
|
145
|
+
function escapeXml(value) {
|
|
146
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
147
|
+
}
|
|
148
|
+
function applyLetterFavicon(letter) {
|
|
149
|
+
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>`;
|
|
150
|
+
const href = `data:image/svg+xml,${encodeURIComponent(svg)}`;
|
|
151
|
+
let link = document.head.querySelector('link[rel="icon"]');
|
|
152
|
+
if (!link) {
|
|
153
|
+
link = document.createElement("link");
|
|
154
|
+
link.rel = "icon";
|
|
155
|
+
document.head.appendChild(link);
|
|
156
|
+
}
|
|
157
|
+
link.type = "image/svg+xml";
|
|
158
|
+
if (link.href !== href) link.href = href;
|
|
159
|
+
}
|
|
160
|
+
function applyBrandChrome(content) {
|
|
161
|
+
const name = content[BRAND_NAME_KEY];
|
|
162
|
+
if (typeof name === "string" && name.length > 0) {
|
|
163
|
+
document.querySelectorAll("[data-ohw-wordmark]").forEach((el) => {
|
|
164
|
+
if (el.textContent !== name) el.textContent = name;
|
|
165
|
+
if (el.getAttribute("title") !== name) el.setAttribute("title", name);
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
const title = content[BRAND_TITLE_KEY];
|
|
169
|
+
if (typeof title === "string" && title.length > 0) {
|
|
170
|
+
if (document.title !== title) document.title = title;
|
|
171
|
+
upsertMeta('meta[property="og:title"]', "property", "og:title", title);
|
|
172
|
+
upsertMeta('meta[name="twitter:title"]', "name", "twitter:title", title);
|
|
173
|
+
}
|
|
174
|
+
const letter = content[BRAND_FAVICON_LETTER_KEY];
|
|
175
|
+
if (typeof letter === "string" && letter.length > 0) {
|
|
176
|
+
applyLetterFavicon(letter);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// src/lib/brand-kit.ts
|
|
181
|
+
var BRAND_KIT_KEY = "__ohw_brand";
|
|
182
|
+
var BRAND_VAR_PREFIX = "--ohw-brand-";
|
|
183
|
+
var BRAND_VAR_NAMES = ["primary", "accent", "light", "dark", "surface", "border", "muted"].map(
|
|
184
|
+
(role) => `${BRAND_VAR_PREFIX}${role}`
|
|
185
|
+
);
|
|
186
|
+
var FONT_VARS = {
|
|
187
|
+
heading: ["--font-heading", "--font-display", "--brand-font-heading"],
|
|
188
|
+
body: ["--font-body", "--brand-font-body"]
|
|
189
|
+
};
|
|
190
|
+
var BRAND_FONT_LINK_ID = "ohw-brand-fonts";
|
|
191
|
+
function brandColorVars(kit) {
|
|
192
|
+
const { dark, primary, accent, light } = kit.palette;
|
|
193
|
+
const mix = (a, aPct, b) => `color-mix(in srgb, ${a} ${aPct}%, ${b})`;
|
|
194
|
+
return {
|
|
195
|
+
[`${BRAND_VAR_PREFIX}primary`]: primary,
|
|
196
|
+
[`${BRAND_VAR_PREFIX}accent`]: accent,
|
|
197
|
+
[`${BRAND_VAR_PREFIX}light`]: light,
|
|
198
|
+
[`${BRAND_VAR_PREFIX}dark`]: dark,
|
|
199
|
+
[`${BRAND_VAR_PREFIX}surface`]: mix(light, 95, dark),
|
|
200
|
+
[`${BRAND_VAR_PREFIX}border`]: mix(light, 85, dark),
|
|
201
|
+
[`${BRAND_VAR_PREFIX}muted`]: mix(dark, 62, light)
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
function parseBrandKit(raw) {
|
|
205
|
+
if (!raw) return null;
|
|
206
|
+
try {
|
|
207
|
+
const parsed = JSON.parse(raw);
|
|
208
|
+
const p = parsed?.palette;
|
|
209
|
+
const f = parsed?.fonts;
|
|
210
|
+
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") {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
return {
|
|
214
|
+
palette: { dark: p.dark, primary: p.primary, accent: p.accent, light: p.light },
|
|
215
|
+
fonts: { heading: f.heading, body: f.body }
|
|
216
|
+
};
|
|
217
|
+
} catch {
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
function familyOf(stack) {
|
|
222
|
+
const first = stack.split(",")[0]?.trim() ?? "";
|
|
223
|
+
return first.replace(/^['"]|['"]$/g, "");
|
|
224
|
+
}
|
|
225
|
+
function loadBrandFonts(families) {
|
|
226
|
+
const unique = [...new Set(families.map((f) => f.trim()).filter(Boolean))];
|
|
227
|
+
if (unique.length === 0) return;
|
|
228
|
+
const spec = unique.map((f) => `${f.replace(/ /g, "+")}:400,500,600,700`).join("|");
|
|
229
|
+
const href = `https://fonts.googleapis.com/css?family=${spec}&display=swap`;
|
|
230
|
+
let link = document.getElementById(BRAND_FONT_LINK_ID);
|
|
231
|
+
if (!link) {
|
|
232
|
+
link = document.createElement("link");
|
|
233
|
+
link.id = BRAND_FONT_LINK_ID;
|
|
234
|
+
link.rel = "stylesheet";
|
|
235
|
+
document.head.appendChild(link);
|
|
236
|
+
}
|
|
237
|
+
if (link.href !== href) link.href = href;
|
|
238
|
+
}
|
|
239
|
+
function applyBrandToDom(kit) {
|
|
240
|
+
const root = document.documentElement;
|
|
241
|
+
if (!kit) {
|
|
242
|
+
for (const name of BRAND_VAR_NAMES) root.style.removeProperty(name);
|
|
243
|
+
for (const name of [...FONT_VARS.heading, ...FONT_VARS.body]) root.style.removeProperty(name);
|
|
244
|
+
document.getElementById(BRAND_FONT_LINK_ID)?.remove();
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
for (const [name, value] of Object.entries(brandColorVars(kit))) root.style.setProperty(name, value);
|
|
248
|
+
for (const name of FONT_VARS.heading) root.style.setProperty(name, kit.fonts.heading);
|
|
249
|
+
for (const name of FONT_VARS.body) root.style.setProperty(name, kit.fonts.body);
|
|
250
|
+
loadBrandFonts([familyOf(kit.fonts.heading), familyOf(kit.fonts.body)]);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// src/lib/section-styles.ts
|
|
254
|
+
var STYLE_STORE_KEY = "__ohw_styles";
|
|
255
|
+
var STYLE_SHEET_ID = "ohw-section-styles";
|
|
256
|
+
function parseStyleStore(raw) {
|
|
257
|
+
if (!raw) return null;
|
|
258
|
+
try {
|
|
259
|
+
const parsed = JSON.parse(raw);
|
|
260
|
+
if (parsed?.v !== 1) return null;
|
|
261
|
+
return {
|
|
262
|
+
v: 1,
|
|
263
|
+
sections: typeof parsed.sections === "object" && parsed.sections ? parsed.sections : {},
|
|
264
|
+
nodes: typeof parsed.nodes === "object" && parsed.nodes ? parsed.nodes : {}
|
|
265
|
+
};
|
|
266
|
+
} catch {
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
var BG_VALUES = {
|
|
271
|
+
surface: "color-mix(in srgb, var(--ohw-brand-light, var(--color-light, #ECECEB)) 94%, var(--ohw-brand-dark, var(--color-dark, #0C0A09)))",
|
|
272
|
+
accent: "var(--ohw-brand-primary, var(--color-primary, #0078E5))",
|
|
273
|
+
"accent-soft": "color-mix(in srgb, var(--ohw-brand-primary, var(--color-primary, #0078E5)) 12%, var(--ohw-brand-light, var(--color-light, #FFFFFF)))"
|
|
274
|
+
};
|
|
275
|
+
var HEADLINE_SIZES = { md: 24, lg: 32, xl: 40, display: 56 };
|
|
276
|
+
function styleSheetCss() {
|
|
277
|
+
const rules = [];
|
|
278
|
+
for (const [tone, value] of Object.entries(BG_VALUES)) {
|
|
279
|
+
rules.push(`[data-ohw-style-bg="${tone}"] { background: ${value} !important; }`);
|
|
280
|
+
}
|
|
281
|
+
rules.push(
|
|
282
|
+
`[data-ohw-style-bg="accent"] { color: var(--ohw-brand-light, var(--color-light, #FFFFFF)) !important; }`
|
|
283
|
+
);
|
|
284
|
+
rules.push(
|
|
285
|
+
`[data-ohw-style-distribution="space-between"] { display: flex !important; flex-direction: column; justify-content: space-between; }`,
|
|
286
|
+
`[data-ohw-style-distribution="center"] { display: flex !important; flex-direction: column; justify-content: center; }`
|
|
287
|
+
);
|
|
288
|
+
for (const [scale, size] of Object.entries(HEADLINE_SIZES)) {
|
|
289
|
+
rules.push(
|
|
290
|
+
`[data-ohw-style-headline="${scale}"] :is(h1, h2, h3) { font-size: ${size}px !important; line-height: 1.15 !important; }`
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
rules.push(
|
|
294
|
+
`[data-ohw-style-aspect="fill-height"] img { height: 100% !important; aspect-ratio: auto !important; object-fit: cover; }`
|
|
295
|
+
);
|
|
296
|
+
for (const aspect of ["1:1", "4:5", "3:4", "3:2", "16:9", "2:1", "3:1"]) {
|
|
297
|
+
rules.push(
|
|
298
|
+
`[data-ohw-style-aspect="${aspect.replace(":", "-")}"] img { aspect-ratio: ${aspect.replace(":", " / ")} !important; height: auto !important; object-fit: cover; }`
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
const pad = { tight: 40, balanced: 64, airy: 96 };
|
|
302
|
+
for (const [spacing, px] of Object.entries(pad)) {
|
|
303
|
+
rules.push(
|
|
304
|
+
`[data-ohw-style-spacing="${spacing}"] { padding-top: ${px}px !important; padding-bottom: ${px}px !important; }`
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
return rules.join("\n");
|
|
308
|
+
}
|
|
309
|
+
var STYLE_FONT_LINK_ID = "ohw-style-fonts";
|
|
310
|
+
function loadStyleFonts(families) {
|
|
311
|
+
const unique = [...new Set(families.map((f) => f.trim()).filter(Boolean))];
|
|
312
|
+
const existing = document.getElementById(STYLE_FONT_LINK_ID);
|
|
313
|
+
if (unique.length === 0) {
|
|
314
|
+
existing?.remove();
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
const spec = unique.map((f) => `${f.replace(/ /g, "+")}:400,500,600,700`).join("|");
|
|
318
|
+
const href = `https://fonts.googleapis.com/css?family=${spec}&display=swap`;
|
|
319
|
+
let link = existing;
|
|
320
|
+
if (!link) {
|
|
321
|
+
link = document.createElement("link");
|
|
322
|
+
link.id = STYLE_FONT_LINK_ID;
|
|
323
|
+
link.rel = "stylesheet";
|
|
324
|
+
document.head.appendChild(link);
|
|
325
|
+
}
|
|
326
|
+
if (link.href !== href) link.href = href;
|
|
327
|
+
}
|
|
328
|
+
var SECTION_ATTRS = {
|
|
329
|
+
sectionBackground: "data-ohw-style-bg",
|
|
330
|
+
textDistribution: "data-ohw-style-distribution",
|
|
331
|
+
headlineScale: "data-ohw-style-headline",
|
|
332
|
+
imageAspect: "data-ohw-style-aspect",
|
|
333
|
+
spacing: "data-ohw-style-spacing"
|
|
334
|
+
};
|
|
335
|
+
var NODE_WROTE_ATTR = "data-ohw-style-node";
|
|
336
|
+
var NODE_PROPS = ["color", "font-family", "font-size", "background"];
|
|
337
|
+
function saveInline(el, prop) {
|
|
338
|
+
const attr = `data-ohw-style-prev-${prop}`;
|
|
339
|
+
if (el.hasAttribute(attr)) return;
|
|
340
|
+
const value = el.style.getPropertyValue(prop) || (prop === "background" ? el.style.getPropertyValue("background-color") : "");
|
|
341
|
+
el.setAttribute(attr, value);
|
|
342
|
+
}
|
|
343
|
+
function restoreInline(el, prop) {
|
|
344
|
+
const attr = `data-ohw-style-prev-${prop}`;
|
|
345
|
+
if (!el.hasAttribute(attr)) return;
|
|
346
|
+
const prev = el.getAttribute(attr) ?? "";
|
|
347
|
+
if (prev) el.style.setProperty(prop, prev);
|
|
348
|
+
else el.style.removeProperty(prop);
|
|
349
|
+
el.removeAttribute(attr);
|
|
350
|
+
}
|
|
351
|
+
function ensureStyleSheet() {
|
|
352
|
+
let el = document.getElementById(STYLE_SHEET_ID);
|
|
353
|
+
if (!el) {
|
|
354
|
+
el = document.createElement("style");
|
|
355
|
+
el.id = STYLE_SHEET_ID;
|
|
356
|
+
document.head.appendChild(el);
|
|
357
|
+
}
|
|
358
|
+
const css = styleSheetCss();
|
|
359
|
+
if (el.textContent !== css) el.textContent = css;
|
|
360
|
+
}
|
|
361
|
+
function clearSectionAttrs(root) {
|
|
362
|
+
for (const attr of Object.values(SECTION_ATTRS)) {
|
|
363
|
+
for (const el of Array.from(root.querySelectorAll(`[${attr}]`))) el.removeAttribute(attr);
|
|
364
|
+
}
|
|
365
|
+
for (const el of Array.from(root.querySelectorAll("[data-ohw-style-bgcolor]"))) {
|
|
366
|
+
restoreInline(el, "background");
|
|
367
|
+
el.removeAttribute("data-ohw-style-bgcolor");
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
function clearNodeProps(root) {
|
|
371
|
+
for (const el of Array.from(root.querySelectorAll(`[${NODE_WROTE_ATTR}]`))) {
|
|
372
|
+
const h = el;
|
|
373
|
+
for (const prop of NODE_PROPS) restoreInline(h, prop);
|
|
374
|
+
h.removeAttribute(NODE_WROTE_ATTR);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
function buttonSurfaceOf(el) {
|
|
378
|
+
return el.closest("a, button") ?? el;
|
|
379
|
+
}
|
|
380
|
+
function applyStylesToDom(store) {
|
|
381
|
+
ensureStyleSheet();
|
|
382
|
+
clearSectionAttrs(document);
|
|
383
|
+
clearNodeProps(document);
|
|
384
|
+
loadStyleFonts(
|
|
385
|
+
store ? Object.values(store.nodes).flatMap((n) => n.fontFamily ? [n.fontFamily] : []) : []
|
|
386
|
+
);
|
|
387
|
+
if (!store) return;
|
|
388
|
+
for (const [sectionId, override] of Object.entries(store.sections)) {
|
|
389
|
+
const sections = document.querySelectorAll(
|
|
390
|
+
`[data-ohw-section="${CSS.escape(sectionId)}"]`
|
|
391
|
+
);
|
|
392
|
+
for (const marker of Array.from(sections)) {
|
|
393
|
+
const section = marker.querySelector(":scope > [data-ai-section]") ?? marker;
|
|
394
|
+
for (const [prop, attr] of Object.entries(SECTION_ATTRS)) {
|
|
395
|
+
const value = override[prop];
|
|
396
|
+
if (value === void 0) continue;
|
|
397
|
+
if (prop === "sectionBackground" && override.sectionBackgroundColor !== void 0) continue;
|
|
398
|
+
section.setAttribute(attr, String(value).replace(":", "-"));
|
|
399
|
+
}
|
|
400
|
+
if (override.sectionBackgroundColor !== void 0) {
|
|
401
|
+
saveInline(section, "background");
|
|
402
|
+
section.style.setProperty("background", override.sectionBackgroundColor, "important");
|
|
403
|
+
section.setAttribute("data-ohw-style-bgcolor", "");
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
for (const [key, override] of Object.entries(store.nodes)) {
|
|
408
|
+
const nodes = document.querySelectorAll(`[data-ohw-key="${CSS.escape(key)}"]`);
|
|
409
|
+
for (const el of Array.from(nodes)) {
|
|
410
|
+
if (override.color !== void 0) {
|
|
411
|
+
saveInline(el, "color");
|
|
412
|
+
el.style.setProperty("color", override.color, "important");
|
|
413
|
+
el.setAttribute(NODE_WROTE_ATTR, "");
|
|
414
|
+
}
|
|
415
|
+
if (override.fontFamily !== void 0) {
|
|
416
|
+
saveInline(el, "font-family");
|
|
417
|
+
el.style.setProperty("font-family", `'${override.fontFamily}'`, "important");
|
|
418
|
+
el.setAttribute(NODE_WROTE_ATTR, "");
|
|
419
|
+
}
|
|
420
|
+
if (override.fontSize !== void 0) {
|
|
421
|
+
saveInline(el, "font-size");
|
|
422
|
+
el.style.setProperty("font-size", `${override.fontSize}px`, "important");
|
|
423
|
+
el.setAttribute(NODE_WROTE_ATTR, "");
|
|
424
|
+
}
|
|
425
|
+
if (override.buttonBackground !== void 0 || override.buttonText !== void 0) {
|
|
426
|
+
const surface = buttonSurfaceOf(el);
|
|
427
|
+
if (override.buttonBackground !== void 0) {
|
|
428
|
+
saveInline(surface, "background");
|
|
429
|
+
surface.style.setProperty("background", override.buttonBackground, "important");
|
|
430
|
+
}
|
|
431
|
+
if (override.buttonText !== void 0) {
|
|
432
|
+
saveInline(surface, "color");
|
|
433
|
+
surface.style.setProperty("color", override.buttonText, "important");
|
|
434
|
+
}
|
|
435
|
+
surface.setAttribute(NODE_WROTE_ATTR, "");
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
121
441
|
// src/ui/ai-tree/aiSectionsManager.tsx
|
|
122
442
|
import { flushSync } from "react-dom";
|
|
123
443
|
import { createRoot } from "react-dom/client";
|
|
@@ -132,7 +452,8 @@ function lucideByName(name) {
|
|
|
132
452
|
}
|
|
133
453
|
var typeStyle = (spec, font) => ({
|
|
134
454
|
fontFamily: font,
|
|
135
|
-
|
|
455
|
+
// Headings shrink with the viewport (reaching full size around ~900px wide); body copy stays put.
|
|
456
|
+
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,
|
|
136
457
|
lineHeight: spec.line,
|
|
137
458
|
fontWeight: spec.weight
|
|
138
459
|
});
|
|
@@ -141,12 +462,58 @@ var featureLines = (value) => value.split(/\n|<br\s*\/?>/i).map((line) => line.t
|
|
|
141
462
|
var CHECK_MASK = `data:image/svg+xml,${encodeURIComponent(
|
|
142
463
|
'<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>'
|
|
143
464
|
)}`;
|
|
465
|
+
var AI_MOBILE_CSS = [
|
|
466
|
+
"@media (max-width: 768px){",
|
|
467
|
+
"[data-ai-section]{overflow-x:hidden}",
|
|
468
|
+
"[data-ai-container]{padding:0 20px !important}",
|
|
469
|
+
"[data-ai-row]{display:flex !important;flex-direction:column !important;align-items:stretch !important}",
|
|
470
|
+
"[data-ai-cell]{width:100%;min-width:0}",
|
|
471
|
+
"[data-ai-grid]{grid-template-columns:1fr !important}",
|
|
472
|
+
// Group containers flatten to a column on phones; span placements come along for free.
|
|
473
|
+
"[data-ai-group]{display:flex !important;flex-direction:column !important}",
|
|
474
|
+
"[data-ai-group] > *{grid-column:auto !important}",
|
|
475
|
+
"[data-ai-section] img{max-width:100%}",
|
|
476
|
+
"}",
|
|
477
|
+
"@media (min-width: 769px) and (max-width: 1024px){",
|
|
478
|
+
"[data-ai-grid]{grid-template-columns:repeat(2, 1fr) !important}",
|
|
479
|
+
"}"
|
|
480
|
+
].join("");
|
|
144
481
|
var FEATURE_LINE_CSS = [
|
|
145
482
|
"[data-ai-features]>div{position:relative;padding-left:40px;min-height:24px}",
|
|
146
483
|
'[data-ai-features]>div::before{content:"";position:absolute;left:0;top:1px;width:24px;height:24px;',
|
|
147
484
|
`background-color:currentColor;-webkit-mask:url("${CHECK_MASK}") no-repeat 0 0/24px 24px;`,
|
|
148
485
|
`mask:url("${CHECK_MASK}") no-repeat 0 0/24px 24px}`
|
|
149
486
|
].join("");
|
|
487
|
+
function hexLuminance(color) {
|
|
488
|
+
const m = /^#([0-9a-f]{6})$/i.exec(color.trim());
|
|
489
|
+
if (!m) return null;
|
|
490
|
+
const [r2, g, b] = [0, 2, 4].map((i) => {
|
|
491
|
+
const c = parseInt(m[1].slice(i, i + 2), 16) / 255;
|
|
492
|
+
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
|
493
|
+
});
|
|
494
|
+
return 0.2126 * r2 + 0.7152 * g + 0.0722 * b;
|
|
495
|
+
}
|
|
496
|
+
function hexContrast(a, b) {
|
|
497
|
+
const la = hexLuminance(a);
|
|
498
|
+
const lb = hexLuminance(b);
|
|
499
|
+
if (la === null || lb === null) return null;
|
|
500
|
+
const [hi, lo] = la > lb ? [la, lb] : [lb, la];
|
|
501
|
+
return (hi + 0.05) / (lo + 0.05);
|
|
502
|
+
}
|
|
503
|
+
function accentBandContext(brand) {
|
|
504
|
+
const p = brand.palette;
|
|
505
|
+
const lightWins = (hexContrast(p.light, p.primary) ?? 99) >= (hexContrast(p.dark, p.primary) ?? 0);
|
|
506
|
+
if (lightWins) {
|
|
507
|
+
return {
|
|
508
|
+
brand: { ...brand, palette: { dark: p.light, primary: p.light, accent: p.light, light: p.primary } },
|
|
509
|
+
buttonLabel: p.primary
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
return {
|
|
513
|
+
brand: { ...brand, palette: { dark: p.dark, primary: p.dark, accent: p.dark, light: p.light } },
|
|
514
|
+
buttonLabel: p.light
|
|
515
|
+
};
|
|
516
|
+
}
|
|
150
517
|
function textAttrs(ctx, path) {
|
|
151
518
|
return ctx.keyFor ? { "data-ohw-key": ctx.keyFor(path), "data-ohw-editable": "text" } : {};
|
|
152
519
|
}
|
|
@@ -158,7 +525,12 @@ var AI_RESPONSIVE_CSS = [
|
|
|
158
525
|
"@media (max-width: 640px) {",
|
|
159
526
|
" [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
|
|
160
527
|
" [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
|
|
528
|
+
// Group containers flatten to a column on phones; span placements come along for free.
|
|
529
|
+
" [data-ai-responsive] [data-ai-group] { display: flex !important; flex-direction: column !important; }",
|
|
530
|
+
" [data-ai-responsive] [data-ai-group] > * { grid-column: auto !important; }",
|
|
161
531
|
" [data-ai-responsive] [data-ai-section-inner] { padding-left: 20px !important; padding-right: 20px !important; }",
|
|
532
|
+
" [data-ai-responsive] { overflow-x: hidden; }",
|
|
533
|
+
" [data-ai-responsive] img { max-width: 100%; }",
|
|
162
534
|
"}"
|
|
163
535
|
].join("\n");
|
|
164
536
|
var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
|
|
@@ -236,7 +608,7 @@ function ButtonEl({
|
|
|
236
608
|
}) {
|
|
237
609
|
const secondary = slots.variant === "secondary";
|
|
238
610
|
const href = str(slots.href);
|
|
239
|
-
const linkAttrs = ctx.keyFor && path ? { "data-ohw-href-key": ctx.keyFor(`${path}.href`) } : {};
|
|
611
|
+
const linkAttrs = ctx.keyFor && path ? { "data-ohw-href-key": ctx.keyFor(`${path}.href`), "data-ohw-role": "button" } : {};
|
|
240
612
|
return /* @__PURE__ */ jsx(
|
|
241
613
|
"a",
|
|
242
614
|
{
|
|
@@ -252,7 +624,7 @@ function ButtonEl({
|
|
|
252
624
|
textDecoration: "none",
|
|
253
625
|
cursor: "pointer",
|
|
254
626
|
...typeStyle(AI_TREE_TOKENS.type.button, ctx.brand.fonts.body),
|
|
255
|
-
...secondary ? { border: `1px solid ${ctx.brand.palette.dark}`, color: ctx.brand.palette.dark } : { background: ctx.brand.palette.primary, color: AI_TREE_TOKENS.textPrimaryForeground }
|
|
627
|
+
...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 }
|
|
256
628
|
},
|
|
257
629
|
children: /* @__PURE__ */ jsx("span", { ...path ? textAttrs(ctx, `${path}.label`) : {}, children: str(slots.label) })
|
|
258
630
|
}
|
|
@@ -758,7 +1130,24 @@ function CardBlock({ node, ctx, path }) {
|
|
|
758
1130
|
minWidth: 0
|
|
759
1131
|
},
|
|
760
1132
|
children: [
|
|
761
|
-
media && (horizontal ? /* @__PURE__ */ jsx(
|
|
1133
|
+
media && (horizontal ? /* @__PURE__ */ jsx(
|
|
1134
|
+
"div",
|
|
1135
|
+
{
|
|
1136
|
+
style: (
|
|
1137
|
+
// An icon hugs its glyph — flex:1 gave a 48px icon half the card and pushed the
|
|
1138
|
+
// text to the far side. Photos keep the half-and-half split. The inset has no
|
|
1139
|
+
// inner padding (the photo split absorbed that), so the icon carries its own gap.
|
|
1140
|
+
/^(lucide|simple):/.test(mediaRef) ? {
|
|
1141
|
+
flexShrink: 0,
|
|
1142
|
+
display: "flex",
|
|
1143
|
+
alignItems: "center",
|
|
1144
|
+
padding: mediaInset,
|
|
1145
|
+
[mediaPosition === "right" ? "marginLeft" : "marginRight"]: 20
|
|
1146
|
+
} : { flex: 1, minWidth: 0, display: "flex", alignItems: "center", padding: mediaInset }
|
|
1147
|
+
),
|
|
1148
|
+
children: media
|
|
1149
|
+
}
|
|
1150
|
+
) : /* @__PURE__ */ jsx(
|
|
762
1151
|
"div",
|
|
763
1152
|
{
|
|
764
1153
|
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" },
|
|
@@ -849,13 +1238,44 @@ function AccordionBlock({ node, ctx, path }) {
|
|
|
849
1238
|
) })
|
|
850
1239
|
] }, i)) });
|
|
851
1240
|
}
|
|
1241
|
+
function useIsMobile() {
|
|
1242
|
+
const [mobile, setMobile] = React.useState(
|
|
1243
|
+
() => typeof window !== "undefined" && window.matchMedia("(max-width: 768px)").matches
|
|
1244
|
+
);
|
|
1245
|
+
React.useEffect(() => {
|
|
1246
|
+
const mq = window.matchMedia("(max-width: 768px)");
|
|
1247
|
+
const update = () => setMobile(mq.matches);
|
|
1248
|
+
update();
|
|
1249
|
+
mq.addEventListener("change", update);
|
|
1250
|
+
return () => mq.removeEventListener("change", update);
|
|
1251
|
+
}, []);
|
|
1252
|
+
return mobile;
|
|
1253
|
+
}
|
|
852
1254
|
function Carousel({ items, itemsPerRow, ctx }) {
|
|
1255
|
+
const isMobile = useIsMobile();
|
|
1256
|
+
const perPage = isMobile ? 1 : itemsPerRow;
|
|
1257
|
+
const pages = Math.max(1, Math.ceil(items.length / perPage));
|
|
853
1258
|
const [page, setPage] = React.useState(0);
|
|
854
|
-
const pages = Math.max(1, Math.ceil(items.length / itemsPerRow));
|
|
855
1259
|
const current = Math.min(page, pages - 1);
|
|
1260
|
+
if (pages <= 1) {
|
|
1261
|
+
const cols = Math.max(1, Math.min(items.length, itemsPerRow));
|
|
1262
|
+
return /* @__PURE__ */ jsx(
|
|
1263
|
+
"div",
|
|
1264
|
+
{
|
|
1265
|
+
"data-ai-grid": String(cols),
|
|
1266
|
+
style: {
|
|
1267
|
+
display: "grid",
|
|
1268
|
+
gridTemplateColumns: `repeat(${cols}, 1fr)`,
|
|
1269
|
+
gap: AI_TREE_TOKENS.spacing8,
|
|
1270
|
+
alignItems: "start"
|
|
1271
|
+
},
|
|
1272
|
+
children: items
|
|
1273
|
+
}
|
|
1274
|
+
);
|
|
1275
|
+
}
|
|
856
1276
|
const pageGroups = Array.from(
|
|
857
1277
|
{ length: pages },
|
|
858
|
-
(_, p) => items.slice(p *
|
|
1278
|
+
(_, p) => items.slice(p * perPage, (p + 1) * perPage)
|
|
859
1279
|
);
|
|
860
1280
|
const chrome = (enabled) => ({
|
|
861
1281
|
border: `1px solid ${ctx.brand.palette.dark}`,
|
|
@@ -880,55 +1300,69 @@ function Carousel({ items, itemsPerRow, ctx }) {
|
|
|
880
1300
|
cursor: "pointer",
|
|
881
1301
|
padding: 0
|
|
882
1302
|
});
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
children: /* @__PURE__ */ jsx(ArrowLeft, { size: 24, strokeWidth: 1.5, "aria-hidden": true })
|
|
893
|
-
}
|
|
894
|
-
),
|
|
895
|
-
/* @__PURE__ */ jsx("div", { style: { flex: 1, minWidth: 0, overflow: "hidden" }, children: /* @__PURE__ */ jsx(
|
|
1303
|
+
const viewport = /* @__PURE__ */ jsx("div", { style: { flex: isMobile ? "0 0 auto" : 1, minWidth: 0, width: "100%", overflow: "hidden" }, children: /* @__PURE__ */ jsx(
|
|
1304
|
+
"div",
|
|
1305
|
+
{
|
|
1306
|
+
style: {
|
|
1307
|
+
display: "flex",
|
|
1308
|
+
transform: `translateX(-${current * 100}%)`,
|
|
1309
|
+
transition: "transform 0.4s ease"
|
|
1310
|
+
},
|
|
1311
|
+
children: pageGroups.map((group, p) => /* @__PURE__ */ jsx(
|
|
896
1312
|
"div",
|
|
897
1313
|
{
|
|
1314
|
+
"data-ai-grid": String(perPage),
|
|
898
1315
|
style: {
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
1316
|
+
flex: "0 0 100%",
|
|
1317
|
+
display: "grid",
|
|
1318
|
+
gridTemplateColumns: `repeat(${perPage}, 1fr)`,
|
|
1319
|
+
gap: AI_TREE_TOKENS.spacing8,
|
|
1320
|
+
alignItems: "start"
|
|
902
1321
|
},
|
|
903
|
-
children:
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
1322
|
+
children: group
|
|
1323
|
+
},
|
|
1324
|
+
p
|
|
1325
|
+
))
|
|
1326
|
+
}
|
|
1327
|
+
) });
|
|
1328
|
+
const prevBtn = /* @__PURE__ */ jsx(
|
|
1329
|
+
"button",
|
|
1330
|
+
{
|
|
1331
|
+
type: "button",
|
|
1332
|
+
"aria-label": "Previous",
|
|
1333
|
+
onClick: () => setPage((p) => Math.max(0, p - 1)),
|
|
1334
|
+
style: chrome(current > 0),
|
|
1335
|
+
children: /* @__PURE__ */ jsx(ArrowLeft, { size: 24, strokeWidth: 1.5, "aria-hidden": true })
|
|
1336
|
+
}
|
|
1337
|
+
);
|
|
1338
|
+
const nextBtn = /* @__PURE__ */ jsx(
|
|
1339
|
+
"button",
|
|
1340
|
+
{
|
|
1341
|
+
type: "button",
|
|
1342
|
+
"aria-label": "Next",
|
|
1343
|
+
onClick: () => setPage((p) => Math.min(pages - 1, p + 1)),
|
|
1344
|
+
style: chrome(current < pages - 1),
|
|
1345
|
+
children: /* @__PURE__ */ jsx(ArrowRight, { size: 24, strokeWidth: 1.5, "aria-hidden": true })
|
|
1346
|
+
}
|
|
1347
|
+
);
|
|
1348
|
+
const dots = /* @__PURE__ */ jsx("div", { style: { display: "flex", gap: 9, justifyContent: "center" }, children: pageGroups.map((_, p) => /* @__PURE__ */ jsx("button", { type: "button", "aria-label": `Page ${p + 1}`, onClick: () => setPage(p), style: dot(p === current) }, p)) });
|
|
1349
|
+
if (isMobile) {
|
|
1350
|
+
return /* @__PURE__ */ jsxs("div", { "data-ai-carousel": "", style: { display: "flex", flexDirection: "column", gap: AI_TREE_TOKENS.spacing6, alignItems: "center" }, children: [
|
|
1351
|
+
viewport,
|
|
1352
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: AI_TREE_TOKENS.spacing6, justifyContent: "center" }, children: [
|
|
1353
|
+
prevBtn,
|
|
1354
|
+
nextBtn
|
|
1355
|
+
] }),
|
|
1356
|
+
dots
|
|
1357
|
+
] });
|
|
1358
|
+
}
|
|
1359
|
+
return /* @__PURE__ */ jsxs("div", { "data-ai-carousel": "", style: { display: "flex", flexDirection: "column", gap: AI_TREE_TOKENS.spacing8, alignItems: "center" }, children: [
|
|
1360
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: AI_TREE_TOKENS.spacing6, width: "100%" }, children: [
|
|
1361
|
+
prevBtn,
|
|
1362
|
+
viewport,
|
|
1363
|
+
nextBtn
|
|
930
1364
|
] }),
|
|
931
|
-
|
|
1365
|
+
dots
|
|
932
1366
|
] });
|
|
933
1367
|
}
|
|
934
1368
|
function CollectionBlock({ node, ctx, path }) {
|
|
@@ -1022,6 +1456,49 @@ function renderNode(node, ctx, path) {
|
|
|
1022
1456
|
switch (node.type) {
|
|
1023
1457
|
case "text":
|
|
1024
1458
|
return /* @__PURE__ */ jsx(TextBlock, { slots, ctx, path });
|
|
1459
|
+
// Layout container: arranges child blocks, contributes no content of its own. `grid` is a
|
|
1460
|
+
// nested 12-column grid the children span (a collage is 3–5 media on it, bottom-aligned so
|
|
1461
|
+
// mixed aspects read as a composition); `split` is exactly two children at a ratio; `stack`
|
|
1462
|
+
// is a column. Children render through this same dispatcher, so edit markers, media
|
|
1463
|
+
// resolution, and copy paths all work unchanged inside a group.
|
|
1464
|
+
case "group": {
|
|
1465
|
+
const layout = str(slots.layout);
|
|
1466
|
+
const gap = slots.spacing === "tight" ? AI_TREE_TOKENS.spacing3 : slots.spacing === "airy" ? AI_TREE_TOKENS.spacing8 : AI_TREE_TOKENS.spacing6;
|
|
1467
|
+
const kids = (node.children ?? []).map((child, i) => /* @__PURE__ */ jsx(
|
|
1468
|
+
"div",
|
|
1469
|
+
{
|
|
1470
|
+
style: layout === "grid" ? {
|
|
1471
|
+
gridColumn: `span ${typeof child.span === "number" ? Math.min(12, Math.max(1, child.span)) : 12}`,
|
|
1472
|
+
minWidth: 0
|
|
1473
|
+
} : { minWidth: 0 },
|
|
1474
|
+
children: renderNode(child, ctx, `${path}.c${i}`)
|
|
1475
|
+
},
|
|
1476
|
+
i
|
|
1477
|
+
));
|
|
1478
|
+
if (layout === "grid") {
|
|
1479
|
+
return /* @__PURE__ */ jsx(
|
|
1480
|
+
"div",
|
|
1481
|
+
{
|
|
1482
|
+
"data-ai-group": "grid",
|
|
1483
|
+
style: { display: "grid", gridTemplateColumns: "repeat(12, 1fr)", gap, alignItems: "end" },
|
|
1484
|
+
children: kids
|
|
1485
|
+
}
|
|
1486
|
+
);
|
|
1487
|
+
}
|
|
1488
|
+
if (layout === "split") {
|
|
1489
|
+
const ratio = str(slots.ratio);
|
|
1490
|
+
const cols = ratio === "3:5" ? "3fr 5fr" : ratio === "5:3" ? "5fr 3fr" : "1fr 1fr";
|
|
1491
|
+
return /* @__PURE__ */ jsx(
|
|
1492
|
+
"div",
|
|
1493
|
+
{
|
|
1494
|
+
"data-ai-group": "split",
|
|
1495
|
+
style: { display: "grid", gridTemplateColumns: cols, gap, alignItems: "center" },
|
|
1496
|
+
children: kids
|
|
1497
|
+
}
|
|
1498
|
+
);
|
|
1499
|
+
}
|
|
1500
|
+
return /* @__PURE__ */ jsx("div", { "data-ai-group": "stack", style: { display: "flex", flexDirection: "column", gap }, children: kids });
|
|
1501
|
+
}
|
|
1025
1502
|
case "button":
|
|
1026
1503
|
return /* @__PURE__ */ jsx(ButtonEl, { slots, ctx, path });
|
|
1027
1504
|
case "button-row":
|
|
@@ -1102,33 +1579,111 @@ function renderNode(node, ctx, path) {
|
|
|
1102
1579
|
}
|
|
1103
1580
|
);
|
|
1104
1581
|
}
|
|
1105
|
-
case "form":
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1582
|
+
case "form": {
|
|
1583
|
+
const formAttrs = ctx.keyFor ? {
|
|
1584
|
+
"data-ohw-editable": "form",
|
|
1585
|
+
"data-ohw-key": ctx.keyFor(`${path}.form`),
|
|
1586
|
+
"data-ohw-success-text": "Thanks \u2014 we'll be in touch shortly."
|
|
1587
|
+
} : {};
|
|
1588
|
+
const fieldStyle = {
|
|
1589
|
+
width: "100%",
|
|
1590
|
+
boxSizing: "border-box",
|
|
1591
|
+
border: `1px solid color-mix(in srgb, ${ctx.brand.palette.dark} 45%, #ffffff)`,
|
|
1592
|
+
borderRadius: 0,
|
|
1593
|
+
padding: 12,
|
|
1594
|
+
background: "#fff",
|
|
1595
|
+
color: ctx.brand.palette.dark,
|
|
1596
|
+
outline: "none",
|
|
1597
|
+
...typeStyle(AI_TREE_TOKENS.type.bodyM, ctx.brand.fonts.body)
|
|
1598
|
+
};
|
|
1599
|
+
const labelStyle = {
|
|
1600
|
+
...typeStyle(AI_TREE_TOKENS.type.bodyMBold, ctx.brand.fonts.body),
|
|
1601
|
+
color: ctx.brand.palette.dark,
|
|
1602
|
+
textAlign: "left",
|
|
1603
|
+
width: "100%"
|
|
1604
|
+
};
|
|
1605
|
+
const centered = ctx.sectionAlignment === "center";
|
|
1606
|
+
const submitAlign = centered ? "center" : "flex-start";
|
|
1607
|
+
const children = node.children ?? [];
|
|
1608
|
+
return (
|
|
1609
|
+
// 32px between the field group and the submit. In a stacked (centered) section the form is
|
|
1610
|
+
// capped at 780px and centered — the section's 12-col grid would otherwise leave it hugging
|
|
1611
|
+
// the left edge; a split section lets it fill its own column.
|
|
1612
|
+
/* @__PURE__ */ jsxs(
|
|
1613
|
+
"form",
|
|
1614
|
+
{
|
|
1615
|
+
...formAttrs,
|
|
1616
|
+
"data-ai-form": "",
|
|
1617
|
+
style: {
|
|
1618
|
+
display: "flex",
|
|
1619
|
+
flexDirection: "column",
|
|
1620
|
+
gap: 32,
|
|
1621
|
+
width: "100%",
|
|
1622
|
+
...centered ? { maxWidth: 780, marginLeft: "auto", marginRight: "auto" } : {}
|
|
1623
|
+
},
|
|
1624
|
+
children: [
|
|
1625
|
+
/* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", gap: 24, width: "100%", alignItems: "flex-start" }, children: children.map((child, i) => {
|
|
1626
|
+
if (child.type !== "input") return null;
|
|
1627
|
+
const cs = child.slots ?? {};
|
|
1628
|
+
const kind = str(cs.kind);
|
|
1629
|
+
const label = str(cs.label);
|
|
1630
|
+
const placeholder = str(cs.placeholder);
|
|
1631
|
+
const required = cs.required === true;
|
|
1632
|
+
const name = label.toLowerCase().replace(/[^a-z0-9]+/gu, "-").replace(/^-+|-+$/gu, "") || `field-${i}`;
|
|
1633
|
+
const isTextarea = kind === "textarea";
|
|
1634
|
+
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8, width: "100%" }, children: [
|
|
1635
|
+
/* @__PURE__ */ jsx("label", { ...textAttrs(ctx, `${path}.c${i}.label`), style: labelStyle, children: label }),
|
|
1636
|
+
isTextarea ? /* @__PURE__ */ jsx(
|
|
1637
|
+
"textarea",
|
|
1638
|
+
{
|
|
1639
|
+
name,
|
|
1640
|
+
placeholder,
|
|
1641
|
+
required,
|
|
1642
|
+
style: { ...fieldStyle, height: 180, resize: "vertical" }
|
|
1643
|
+
}
|
|
1644
|
+
) : /* @__PURE__ */ jsx(
|
|
1645
|
+
"input",
|
|
1646
|
+
{
|
|
1647
|
+
name,
|
|
1648
|
+
type: kind === "email" ? "email" : "text",
|
|
1649
|
+
placeholder,
|
|
1650
|
+
required,
|
|
1651
|
+
style: { ...fieldStyle, height: 48 }
|
|
1652
|
+
}
|
|
1653
|
+
)
|
|
1654
|
+
] }, i);
|
|
1655
|
+
}) }),
|
|
1656
|
+
children.map((child, i) => {
|
|
1657
|
+
if (child.type === "input") return null;
|
|
1658
|
+
const cs = child.slots ?? {};
|
|
1659
|
+
return /* @__PURE__ */ jsx(
|
|
1660
|
+
"button",
|
|
1661
|
+
{
|
|
1662
|
+
type: "submit",
|
|
1663
|
+
style: {
|
|
1664
|
+
alignSelf: submitAlign,
|
|
1665
|
+
border: "none",
|
|
1666
|
+
cursor: "pointer",
|
|
1667
|
+
padding: "12px 24px",
|
|
1668
|
+
// Corner radius follows the host template's own buttons (measured from a template
|
|
1669
|
+
// CTA); 8px only when the page has no template button to match.
|
|
1670
|
+
borderRadius: ctx.buttonRadius ?? 8,
|
|
1671
|
+
// Brand-styled: primary fill, brand-derived label colour (not a fixed token) so it
|
|
1672
|
+
// reads correctly on custom palettes.
|
|
1673
|
+
background: ctx.brand.palette.primary,
|
|
1674
|
+
color: ctx.buttonLabel ?? ctx.brand.palette.light,
|
|
1675
|
+
...typeStyle(AI_TREE_TOKENS.type.button, ctx.brand.fonts.body)
|
|
1676
|
+
},
|
|
1677
|
+
children: /* @__PURE__ */ jsx("span", { ...textAttrs(ctx, `${path}.c${i}.label`), children: str(cs.label) })
|
|
1678
|
+
},
|
|
1679
|
+
i
|
|
1680
|
+
);
|
|
1681
|
+
})
|
|
1682
|
+
]
|
|
1683
|
+
}
|
|
1684
|
+
)
|
|
1685
|
+
);
|
|
1686
|
+
}
|
|
1132
1687
|
case "schedule-widget":
|
|
1133
1688
|
return /* @__PURE__ */ jsx(
|
|
1134
1689
|
"div",
|
|
@@ -1149,16 +1704,27 @@ function renderNode(node, ctx, path) {
|
|
|
1149
1704
|
return null;
|
|
1150
1705
|
}
|
|
1151
1706
|
}
|
|
1152
|
-
function AiTreeRenderer({
|
|
1707
|
+
function AiTreeRenderer({
|
|
1708
|
+
tree,
|
|
1709
|
+
brand,
|
|
1710
|
+
buttonRadius,
|
|
1711
|
+
resolveMedia,
|
|
1712
|
+
editKeyPrefix
|
|
1713
|
+
}) {
|
|
1153
1714
|
if (!isRenderableTree(tree)) {
|
|
1154
1715
|
return null;
|
|
1155
1716
|
}
|
|
1156
1717
|
const resolvedBrand = brand ?? AI_DEFAULT_BRAND;
|
|
1718
|
+
const band = (tree.settings ?? {}).sectionBackground === "accent" ? accentBandContext(resolvedBrand) : null;
|
|
1719
|
+
const blockBrand = band?.brand ?? resolvedBrand;
|
|
1157
1720
|
const ctx = {
|
|
1158
|
-
brand:
|
|
1721
|
+
brand: blockBrand,
|
|
1159
1722
|
resolveMedia: resolveMedia ?? (() => null),
|
|
1160
|
-
cardSurface:
|
|
1161
|
-
keyFor: editKeyPrefix ? (path) => `${editKeyPrefix}.${path}` : null
|
|
1723
|
+
cardSurface: blockBrand.palette.light.toUpperCase() === AI_DEFAULT_BRAND.palette.light.toUpperCase() ? "#ECECEB" : `color-mix(in srgb, ${blockBrand.palette.light} 90%, ${blockBrand.palette.dark})`,
|
|
1724
|
+
keyFor: editKeyPrefix ? (path) => `${editKeyPrefix}.${path}` : null,
|
|
1725
|
+
sectionAlignment: (tree.settings ?? {}).alignment === "center" ? "center" : "left",
|
|
1726
|
+
buttonRadius,
|
|
1727
|
+
...band ? { buttonLabel: band.buttonLabel } : {}
|
|
1162
1728
|
};
|
|
1163
1729
|
const settings = tree.settings ?? {};
|
|
1164
1730
|
const pad = AI_TREE_TOKENS.sectionPadding[settings.spacing ?? "balanced"];
|
|
@@ -1166,6 +1732,20 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
|
|
|
1166
1732
|
const overlayAlpha = { low: 0.25, medium: 0.45, high: 0.65 }[settings.overlayIntensity ?? "medium"];
|
|
1167
1733
|
const backgroundUrl = isOverlay && settings.backgroundMedia ? ctx.resolveMedia(settings.backgroundMedia) : null;
|
|
1168
1734
|
const bgAttrs = ctx.keyFor && isOverlay && settings.backgroundMedia ? { "data-ohw-key": ctx.keyFor("settings.backgroundMedia"), "data-ohw-editable": "bg-image" } : {};
|
|
1735
|
+
const toneBackground = (() => {
|
|
1736
|
+
const { dark, primary, light } = resolvedBrand.palette;
|
|
1737
|
+
switch (settings.sectionBackground) {
|
|
1738
|
+
case "surface":
|
|
1739
|
+
return `color-mix(in srgb, ${light} 94%, ${dark})`;
|
|
1740
|
+
case "accent":
|
|
1741
|
+
return primary;
|
|
1742
|
+
case "accent-soft":
|
|
1743
|
+
return `color-mix(in srgb, ${primary} 12%, ${light})`;
|
|
1744
|
+
default:
|
|
1745
|
+
return void 0;
|
|
1746
|
+
}
|
|
1747
|
+
})();
|
|
1748
|
+
const distributed = !isOverlay && settings.textDistribution;
|
|
1169
1749
|
return /* @__PURE__ */ jsxs(
|
|
1170
1750
|
"section",
|
|
1171
1751
|
{
|
|
@@ -1175,13 +1755,15 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
|
|
|
1175
1755
|
style: {
|
|
1176
1756
|
position: "relative",
|
|
1177
1757
|
padding: `${pad}px 0`,
|
|
1178
|
-
background: isOverlay && !backgroundUrl ? "#DBEAFE" : void 0,
|
|
1758
|
+
background: toneBackground ?? (isOverlay && !backgroundUrl ? "#DBEAFE" : void 0),
|
|
1179
1759
|
backgroundImage: backgroundUrl ? `url(${backgroundUrl})` : void 0,
|
|
1180
1760
|
backgroundSize: "cover",
|
|
1181
|
-
backgroundPosition: "center"
|
|
1761
|
+
backgroundPosition: "center",
|
|
1762
|
+
color: settings.sectionBackground === "accent" ? blockBrand.palette.dark : void 0
|
|
1182
1763
|
},
|
|
1183
1764
|
children: [
|
|
1184
1765
|
/* @__PURE__ */ jsx("style", { children: AI_RESPONSIVE_CSS }),
|
|
1766
|
+
/* @__PURE__ */ jsx("style", { children: AI_MOBILE_CSS }),
|
|
1185
1767
|
isOverlay && backgroundUrl && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
|
|
1186
1768
|
/* @__PURE__ */ jsx(
|
|
1187
1769
|
"div",
|
|
@@ -1202,10 +1784,24 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
|
|
|
1202
1784
|
display: "grid",
|
|
1203
1785
|
gridTemplateColumns: "repeat(12, 1fr)",
|
|
1204
1786
|
gap: AI_TREE_TOKENS.spacing6,
|
|
1205
|
-
alignItems: settings.verticalPosition === "top" ? "start" : "center",
|
|
1787
|
+
alignItems: distributed === "space-between" ? "stretch" : (distributed ?? settings.verticalPosition) === "top" ? "start" : "center",
|
|
1206
1788
|
marginTop: r2 > 0 ? AI_TREE_TOKENS.spacing8 : 0
|
|
1207
1789
|
},
|
|
1208
|
-
children: (row.blocks ?? []).map((block, b) => /* @__PURE__ */ jsx(
|
|
1790
|
+
children: (row.blocks ?? []).map((block, b) => /* @__PURE__ */ jsx(
|
|
1791
|
+
"div",
|
|
1792
|
+
{
|
|
1793
|
+
"data-ai-cell": "",
|
|
1794
|
+
style: {
|
|
1795
|
+
gridColumn: `span ${Math.min(12, Math.max(1, block.span))}`,
|
|
1796
|
+
minWidth: 0,
|
|
1797
|
+
// space-between: each column becomes a flex column whose content spreads over
|
|
1798
|
+
// the full row height instead of clumping at the top.
|
|
1799
|
+
...distributed === "space-between" ? { display: "flex", flexDirection: "column", justifyContent: "space-between" } : {}
|
|
1800
|
+
},
|
|
1801
|
+
children: renderNode(block, ctx, `r${r2}.b${b}`)
|
|
1802
|
+
},
|
|
1803
|
+
b
|
|
1804
|
+
))
|
|
1209
1805
|
},
|
|
1210
1806
|
r2
|
|
1211
1807
|
))
|
|
@@ -1221,17 +1817,36 @@ import { jsx as jsx2 } from "react/jsx-runtime";
|
|
|
1221
1817
|
var CONTAINER_ATTR = "data-ohw-ai-generated";
|
|
1222
1818
|
var REPLACED_ATTR = "data-ohw-ai-replaced-by";
|
|
1223
1819
|
var REMOVED_ATTR = "data-ohw-ai-removed";
|
|
1820
|
+
var TEMPLATE_HIDDEN_ATTR = "data-ohw-ai-template-hidden";
|
|
1821
|
+
var CHROME_SECTION_IDS = /* @__PURE__ */ new Set(["navbar", "footer"]);
|
|
1822
|
+
function readRootVar(name) {
|
|
1823
|
+
if (typeof document === "undefined") return "";
|
|
1824
|
+
return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
|
|
1825
|
+
}
|
|
1826
|
+
function deriveBrandOverride() {
|
|
1827
|
+
const dark = readRootVar("--ohw-brand-dark");
|
|
1828
|
+
const primary = readRootVar("--ohw-brand-primary");
|
|
1829
|
+
const light = readRootVar("--ohw-brand-light");
|
|
1830
|
+
if (!dark || !primary || !light) return null;
|
|
1831
|
+
const accent = readRootVar("--ohw-brand-accent");
|
|
1832
|
+
const heading = readRootVar("--font-heading") || readRootVar("--font-display");
|
|
1833
|
+
const body = readRootVar("--font-body");
|
|
1834
|
+
return {
|
|
1835
|
+
palette: { dark, primary, accent: accent || dark, light },
|
|
1836
|
+
fonts: {
|
|
1837
|
+
heading: heading || AI_DEFAULT_BRAND.fonts.heading,
|
|
1838
|
+
body: body || AI_DEFAULT_BRAND.fonts.body
|
|
1839
|
+
}
|
|
1840
|
+
};
|
|
1841
|
+
}
|
|
1224
1842
|
function deriveTemplateBrand() {
|
|
1225
|
-
|
|
1226
|
-
const
|
|
1227
|
-
const
|
|
1228
|
-
const dark = read("--color-dark");
|
|
1229
|
-
const primary = read("--color-primary");
|
|
1230
|
-
const light = read("--color-light");
|
|
1843
|
+
const dark = readRootVar("--color-dark");
|
|
1844
|
+
const primary = readRootVar("--color-primary");
|
|
1845
|
+
const light = readRootVar("--color-light");
|
|
1231
1846
|
if (!dark || !primary || !light) return null;
|
|
1232
|
-
const accent =
|
|
1233
|
-
const heading =
|
|
1234
|
-
const body =
|
|
1847
|
+
const accent = readRootVar("--color-accent");
|
|
1848
|
+
const heading = readRootVar("--font-heading") || readRootVar("--font-display");
|
|
1849
|
+
const body = readRootVar("--font-body");
|
|
1235
1850
|
return {
|
|
1236
1851
|
palette: { dark, primary, accent: accent || dark, light },
|
|
1237
1852
|
fonts: {
|
|
@@ -1240,6 +1855,13 @@ function deriveTemplateBrand() {
|
|
|
1240
1855
|
}
|
|
1241
1856
|
};
|
|
1242
1857
|
}
|
|
1858
|
+
function deriveTemplateButtonRadius() {
|
|
1859
|
+
if (typeof document === "undefined") return null;
|
|
1860
|
+
const btn = document.querySelector('[data-ohw-role="button"]');
|
|
1861
|
+
if (!btn) return null;
|
|
1862
|
+
const radius = getComputedStyle(btn).borderTopLeftRadius;
|
|
1863
|
+
return radius || null;
|
|
1864
|
+
}
|
|
1243
1865
|
var mounted = /* @__PURE__ */ new Map();
|
|
1244
1866
|
function findTemplateSection(id) {
|
|
1245
1867
|
for (const el of document.querySelectorAll(`[data-ohw-section="${CSS.escape(id)}"]`)) {
|
|
@@ -1303,6 +1925,24 @@ function syncRemovedSections(state) {
|
|
|
1303
1925
|
}
|
|
1304
1926
|
}
|
|
1305
1927
|
}
|
|
1928
|
+
function syncTemplateHidden(state, pageHasSections) {
|
|
1929
|
+
const hide = state.hideTemplate === true && pageHasSections;
|
|
1930
|
+
for (const el of Array.from(document.querySelectorAll(`[${TEMPLATE_HIDDEN_ATTR}]`))) {
|
|
1931
|
+
if (!hide) {
|
|
1932
|
+
el.style.removeProperty("display");
|
|
1933
|
+
el.removeAttribute(TEMPLATE_HIDDEN_ATTR);
|
|
1934
|
+
}
|
|
1935
|
+
}
|
|
1936
|
+
if (!hide) return;
|
|
1937
|
+
for (const el of Array.from(document.querySelectorAll("[data-ohw-section]"))) {
|
|
1938
|
+
if (el.hasAttribute(CONTAINER_ATTR)) continue;
|
|
1939
|
+
if (CHROME_SECTION_IDS.has(el.getAttribute("data-ohw-section") ?? "")) continue;
|
|
1940
|
+
if (el.parentElement?.closest("[data-ohw-section]")) continue;
|
|
1941
|
+
if (el.hasAttribute(REPLACED_ATTR) || el.hasAttribute(REMOVED_ATTR)) continue;
|
|
1942
|
+
el.style.display = "none";
|
|
1943
|
+
el.setAttribute(TEMPLATE_HIDDEN_ATTR, "");
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1306
1946
|
function syncReplacedOriginals(state) {
|
|
1307
1947
|
for (const el of document.querySelectorAll(`[${REPLACED_ATTR}]`)) {
|
|
1308
1948
|
const byId = el.getAttribute(REPLACED_ATTR) ?? "";
|
|
@@ -1321,10 +1961,64 @@ function syncReplacedOriginals(state) {
|
|
|
1321
1961
|
}
|
|
1322
1962
|
}
|
|
1323
1963
|
}
|
|
1964
|
+
var sectionOrderIndex = /* @__PURE__ */ new Map();
|
|
1965
|
+
function setAiSectionOrder(raw, currentPath) {
|
|
1966
|
+
const next = /* @__PURE__ */ new Map();
|
|
1967
|
+
if (raw) {
|
|
1968
|
+
try {
|
|
1969
|
+
const entries = JSON.parse(raw);
|
|
1970
|
+
for (const entry of entries) {
|
|
1971
|
+
if (!entry.pagePath || entry.pagePath === currentPath) next.set(entry.instanceId, entry.order);
|
|
1972
|
+
}
|
|
1973
|
+
} catch {
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
sectionOrderIndex = next;
|
|
1977
|
+
}
|
|
1978
|
+
function applyExplicitOrder(entries) {
|
|
1979
|
+
if (sectionOrderIndex.size === 0) return entries;
|
|
1980
|
+
return entries.map((entry, index) => ({ entry, index, order: sectionOrderIndex.get(entry.id) })).sort((a, b) => {
|
|
1981
|
+
if (a.order === void 0 && b.order === void 0) return a.index - b.index;
|
|
1982
|
+
if (a.order === void 0) return 1;
|
|
1983
|
+
if (b.order === void 0) return -1;
|
|
1984
|
+
return a.order - b.order;
|
|
1985
|
+
}).map((item) => item.entry);
|
|
1986
|
+
}
|
|
1987
|
+
function orderByChain(sections) {
|
|
1988
|
+
const ids = new Set(sections.map((entry) => entry.id));
|
|
1989
|
+
const after = /* @__PURE__ */ new Map();
|
|
1990
|
+
const roots = [];
|
|
1991
|
+
for (const entry of sections) {
|
|
1992
|
+
const anchor = entry.replaces ?? entry.beforeSection ?? entry.afterSection ?? null;
|
|
1993
|
+
if (anchor && ids.has(anchor)) {
|
|
1994
|
+
const bucket = after.get(anchor);
|
|
1995
|
+
if (bucket) bucket.push(entry);
|
|
1996
|
+
else after.set(anchor, [entry]);
|
|
1997
|
+
} else {
|
|
1998
|
+
roots.push(entry);
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
const out = [];
|
|
2002
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2003
|
+
const visit = (entry) => {
|
|
2004
|
+
if (seen.has(entry.id)) return;
|
|
2005
|
+
seen.add(entry.id);
|
|
2006
|
+
out.push(entry);
|
|
2007
|
+
for (const child of after.get(entry.id) ?? []) visit(child);
|
|
2008
|
+
};
|
|
2009
|
+
for (const root of roots) visit(root);
|
|
2010
|
+
return out.length === sections.length ? out : sections;
|
|
2011
|
+
}
|
|
1324
2012
|
function applyAiSectionsToDom(state, options) {
|
|
1325
2013
|
if (typeof document === "undefined") return;
|
|
2014
|
+
const brandOverride = deriveBrandOverride();
|
|
1326
2015
|
const templateBrand = deriveTemplateBrand();
|
|
1327
|
-
const
|
|
2016
|
+
const templateButtonRadius = deriveTemplateButtonRadius();
|
|
2017
|
+
const brandKey = brandOverride ? JSON.stringify(brandOverride) : "";
|
|
2018
|
+
const pagePath = window.location.pathname;
|
|
2019
|
+
const pageSections = state.sections.filter((entry) => (entry.path ?? "/") === pagePath);
|
|
2020
|
+
const activeIds = new Set(pageSections.map((entry) => entry.id));
|
|
2021
|
+
const ordered = state.hideTemplate === true ? applyExplicitOrder(orderByChain(pageSections)) : pageSections;
|
|
1328
2022
|
for (const [id, section] of mounted) {
|
|
1329
2023
|
if (!activeIds.has(id)) {
|
|
1330
2024
|
section.root.unmount();
|
|
@@ -1332,8 +2026,8 @@ function applyAiSectionsToDom(state, options) {
|
|
|
1332
2026
|
mounted.delete(id);
|
|
1333
2027
|
}
|
|
1334
2028
|
}
|
|
1335
|
-
for (const entry of
|
|
1336
|
-
const serialized = JSON.stringify(entry);
|
|
2029
|
+
for (const entry of ordered) {
|
|
2030
|
+
const serialized = JSON.stringify(entry) + brandKey;
|
|
1337
2031
|
const existing = mounted.get(entry.id);
|
|
1338
2032
|
if (existing && existing.serialized === serialized && existing.container.isConnected) {
|
|
1339
2033
|
continue;
|
|
@@ -1358,7 +2052,8 @@ function applyAiSectionsToDom(state, options) {
|
|
|
1358
2052
|
AiTreeRenderer,
|
|
1359
2053
|
{
|
|
1360
2054
|
tree: entry.tree,
|
|
1361
|
-
brand: entry.brand ?? options?.brand ?? templateBrand ?? void 0,
|
|
2055
|
+
brand: brandOverride ?? entry.brand ?? options?.brand ?? templateBrand ?? void 0,
|
|
2056
|
+
buttonRadius: templateButtonRadius,
|
|
1362
2057
|
resolveMedia,
|
|
1363
2058
|
editKeyPrefix: `ai.${entry.id}`
|
|
1364
2059
|
}
|
|
@@ -1367,8 +2062,20 @@ function applyAiSectionsToDom(state, options) {
|
|
|
1367
2062
|
});
|
|
1368
2063
|
mounted.set(entry.id, { root, container, serialized });
|
|
1369
2064
|
}
|
|
2065
|
+
if (state.hideTemplate === true) {
|
|
2066
|
+
let prev = null;
|
|
2067
|
+
for (const entry of ordered) {
|
|
2068
|
+
const el = mounted.get(entry.id)?.container;
|
|
2069
|
+
if (!el) continue;
|
|
2070
|
+
if (prev && !(prev.compareDocumentPosition(el) & Node.DOCUMENT_POSITION_FOLLOWING)) {
|
|
2071
|
+
prev.insertAdjacentElement("afterend", el);
|
|
2072
|
+
}
|
|
2073
|
+
prev = el;
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
1370
2076
|
syncReplacedOriginals(state);
|
|
1371
2077
|
syncRemovedSections(state);
|
|
2078
|
+
syncTemplateHidden(state, pageSections.length > 0);
|
|
1372
2079
|
}
|
|
1373
2080
|
|
|
1374
2081
|
// src/useLinkHrefGuardian.ts
|
|
@@ -1495,6 +2202,7 @@ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
|
|
|
1495
2202
|
/* @__PURE__ */ jsx3(
|
|
1496
2203
|
Dialog.Overlay,
|
|
1497
2204
|
{
|
|
2205
|
+
"data-ohw-scheduling-modal": "",
|
|
1498
2206
|
className: "fixed inset-0 z-50",
|
|
1499
2207
|
style: { background: "rgba(0,0,0,0.45)" }
|
|
1500
2208
|
}
|
|
@@ -1502,6 +2210,7 @@ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
|
|
|
1502
2210
|
/* @__PURE__ */ jsxs2(
|
|
1503
2211
|
Dialog.Content,
|
|
1504
2212
|
{
|
|
2213
|
+
"data-ohw-scheduling-modal": "",
|
|
1505
2214
|
className: "fixed left-1/2 top-1/2 z-50 w-full -translate-x-1/2 -translate-y-1/2 bg-white rounded-xl shadow-xl outline-none font-body box-border overflow-hidden",
|
|
1506
2215
|
style: { maxWidth: 400 },
|
|
1507
2216
|
children: [
|
|
@@ -1975,7 +2684,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
|
|
|
1975
2684
|
const autoId = useId();
|
|
1976
2685
|
const insertAfter = insertAfterProp ?? autoId;
|
|
1977
2686
|
const [schedule, setSchedule] = useState2(null);
|
|
1978
|
-
const [loading, setLoading] = useState2(
|
|
2687
|
+
const [loading, setLoading] = useState2(initialScheduleId !== null);
|
|
1979
2688
|
const [inEditor, setInEditor] = useState2(false);
|
|
1980
2689
|
const [isHovered, setIsHovered] = useState2(false);
|
|
1981
2690
|
const [modalState, setModalState] = useState2(null);
|
|
@@ -2149,8 +2858,10 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
|
|
|
2149
2858
|
"*"
|
|
2150
2859
|
);
|
|
2151
2860
|
};
|
|
2152
|
-
if (!inEditor && !loading && !schedule) return null;
|
|
2153
2861
|
const sectionId = `scheduling-${insertAfter}`;
|
|
2862
|
+
if (!inEditor && !loading && !schedule) {
|
|
2863
|
+
return /* @__PURE__ */ jsx4("section", { "data-ohw-section": sectionId, "data-ohw-scheduling-anchor": insertAfter, style: { display: "none" } });
|
|
2864
|
+
}
|
|
2154
2865
|
return /* @__PURE__ */ jsxs3(
|
|
2155
2866
|
"section",
|
|
2156
2867
|
{
|
|
@@ -7067,13 +7778,17 @@ function MediaOverlay({
|
|
|
7067
7778
|
hover,
|
|
7068
7779
|
isUploading,
|
|
7069
7780
|
fadingOut = false,
|
|
7781
|
+
selected = false,
|
|
7782
|
+
hovered = false,
|
|
7070
7783
|
onFadeOutComplete,
|
|
7071
7784
|
onReplace,
|
|
7785
|
+
onSelect,
|
|
7072
7786
|
onVideoSettingsChange
|
|
7073
7787
|
}) {
|
|
7074
7788
|
const { rect } = hover;
|
|
7075
7789
|
const skeletonRef = React8.useRef(null);
|
|
7076
7790
|
const isVideo = hover.elementType === "video";
|
|
7791
|
+
const showChrome = !selected || hovered;
|
|
7077
7792
|
const autoplay = hover.videoAutoplay ?? true;
|
|
7078
7793
|
const muted = hover.videoMuted ?? true;
|
|
7079
7794
|
const probeRef = React8.useRef(null);
|
|
@@ -7087,6 +7802,7 @@ function MediaOverlay({
|
|
|
7087
7802
|
(prev) => prev && prev.fullWidth === width && prev.height === height ? prev : { fullWidth: width, height }
|
|
7088
7803
|
);
|
|
7089
7804
|
}, [isVideo]);
|
|
7805
|
+
const replaceLabel = isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image";
|
|
7090
7806
|
const replaceMode = buttonSize ? replaceButtonMode({ width: rect.width, height: rect.height }, buttonSize) : "none";
|
|
7091
7807
|
const box = {
|
|
7092
7808
|
position: "fixed",
|
|
@@ -7120,7 +7836,7 @@ function MediaOverlay({
|
|
|
7120
7836
|
}
|
|
7121
7837
|
);
|
|
7122
7838
|
}
|
|
7123
|
-
const settingsBar = isVideo && !hover.isDragOver ? /* @__PURE__ */ jsxs7(
|
|
7839
|
+
const settingsBar = isVideo && !hover.isDragOver && showChrome ? /* @__PURE__ */ jsxs7(
|
|
7124
7840
|
"div",
|
|
7125
7841
|
{
|
|
7126
7842
|
"data-ohw-bridge": "",
|
|
@@ -7190,10 +7906,12 @@ function MediaOverlay({
|
|
|
7190
7906
|
// in-document, pointer-events does it natively. The button below opts back in, so
|
|
7191
7907
|
// Replace still works.
|
|
7192
7908
|
pointerEvents: hover.hasTextOverlap ? "none" : "auto",
|
|
7193
|
-
|
|
7194
|
-
|
|
7909
|
+
// Selected: a firm component ring with no wash, so the image reads as chosen rather
|
|
7910
|
+
// than hovered. Hover keeps the existing tinted preview.
|
|
7911
|
+
boxShadow: selected ? "inset 0 0 0 2px var(--color-primary)" : "inset 0 0 0 1.5px var(--color-primary)",
|
|
7912
|
+
background: selected ? "transparent" : "color-mix(in srgb, var(--color-primary) 20%, transparent)"
|
|
7195
7913
|
},
|
|
7196
|
-
onClick: () => onReplace(hover.key),
|
|
7914
|
+
onClick: () => onSelect && !selected ? onSelect(hover.key) : onReplace(hover.key),
|
|
7197
7915
|
children: [
|
|
7198
7916
|
/* @__PURE__ */ jsxs7(
|
|
7199
7917
|
Button,
|
|
@@ -7214,17 +7932,17 @@ function MediaOverlay({
|
|
|
7214
7932
|
},
|
|
7215
7933
|
children: [
|
|
7216
7934
|
isVideo ? /* @__PURE__ */ jsx15(Film, { size: 14 }) : /* @__PURE__ */ jsx15(ImageIcon, { size: 14 }),
|
|
7217
|
-
|
|
7935
|
+
replaceLabel
|
|
7218
7936
|
]
|
|
7219
7937
|
}
|
|
7220
7938
|
),
|
|
7221
|
-
replaceMode
|
|
7939
|
+
showChrome && replaceMode !== "none" && /* @__PURE__ */ jsxs7(
|
|
7222
7940
|
Button,
|
|
7223
7941
|
{
|
|
7224
7942
|
"data-ohw-media-overlay": "",
|
|
7225
7943
|
variant: "outline",
|
|
7226
7944
|
size: "sm",
|
|
7227
|
-
"aria-label":
|
|
7945
|
+
"aria-label": replaceLabel,
|
|
7228
7946
|
className: "gap-1.5 cursor-pointer hover:bg-background",
|
|
7229
7947
|
style: {
|
|
7230
7948
|
...OVERLAY_BUTTON_STYLE,
|
|
@@ -7247,7 +7965,7 @@ function MediaOverlay({
|
|
|
7247
7965
|
},
|
|
7248
7966
|
children: [
|
|
7249
7967
|
isVideo ? /* @__PURE__ */ jsx15(Film, { size: 14 }) : /* @__PURE__ */ jsx15(ImageIcon, { size: 14 }),
|
|
7250
|
-
replaceMode === "full" ?
|
|
7968
|
+
replaceMode === "full" ? replaceLabel : null
|
|
7251
7969
|
]
|
|
7252
7970
|
}
|
|
7253
7971
|
)
|
|
@@ -7331,6 +8049,8 @@ function parseSectionsFromRoot(root) {
|
|
|
7331
8049
|
const id = el.getAttribute("data-ohw-section") ?? "";
|
|
7332
8050
|
if (!id || seen.has(id) || LINK_PICKER_EXCLUDED_IDS.has(id)) continue;
|
|
7333
8051
|
if (el.parentElement?.closest("[data-ohw-section]")) continue;
|
|
8052
|
+
if (el.hasAttribute("data-ohw-ai-template-hidden") || el.hasAttribute("data-ohw-ai-removed") || el.hasAttribute("data-ohw-ai-replaced-by"))
|
|
8053
|
+
continue;
|
|
7334
8054
|
seen.add(id);
|
|
7335
8055
|
const label = el.getAttribute("data-ohw-section-label") ?? titleCaseSectionId(id);
|
|
7336
8056
|
sections.push({ id, label });
|
|
@@ -7616,6 +8336,7 @@ function AiSectionOverlay({
|
|
|
7616
8336
|
}) {
|
|
7617
8337
|
const [selectedId, setSelectedId] = useState5(null);
|
|
7618
8338
|
const [reviewId, setReviewId] = useState5(null);
|
|
8339
|
+
const [reviewButtonsHidden, setReviewButtonsHidden] = useState5(false);
|
|
7619
8340
|
const reviewIdRef = useRef4(null);
|
|
7620
8341
|
reviewIdRef.current = reviewId;
|
|
7621
8342
|
const selectedIdRef = useRef4(null);
|
|
@@ -7677,6 +8398,7 @@ function AiSectionOverlay({
|
|
|
7677
8398
|
}
|
|
7678
8399
|
const found = readRect(sectionId) != null;
|
|
7679
8400
|
setReviewId(found ? sectionId : null);
|
|
8401
|
+
setReviewButtonsHidden(e.data.hideButtons === true);
|
|
7680
8402
|
postToParent2({ type: "ow:ai-review-started", sectionId, found });
|
|
7681
8403
|
if (found) {
|
|
7682
8404
|
document.querySelector(`[data-ohw-instance="${CSS.escape(sectionId)}"]`)?.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
|
@@ -7804,13 +8526,16 @@ function AiSectionOverlay({
|
|
|
7804
8526
|
border: `2px solid ${PRIMARY2}`,
|
|
7805
8527
|
borderRadius: edgeAwareRadius(reviewRect),
|
|
7806
8528
|
zIndex: 2147483200,
|
|
7807
|
-
// The veil itself: swallows clicks so the section stays locked until decided.
|
|
8529
|
+
// The veil itself: swallows clicks so the section stays locked until decided. This
|
|
8530
|
+
// stopPropagation only guards the bubble phase; the bridge's capture-phase click
|
|
8531
|
+
// handlers additionally bail on `[data-ohw-ai-review]`, without which a click on
|
|
8532
|
+
// Accept/Discard resolves to the media beneath and opens the file picker.
|
|
7808
8533
|
background: "rgba(8, 133, 254, 0.04)",
|
|
7809
8534
|
pointerEvents: "auto",
|
|
7810
8535
|
cursor: "default"
|
|
7811
8536
|
},
|
|
7812
8537
|
onClick: (e) => e.stopPropagation(),
|
|
7813
|
-
children: /* @__PURE__ */ jsxs9(
|
|
8538
|
+
children: !reviewButtonsHidden && /* @__PURE__ */ jsxs9(
|
|
7814
8539
|
"div",
|
|
7815
8540
|
{
|
|
7816
8541
|
style: {
|
|
@@ -10377,8 +11102,13 @@ function referenceBox(slot) {
|
|
|
10377
11102
|
const neighbour = row ? Array.from(row.querySelectorAll('[data-ohw-editable="icon"]')).find(
|
|
10378
11103
|
(el) => el !== slot && !el.hasAttribute("data-ohw-social-icon-placeholder")
|
|
10379
11104
|
) : null;
|
|
10380
|
-
|
|
10381
|
-
|
|
11105
|
+
if (neighbour) {
|
|
11106
|
+
const box2 = neighbour.querySelector(GLYPH_SELECTOR)?.getBoundingClientRect();
|
|
11107
|
+
if (box2?.width && box2.height) return box2;
|
|
11108
|
+
}
|
|
11109
|
+
const own = slot.getBoundingClientRect();
|
|
11110
|
+
if (own.width && own.height) return own;
|
|
11111
|
+
const box = slot.querySelector(GLYPH_SELECTOR)?.getBoundingClientRect() ?? null;
|
|
10382
11112
|
return box?.width && box.height ? box : null;
|
|
10383
11113
|
}
|
|
10384
11114
|
function iconMarkupSizedFor(slot, markup) {
|
|
@@ -12179,6 +12909,7 @@ function readLogoSizeState(content, placement) {
|
|
|
12179
12909
|
function getLogoElement(el) {
|
|
12180
12910
|
const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
|
|
12181
12911
|
if (marked) return marked;
|
|
12912
|
+
if (el.closest('[data-ohw-editable="icon"]')) return null;
|
|
12182
12913
|
const root = el.closest("nav, [data-ohw-nav-root], footer");
|
|
12183
12914
|
if (!root) return null;
|
|
12184
12915
|
const anchor = el.closest("a");
|
|
@@ -13253,6 +13984,7 @@ function useSectionDrag({
|
|
|
13253
13984
|
}
|
|
13254
13985
|
const orderJson = JSON.stringify(entries);
|
|
13255
13986
|
editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
|
|
13987
|
+
setAiSectionOrder(orderJson, window.location.pathname);
|
|
13256
13988
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
|
|
13257
13989
|
applyPersistedOrder(entries);
|
|
13258
13990
|
clearSectionDragVisuals();
|
|
@@ -14112,21 +14844,10 @@ function parseSchedulingInsertAfter(insertAfter) {
|
|
|
14112
14844
|
insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
|
|
14113
14845
|
};
|
|
14114
14846
|
}
|
|
14115
|
-
function
|
|
14116
|
-
|
|
14117
|
-
const
|
|
14118
|
-
|
|
14119
|
-
return { effectiveInsertAfter, insertBefore };
|
|
14120
|
-
}
|
|
14121
|
-
function getSchedulingMountPoint(insertAfter) {
|
|
14122
|
-
const { anchor } = parseSchedulingInsertAfter(insertAfter);
|
|
14123
|
-
let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
|
|
14124
|
-
if (!anchorEl && anchor === "scheduling") {
|
|
14125
|
-
const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
|
|
14126
|
-
anchorEl = widgets.at(-1) ?? null;
|
|
14127
|
-
}
|
|
14128
|
-
if (!anchorEl) return null;
|
|
14129
|
-
return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
|
|
14847
|
+
function resolveEntryAnchor(entry) {
|
|
14848
|
+
if (entry.anchorId !== void 0) return { anchorId: entry.anchorId, beforeId: entry.beforeId ?? null };
|
|
14849
|
+
const parsed = parseSchedulingInsertAfter(entry.insertAfter);
|
|
14850
|
+
return { anchorId: parsed.anchor, beforeId: parsed.insertBefore };
|
|
14130
14851
|
}
|
|
14131
14852
|
function schedulingMountDepth(insertAfter) {
|
|
14132
14853
|
if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
|
|
@@ -14143,8 +14864,7 @@ function getPageSchedulingEntries(raw) {
|
|
|
14143
14864
|
}
|
|
14144
14865
|
}
|
|
14145
14866
|
function isSchedulingWidgetMissing(entry) {
|
|
14146
|
-
|
|
14147
|
-
return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
|
|
14867
|
+
return !document.querySelector(`[data-ohw-section="${schedulingSectionId(entry.insertAfter)}"]`);
|
|
14148
14868
|
}
|
|
14149
14869
|
function hasMissingSchedulingWidgets(entries) {
|
|
14150
14870
|
return entries.some(isSchedulingWidgetMissing);
|
|
@@ -14174,16 +14894,17 @@ function initSectionsFromContent(content, removeExisting = false) {
|
|
|
14174
14894
|
} catch {
|
|
14175
14895
|
}
|
|
14176
14896
|
}
|
|
14177
|
-
function mountSchedulingWidget(
|
|
14178
|
-
const
|
|
14179
|
-
const sectionId = schedulingSectionId(
|
|
14897
|
+
function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, beforeId, existingWidgetId) {
|
|
14898
|
+
const widgetId = existingWidgetId ?? (beforeId ? `${anchorId}${INSERT_BEFORE_MARKER}${beforeId}` : anchorId);
|
|
14899
|
+
const sectionId = schedulingSectionId(widgetId);
|
|
14180
14900
|
if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
|
|
14181
|
-
const
|
|
14182
|
-
if (!
|
|
14901
|
+
const anchorEl = document.querySelector(`[data-ohw-section="${anchorId}"]`);
|
|
14902
|
+
if (!anchorEl) return false;
|
|
14903
|
+
const mountPoint = anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
|
|
14183
14904
|
const container = document.createElement("div");
|
|
14184
14905
|
container.dataset.ohwSectionContainer = "scheduling";
|
|
14185
|
-
if (
|
|
14186
|
-
const beforeAnchor = document.querySelector(`[data-ohw-section="${
|
|
14906
|
+
if (beforeId) {
|
|
14907
|
+
const beforeAnchor = document.querySelector(`[data-ohw-section="${beforeId}"]`);
|
|
14187
14908
|
const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
|
|
14188
14909
|
if (!beforePoint) return false;
|
|
14189
14910
|
beforePoint.insertAdjacentElement("beforebegin", container);
|
|
@@ -14194,19 +14915,25 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
|
|
|
14194
14915
|
}
|
|
14195
14916
|
tail.insertAdjacentElement("afterend", container);
|
|
14196
14917
|
}
|
|
14197
|
-
|
|
14198
|
-
|
|
14199
|
-
|
|
14200
|
-
|
|
14201
|
-
|
|
14202
|
-
|
|
14203
|
-
|
|
14204
|
-
|
|
14205
|
-
|
|
14206
|
-
|
|
14207
|
-
|
|
14208
|
-
|
|
14209
|
-
|
|
14918
|
+
try {
|
|
14919
|
+
const root = createRoot2(container);
|
|
14920
|
+
flushSync2(() => {
|
|
14921
|
+
root.render(
|
|
14922
|
+
/* @__PURE__ */ jsx33(
|
|
14923
|
+
SchedulingWidget,
|
|
14924
|
+
{
|
|
14925
|
+
notifyOnConnect,
|
|
14926
|
+
initialScheduleId: scheduleId,
|
|
14927
|
+
insertAfter: widgetId
|
|
14928
|
+
}
|
|
14929
|
+
)
|
|
14930
|
+
);
|
|
14931
|
+
});
|
|
14932
|
+
} catch (err) {
|
|
14933
|
+
console.error("[ow:scheduling] render threw", err);
|
|
14934
|
+
container.remove();
|
|
14935
|
+
return false;
|
|
14936
|
+
}
|
|
14210
14937
|
const tracker = getSectionsTracker();
|
|
14211
14938
|
let sections = [];
|
|
14212
14939
|
try {
|
|
@@ -14214,10 +14941,12 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
|
|
|
14214
14941
|
} catch {
|
|
14215
14942
|
}
|
|
14216
14943
|
const inEditor = typeof window !== "undefined" && window.self !== window.top;
|
|
14217
|
-
if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter ===
|
|
14944
|
+
if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === widgetId)) {
|
|
14218
14945
|
sections.push({
|
|
14219
14946
|
type: "scheduling",
|
|
14220
|
-
insertAfter:
|
|
14947
|
+
insertAfter: widgetId,
|
|
14948
|
+
anchorId,
|
|
14949
|
+
beforeId: beforeId ?? null,
|
|
14221
14950
|
pagePath: window.location.pathname,
|
|
14222
14951
|
...scheduleId ? { scheduleId } : {}
|
|
14223
14952
|
});
|
|
@@ -14231,7 +14960,8 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
|
|
|
14231
14960
|
for (let i = pending.length - 1; i >= 0; i--) {
|
|
14232
14961
|
const entry = pending[i];
|
|
14233
14962
|
const shouldNotify = typeof notifyOnConnect === "function" ? notifyOnConnect(entry) : notifyOnConnect;
|
|
14234
|
-
|
|
14963
|
+
const { anchorId, beforeId } = resolveEntryAnchor(entry);
|
|
14964
|
+
if (mountSchedulingWidget(anchorId, shouldNotify, entry.scheduleId ?? null, beforeId, entry.insertAfter)) {
|
|
14235
14965
|
pending.splice(i, 1);
|
|
14236
14966
|
}
|
|
14237
14967
|
}
|
|
@@ -14389,6 +15119,11 @@ function applyLinkByKey(key, val) {
|
|
|
14389
15119
|
hrefAnchors.forEach((el) => applyLinkHref(el, val));
|
|
14390
15120
|
}
|
|
14391
15121
|
}
|
|
15122
|
+
function isInsideLinkEditor(target) {
|
|
15123
|
+
return Boolean(
|
|
15124
|
+
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"]')
|
|
15125
|
+
);
|
|
15126
|
+
}
|
|
14392
15127
|
function isInsideFloatingPanel(target) {
|
|
14393
15128
|
return Boolean(target.closest("[data-ohw-floating-panel]"));
|
|
14394
15129
|
}
|
|
@@ -14396,11 +15131,6 @@ function isPointOverFloatingPanel(clientX, clientY) {
|
|
|
14396
15131
|
const el = document.elementFromPoint(clientX, clientY);
|
|
14397
15132
|
return Boolean(el instanceof Element && el.closest("[data-ohw-floating-panel]"));
|
|
14398
15133
|
}
|
|
14399
|
-
function isInsideLinkEditor(target) {
|
|
14400
|
-
return Boolean(
|
|
14401
|
-
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"]')
|
|
14402
|
-
);
|
|
14403
|
-
}
|
|
14404
15134
|
function getHrefKeyFromElement(el) {
|
|
14405
15135
|
if (!el) return null;
|
|
14406
15136
|
const anchor = el.closest("[data-ohw-href-key]");
|
|
@@ -14659,7 +15389,7 @@ function getNavigationSelectionParent(el) {
|
|
|
14659
15389
|
if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
|
|
14660
15390
|
return getFooterLinksContainer();
|
|
14661
15391
|
}
|
|
14662
|
-
if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
|
|
15392
|
+
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)) {
|
|
14663
15393
|
return getNavigationRoot(el);
|
|
14664
15394
|
}
|
|
14665
15395
|
return null;
|
|
@@ -14874,7 +15604,6 @@ var ICONS = {
|
|
|
14874
15604
|
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"/>',
|
|
14875
15605
|
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"/>'
|
|
14876
15606
|
};
|
|
14877
|
-
var HEIGHT_SETTLE_DELAYS = [150, 400, 800];
|
|
14878
15607
|
var SELECTION_CHROME_GAP2 = 4;
|
|
14879
15608
|
var TOOLBAR_STROKE_GAP2 = 4;
|
|
14880
15609
|
var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
|
|
@@ -15254,6 +15983,8 @@ function StateToggle({
|
|
|
15254
15983
|
);
|
|
15255
15984
|
}
|
|
15256
15985
|
var contentCache = /* @__PURE__ */ new Map();
|
|
15986
|
+
var fetchedContentPaths = /* @__PURE__ */ new Set();
|
|
15987
|
+
var brandingCache = /* @__PURE__ */ new Map();
|
|
15257
15988
|
var OHW_LOADER_STYLE = {
|
|
15258
15989
|
position: "fixed",
|
|
15259
15990
|
inset: 0,
|
|
@@ -15291,6 +16022,89 @@ function OhwLoaderSpinner() {
|
|
|
15291
16022
|
)
|
|
15292
16023
|
] });
|
|
15293
16024
|
}
|
|
16025
|
+
function OhwBrandMark() {
|
|
16026
|
+
return /* @__PURE__ */ jsxs20(
|
|
16027
|
+
"svg",
|
|
16028
|
+
{
|
|
16029
|
+
width: "16",
|
|
16030
|
+
height: "16",
|
|
16031
|
+
viewBox: "0 0 48 48",
|
|
16032
|
+
fill: "none",
|
|
16033
|
+
"aria-hidden": true,
|
|
16034
|
+
style: { display: "block", flexShrink: 0 },
|
|
16035
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
16036
|
+
children: [
|
|
16037
|
+
/* @__PURE__ */ jsx33(
|
|
16038
|
+
"mask",
|
|
16039
|
+
{
|
|
16040
|
+
id: "ohw-badge-mark",
|
|
16041
|
+
style: { maskType: "luminance" },
|
|
16042
|
+
maskUnits: "userSpaceOnUse",
|
|
16043
|
+
x: "0",
|
|
16044
|
+
y: "0",
|
|
16045
|
+
width: "48",
|
|
16046
|
+
height: "48",
|
|
16047
|
+
children: /* @__PURE__ */ jsx33("path", { d: "M23.8741 48C37.0594 48 47.7481 37.2548 47.7481 24C47.7481 10.7452 37.0594 0 23.8741 0C10.6888 0 0 10.7452 0 24C0 37.2548 10.6888 48 23.8741 48Z", fill: "white" })
|
|
16048
|
+
}
|
|
16049
|
+
),
|
|
16050
|
+
/* @__PURE__ */ jsxs20("g", { mask: "url(#ohw-badge-mark)", children: [
|
|
16051
|
+
/* @__PURE__ */ jsx33("path", { d: "M23.8731 48.0497C37.0584 48.0497 47.7472 37.3046 47.7472 24.0497C47.7472 10.7949 37.0584 0.0497208 23.8731 0.0497208C10.6878 0.0497208 -0.000976562 10.7949 -0.000976562 24.0497C-0.000976562 37.3046 10.6878 48.0497 23.8731 48.0497Z", fill: "#0078E5" }),
|
|
16052
|
+
/* @__PURE__ */ jsx33("path", { d: "M17.1307 14.7172C13.1687 14.7172 9.38102 18.1154 8.65114 22.34C8.38885 23.8488 8.5598 25.2581 9.06929 26.4451C6.20005 29.1677 1.77216 27.8721 -1.40212 26.1536C-2.73618 25.4317 -3.92695 27.4745 -2.59037 28.1981C1.33389 30.3226 6.86037 31.6621 10.4402 28.4188C11.4718 29.3859 12.867 29.9621 14.4894 29.9621C18.4161 29.9621 22.2038 26.5318 22.9337 22.34C23.6636 18.1162 21.0566 14.7172 17.1298 14.7172H17.1307ZM19.9798 22.34C19.5281 25.0399 17.2689 27.231 14.9754 27.231C12.6466 27.231 11.1877 25.0399 11.6394 22.34C12.1262 19.6401 14.3151 17.4482 16.6438 17.4482C18.9374 17.4482 20.4667 19.6401 19.9798 22.34Z", fill: "white" }),
|
|
16053
|
+
/* @__PURE__ */ jsx33("path", { d: "M40.0017 27.0262C39.1797 27.081 38.2721 26.995 37.4668 26.7415C37.3344 26.6993 37.28 26.5401 37.3529 26.4205C37.5959 26.0212 37.8255 25.6152 38.009 25.1889C38.1071 24.9918 38.2018 24.793 38.2897 24.5908C38.3274 24.5041 38.4163 24.451 38.5101 24.4619C38.63 24.4754 38.7054 24.4821 38.8881 24.4821L39.1529 24.4796L39.8283 24.4543C45.9229 24.0492 50.4765 20.4319 54.8466 16.9014C56.0172 15.9554 57.6932 17.6208 56.5116 18.5752C51.7687 22.4065 47.1966 26.5081 40.9319 26.9689", fill: "white" }),
|
|
16054
|
+
/* @__PURE__ */ jsx33("path", { d: "M37.9687 24.27C38.4472 23.1319 38.7656 21.9045 38.9609 20.6991C39.5927 16.76 38.5058 14.2193 36.1553 14.2193C34.1835 14.2193 33.1469 17.2427 32.9199 18.8694C32.743 19.9872 32.5914 22.1219 33.6028 23.9524C33.7553 24.259 34.15 24.7712 34.471 25.1259C34.5447 25.2067 34.6746 25.2 34.7349 25.1082C34.9528 24.7788 35.1615 24.4039 35.3584 24.0257C35.5444 23.6677 35.5888 23.6138 35.8587 23.0207C35.8838 22.966 35.8813 22.9002 35.8478 22.8505C35.5888 22.4597 35.2168 21.9787 35.1204 21.4614C34.9184 20.4455 34.9436 19.2257 35.2218 18.1078C35.4413 17.3118 35.7195 16.7844 35.9039 16.5106C35.9466 16.4466 36.0279 16.4129 36.0975 16.4449C36.369 16.5671 36.5827 16.8838 36.7385 17.396C37.0167 18.2089 37.0167 19.3782 36.814 20.6999C36.6991 21.5271 36.4771 22.3729 36.1746 23.1673C36.1293 23.308 36.0757 23.4461 36.0187 23.5826C36.0187 23.5868 36.0187 23.591 36.0187 23.5961C35.9911 23.6946 35.9207 23.8067 35.8846 23.901C35.5536 24.5497 35.2344 25.1697 34.8439 25.7838C34.8388 25.7863 34.8346 25.7914 34.8296 25.7931C34.6528 26.0525 34.4718 26.2901 34.2866 26.4965C34.2774 26.5099 34.2682 26.5234 34.2589 26.5369C34.2405 26.5638 34.2179 26.5815 34.1944 26.595C33.5064 27.3212 32.7665 27.6893 32.0123 27.6893C31.8606 27.6893 31.6838 27.664 31.507 27.4349C31.3042 27.1299 30.85 26.0879 31.2539 22.9112C31.4785 21.3949 31.8011 20.0268 31.931 19.5408C31.9579 19.4413 31.8908 19.3419 31.7886 19.3293L30.0062 19.1162C29.9241 19.1061 29.8478 19.1566 29.8252 19.2366C29.2704 21.1615 27.0305 27.6885 24.9599 27.6885C24.328 27.6885 24.1512 26.6211 24.1001 26.2909C23.775 23.6264 25.528 18.5492 29.5302 16.2267C29.6048 16.1838 29.635 16.0928 29.5998 16.0136L28.9042 14.467C28.8632 14.3752 28.7501 14.3389 28.6638 14.3886C25.8079 16.0414 24.1847 18.5231 23.2915 20.3436C22.2046 22.5793 21.6993 25.0189 21.9515 26.8738C22.1795 28.7794 23.1398 29.872 24.6054 29.872C26.0543 29.872 27.4369 28.9066 28.7098 27.0187C28.7953 26.8915 28.9889 26.9311 29.0149 27.0819C29.2646 28.5283 29.9811 29.872 31.6579 29.872C33.5282 29.872 35.3232 28.7288 36.7134 26.6447C36.7712 26.5874 36.7972 26.5411 36.8181 26.4906C36.8232 26.4931 36.8282 26.4948 36.8332 26.4973C37.01 26.2025 37.181 25.9051 37.3444 25.6027C37.4508 25.4005 37.5572 25.1992 37.6586 24.9945C37.7181 24.874 37.7743 24.7527 37.8262 24.6289C37.838 24.6002 37.8547 24.5665 37.8706 24.5337", fill: "white" }),
|
|
16055
|
+
/* @__PURE__ */ jsx33("path", { d: "M30.5839 31.6397C25.7546 34.8577 19.4773 34.7853 14.6907 31.5243C13.5368 30.7384 12.5044 32.6498 13.6474 33.4281C19.034 37.0985 26.3077 37.096 31.7218 33.488C32.8791 32.7172 31.7478 30.8639 30.5839 31.6397Z", fill: "white" })
|
|
16056
|
+
] })
|
|
16057
|
+
]
|
|
16058
|
+
}
|
|
16059
|
+
);
|
|
16060
|
+
}
|
|
16061
|
+
var OHW_BADGE_STYLE = {
|
|
16062
|
+
position: "fixed",
|
|
16063
|
+
left: 20,
|
|
16064
|
+
bottom: 20,
|
|
16065
|
+
zIndex: 2147483e3,
|
|
16066
|
+
boxSizing: "border-box",
|
|
16067
|
+
display: "inline-flex",
|
|
16068
|
+
alignItems: "center",
|
|
16069
|
+
gap: 0,
|
|
16070
|
+
padding: "6px 8px",
|
|
16071
|
+
margin: 0,
|
|
16072
|
+
background: "#ffffff",
|
|
16073
|
+
border: "1px solid #e7e5e4",
|
|
16074
|
+
borderRadius: 9999,
|
|
16075
|
+
boxShadow: "0 1px 3px rgba(0, 0, 0, 0.1)",
|
|
16076
|
+
color: "#0c0a09",
|
|
16077
|
+
textDecoration: "none",
|
|
16078
|
+
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif"
|
|
16079
|
+
};
|
|
16080
|
+
var OHW_BADGE_LABEL_STYLE = {
|
|
16081
|
+
padding: "0 4px",
|
|
16082
|
+
fontSize: 14,
|
|
16083
|
+
lineHeight: "24px",
|
|
16084
|
+
fontWeight: 500,
|
|
16085
|
+
fontStyle: "normal",
|
|
16086
|
+
letterSpacing: "normal",
|
|
16087
|
+
textTransform: "none",
|
|
16088
|
+
color: "#0c0a09",
|
|
16089
|
+
whiteSpace: "nowrap"
|
|
16090
|
+
};
|
|
16091
|
+
function MadeWithOhhWells() {
|
|
16092
|
+
return /* @__PURE__ */ jsxs20(
|
|
16093
|
+
"a",
|
|
16094
|
+
{
|
|
16095
|
+
href: "https://ohhwells.com",
|
|
16096
|
+
target: "_blank",
|
|
16097
|
+
rel: "noopener noreferrer",
|
|
16098
|
+
"aria-label": "Made with OhhWells",
|
|
16099
|
+
"data-ohw-badge": "",
|
|
16100
|
+
style: OHW_BADGE_STYLE,
|
|
16101
|
+
children: [
|
|
16102
|
+
/* @__PURE__ */ jsx33(OhwBrandMark, {}),
|
|
16103
|
+
/* @__PURE__ */ jsx33("span", { style: OHW_BADGE_LABEL_STYLE, children: "Made with OhhWells" })
|
|
16104
|
+
]
|
|
16105
|
+
}
|
|
16106
|
+
);
|
|
16107
|
+
}
|
|
15294
16108
|
var OHW_LOADER_PREHYDRATE_SCRIPT = `(function(){try{var p=location.hostname.split(".");var fromHost=p.length>=3&&p[0]!=="www"?p[0]:"";var fromQuery=new URLSearchParams(location.search).get("subdomain")||"";if(!fromHost&&!fromQuery)return;var e=document.getElementById("ohw-loader");if(e)e.style.display="flex"}catch(e){}})();`;
|
|
15295
16109
|
function resolveSubdomain(subdomainFromQuery) {
|
|
15296
16110
|
if (subdomainFromQuery) return subdomainFromQuery;
|
|
@@ -15350,6 +16164,7 @@ function OhhwellsBridge() {
|
|
|
15350
16164
|
}
|
|
15351
16165
|
}, []);
|
|
15352
16166
|
const [fetchState, setFetchState] = useState13("idle");
|
|
16167
|
+
const [showBranding, setShowBranding] = useState13(false);
|
|
15353
16168
|
const autoSaveTimers = useRef10(/* @__PURE__ */ new Map());
|
|
15354
16169
|
const activeElRef = useRef10(null);
|
|
15355
16170
|
const pointerHeldRef = useRef10(false);
|
|
@@ -15372,6 +16187,70 @@ function OhhwellsBridge() {
|
|
|
15372
16187
|
const hoveredImageHasTextOverlapRef = useRef10(false);
|
|
15373
16188
|
const dragOverElRef = useRef10(null);
|
|
15374
16189
|
const [mediaHover, setMediaHover] = useState13(null);
|
|
16190
|
+
const [selectedMedia, setSelectedMedia] = useState13(null);
|
|
16191
|
+
const selectedMediaElRef = useRef10(null);
|
|
16192
|
+
const clearMediaSelection = useCallback8(() => {
|
|
16193
|
+
const prev = selectedMediaElRef.current;
|
|
16194
|
+
selectedMediaElRef.current = null;
|
|
16195
|
+
setSelectedMedia(null);
|
|
16196
|
+
const sectionEl = prev?.closest("[data-ohw-section]") ?? null;
|
|
16197
|
+
if (sectionEl) {
|
|
16198
|
+
postToParentRef.current({
|
|
16199
|
+
type: "ow:section-selected",
|
|
16200
|
+
sectionId: sectionEl.dataset.ohwSection ?? null,
|
|
16201
|
+
sectionLabel: sectionEl.dataset.ohwSectionLabel ?? titleCaseSectionId(sectionEl.dataset.ohwSection ?? ""),
|
|
16202
|
+
key: null
|
|
16203
|
+
});
|
|
16204
|
+
}
|
|
16205
|
+
}, []);
|
|
16206
|
+
const clearMediaSelectionRef = useRef10(clearMediaSelection);
|
|
16207
|
+
clearMediaSelectionRef.current = clearMediaSelection;
|
|
16208
|
+
const selectMediaElement = useCallback8((el) => {
|
|
16209
|
+
const r2 = el.getBoundingClientRect();
|
|
16210
|
+
const video = el.dataset.ohwEditable === "video" ? el.querySelector("video") : null;
|
|
16211
|
+
selectedMediaElRef.current = el;
|
|
16212
|
+
setSelectedMedia({
|
|
16213
|
+
key: el.dataset.ohwKey ?? "",
|
|
16214
|
+
rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height },
|
|
16215
|
+
elementType: el.dataset.ohwEditable ?? "image",
|
|
16216
|
+
hasTextOverlap: false,
|
|
16217
|
+
isDragOver: false,
|
|
16218
|
+
...video ? { videoAutoplay: video.autoplay, videoMuted: video.muted } : {}
|
|
16219
|
+
});
|
|
16220
|
+
const sectionEl = el.closest("[data-ohw-section]");
|
|
16221
|
+
aiSectionApiRef.current?.selectFromElement(el, { report: false });
|
|
16222
|
+
postToParentRef.current({
|
|
16223
|
+
type: "ow:section-selected",
|
|
16224
|
+
sectionId: sectionEl?.dataset.ohwSection ?? null,
|
|
16225
|
+
sectionLabel: sectionEl ? sectionEl.dataset.ohwSectionLabel ?? titleCaseSectionId(sectionEl.dataset.ohwSection ?? "") : null,
|
|
16226
|
+
key: el.dataset.ohwKey ?? null,
|
|
16227
|
+
// Display name for the pill — the raw key prettifies into fragments ("Img"); the
|
|
16228
|
+
// bridge knows what the node IS, so it names it.
|
|
16229
|
+
keyLabel: el.dataset.ohwEditable === "video" ? "Video" : el.dataset.ohwEditable === "bg-image" ? "Background" : "Image"
|
|
16230
|
+
});
|
|
16231
|
+
}, []);
|
|
16232
|
+
const selectMediaElementRef = useRef10(selectMediaElement);
|
|
16233
|
+
selectMediaElementRef.current = selectMediaElement;
|
|
16234
|
+
useEffect13(() => {
|
|
16235
|
+
if (!selectedMedia) return;
|
|
16236
|
+
const update = () => {
|
|
16237
|
+
const el = selectedMediaElRef.current;
|
|
16238
|
+
if (!el || !el.isConnected) {
|
|
16239
|
+
clearMediaSelection();
|
|
16240
|
+
return;
|
|
16241
|
+
}
|
|
16242
|
+
const r2 = el.getBoundingClientRect();
|
|
16243
|
+
setSelectedMedia(
|
|
16244
|
+
(prev) => prev ? { ...prev, rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height } } : prev
|
|
16245
|
+
);
|
|
16246
|
+
};
|
|
16247
|
+
window.addEventListener("scroll", update, true);
|
|
16248
|
+
window.addEventListener("resize", update);
|
|
16249
|
+
return () => {
|
|
16250
|
+
window.removeEventListener("scroll", update, true);
|
|
16251
|
+
window.removeEventListener("resize", update);
|
|
16252
|
+
};
|
|
16253
|
+
}, [selectedMedia !== null]);
|
|
15375
16254
|
const [carouselHover, setCarouselHover] = useState13(null);
|
|
15376
16255
|
const [uploadingRects, setUploadingRects] = useState13({});
|
|
15377
16256
|
const hoveredGapRef = useRef10(null);
|
|
@@ -15634,13 +16513,6 @@ function OhhwellsBridge() {
|
|
|
15634
16513
|
const [isItemDragging, setIsItemDragging] = useState13(false);
|
|
15635
16514
|
const [isFooterFrameSelection, setIsFooterFrameSelection] = useState13(false);
|
|
15636
16515
|
isFooterFrameSelectionRef.current = isFooterFrameSelection;
|
|
15637
|
-
const [floatingPanel, setFloatingPanel] = useState13(null);
|
|
15638
|
-
const floatingPanelOpenRef = useRef10(false);
|
|
15639
|
-
floatingPanelOpenRef.current = floatingPanel !== null;
|
|
15640
|
-
const [floatingPanelPos, setFloatingPanelPos] = useState13(null);
|
|
15641
|
-
const [logoSizeDraft, setLogoSizeDraft] = useState13(null);
|
|
15642
|
-
const [editorViewport, setEditorViewport] = useState13("desktop");
|
|
15643
|
-
const [parentScrollSnap, setParentScrollSnap] = useState13(null);
|
|
15644
16516
|
const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = useState13(null);
|
|
15645
16517
|
const [footerHeadingVisible, setFooterHeadingVisible] = useState13(null);
|
|
15646
16518
|
const footerDragRef = useRef10(null);
|
|
@@ -15655,7 +16527,16 @@ function OhhwellsBridge() {
|
|
|
15655
16527
|
const addNavAfterAnchorRef = useRef10(null);
|
|
15656
16528
|
const editContentRef = useRef10({});
|
|
15657
16529
|
const aiSectionsRef = useRef10("");
|
|
16530
|
+
const brandKitRef = useRef10("");
|
|
16531
|
+
const stylesRef = useRef10("");
|
|
15658
16532
|
const pendingDeleteUndoRef = useRef10(null);
|
|
16533
|
+
const [floatingPanel, setFloatingPanel] = useState13(null);
|
|
16534
|
+
const floatingPanelOpenRef = useRef10(false);
|
|
16535
|
+
const setFloatingPanelRef = useRef10(setFloatingPanel);
|
|
16536
|
+
const [floatingPanelPos, setFloatingPanelPos] = useState13(null);
|
|
16537
|
+
const [logoSizeDraft, setLogoSizeDraft] = useState13(null);
|
|
16538
|
+
const [editorViewport, setEditorViewport] = useState13("desktop");
|
|
16539
|
+
const [parentScrollSnap, setParentScrollSnap] = useState13(null);
|
|
15659
16540
|
const [sitePages, setSitePages] = useState13([]);
|
|
15660
16541
|
const [sectionsByPath, setSectionsByPath] = useState13({});
|
|
15661
16542
|
const sectionsPrefetchGenRef = useRef10(0);
|
|
@@ -15664,7 +16545,18 @@ function OhhwellsBridge() {
|
|
|
15664
16545
|
const linkPopoverOpenRef = useRef10(false);
|
|
15665
16546
|
const linkPopoverGraceUntilRef = useRef10(0);
|
|
15666
16547
|
setLinkPopoverRef.current = setLinkPopover;
|
|
16548
|
+
setFloatingPanelRef.current = setFloatingPanel;
|
|
15667
16549
|
linkPopoverSessionRef.current = linkPopover;
|
|
16550
|
+
floatingPanelOpenRef.current = Boolean(floatingPanel);
|
|
16551
|
+
useEffect13(() => {
|
|
16552
|
+
const syncViewport = () => {
|
|
16553
|
+
const next = window.innerWidth <= 480 ? "mobile" : "desktop";
|
|
16554
|
+
setEditorViewport((prev) => prev === next ? prev : next);
|
|
16555
|
+
};
|
|
16556
|
+
syncViewport();
|
|
16557
|
+
window.addEventListener("resize", syncViewport);
|
|
16558
|
+
return () => window.removeEventListener("resize", syncViewport);
|
|
16559
|
+
}, []);
|
|
15668
16560
|
const {
|
|
15669
16561
|
navDragRef,
|
|
15670
16562
|
navDropSlots,
|
|
@@ -16975,15 +17867,31 @@ function OhhwellsBridge() {
|
|
|
16975
17867
|
}
|
|
16976
17868
|
const applyContent = (content) => {
|
|
16977
17869
|
const imageLoads = [];
|
|
17870
|
+
if (typeof content[BRAND_KIT_KEY] === "string") {
|
|
17871
|
+
brandKitRef.current = content[BRAND_KIT_KEY];
|
|
17872
|
+
applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
|
|
17873
|
+
} else {
|
|
17874
|
+
brandKitRef.current = "";
|
|
17875
|
+
applyBrandToDom(null);
|
|
17876
|
+
}
|
|
16978
17877
|
if (typeof content[AI_SECTIONS_KEY] === "string") {
|
|
16979
17878
|
aiSectionsRef.current = content[AI_SECTIONS_KEY];
|
|
17879
|
+
setAiSectionOrder(content[SECTION_ORDER_KEY], window.location.pathname);
|
|
16980
17880
|
applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
|
|
16981
17881
|
}
|
|
17882
|
+
if (typeof content[STYLE_STORE_KEY] === "string") {
|
|
17883
|
+
stylesRef.current = content[STYLE_STORE_KEY];
|
|
17884
|
+
applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
|
|
17885
|
+
}
|
|
17886
|
+
applyBrandChrome(content);
|
|
16982
17887
|
for (const [key, val] of Object.entries(content)) {
|
|
16983
17888
|
if (key === "__ohw_sections") continue;
|
|
16984
17889
|
if (key === AI_SECTIONS_KEY) continue;
|
|
16985
17890
|
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
16986
17891
|
if (LOGO_IMAGE_KEYS.includes(key)) continue;
|
|
17892
|
+
if (key === BRAND_KIT_KEY) continue;
|
|
17893
|
+
if (key === STYLE_STORE_KEY) continue;
|
|
17894
|
+
if (BRAND_CHROME_KEYS.has(key)) continue;
|
|
16987
17895
|
if (applyVideoSettingNode(key, val)) continue;
|
|
16988
17896
|
if (applyCarouselNode(key, val)) continue;
|
|
16989
17897
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
@@ -17043,16 +17951,22 @@ function OhhwellsBridge() {
|
|
|
17043
17951
|
};
|
|
17044
17952
|
const cached = contentCache.get(subdomain);
|
|
17045
17953
|
if (cached) {
|
|
17954
|
+
setShowBranding(brandingCache.get(subdomain) ?? false);
|
|
17046
17955
|
applyContent(cached).finally(() => setFetchState("done"));
|
|
17047
17956
|
return;
|
|
17048
17957
|
}
|
|
17049
17958
|
let cancelled = false;
|
|
17050
17959
|
setFetchState("loading");
|
|
17051
17960
|
const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
|
|
17052
|
-
|
|
17961
|
+
const initialPath = pathname;
|
|
17962
|
+
fetchedContentPaths.add(`${subdomain}::${initialPath}`);
|
|
17963
|
+
fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
|
|
17053
17964
|
if (cancelled) return;
|
|
17054
17965
|
const content = data?.content ?? {};
|
|
17966
|
+
const branding = Boolean(data?.showBranding);
|
|
17055
17967
|
contentCache.set(subdomain, content);
|
|
17968
|
+
brandingCache.set(subdomain, branding);
|
|
17969
|
+
setShowBranding(branding);
|
|
17056
17970
|
return applyContent(content);
|
|
17057
17971
|
}).catch(() => {
|
|
17058
17972
|
}).finally(() => {
|
|
@@ -17169,10 +18083,28 @@ function OhhwellsBridge() {
|
|
|
17169
18083
|
initSectionInstancesFromContent(content, window.location.pathname);
|
|
17170
18084
|
observer?.disconnect();
|
|
17171
18085
|
try {
|
|
18086
|
+
applyBrandChrome(content);
|
|
18087
|
+
if (typeof content[BRAND_KIT_KEY] === "string") {
|
|
18088
|
+
applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
|
|
18089
|
+
} else {
|
|
18090
|
+
applyBrandToDom(null);
|
|
18091
|
+
}
|
|
18092
|
+
if (typeof content[AI_SECTIONS_KEY] === "string") {
|
|
18093
|
+
setAiSectionOrder(content[SECTION_ORDER_KEY], window.location.pathname);
|
|
18094
|
+
applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
|
|
18095
|
+
}
|
|
18096
|
+
if (typeof content[STYLE_STORE_KEY] === "string") {
|
|
18097
|
+
applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
|
|
18098
|
+
}
|
|
17172
18099
|
for (const [key, val] of Object.entries(content)) {
|
|
17173
18100
|
if (key === "__ohw_sections") continue;
|
|
18101
|
+
if (key === AI_SECTIONS_KEY) continue;
|
|
17174
18102
|
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
17175
18103
|
if (LOGO_IMAGE_KEYS.includes(key)) continue;
|
|
18104
|
+
if (key === BRAND_KIT_KEY) continue;
|
|
18105
|
+
if (key === STYLE_STORE_KEY) continue;
|
|
18106
|
+
if (key === STYLE_STORE_KEY) continue;
|
|
18107
|
+
if (BRAND_CHROME_KEYS.has(key)) continue;
|
|
17176
18108
|
if (applyVideoSettingNode(key, val)) continue;
|
|
17177
18109
|
if (applyCarouselNode(key, val)) continue;
|
|
17178
18110
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
@@ -17218,6 +18150,17 @@ function OhhwellsBridge() {
|
|
|
17218
18150
|
debounceTimer = setTimeout(applyFromCache, 150);
|
|
17219
18151
|
};
|
|
17220
18152
|
applyFromCache();
|
|
18153
|
+
const pathCacheKey = `${subdomain}::${pathname}`;
|
|
18154
|
+
if (!fetchedContentPaths.has(pathCacheKey)) {
|
|
18155
|
+
fetchedContentPaths.add(pathCacheKey);
|
|
18156
|
+
const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
|
|
18157
|
+
fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(pathname)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
|
|
18158
|
+
if (!data?.content) return;
|
|
18159
|
+
contentCache.set(subdomain, data.content);
|
|
18160
|
+
applyFromCache();
|
|
18161
|
+
}).catch(() => {
|
|
18162
|
+
});
|
|
18163
|
+
}
|
|
17221
18164
|
observer = new MutationObserver(scheduleApply);
|
|
17222
18165
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
17223
18166
|
return () => {
|
|
@@ -17313,30 +18256,31 @@ function OhhwellsBridge() {
|
|
|
17313
18256
|
}, [isEditMode, pathname, subdomain, fetchState, resyncSelectedNavigationItem]);
|
|
17314
18257
|
useEffect13(() => {
|
|
17315
18258
|
if (!isEditMode) return;
|
|
18259
|
+
let lastPosted = 0;
|
|
17316
18260
|
const measure = () => {
|
|
17317
18261
|
const h = document.body.scrollHeight;
|
|
17318
|
-
if (h > 50
|
|
18262
|
+
if (h > 50 && Math.abs(h - lastPosted) > 1) {
|
|
18263
|
+
lastPosted = h;
|
|
18264
|
+
postToParent2({ type: "ow:height", height: h });
|
|
18265
|
+
}
|
|
18266
|
+
};
|
|
18267
|
+
let raf = null;
|
|
18268
|
+
const schedule = () => {
|
|
18269
|
+
if (raf != null) return;
|
|
18270
|
+
raf = requestAnimationFrame(() => {
|
|
18271
|
+
raf = null;
|
|
18272
|
+
measure();
|
|
18273
|
+
});
|
|
17319
18274
|
};
|
|
17320
18275
|
const t1 = setTimeout(measure, 50);
|
|
17321
18276
|
const t2 = setTimeout(measure, 500);
|
|
17322
|
-
|
|
17323
|
-
|
|
17324
|
-
const clearResizeTimers = () => {
|
|
17325
|
-
resizeTimers.forEach(clearTimeout);
|
|
17326
|
-
resizeTimers = [];
|
|
17327
|
-
};
|
|
17328
|
-
const handleResize = () => {
|
|
17329
|
-
if (window.innerWidth === lastWidth) return;
|
|
17330
|
-
lastWidth = window.innerWidth;
|
|
17331
|
-
clearResizeTimers();
|
|
17332
|
-
resizeTimers = HEIGHT_SETTLE_DELAYS.map((delay) => setTimeout(measure, delay));
|
|
17333
|
-
};
|
|
17334
|
-
window.addEventListener("resize", handleResize);
|
|
18277
|
+
const ro = new ResizeObserver(schedule);
|
|
18278
|
+
ro.observe(document.body);
|
|
17335
18279
|
return () => {
|
|
17336
18280
|
clearTimeout(t1);
|
|
17337
18281
|
clearTimeout(t2);
|
|
17338
|
-
|
|
17339
|
-
|
|
18282
|
+
if (raf != null) cancelAnimationFrame(raf);
|
|
18283
|
+
ro.disconnect();
|
|
17340
18284
|
};
|
|
17341
18285
|
}, [pathname, isEditMode, postToParent2]);
|
|
17342
18286
|
useEffect13(() => {
|
|
@@ -17577,6 +18521,7 @@ function OhhwellsBridge() {
|
|
|
17577
18521
|
return;
|
|
17578
18522
|
}
|
|
17579
18523
|
const target = e.target;
|
|
18524
|
+
if (target.closest("[data-ohw-ai-review]")) return;
|
|
17580
18525
|
if (target.closest("[data-ohw-toolbar]")) return;
|
|
17581
18526
|
if (target.closest("[data-ohw-state-toggle]")) return;
|
|
17582
18527
|
if (target.closest("[data-ohw-max-badge]")) return;
|
|
@@ -17588,6 +18533,9 @@ function OhhwellsBridge() {
|
|
|
17588
18533
|
)) {
|
|
17589
18534
|
return;
|
|
17590
18535
|
}
|
|
18536
|
+
if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
|
|
18537
|
+
clearMediaSelectionRef.current();
|
|
18538
|
+
}
|
|
17591
18539
|
{
|
|
17592
18540
|
const formEl = getFormElement(target);
|
|
17593
18541
|
const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
|
|
@@ -17739,19 +18687,14 @@ function OhhwellsBridge() {
|
|
|
17739
18687
|
}
|
|
17740
18688
|
const clickedButton = findClosestButtonLike(target);
|
|
17741
18689
|
const buttonOnMedia = Boolean(clickedButton && isMediaEditable(editable) && editable.contains(clickedButton));
|
|
17742
|
-
console.log("[click-debug]", {
|
|
17743
|
-
editableType: editable.dataset.ohwEditable,
|
|
17744
|
-
editableTag: editable.tagName,
|
|
17745
|
-
targetTag: target.tagName,
|
|
17746
|
-
clickedButtonTag: clickedButton?.tagName ?? null,
|
|
17747
|
-
buttonOnMedia,
|
|
17748
|
-
isMediaEditableEditable: isMediaEditable(editable)
|
|
17749
|
-
});
|
|
17750
18690
|
if (isMediaEditable(editable) && !buttonOnMedia) {
|
|
17751
18691
|
e.preventDefault();
|
|
17752
18692
|
e.stopPropagation();
|
|
17753
|
-
|
|
17754
|
-
|
|
18693
|
+
if (selectedMediaElRef.current === editable) {
|
|
18694
|
+
postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "", elementType: editable.dataset.ohwEditable ?? "image" });
|
|
18695
|
+
} else {
|
|
18696
|
+
selectMediaElementRef.current(editable);
|
|
18697
|
+
}
|
|
17755
18698
|
return;
|
|
17756
18699
|
}
|
|
17757
18700
|
const socialItem = getSocialItem(editable);
|
|
@@ -17770,11 +18713,6 @@ function OhhwellsBridge() {
|
|
|
17770
18713
|
const hrefLookupTarget = buttonOnMedia ? clickedButton : editable;
|
|
17771
18714
|
const hrefCtx = getHrefKeyFromElement(hrefLookupTarget);
|
|
17772
18715
|
const navAnchor = hrefCtx ? getNavigationItemAnchor(hrefCtx.anchor) : null;
|
|
17773
|
-
console.log("[click-debug 2]", {
|
|
17774
|
-
hrefLookupTargetTag: hrefLookupTarget.tagName,
|
|
17775
|
-
hrefCtx: hrefCtx ? { key: hrefCtx.key } : null,
|
|
17776
|
-
navAnchorTag: navAnchor?.tagName ?? null
|
|
17777
|
-
});
|
|
17778
18716
|
if (navAnchor) {
|
|
17779
18717
|
e.preventDefault();
|
|
17780
18718
|
e.stopPropagation();
|
|
@@ -17892,6 +18830,7 @@ function OhhwellsBridge() {
|
|
|
17892
18830
|
};
|
|
17893
18831
|
const handleDblClick = (e) => {
|
|
17894
18832
|
const target = e.target;
|
|
18833
|
+
if (target.closest("[data-ohw-ai-review]")) return;
|
|
17895
18834
|
if (target.closest("[data-ohw-toolbar]")) return;
|
|
17896
18835
|
if (target.closest("[data-ohw-state-toggle]")) return;
|
|
17897
18836
|
if (target.closest("[data-ohw-max-badge]")) return;
|
|
@@ -17943,6 +18882,9 @@ function OhhwellsBridge() {
|
|
|
17943
18882
|
setHoveredItemRect(null);
|
|
17944
18883
|
hoveredNavContainerRef.current = null;
|
|
17945
18884
|
setHoveredNavContainerRect(null);
|
|
18885
|
+
siblingHintElRef.current = null;
|
|
18886
|
+
setSiblingHintRect(null);
|
|
18887
|
+
setSiblingHintRects([]);
|
|
17946
18888
|
return;
|
|
17947
18889
|
}
|
|
17948
18890
|
{
|
|
@@ -18061,7 +19003,6 @@ function OhhwellsBridge() {
|
|
|
18061
19003
|
hoveredNavContainerRef.current = null;
|
|
18062
19004
|
setHoveredNavContainerRect(null);
|
|
18063
19005
|
hoveredItemElRef.current = editable;
|
|
18064
|
-
setHoveredItemRect(isSelectedForHoverRef.current(editable) ? null : editable.getBoundingClientRect());
|
|
18065
19006
|
}
|
|
18066
19007
|
}
|
|
18067
19008
|
}
|
|
@@ -18358,7 +19299,7 @@ function OhhwellsBridge() {
|
|
|
18358
19299
|
}
|
|
18359
19300
|
};
|
|
18360
19301
|
const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
|
|
18361
|
-
if (linkPopoverOpenRef.current) {
|
|
19302
|
+
if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
|
|
18362
19303
|
if (hoveredImageRef.current) {
|
|
18363
19304
|
hoveredImageRef.current = null;
|
|
18364
19305
|
hoveredImageHasTextOverlapRef.current = false;
|
|
@@ -18692,7 +19633,9 @@ function OhhwellsBridge() {
|
|
|
18692
19633
|
return;
|
|
18693
19634
|
}
|
|
18694
19635
|
const { y } = toProbeCoords(clientX, clientY, fromParentViewport);
|
|
18695
|
-
const sections = Array.from(document.querySelectorAll("[data-ohw-section]")).
|
|
19636
|
+
const sections = Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
|
|
19637
|
+
(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
|
|
19638
|
+
).sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);
|
|
18696
19639
|
const ZONE = 20;
|
|
18697
19640
|
for (let i = 0; i < sections.length; i++) {
|
|
18698
19641
|
const a = sections[i];
|
|
@@ -18721,8 +19664,7 @@ function OhhwellsBridge() {
|
|
|
18721
19664
|
};
|
|
18722
19665
|
const handleMouseMove = (e) => {
|
|
18723
19666
|
const { clientX, clientY } = e;
|
|
18724
|
-
if (
|
|
18725
|
-
if (isOverEditorChrome(clientX, clientY)) {
|
|
19667
|
+
if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY) || isOverEditorChrome(clientX, clientY)) {
|
|
18726
19668
|
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
18727
19669
|
formHoverElRef.current = null;
|
|
18728
19670
|
setFormHoverRect(null);
|
|
@@ -18730,6 +19672,12 @@ function OhhwellsBridge() {
|
|
|
18730
19672
|
setHoveredItemRect(null);
|
|
18731
19673
|
hoveredNavContainerRef.current = null;
|
|
18732
19674
|
setHoveredNavContainerRect(null);
|
|
19675
|
+
siblingHintElRef.current = null;
|
|
19676
|
+
setSiblingHintRect(null);
|
|
19677
|
+
setSiblingHintRects([]);
|
|
19678
|
+
dismissImageHover();
|
|
19679
|
+
clearImageHover();
|
|
19680
|
+
setSectionGap(null);
|
|
18733
19681
|
return;
|
|
18734
19682
|
}
|
|
18735
19683
|
if (probeSocialsRowAt(clientX, clientY)) return;
|
|
@@ -18741,7 +19689,11 @@ function OhhwellsBridge() {
|
|
|
18741
19689
|
if (e.data?.type !== "ow:pointer-sync") return;
|
|
18742
19690
|
const { clientX, clientY } = e.data;
|
|
18743
19691
|
if (typeof clientX !== "number" || typeof clientY !== "number") return;
|
|
18744
|
-
if (
|
|
19692
|
+
if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
|
|
19693
|
+
dismissImageHover();
|
|
19694
|
+
clearImageHover();
|
|
19695
|
+
return;
|
|
19696
|
+
}
|
|
18745
19697
|
if (probeSocialsRowAt(clientX, clientY)) return;
|
|
18746
19698
|
probeSectionGapAt(clientX, clientY);
|
|
18747
19699
|
probeImageAt(clientX, clientY);
|
|
@@ -19024,10 +19976,23 @@ function OhhwellsBridge() {
|
|
|
19024
19976
|
if (e.data?.type !== "ow:hydrate") return;
|
|
19025
19977
|
const content = e.data.content;
|
|
19026
19978
|
if (!content) return;
|
|
19979
|
+
if (typeof content[BRAND_KIT_KEY] === "string") {
|
|
19980
|
+
brandKitRef.current = content[BRAND_KIT_KEY];
|
|
19981
|
+
applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
|
|
19982
|
+
} else {
|
|
19983
|
+
brandKitRef.current = "";
|
|
19984
|
+
applyBrandToDom(null);
|
|
19985
|
+
}
|
|
19027
19986
|
if (typeof content[AI_SECTIONS_KEY] === "string") {
|
|
19028
19987
|
aiSectionsRef.current = content[AI_SECTIONS_KEY];
|
|
19988
|
+
setAiSectionOrder(content[SECTION_ORDER_KEY], window.location.pathname);
|
|
19029
19989
|
applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
|
|
19030
19990
|
}
|
|
19991
|
+
if (typeof content[STYLE_STORE_KEY] === "string") {
|
|
19992
|
+
stylesRef.current = content[STYLE_STORE_KEY];
|
|
19993
|
+
applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
|
|
19994
|
+
}
|
|
19995
|
+
applyBrandChrome(content);
|
|
19031
19996
|
let sectionsJson = null;
|
|
19032
19997
|
for (const [key, val] of Object.entries(content)) {
|
|
19033
19998
|
if (key === "__ohw_sections") {
|
|
@@ -19037,6 +20002,9 @@ function OhhwellsBridge() {
|
|
|
19037
20002
|
if (key === AI_SECTIONS_KEY) continue;
|
|
19038
20003
|
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
19039
20004
|
if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
|
|
20005
|
+
if (key === BRAND_KIT_KEY) continue;
|
|
20006
|
+
if (key === STYLE_STORE_KEY) continue;
|
|
20007
|
+
if (BRAND_CHROME_KEYS.has(key)) continue;
|
|
19040
20008
|
if (applyVideoSettingNode(key, val)) continue;
|
|
19041
20009
|
if (applyCarouselNode(key, val)) continue;
|
|
19042
20010
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
@@ -19050,6 +20018,8 @@ function OhhwellsBridge() {
|
|
|
19050
20018
|
if (video && video.src !== val) applyVideoSrc(video, val);
|
|
19051
20019
|
} else if (el.dataset.ohwEditable === "link") {
|
|
19052
20020
|
applyLinkHref(el, val);
|
|
20021
|
+
} else if (el.dataset.ohwEditable === "icon") {
|
|
20022
|
+
applyIconMarkup(el, val);
|
|
19053
20023
|
} else if (isIconMarkupValue(val)) {
|
|
19054
20024
|
} else {
|
|
19055
20025
|
el.innerHTML = val;
|
|
@@ -19134,12 +20104,21 @@ function OhhwellsBridge() {
|
|
|
19134
20104
|
nodes: collectEditableNodes(editContentRef.current)
|
|
19135
20105
|
});
|
|
19136
20106
|
};
|
|
20107
|
+
const clearInteractionChrome = () => {
|
|
20108
|
+
deactivateRef.current();
|
|
20109
|
+
deselectRef.current();
|
|
20110
|
+
clearMediaSelectionRef.current();
|
|
20111
|
+
};
|
|
19137
20112
|
const handleAiApplyTree = (e) => {
|
|
19138
20113
|
if (e.data?.type !== "ow:ai-apply-tree") return;
|
|
19139
20114
|
const payload = e.data.payload;
|
|
19140
20115
|
if (!payload || typeof payload.id !== "string" || !isRenderableTree(payload.tree)) return;
|
|
20116
|
+
clearInteractionChrome();
|
|
19141
20117
|
const previous = aiSectionsRef.current;
|
|
19142
|
-
const nextState = applyTreeToState(parseAiSectionsState(previous),
|
|
20118
|
+
const nextState = applyTreeToState(parseAiSectionsState(previous), {
|
|
20119
|
+
...payload,
|
|
20120
|
+
path: payload.path ?? window.location.pathname
|
|
20121
|
+
});
|
|
19143
20122
|
const nextValue = serializeAiSectionsState(nextState);
|
|
19144
20123
|
aiSectionsRef.current = nextValue;
|
|
19145
20124
|
applyAiSectionsToDom(nextState);
|
|
@@ -19160,6 +20139,7 @@ function OhhwellsBridge() {
|
|
|
19160
20139
|
const exists = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
|
|
19161
20140
|
if (!exists) return;
|
|
19162
20141
|
if (isPageFrameSection(exists)) return;
|
|
20142
|
+
clearInteractionChrome();
|
|
19163
20143
|
const previous = aiSectionsRef.current;
|
|
19164
20144
|
const nextState = deleteSectionFromState(parseAiSectionsState(previous), sectionId);
|
|
19165
20145
|
const nextValue = serializeAiSectionsState(nextState);
|
|
@@ -19175,8 +20155,10 @@ function OhhwellsBridge() {
|
|
|
19175
20155
|
const handleAiSetSections = (e) => {
|
|
19176
20156
|
if (e.data?.type !== "ow:ai-set-sections") return;
|
|
19177
20157
|
const value = typeof e.data.value === "string" ? e.data.value : "";
|
|
20158
|
+
clearInteractionChrome();
|
|
19178
20159
|
aiSectionsRef.current = value;
|
|
19179
20160
|
applyAiSectionsToDom(parseAiSectionsState(value));
|
|
20161
|
+
applyStylesToDom(parseStyleStore(stylesRef.current));
|
|
19180
20162
|
const restoredHeight = document.body.scrollHeight;
|
|
19181
20163
|
if (restoredHeight > 50) postToParentRef.current({ type: "ow:height", height: restoredHeight });
|
|
19182
20164
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: AI_SECTIONS_KEY, text: value }] });
|
|
@@ -19192,10 +20174,40 @@ function OhhwellsBridge() {
|
|
|
19192
20174
|
if (!entries) return;
|
|
19193
20175
|
const orderJson = JSON.stringify(entries);
|
|
19194
20176
|
editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
|
|
20177
|
+
setAiSectionOrder(orderJson, window.location.pathname);
|
|
19195
20178
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
|
|
19196
20179
|
window.dispatchEvent(new Event("resize"));
|
|
19197
20180
|
};
|
|
19198
20181
|
window.addEventListener("message", handleMoveSection);
|
|
20182
|
+
const handleAiSetBrand = (e) => {
|
|
20183
|
+
if (e.data?.type !== "ow:ai-set-brand") return;
|
|
20184
|
+
const value = typeof e.data.value === "string" ? e.data.value : "";
|
|
20185
|
+
const previous = brandKitRef.current;
|
|
20186
|
+
brandKitRef.current = value;
|
|
20187
|
+
applyBrandToDom(parseBrandKit(value));
|
|
20188
|
+
if (aiSectionsRef.current) applyAiSectionsToDom(parseAiSectionsState(aiSectionsRef.current));
|
|
20189
|
+
applyStylesToDom(parseStyleStore(stylesRef.current));
|
|
20190
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: BRAND_KIT_KEY, text: value }] });
|
|
20191
|
+
postToParentRef.current({ type: "ow:ai-brand-applied", previous, value });
|
|
20192
|
+
};
|
|
20193
|
+
window.addEventListener("message", handleAiSetBrand);
|
|
20194
|
+
const handleAiSetStyles = (e) => {
|
|
20195
|
+
if (e.data?.type !== "ow:ai-set-styles") return;
|
|
20196
|
+
const value = typeof e.data.value === "string" ? e.data.value : "";
|
|
20197
|
+
const previous = stylesRef.current;
|
|
20198
|
+
stylesRef.current = value;
|
|
20199
|
+
applyStylesToDom(parseStyleStore(value));
|
|
20200
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: STYLE_STORE_KEY, text: value }] });
|
|
20201
|
+
postToParentRef.current({ type: "ow:ai-styles-applied", previous, value });
|
|
20202
|
+
};
|
|
20203
|
+
window.addEventListener("message", handleAiSetStyles);
|
|
20204
|
+
const handleGetBrand = (e) => {
|
|
20205
|
+
if (e.data?.type !== "ow:get-brand") return;
|
|
20206
|
+
const template = deriveTemplateBrand();
|
|
20207
|
+
const value = brandKitRef.current || (template ? JSON.stringify(template) : "");
|
|
20208
|
+
postToParentRef.current({ type: "ow:brand-value", value });
|
|
20209
|
+
};
|
|
20210
|
+
window.addEventListener("message", handleGetBrand);
|
|
19199
20211
|
const handlePanelDragging = (e) => {
|
|
19200
20212
|
if (e.data?.type !== "ow:panel-dragging") return;
|
|
19201
20213
|
if (e.data.dragging) document.documentElement.setAttribute("data-ohw-panel-dragging", "");
|
|
@@ -19253,8 +20265,15 @@ function OhhwellsBridge() {
|
|
|
19253
20265
|
closeLinkPopoverRef.current();
|
|
19254
20266
|
return;
|
|
19255
20267
|
}
|
|
20268
|
+
if (floatingPanelOpenRef.current) {
|
|
20269
|
+
setFloatingPanelRef.current(null);
|
|
20270
|
+
deselectRef.current();
|
|
20271
|
+
deactivateRef.current();
|
|
20272
|
+
return;
|
|
20273
|
+
}
|
|
19256
20274
|
deselectRef.current();
|
|
19257
20275
|
deactivateRef.current();
|
|
20276
|
+
clearMediaSelectionRef.current();
|
|
19258
20277
|
};
|
|
19259
20278
|
window.addEventListener("message", handleDeactivate);
|
|
19260
20279
|
const handleToastAction = (e) => {
|
|
@@ -19340,6 +20359,10 @@ function OhhwellsBridge() {
|
|
|
19340
20359
|
const handleKeyDown = (e) => {
|
|
19341
20360
|
if (e.key === "Escape" && document.querySelector("[data-ohw-section-picker]")) return;
|
|
19342
20361
|
if (e.key === "Escape" && document.querySelector("[data-ohw-more-menu]")) return;
|
|
20362
|
+
if (e.key === "Escape" && selectedMediaElRef.current) {
|
|
20363
|
+
clearMediaSelectionRef.current();
|
|
20364
|
+
return;
|
|
20365
|
+
}
|
|
19343
20366
|
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "a" && activeElRef.current) {
|
|
19344
20367
|
e.preventDefault();
|
|
19345
20368
|
selectAllTextInEditable(activeElRef.current);
|
|
@@ -19499,6 +20522,12 @@ function OhhwellsBridge() {
|
|
|
19499
20522
|
if (aiSectionsRef.current) {
|
|
19500
20523
|
nodes.push({ key: AI_SECTIONS_KEY, type: "sections", text: aiSectionsRef.current });
|
|
19501
20524
|
}
|
|
20525
|
+
if (stylesRef.current) {
|
|
20526
|
+
nodes.push({ key: STYLE_STORE_KEY, type: "styles", text: stylesRef.current });
|
|
20527
|
+
}
|
|
20528
|
+
if (brandKitRef.current) {
|
|
20529
|
+
nodes.push({ key: BRAND_KIT_KEY, type: "brand", text: brandKitRef.current });
|
|
20530
|
+
}
|
|
19502
20531
|
document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
|
|
19503
20532
|
const formKey = formKeyOf(form);
|
|
19504
20533
|
if (!formKey) return;
|
|
@@ -19516,8 +20545,12 @@ function OhhwellsBridge() {
|
|
|
19516
20545
|
if (inserted) {
|
|
19517
20546
|
const tracker = getSectionsTracker();
|
|
19518
20547
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
|
|
19519
|
-
const
|
|
19520
|
-
|
|
20548
|
+
const reportHeight = () => {
|
|
20549
|
+
const h = document.body.scrollHeight;
|
|
20550
|
+
if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
|
|
20551
|
+
};
|
|
20552
|
+
reportHeight();
|
|
20553
|
+
setTimeout(reportHeight, 500);
|
|
19521
20554
|
}
|
|
19522
20555
|
};
|
|
19523
20556
|
const handleSwitchSchedule = (e) => {
|
|
@@ -19914,13 +20947,16 @@ function OhhwellsBridge() {
|
|
|
19914
20947
|
window.removeEventListener("message", handleAiDeleteSection);
|
|
19915
20948
|
window.removeEventListener("message", handleAiSetSections);
|
|
19916
20949
|
window.removeEventListener("message", handleMoveSection);
|
|
20950
|
+
window.removeEventListener("message", handleAiSetBrand);
|
|
20951
|
+
window.removeEventListener("message", handleAiSetStyles);
|
|
20952
|
+
window.removeEventListener("message", handleGetBrand);
|
|
19917
20953
|
window.removeEventListener("message", handlePanelDragging);
|
|
19918
20954
|
window.removeEventListener("message", handleDeleteSection);
|
|
19919
20955
|
window.removeEventListener("message", handleDeactivate);
|
|
19920
|
-
document.documentElement.removeAttribute("data-ohw-panel-dragging");
|
|
19921
20956
|
window.removeEventListener("message", handleToastAction);
|
|
19922
20957
|
window.removeEventListener("message", handleFormCount);
|
|
19923
20958
|
window.removeEventListener("message", handleUiEscape);
|
|
20959
|
+
document.documentElement.removeAttribute("data-ohw-panel-dragging");
|
|
19924
20960
|
autoSaveTimers.current.forEach(clearTimeout);
|
|
19925
20961
|
autoSaveTimers.current.clear();
|
|
19926
20962
|
if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
|
|
@@ -20123,7 +21159,7 @@ function OhhwellsBridge() {
|
|
|
20123
21159
|
postToParent2({
|
|
20124
21160
|
type: "ow:ready",
|
|
20125
21161
|
version: "1",
|
|
20126
|
-
bridgeVersion: "0.1.
|
|
21162
|
+
bridgeVersion: "0.1.79",
|
|
20127
21163
|
path: pathname,
|
|
20128
21164
|
nodes: collectEditableNodes(editContentRef.current),
|
|
20129
21165
|
sections
|
|
@@ -20530,11 +21566,22 @@ function OhhwellsBridge() {
|
|
|
20530
21566
|
const showEditLink = toolbarShowEditLink;
|
|
20531
21567
|
const currentSections = sectionsByPath[pathname] ?? [];
|
|
20532
21568
|
linkPopoverOpenRef.current = linkPopover !== null;
|
|
21569
|
+
const handleMediaSelect = useCallback8((key) => {
|
|
21570
|
+
const el = hoveredImageRef.current?.dataset.ohwKey === key ? hoveredImageRef.current : Array.from(document.querySelectorAll(MEDIA_SELECTOR)).find(
|
|
21571
|
+
(m) => (m.dataset.ohwKey ?? "") === key
|
|
21572
|
+
) ?? null;
|
|
21573
|
+
if (!el) return;
|
|
21574
|
+
selectMediaElementRef.current(el);
|
|
21575
|
+
}, []);
|
|
20533
21576
|
const handleMediaReplace = useCallback8(
|
|
20534
21577
|
(key) => {
|
|
20535
|
-
postToParent2({
|
|
21578
|
+
postToParent2({
|
|
21579
|
+
type: "ow:image-pick",
|
|
21580
|
+
key,
|
|
21581
|
+
elementType: mediaHover?.elementType ?? selectedMedia?.elementType ?? "image"
|
|
21582
|
+
});
|
|
20536
21583
|
},
|
|
20537
|
-
[postToParent2, mediaHover?.elementType]
|
|
21584
|
+
[postToParent2, mediaHover?.elementType, selectedMedia?.elementType]
|
|
20538
21585
|
);
|
|
20539
21586
|
const handleEditCarousel = useCallback8(
|
|
20540
21587
|
(key) => {
|
|
@@ -20575,6 +21622,7 @@ function OhhwellsBridge() {
|
|
|
20575
21622
|
return /* @__PURE__ */ jsxs20(Fragment8, { children: [
|
|
20576
21623
|
/* @__PURE__ */ jsx33("div", { id: "ohw-loader", suppressHydrationWarning: true, style: { ...OHW_LOADER_STYLE, display: "none" }, children: /* @__PURE__ */ jsx33(OhwLoaderSpinner, {}) }),
|
|
20577
21624
|
/* @__PURE__ */ jsx33("script", { suppressHydrationWarning: true, dangerouslySetInnerHTML: { __html: OHW_LOADER_PREHYDRATE_SCRIPT } }),
|
|
21625
|
+
subdomain && !isEditMode && showBranding && /* @__PURE__ */ jsx33(MadeWithOhhWells, {}),
|
|
20578
21626
|
bridgeRoot ? createPortal2(
|
|
20579
21627
|
/* @__PURE__ */ jsxs20(Fragment8, { children: [
|
|
20580
21628
|
/* @__PURE__ */ jsx33("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
|
|
@@ -20606,12 +21654,25 @@ function OhhwellsBridge() {
|
|
|
20606
21654
|
},
|
|
20607
21655
|
`uploading-${key}`
|
|
20608
21656
|
)),
|
|
20609
|
-
mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ jsx33(
|
|
21657
|
+
mediaHover && !(mediaHover.key in uploadingRects) && mediaHover.key !== selectedMedia?.key && /* @__PURE__ */ jsx33(
|
|
20610
21658
|
MediaOverlay,
|
|
20611
21659
|
{
|
|
20612
21660
|
hover: mediaHover,
|
|
20613
21661
|
isUploading: false,
|
|
20614
21662
|
onReplace: handleMediaReplace,
|
|
21663
|
+
onSelect: handleMediaSelect,
|
|
21664
|
+
onVideoSettingsChange: handleVideoSettingsChange
|
|
21665
|
+
}
|
|
21666
|
+
),
|
|
21667
|
+
selectedMedia && !(selectedMedia.key in uploadingRects) && !linkPopover && /* @__PURE__ */ jsx33(
|
|
21668
|
+
MediaOverlay,
|
|
21669
|
+
{
|
|
21670
|
+
hover: selectedMedia,
|
|
21671
|
+
selected: true,
|
|
21672
|
+
hovered: mediaHover?.key === selectedMedia.key,
|
|
21673
|
+
isUploading: false,
|
|
21674
|
+
onReplace: handleMediaReplace,
|
|
21675
|
+
onSelect: handleMediaSelect,
|
|
20615
21676
|
onVideoSettingsChange: handleVideoSettingsChange
|
|
20616
21677
|
}
|
|
20617
21678
|
),
|
|
@@ -21017,6 +22078,59 @@ function OhhwellsBridge() {
|
|
|
21017
22078
|
) : null
|
|
21018
22079
|
] });
|
|
21019
22080
|
}
|
|
22081
|
+
|
|
22082
|
+
// src/ui/EmptySection.tsx
|
|
22083
|
+
import Link3 from "next/link";
|
|
22084
|
+
import { Fragment as Fragment9, jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
22085
|
+
function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey }) {
|
|
22086
|
+
return /* @__PURE__ */ jsxs21(Fragment9, { children: [
|
|
22087
|
+
/* @__PURE__ */ jsx34(
|
|
22088
|
+
"p",
|
|
22089
|
+
{
|
|
22090
|
+
style: {
|
|
22091
|
+
fontFamily: "var(--brand-font-body)",
|
|
22092
|
+
fontSize: "0.75rem",
|
|
22093
|
+
fontWeight: 500,
|
|
22094
|
+
letterSpacing: "0.15em",
|
|
22095
|
+
textTransform: "uppercase",
|
|
22096
|
+
color: "var(--brand-accent)",
|
|
22097
|
+
marginBottom: "1.5rem"
|
|
22098
|
+
},
|
|
22099
|
+
children: /* @__PURE__ */ jsx34(Link3, { href: homeHref, style: { color: "inherit" }, children: /* @__PURE__ */ jsx34("span", { ...eyebrowKey ? { "data-ohw-editable": "text", "data-ohw-key": eyebrowKey } : {}, children: "Home" }) })
|
|
22100
|
+
}
|
|
22101
|
+
),
|
|
22102
|
+
/* @__PURE__ */ jsx34(
|
|
22103
|
+
"h1",
|
|
22104
|
+
{
|
|
22105
|
+
style: {
|
|
22106
|
+
fontFamily: "var(--brand-font-heading)",
|
|
22107
|
+
fontSize: "clamp(2rem, 3.5vw, 2.75rem)",
|
|
22108
|
+
lineHeight: 1.1,
|
|
22109
|
+
letterSpacing: "-0.025em",
|
|
22110
|
+
color: "var(--brand-text)",
|
|
22111
|
+
marginBottom: "1rem"
|
|
22112
|
+
},
|
|
22113
|
+
...titleKey ? { "data-ohw-editable": "text", "data-ohw-key": titleKey, "data-ohw-max-length": 80 } : {},
|
|
22114
|
+
children: title
|
|
22115
|
+
}
|
|
22116
|
+
),
|
|
22117
|
+
/* @__PURE__ */ jsx34(
|
|
22118
|
+
"p",
|
|
22119
|
+
{
|
|
22120
|
+
style: {
|
|
22121
|
+
fontFamily: "var(--brand-font-body)",
|
|
22122
|
+
fontSize: "1rem",
|
|
22123
|
+
lineHeight: 1.7,
|
|
22124
|
+
fontWeight: 300,
|
|
22125
|
+
color: "var(--brand-text-muted)",
|
|
22126
|
+
maxWidth: "340px"
|
|
22127
|
+
},
|
|
22128
|
+
...subtitleKey ? { "data-ohw-editable": "text", "data-ohw-key": subtitleKey, "data-ohw-max-length": 160 } : {},
|
|
22129
|
+
children: "This page doesn't have any content yet."
|
|
22130
|
+
}
|
|
22131
|
+
)
|
|
22132
|
+
] });
|
|
22133
|
+
}
|
|
21020
22134
|
export {
|
|
21021
22135
|
AI_DEFAULT_BRAND,
|
|
21022
22136
|
AI_TREE_SCHEMA_VERSIONS,
|
|
@@ -21033,6 +22147,7 @@ export {
|
|
|
21033
22147
|
DropdownMenuItem,
|
|
21034
22148
|
DropdownMenuSeparator,
|
|
21035
22149
|
DropdownMenuTrigger,
|
|
22150
|
+
EmptySection,
|
|
21036
22151
|
ItemActionToolbar,
|
|
21037
22152
|
ItemInteractionLayer,
|
|
21038
22153
|
LinkEditorPanel,
|