@ohhwells/bridge 0.1.78 → 0.1.79-next.238
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1239 -213
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -5
- package/dist/index.d.ts +21 -5
- package/dist/index.js +1238 -213
- package/dist/index.js.map +1 -1
- package/dist/pages.cjs +141 -0
- package/dist/pages.cjs.map +1 -0
- package/dist/pages.d.cts +45 -0
- package/dist/pages.d.ts +45 -0
- package/dist/pages.js +107 -0
- package/dist/pages.js.map +1 -0
- package/dist/styles.css +510 -452
- package/package.json +8 -3
package/dist/index.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,7 @@ function StateToggle({
|
|
|
15254
15983
|
);
|
|
15255
15984
|
}
|
|
15256
15985
|
var contentCache = /* @__PURE__ */ new Map();
|
|
15986
|
+
var fetchedContentPaths = /* @__PURE__ */ new Set();
|
|
15257
15987
|
var OHW_LOADER_STYLE = {
|
|
15258
15988
|
position: "fixed",
|
|
15259
15989
|
inset: 0,
|
|
@@ -15372,6 +16102,70 @@ function OhhwellsBridge() {
|
|
|
15372
16102
|
const hoveredImageHasTextOverlapRef = useRef10(false);
|
|
15373
16103
|
const dragOverElRef = useRef10(null);
|
|
15374
16104
|
const [mediaHover, setMediaHover] = useState13(null);
|
|
16105
|
+
const [selectedMedia, setSelectedMedia] = useState13(null);
|
|
16106
|
+
const selectedMediaElRef = useRef10(null);
|
|
16107
|
+
const clearMediaSelection = useCallback8(() => {
|
|
16108
|
+
const prev = selectedMediaElRef.current;
|
|
16109
|
+
selectedMediaElRef.current = null;
|
|
16110
|
+
setSelectedMedia(null);
|
|
16111
|
+
const sectionEl = prev?.closest("[data-ohw-section]") ?? null;
|
|
16112
|
+
if (sectionEl) {
|
|
16113
|
+
postToParentRef.current({
|
|
16114
|
+
type: "ow:section-selected",
|
|
16115
|
+
sectionId: sectionEl.dataset.ohwSection ?? null,
|
|
16116
|
+
sectionLabel: sectionEl.dataset.ohwSectionLabel ?? titleCaseSectionId(sectionEl.dataset.ohwSection ?? ""),
|
|
16117
|
+
key: null
|
|
16118
|
+
});
|
|
16119
|
+
}
|
|
16120
|
+
}, []);
|
|
16121
|
+
const clearMediaSelectionRef = useRef10(clearMediaSelection);
|
|
16122
|
+
clearMediaSelectionRef.current = clearMediaSelection;
|
|
16123
|
+
const selectMediaElement = useCallback8((el) => {
|
|
16124
|
+
const r2 = el.getBoundingClientRect();
|
|
16125
|
+
const video = el.dataset.ohwEditable === "video" ? el.querySelector("video") : null;
|
|
16126
|
+
selectedMediaElRef.current = el;
|
|
16127
|
+
setSelectedMedia({
|
|
16128
|
+
key: el.dataset.ohwKey ?? "",
|
|
16129
|
+
rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height },
|
|
16130
|
+
elementType: el.dataset.ohwEditable ?? "image",
|
|
16131
|
+
hasTextOverlap: false,
|
|
16132
|
+
isDragOver: false,
|
|
16133
|
+
...video ? { videoAutoplay: video.autoplay, videoMuted: video.muted } : {}
|
|
16134
|
+
});
|
|
16135
|
+
const sectionEl = el.closest("[data-ohw-section]");
|
|
16136
|
+
aiSectionApiRef.current?.selectFromElement(el, { report: false });
|
|
16137
|
+
postToParentRef.current({
|
|
16138
|
+
type: "ow:section-selected",
|
|
16139
|
+
sectionId: sectionEl?.dataset.ohwSection ?? null,
|
|
16140
|
+
sectionLabel: sectionEl ? sectionEl.dataset.ohwSectionLabel ?? titleCaseSectionId(sectionEl.dataset.ohwSection ?? "") : null,
|
|
16141
|
+
key: el.dataset.ohwKey ?? null,
|
|
16142
|
+
// Display name for the pill — the raw key prettifies into fragments ("Img"); the
|
|
16143
|
+
// bridge knows what the node IS, so it names it.
|
|
16144
|
+
keyLabel: el.dataset.ohwEditable === "video" ? "Video" : el.dataset.ohwEditable === "bg-image" ? "Background" : "Image"
|
|
16145
|
+
});
|
|
16146
|
+
}, []);
|
|
16147
|
+
const selectMediaElementRef = useRef10(selectMediaElement);
|
|
16148
|
+
selectMediaElementRef.current = selectMediaElement;
|
|
16149
|
+
useEffect13(() => {
|
|
16150
|
+
if (!selectedMedia) return;
|
|
16151
|
+
const update = () => {
|
|
16152
|
+
const el = selectedMediaElRef.current;
|
|
16153
|
+
if (!el || !el.isConnected) {
|
|
16154
|
+
clearMediaSelection();
|
|
16155
|
+
return;
|
|
16156
|
+
}
|
|
16157
|
+
const r2 = el.getBoundingClientRect();
|
|
16158
|
+
setSelectedMedia(
|
|
16159
|
+
(prev) => prev ? { ...prev, rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height } } : prev
|
|
16160
|
+
);
|
|
16161
|
+
};
|
|
16162
|
+
window.addEventListener("scroll", update, true);
|
|
16163
|
+
window.addEventListener("resize", update);
|
|
16164
|
+
return () => {
|
|
16165
|
+
window.removeEventListener("scroll", update, true);
|
|
16166
|
+
window.removeEventListener("resize", update);
|
|
16167
|
+
};
|
|
16168
|
+
}, [selectedMedia !== null]);
|
|
15375
16169
|
const [carouselHover, setCarouselHover] = useState13(null);
|
|
15376
16170
|
const [uploadingRects, setUploadingRects] = useState13({});
|
|
15377
16171
|
const hoveredGapRef = useRef10(null);
|
|
@@ -15634,13 +16428,6 @@ function OhhwellsBridge() {
|
|
|
15634
16428
|
const [isItemDragging, setIsItemDragging] = useState13(false);
|
|
15635
16429
|
const [isFooterFrameSelection, setIsFooterFrameSelection] = useState13(false);
|
|
15636
16430
|
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
16431
|
const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = useState13(null);
|
|
15645
16432
|
const [footerHeadingVisible, setFooterHeadingVisible] = useState13(null);
|
|
15646
16433
|
const footerDragRef = useRef10(null);
|
|
@@ -15655,7 +16442,16 @@ function OhhwellsBridge() {
|
|
|
15655
16442
|
const addNavAfterAnchorRef = useRef10(null);
|
|
15656
16443
|
const editContentRef = useRef10({});
|
|
15657
16444
|
const aiSectionsRef = useRef10("");
|
|
16445
|
+
const brandKitRef = useRef10("");
|
|
16446
|
+
const stylesRef = useRef10("");
|
|
15658
16447
|
const pendingDeleteUndoRef = useRef10(null);
|
|
16448
|
+
const [floatingPanel, setFloatingPanel] = useState13(null);
|
|
16449
|
+
const floatingPanelOpenRef = useRef10(false);
|
|
16450
|
+
const setFloatingPanelRef = useRef10(setFloatingPanel);
|
|
16451
|
+
const [floatingPanelPos, setFloatingPanelPos] = useState13(null);
|
|
16452
|
+
const [logoSizeDraft, setLogoSizeDraft] = useState13(null);
|
|
16453
|
+
const [editorViewport, setEditorViewport] = useState13("desktop");
|
|
16454
|
+
const [parentScrollSnap, setParentScrollSnap] = useState13(null);
|
|
15659
16455
|
const [sitePages, setSitePages] = useState13([]);
|
|
15660
16456
|
const [sectionsByPath, setSectionsByPath] = useState13({});
|
|
15661
16457
|
const sectionsPrefetchGenRef = useRef10(0);
|
|
@@ -15664,7 +16460,18 @@ function OhhwellsBridge() {
|
|
|
15664
16460
|
const linkPopoverOpenRef = useRef10(false);
|
|
15665
16461
|
const linkPopoverGraceUntilRef = useRef10(0);
|
|
15666
16462
|
setLinkPopoverRef.current = setLinkPopover;
|
|
16463
|
+
setFloatingPanelRef.current = setFloatingPanel;
|
|
15667
16464
|
linkPopoverSessionRef.current = linkPopover;
|
|
16465
|
+
floatingPanelOpenRef.current = Boolean(floatingPanel);
|
|
16466
|
+
useEffect13(() => {
|
|
16467
|
+
const syncViewport = () => {
|
|
16468
|
+
const next = window.innerWidth <= 480 ? "mobile" : "desktop";
|
|
16469
|
+
setEditorViewport((prev) => prev === next ? prev : next);
|
|
16470
|
+
};
|
|
16471
|
+
syncViewport();
|
|
16472
|
+
window.addEventListener("resize", syncViewport);
|
|
16473
|
+
return () => window.removeEventListener("resize", syncViewport);
|
|
16474
|
+
}, []);
|
|
15668
16475
|
const {
|
|
15669
16476
|
navDragRef,
|
|
15670
16477
|
navDropSlots,
|
|
@@ -16975,15 +17782,31 @@ function OhhwellsBridge() {
|
|
|
16975
17782
|
}
|
|
16976
17783
|
const applyContent = (content) => {
|
|
16977
17784
|
const imageLoads = [];
|
|
17785
|
+
if (typeof content[BRAND_KIT_KEY] === "string") {
|
|
17786
|
+
brandKitRef.current = content[BRAND_KIT_KEY];
|
|
17787
|
+
applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
|
|
17788
|
+
} else {
|
|
17789
|
+
brandKitRef.current = "";
|
|
17790
|
+
applyBrandToDom(null);
|
|
17791
|
+
}
|
|
16978
17792
|
if (typeof content[AI_SECTIONS_KEY] === "string") {
|
|
16979
17793
|
aiSectionsRef.current = content[AI_SECTIONS_KEY];
|
|
17794
|
+
setAiSectionOrder(content[SECTION_ORDER_KEY], window.location.pathname);
|
|
16980
17795
|
applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
|
|
16981
17796
|
}
|
|
17797
|
+
if (typeof content[STYLE_STORE_KEY] === "string") {
|
|
17798
|
+
stylesRef.current = content[STYLE_STORE_KEY];
|
|
17799
|
+
applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
|
|
17800
|
+
}
|
|
17801
|
+
applyBrandChrome(content);
|
|
16982
17802
|
for (const [key, val] of Object.entries(content)) {
|
|
16983
17803
|
if (key === "__ohw_sections") continue;
|
|
16984
17804
|
if (key === AI_SECTIONS_KEY) continue;
|
|
16985
17805
|
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
16986
17806
|
if (LOGO_IMAGE_KEYS.includes(key)) continue;
|
|
17807
|
+
if (key === BRAND_KIT_KEY) continue;
|
|
17808
|
+
if (key === STYLE_STORE_KEY) continue;
|
|
17809
|
+
if (BRAND_CHROME_KEYS.has(key)) continue;
|
|
16987
17810
|
if (applyVideoSettingNode(key, val)) continue;
|
|
16988
17811
|
if (applyCarouselNode(key, val)) continue;
|
|
16989
17812
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
@@ -17049,7 +17872,9 @@ function OhhwellsBridge() {
|
|
|
17049
17872
|
let cancelled = false;
|
|
17050
17873
|
setFetchState("loading");
|
|
17051
17874
|
const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
|
|
17052
|
-
|
|
17875
|
+
const initialPath = pathname;
|
|
17876
|
+
fetchedContentPaths.add(`${subdomain}::${initialPath}`);
|
|
17877
|
+
fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
|
|
17053
17878
|
if (cancelled) return;
|
|
17054
17879
|
const content = data?.content ?? {};
|
|
17055
17880
|
contentCache.set(subdomain, content);
|
|
@@ -17169,10 +17994,28 @@ function OhhwellsBridge() {
|
|
|
17169
17994
|
initSectionInstancesFromContent(content, window.location.pathname);
|
|
17170
17995
|
observer?.disconnect();
|
|
17171
17996
|
try {
|
|
17997
|
+
applyBrandChrome(content);
|
|
17998
|
+
if (typeof content[BRAND_KIT_KEY] === "string") {
|
|
17999
|
+
applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
|
|
18000
|
+
} else {
|
|
18001
|
+
applyBrandToDom(null);
|
|
18002
|
+
}
|
|
18003
|
+
if (typeof content[AI_SECTIONS_KEY] === "string") {
|
|
18004
|
+
setAiSectionOrder(content[SECTION_ORDER_KEY], window.location.pathname);
|
|
18005
|
+
applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
|
|
18006
|
+
}
|
|
18007
|
+
if (typeof content[STYLE_STORE_KEY] === "string") {
|
|
18008
|
+
applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
|
|
18009
|
+
}
|
|
17172
18010
|
for (const [key, val] of Object.entries(content)) {
|
|
17173
18011
|
if (key === "__ohw_sections") continue;
|
|
18012
|
+
if (key === AI_SECTIONS_KEY) continue;
|
|
17174
18013
|
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
17175
18014
|
if (LOGO_IMAGE_KEYS.includes(key)) continue;
|
|
18015
|
+
if (key === BRAND_KIT_KEY) continue;
|
|
18016
|
+
if (key === STYLE_STORE_KEY) continue;
|
|
18017
|
+
if (key === STYLE_STORE_KEY) continue;
|
|
18018
|
+
if (BRAND_CHROME_KEYS.has(key)) continue;
|
|
17176
18019
|
if (applyVideoSettingNode(key, val)) continue;
|
|
17177
18020
|
if (applyCarouselNode(key, val)) continue;
|
|
17178
18021
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
@@ -17218,6 +18061,17 @@ function OhhwellsBridge() {
|
|
|
17218
18061
|
debounceTimer = setTimeout(applyFromCache, 150);
|
|
17219
18062
|
};
|
|
17220
18063
|
applyFromCache();
|
|
18064
|
+
const pathCacheKey = `${subdomain}::${pathname}`;
|
|
18065
|
+
if (!fetchedContentPaths.has(pathCacheKey)) {
|
|
18066
|
+
fetchedContentPaths.add(pathCacheKey);
|
|
18067
|
+
const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
|
|
18068
|
+
fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(pathname)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
|
|
18069
|
+
if (!data?.content) return;
|
|
18070
|
+
contentCache.set(subdomain, data.content);
|
|
18071
|
+
applyFromCache();
|
|
18072
|
+
}).catch(() => {
|
|
18073
|
+
});
|
|
18074
|
+
}
|
|
17221
18075
|
observer = new MutationObserver(scheduleApply);
|
|
17222
18076
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
17223
18077
|
return () => {
|
|
@@ -17313,30 +18167,31 @@ function OhhwellsBridge() {
|
|
|
17313
18167
|
}, [isEditMode, pathname, subdomain, fetchState, resyncSelectedNavigationItem]);
|
|
17314
18168
|
useEffect13(() => {
|
|
17315
18169
|
if (!isEditMode) return;
|
|
18170
|
+
let lastPosted = 0;
|
|
17316
18171
|
const measure = () => {
|
|
17317
18172
|
const h = document.body.scrollHeight;
|
|
17318
|
-
if (h > 50
|
|
18173
|
+
if (h > 50 && Math.abs(h - lastPosted) > 1) {
|
|
18174
|
+
lastPosted = h;
|
|
18175
|
+
postToParent2({ type: "ow:height", height: h });
|
|
18176
|
+
}
|
|
18177
|
+
};
|
|
18178
|
+
let raf = null;
|
|
18179
|
+
const schedule = () => {
|
|
18180
|
+
if (raf != null) return;
|
|
18181
|
+
raf = requestAnimationFrame(() => {
|
|
18182
|
+
raf = null;
|
|
18183
|
+
measure();
|
|
18184
|
+
});
|
|
17319
18185
|
};
|
|
17320
18186
|
const t1 = setTimeout(measure, 50);
|
|
17321
18187
|
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);
|
|
18188
|
+
const ro = new ResizeObserver(schedule);
|
|
18189
|
+
ro.observe(document.body);
|
|
17335
18190
|
return () => {
|
|
17336
18191
|
clearTimeout(t1);
|
|
17337
18192
|
clearTimeout(t2);
|
|
17338
|
-
|
|
17339
|
-
|
|
18193
|
+
if (raf != null) cancelAnimationFrame(raf);
|
|
18194
|
+
ro.disconnect();
|
|
17340
18195
|
};
|
|
17341
18196
|
}, [pathname, isEditMode, postToParent2]);
|
|
17342
18197
|
useEffect13(() => {
|
|
@@ -17577,6 +18432,7 @@ function OhhwellsBridge() {
|
|
|
17577
18432
|
return;
|
|
17578
18433
|
}
|
|
17579
18434
|
const target = e.target;
|
|
18435
|
+
if (target.closest("[data-ohw-ai-review]")) return;
|
|
17580
18436
|
if (target.closest("[data-ohw-toolbar]")) return;
|
|
17581
18437
|
if (target.closest("[data-ohw-state-toggle]")) return;
|
|
17582
18438
|
if (target.closest("[data-ohw-max-badge]")) return;
|
|
@@ -17588,6 +18444,9 @@ function OhhwellsBridge() {
|
|
|
17588
18444
|
)) {
|
|
17589
18445
|
return;
|
|
17590
18446
|
}
|
|
18447
|
+
if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
|
|
18448
|
+
clearMediaSelectionRef.current();
|
|
18449
|
+
}
|
|
17591
18450
|
{
|
|
17592
18451
|
const formEl = getFormElement(target);
|
|
17593
18452
|
const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
|
|
@@ -17739,19 +18598,14 @@ function OhhwellsBridge() {
|
|
|
17739
18598
|
}
|
|
17740
18599
|
const clickedButton = findClosestButtonLike(target);
|
|
17741
18600
|
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
18601
|
if (isMediaEditable(editable) && !buttonOnMedia) {
|
|
17751
18602
|
e.preventDefault();
|
|
17752
18603
|
e.stopPropagation();
|
|
17753
|
-
|
|
17754
|
-
|
|
18604
|
+
if (selectedMediaElRef.current === editable) {
|
|
18605
|
+
postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "", elementType: editable.dataset.ohwEditable ?? "image" });
|
|
18606
|
+
} else {
|
|
18607
|
+
selectMediaElementRef.current(editable);
|
|
18608
|
+
}
|
|
17755
18609
|
return;
|
|
17756
18610
|
}
|
|
17757
18611
|
const socialItem = getSocialItem(editable);
|
|
@@ -17770,11 +18624,6 @@ function OhhwellsBridge() {
|
|
|
17770
18624
|
const hrefLookupTarget = buttonOnMedia ? clickedButton : editable;
|
|
17771
18625
|
const hrefCtx = getHrefKeyFromElement(hrefLookupTarget);
|
|
17772
18626
|
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
18627
|
if (navAnchor) {
|
|
17779
18628
|
e.preventDefault();
|
|
17780
18629
|
e.stopPropagation();
|
|
@@ -17892,6 +18741,7 @@ function OhhwellsBridge() {
|
|
|
17892
18741
|
};
|
|
17893
18742
|
const handleDblClick = (e) => {
|
|
17894
18743
|
const target = e.target;
|
|
18744
|
+
if (target.closest("[data-ohw-ai-review]")) return;
|
|
17895
18745
|
if (target.closest("[data-ohw-toolbar]")) return;
|
|
17896
18746
|
if (target.closest("[data-ohw-state-toggle]")) return;
|
|
17897
18747
|
if (target.closest("[data-ohw-max-badge]")) return;
|
|
@@ -17943,6 +18793,9 @@ function OhhwellsBridge() {
|
|
|
17943
18793
|
setHoveredItemRect(null);
|
|
17944
18794
|
hoveredNavContainerRef.current = null;
|
|
17945
18795
|
setHoveredNavContainerRect(null);
|
|
18796
|
+
siblingHintElRef.current = null;
|
|
18797
|
+
setSiblingHintRect(null);
|
|
18798
|
+
setSiblingHintRects([]);
|
|
17946
18799
|
return;
|
|
17947
18800
|
}
|
|
17948
18801
|
{
|
|
@@ -18061,7 +18914,6 @@ function OhhwellsBridge() {
|
|
|
18061
18914
|
hoveredNavContainerRef.current = null;
|
|
18062
18915
|
setHoveredNavContainerRect(null);
|
|
18063
18916
|
hoveredItemElRef.current = editable;
|
|
18064
|
-
setHoveredItemRect(isSelectedForHoverRef.current(editable) ? null : editable.getBoundingClientRect());
|
|
18065
18917
|
}
|
|
18066
18918
|
}
|
|
18067
18919
|
}
|
|
@@ -18358,7 +19210,7 @@ function OhhwellsBridge() {
|
|
|
18358
19210
|
}
|
|
18359
19211
|
};
|
|
18360
19212
|
const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
|
|
18361
|
-
if (linkPopoverOpenRef.current) {
|
|
19213
|
+
if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
|
|
18362
19214
|
if (hoveredImageRef.current) {
|
|
18363
19215
|
hoveredImageRef.current = null;
|
|
18364
19216
|
hoveredImageHasTextOverlapRef.current = false;
|
|
@@ -18692,7 +19544,9 @@ function OhhwellsBridge() {
|
|
|
18692
19544
|
return;
|
|
18693
19545
|
}
|
|
18694
19546
|
const { y } = toProbeCoords(clientX, clientY, fromParentViewport);
|
|
18695
|
-
const sections = Array.from(document.querySelectorAll("[data-ohw-section]")).
|
|
19547
|
+
const sections = Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
|
|
19548
|
+
(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
|
|
19549
|
+
).sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);
|
|
18696
19550
|
const ZONE = 20;
|
|
18697
19551
|
for (let i = 0; i < sections.length; i++) {
|
|
18698
19552
|
const a = sections[i];
|
|
@@ -18721,8 +19575,7 @@ function OhhwellsBridge() {
|
|
|
18721
19575
|
};
|
|
18722
19576
|
const handleMouseMove = (e) => {
|
|
18723
19577
|
const { clientX, clientY } = e;
|
|
18724
|
-
if (
|
|
18725
|
-
if (isOverEditorChrome(clientX, clientY)) {
|
|
19578
|
+
if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY) || isOverEditorChrome(clientX, clientY)) {
|
|
18726
19579
|
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
18727
19580
|
formHoverElRef.current = null;
|
|
18728
19581
|
setFormHoverRect(null);
|
|
@@ -18730,6 +19583,12 @@ function OhhwellsBridge() {
|
|
|
18730
19583
|
setHoveredItemRect(null);
|
|
18731
19584
|
hoveredNavContainerRef.current = null;
|
|
18732
19585
|
setHoveredNavContainerRect(null);
|
|
19586
|
+
siblingHintElRef.current = null;
|
|
19587
|
+
setSiblingHintRect(null);
|
|
19588
|
+
setSiblingHintRects([]);
|
|
19589
|
+
dismissImageHover();
|
|
19590
|
+
clearImageHover();
|
|
19591
|
+
setSectionGap(null);
|
|
18733
19592
|
return;
|
|
18734
19593
|
}
|
|
18735
19594
|
if (probeSocialsRowAt(clientX, clientY)) return;
|
|
@@ -18741,7 +19600,11 @@ function OhhwellsBridge() {
|
|
|
18741
19600
|
if (e.data?.type !== "ow:pointer-sync") return;
|
|
18742
19601
|
const { clientX, clientY } = e.data;
|
|
18743
19602
|
if (typeof clientX !== "number" || typeof clientY !== "number") return;
|
|
18744
|
-
if (
|
|
19603
|
+
if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
|
|
19604
|
+
dismissImageHover();
|
|
19605
|
+
clearImageHover();
|
|
19606
|
+
return;
|
|
19607
|
+
}
|
|
18745
19608
|
if (probeSocialsRowAt(clientX, clientY)) return;
|
|
18746
19609
|
probeSectionGapAt(clientX, clientY);
|
|
18747
19610
|
probeImageAt(clientX, clientY);
|
|
@@ -19024,10 +19887,23 @@ function OhhwellsBridge() {
|
|
|
19024
19887
|
if (e.data?.type !== "ow:hydrate") return;
|
|
19025
19888
|
const content = e.data.content;
|
|
19026
19889
|
if (!content) return;
|
|
19890
|
+
if (typeof content[BRAND_KIT_KEY] === "string") {
|
|
19891
|
+
brandKitRef.current = content[BRAND_KIT_KEY];
|
|
19892
|
+
applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
|
|
19893
|
+
} else {
|
|
19894
|
+
brandKitRef.current = "";
|
|
19895
|
+
applyBrandToDom(null);
|
|
19896
|
+
}
|
|
19027
19897
|
if (typeof content[AI_SECTIONS_KEY] === "string") {
|
|
19028
19898
|
aiSectionsRef.current = content[AI_SECTIONS_KEY];
|
|
19899
|
+
setAiSectionOrder(content[SECTION_ORDER_KEY], window.location.pathname);
|
|
19029
19900
|
applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
|
|
19030
19901
|
}
|
|
19902
|
+
if (typeof content[STYLE_STORE_KEY] === "string") {
|
|
19903
|
+
stylesRef.current = content[STYLE_STORE_KEY];
|
|
19904
|
+
applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
|
|
19905
|
+
}
|
|
19906
|
+
applyBrandChrome(content);
|
|
19031
19907
|
let sectionsJson = null;
|
|
19032
19908
|
for (const [key, val] of Object.entries(content)) {
|
|
19033
19909
|
if (key === "__ohw_sections") {
|
|
@@ -19037,6 +19913,9 @@ function OhhwellsBridge() {
|
|
|
19037
19913
|
if (key === AI_SECTIONS_KEY) continue;
|
|
19038
19914
|
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
19039
19915
|
if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
|
|
19916
|
+
if (key === BRAND_KIT_KEY) continue;
|
|
19917
|
+
if (key === STYLE_STORE_KEY) continue;
|
|
19918
|
+
if (BRAND_CHROME_KEYS.has(key)) continue;
|
|
19040
19919
|
if (applyVideoSettingNode(key, val)) continue;
|
|
19041
19920
|
if (applyCarouselNode(key, val)) continue;
|
|
19042
19921
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
@@ -19050,6 +19929,8 @@ function OhhwellsBridge() {
|
|
|
19050
19929
|
if (video && video.src !== val) applyVideoSrc(video, val);
|
|
19051
19930
|
} else if (el.dataset.ohwEditable === "link") {
|
|
19052
19931
|
applyLinkHref(el, val);
|
|
19932
|
+
} else if (el.dataset.ohwEditable === "icon") {
|
|
19933
|
+
applyIconMarkup(el, val);
|
|
19053
19934
|
} else if (isIconMarkupValue(val)) {
|
|
19054
19935
|
} else {
|
|
19055
19936
|
el.innerHTML = val;
|
|
@@ -19134,12 +20015,21 @@ function OhhwellsBridge() {
|
|
|
19134
20015
|
nodes: collectEditableNodes(editContentRef.current)
|
|
19135
20016
|
});
|
|
19136
20017
|
};
|
|
20018
|
+
const clearInteractionChrome = () => {
|
|
20019
|
+
deactivateRef.current();
|
|
20020
|
+
deselectRef.current();
|
|
20021
|
+
clearMediaSelectionRef.current();
|
|
20022
|
+
};
|
|
19137
20023
|
const handleAiApplyTree = (e) => {
|
|
19138
20024
|
if (e.data?.type !== "ow:ai-apply-tree") return;
|
|
19139
20025
|
const payload = e.data.payload;
|
|
19140
20026
|
if (!payload || typeof payload.id !== "string" || !isRenderableTree(payload.tree)) return;
|
|
20027
|
+
clearInteractionChrome();
|
|
19141
20028
|
const previous = aiSectionsRef.current;
|
|
19142
|
-
const nextState = applyTreeToState(parseAiSectionsState(previous),
|
|
20029
|
+
const nextState = applyTreeToState(parseAiSectionsState(previous), {
|
|
20030
|
+
...payload,
|
|
20031
|
+
path: payload.path ?? window.location.pathname
|
|
20032
|
+
});
|
|
19143
20033
|
const nextValue = serializeAiSectionsState(nextState);
|
|
19144
20034
|
aiSectionsRef.current = nextValue;
|
|
19145
20035
|
applyAiSectionsToDom(nextState);
|
|
@@ -19160,6 +20050,7 @@ function OhhwellsBridge() {
|
|
|
19160
20050
|
const exists = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
|
|
19161
20051
|
if (!exists) return;
|
|
19162
20052
|
if (isPageFrameSection(exists)) return;
|
|
20053
|
+
clearInteractionChrome();
|
|
19163
20054
|
const previous = aiSectionsRef.current;
|
|
19164
20055
|
const nextState = deleteSectionFromState(parseAiSectionsState(previous), sectionId);
|
|
19165
20056
|
const nextValue = serializeAiSectionsState(nextState);
|
|
@@ -19175,8 +20066,10 @@ function OhhwellsBridge() {
|
|
|
19175
20066
|
const handleAiSetSections = (e) => {
|
|
19176
20067
|
if (e.data?.type !== "ow:ai-set-sections") return;
|
|
19177
20068
|
const value = typeof e.data.value === "string" ? e.data.value : "";
|
|
20069
|
+
clearInteractionChrome();
|
|
19178
20070
|
aiSectionsRef.current = value;
|
|
19179
20071
|
applyAiSectionsToDom(parseAiSectionsState(value));
|
|
20072
|
+
applyStylesToDom(parseStyleStore(stylesRef.current));
|
|
19180
20073
|
const restoredHeight = document.body.scrollHeight;
|
|
19181
20074
|
if (restoredHeight > 50) postToParentRef.current({ type: "ow:height", height: restoredHeight });
|
|
19182
20075
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: AI_SECTIONS_KEY, text: value }] });
|
|
@@ -19192,10 +20085,40 @@ function OhhwellsBridge() {
|
|
|
19192
20085
|
if (!entries) return;
|
|
19193
20086
|
const orderJson = JSON.stringify(entries);
|
|
19194
20087
|
editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
|
|
20088
|
+
setAiSectionOrder(orderJson, window.location.pathname);
|
|
19195
20089
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
|
|
19196
20090
|
window.dispatchEvent(new Event("resize"));
|
|
19197
20091
|
};
|
|
19198
20092
|
window.addEventListener("message", handleMoveSection);
|
|
20093
|
+
const handleAiSetBrand = (e) => {
|
|
20094
|
+
if (e.data?.type !== "ow:ai-set-brand") return;
|
|
20095
|
+
const value = typeof e.data.value === "string" ? e.data.value : "";
|
|
20096
|
+
const previous = brandKitRef.current;
|
|
20097
|
+
brandKitRef.current = value;
|
|
20098
|
+
applyBrandToDom(parseBrandKit(value));
|
|
20099
|
+
if (aiSectionsRef.current) applyAiSectionsToDom(parseAiSectionsState(aiSectionsRef.current));
|
|
20100
|
+
applyStylesToDom(parseStyleStore(stylesRef.current));
|
|
20101
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: BRAND_KIT_KEY, text: value }] });
|
|
20102
|
+
postToParentRef.current({ type: "ow:ai-brand-applied", previous, value });
|
|
20103
|
+
};
|
|
20104
|
+
window.addEventListener("message", handleAiSetBrand);
|
|
20105
|
+
const handleAiSetStyles = (e) => {
|
|
20106
|
+
if (e.data?.type !== "ow:ai-set-styles") return;
|
|
20107
|
+
const value = typeof e.data.value === "string" ? e.data.value : "";
|
|
20108
|
+
const previous = stylesRef.current;
|
|
20109
|
+
stylesRef.current = value;
|
|
20110
|
+
applyStylesToDom(parseStyleStore(value));
|
|
20111
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: STYLE_STORE_KEY, text: value }] });
|
|
20112
|
+
postToParentRef.current({ type: "ow:ai-styles-applied", previous, value });
|
|
20113
|
+
};
|
|
20114
|
+
window.addEventListener("message", handleAiSetStyles);
|
|
20115
|
+
const handleGetBrand = (e) => {
|
|
20116
|
+
if (e.data?.type !== "ow:get-brand") return;
|
|
20117
|
+
const template = deriveTemplateBrand();
|
|
20118
|
+
const value = brandKitRef.current || (template ? JSON.stringify(template) : "");
|
|
20119
|
+
postToParentRef.current({ type: "ow:brand-value", value });
|
|
20120
|
+
};
|
|
20121
|
+
window.addEventListener("message", handleGetBrand);
|
|
19199
20122
|
const handlePanelDragging = (e) => {
|
|
19200
20123
|
if (e.data?.type !== "ow:panel-dragging") return;
|
|
19201
20124
|
if (e.data.dragging) document.documentElement.setAttribute("data-ohw-panel-dragging", "");
|
|
@@ -19253,8 +20176,15 @@ function OhhwellsBridge() {
|
|
|
19253
20176
|
closeLinkPopoverRef.current();
|
|
19254
20177
|
return;
|
|
19255
20178
|
}
|
|
20179
|
+
if (floatingPanelOpenRef.current) {
|
|
20180
|
+
setFloatingPanelRef.current(null);
|
|
20181
|
+
deselectRef.current();
|
|
20182
|
+
deactivateRef.current();
|
|
20183
|
+
return;
|
|
20184
|
+
}
|
|
19256
20185
|
deselectRef.current();
|
|
19257
20186
|
deactivateRef.current();
|
|
20187
|
+
clearMediaSelectionRef.current();
|
|
19258
20188
|
};
|
|
19259
20189
|
window.addEventListener("message", handleDeactivate);
|
|
19260
20190
|
const handleToastAction = (e) => {
|
|
@@ -19340,6 +20270,10 @@ function OhhwellsBridge() {
|
|
|
19340
20270
|
const handleKeyDown = (e) => {
|
|
19341
20271
|
if (e.key === "Escape" && document.querySelector("[data-ohw-section-picker]")) return;
|
|
19342
20272
|
if (e.key === "Escape" && document.querySelector("[data-ohw-more-menu]")) return;
|
|
20273
|
+
if (e.key === "Escape" && selectedMediaElRef.current) {
|
|
20274
|
+
clearMediaSelectionRef.current();
|
|
20275
|
+
return;
|
|
20276
|
+
}
|
|
19343
20277
|
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "a" && activeElRef.current) {
|
|
19344
20278
|
e.preventDefault();
|
|
19345
20279
|
selectAllTextInEditable(activeElRef.current);
|
|
@@ -19499,6 +20433,12 @@ function OhhwellsBridge() {
|
|
|
19499
20433
|
if (aiSectionsRef.current) {
|
|
19500
20434
|
nodes.push({ key: AI_SECTIONS_KEY, type: "sections", text: aiSectionsRef.current });
|
|
19501
20435
|
}
|
|
20436
|
+
if (stylesRef.current) {
|
|
20437
|
+
nodes.push({ key: STYLE_STORE_KEY, type: "styles", text: stylesRef.current });
|
|
20438
|
+
}
|
|
20439
|
+
if (brandKitRef.current) {
|
|
20440
|
+
nodes.push({ key: BRAND_KIT_KEY, type: "brand", text: brandKitRef.current });
|
|
20441
|
+
}
|
|
19502
20442
|
document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
|
|
19503
20443
|
const formKey = formKeyOf(form);
|
|
19504
20444
|
if (!formKey) return;
|
|
@@ -19516,8 +20456,12 @@ function OhhwellsBridge() {
|
|
|
19516
20456
|
if (inserted) {
|
|
19517
20457
|
const tracker = getSectionsTracker();
|
|
19518
20458
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
|
|
19519
|
-
const
|
|
19520
|
-
|
|
20459
|
+
const reportHeight = () => {
|
|
20460
|
+
const h = document.body.scrollHeight;
|
|
20461
|
+
if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
|
|
20462
|
+
};
|
|
20463
|
+
reportHeight();
|
|
20464
|
+
setTimeout(reportHeight, 500);
|
|
19521
20465
|
}
|
|
19522
20466
|
};
|
|
19523
20467
|
const handleSwitchSchedule = (e) => {
|
|
@@ -19914,13 +20858,16 @@ function OhhwellsBridge() {
|
|
|
19914
20858
|
window.removeEventListener("message", handleAiDeleteSection);
|
|
19915
20859
|
window.removeEventListener("message", handleAiSetSections);
|
|
19916
20860
|
window.removeEventListener("message", handleMoveSection);
|
|
20861
|
+
window.removeEventListener("message", handleAiSetBrand);
|
|
20862
|
+
window.removeEventListener("message", handleAiSetStyles);
|
|
20863
|
+
window.removeEventListener("message", handleGetBrand);
|
|
19917
20864
|
window.removeEventListener("message", handlePanelDragging);
|
|
19918
20865
|
window.removeEventListener("message", handleDeleteSection);
|
|
19919
20866
|
window.removeEventListener("message", handleDeactivate);
|
|
19920
|
-
document.documentElement.removeAttribute("data-ohw-panel-dragging");
|
|
19921
20867
|
window.removeEventListener("message", handleToastAction);
|
|
19922
20868
|
window.removeEventListener("message", handleFormCount);
|
|
19923
20869
|
window.removeEventListener("message", handleUiEscape);
|
|
20870
|
+
document.documentElement.removeAttribute("data-ohw-panel-dragging");
|
|
19924
20871
|
autoSaveTimers.current.forEach(clearTimeout);
|
|
19925
20872
|
autoSaveTimers.current.clear();
|
|
19926
20873
|
if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
|
|
@@ -20123,7 +21070,7 @@ function OhhwellsBridge() {
|
|
|
20123
21070
|
postToParent2({
|
|
20124
21071
|
type: "ow:ready",
|
|
20125
21072
|
version: "1",
|
|
20126
|
-
bridgeVersion: "0.1.
|
|
21073
|
+
bridgeVersion: "0.1.79",
|
|
20127
21074
|
path: pathname,
|
|
20128
21075
|
nodes: collectEditableNodes(editContentRef.current),
|
|
20129
21076
|
sections
|
|
@@ -20530,11 +21477,22 @@ function OhhwellsBridge() {
|
|
|
20530
21477
|
const showEditLink = toolbarShowEditLink;
|
|
20531
21478
|
const currentSections = sectionsByPath[pathname] ?? [];
|
|
20532
21479
|
linkPopoverOpenRef.current = linkPopover !== null;
|
|
21480
|
+
const handleMediaSelect = useCallback8((key) => {
|
|
21481
|
+
const el = hoveredImageRef.current?.dataset.ohwKey === key ? hoveredImageRef.current : Array.from(document.querySelectorAll(MEDIA_SELECTOR)).find(
|
|
21482
|
+
(m) => (m.dataset.ohwKey ?? "") === key
|
|
21483
|
+
) ?? null;
|
|
21484
|
+
if (!el) return;
|
|
21485
|
+
selectMediaElementRef.current(el);
|
|
21486
|
+
}, []);
|
|
20533
21487
|
const handleMediaReplace = useCallback8(
|
|
20534
21488
|
(key) => {
|
|
20535
|
-
postToParent2({
|
|
21489
|
+
postToParent2({
|
|
21490
|
+
type: "ow:image-pick",
|
|
21491
|
+
key,
|
|
21492
|
+
elementType: mediaHover?.elementType ?? selectedMedia?.elementType ?? "image"
|
|
21493
|
+
});
|
|
20536
21494
|
},
|
|
20537
|
-
[postToParent2, mediaHover?.elementType]
|
|
21495
|
+
[postToParent2, mediaHover?.elementType, selectedMedia?.elementType]
|
|
20538
21496
|
);
|
|
20539
21497
|
const handleEditCarousel = useCallback8(
|
|
20540
21498
|
(key) => {
|
|
@@ -20606,12 +21564,25 @@ function OhhwellsBridge() {
|
|
|
20606
21564
|
},
|
|
20607
21565
|
`uploading-${key}`
|
|
20608
21566
|
)),
|
|
20609
|
-
mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ jsx33(
|
|
21567
|
+
mediaHover && !(mediaHover.key in uploadingRects) && mediaHover.key !== selectedMedia?.key && /* @__PURE__ */ jsx33(
|
|
20610
21568
|
MediaOverlay,
|
|
20611
21569
|
{
|
|
20612
21570
|
hover: mediaHover,
|
|
20613
21571
|
isUploading: false,
|
|
20614
21572
|
onReplace: handleMediaReplace,
|
|
21573
|
+
onSelect: handleMediaSelect,
|
|
21574
|
+
onVideoSettingsChange: handleVideoSettingsChange
|
|
21575
|
+
}
|
|
21576
|
+
),
|
|
21577
|
+
selectedMedia && !(selectedMedia.key in uploadingRects) && !linkPopover && /* @__PURE__ */ jsx33(
|
|
21578
|
+
MediaOverlay,
|
|
21579
|
+
{
|
|
21580
|
+
hover: selectedMedia,
|
|
21581
|
+
selected: true,
|
|
21582
|
+
hovered: mediaHover?.key === selectedMedia.key,
|
|
21583
|
+
isUploading: false,
|
|
21584
|
+
onReplace: handleMediaReplace,
|
|
21585
|
+
onSelect: handleMediaSelect,
|
|
20615
21586
|
onVideoSettingsChange: handleVideoSettingsChange
|
|
20616
21587
|
}
|
|
20617
21588
|
),
|
|
@@ -21017,6 +21988,59 @@ function OhhwellsBridge() {
|
|
|
21017
21988
|
) : null
|
|
21018
21989
|
] });
|
|
21019
21990
|
}
|
|
21991
|
+
|
|
21992
|
+
// src/ui/EmptySection.tsx
|
|
21993
|
+
import Link3 from "next/link";
|
|
21994
|
+
import { Fragment as Fragment9, jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
21995
|
+
function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey }) {
|
|
21996
|
+
return /* @__PURE__ */ jsxs21(Fragment9, { children: [
|
|
21997
|
+
/* @__PURE__ */ jsx34(
|
|
21998
|
+
"p",
|
|
21999
|
+
{
|
|
22000
|
+
style: {
|
|
22001
|
+
fontFamily: "var(--brand-font-body)",
|
|
22002
|
+
fontSize: "0.75rem",
|
|
22003
|
+
fontWeight: 500,
|
|
22004
|
+
letterSpacing: "0.15em",
|
|
22005
|
+
textTransform: "uppercase",
|
|
22006
|
+
color: "var(--brand-accent)",
|
|
22007
|
+
marginBottom: "1.5rem"
|
|
22008
|
+
},
|
|
22009
|
+
children: /* @__PURE__ */ jsx34(Link3, { href: homeHref, style: { color: "inherit" }, children: /* @__PURE__ */ jsx34("span", { ...eyebrowKey ? { "data-ohw-editable": "text", "data-ohw-key": eyebrowKey } : {}, children: "Home" }) })
|
|
22010
|
+
}
|
|
22011
|
+
),
|
|
22012
|
+
/* @__PURE__ */ jsx34(
|
|
22013
|
+
"h1",
|
|
22014
|
+
{
|
|
22015
|
+
style: {
|
|
22016
|
+
fontFamily: "var(--brand-font-heading)",
|
|
22017
|
+
fontSize: "clamp(2rem, 3.5vw, 2.75rem)",
|
|
22018
|
+
lineHeight: 1.1,
|
|
22019
|
+
letterSpacing: "-0.025em",
|
|
22020
|
+
color: "var(--brand-text)",
|
|
22021
|
+
marginBottom: "1rem"
|
|
22022
|
+
},
|
|
22023
|
+
...titleKey ? { "data-ohw-editable": "text", "data-ohw-key": titleKey, "data-ohw-max-length": 80 } : {},
|
|
22024
|
+
children: title
|
|
22025
|
+
}
|
|
22026
|
+
),
|
|
22027
|
+
/* @__PURE__ */ jsx34(
|
|
22028
|
+
"p",
|
|
22029
|
+
{
|
|
22030
|
+
style: {
|
|
22031
|
+
fontFamily: "var(--brand-font-body)",
|
|
22032
|
+
fontSize: "1rem",
|
|
22033
|
+
lineHeight: 1.7,
|
|
22034
|
+
fontWeight: 300,
|
|
22035
|
+
color: "var(--brand-text-muted)",
|
|
22036
|
+
maxWidth: "340px"
|
|
22037
|
+
},
|
|
22038
|
+
...subtitleKey ? { "data-ohw-editable": "text", "data-ohw-key": subtitleKey, "data-ohw-max-length": 160 } : {},
|
|
22039
|
+
children: "This page doesn't have any content yet."
|
|
22040
|
+
}
|
|
22041
|
+
)
|
|
22042
|
+
] });
|
|
22043
|
+
}
|
|
21020
22044
|
export {
|
|
21021
22045
|
AI_DEFAULT_BRAND,
|
|
21022
22046
|
AI_TREE_SCHEMA_VERSIONS,
|
|
@@ -21033,6 +22057,7 @@ export {
|
|
|
21033
22057
|
DropdownMenuItem,
|
|
21034
22058
|
DropdownMenuSeparator,
|
|
21035
22059
|
DropdownMenuTrigger,
|
|
22060
|
+
EmptySection,
|
|
21036
22061
|
ItemActionToolbar,
|
|
21037
22062
|
ItemInteractionLayer,
|
|
21038
22063
|
LinkEditorPanel,
|