@ohhwells/bridge 0.1.77 → 0.1.78-next.234
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 +1242 -217
- 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 +1241 -217
- 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,317 @@ 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)) el.setAttribute(attr, el.style.getPropertyValue(prop));
|
|
340
|
+
}
|
|
341
|
+
function restoreInline(el, prop) {
|
|
342
|
+
const attr = `data-ohw-style-prev-${prop}`;
|
|
343
|
+
if (!el.hasAttribute(attr)) return;
|
|
344
|
+
const prev = el.getAttribute(attr) ?? "";
|
|
345
|
+
if (prev) el.style.setProperty(prop, prev);
|
|
346
|
+
else el.style.removeProperty(prop);
|
|
347
|
+
el.removeAttribute(attr);
|
|
348
|
+
}
|
|
349
|
+
function ensureStyleSheet() {
|
|
350
|
+
let el = document.getElementById(STYLE_SHEET_ID);
|
|
351
|
+
if (!el) {
|
|
352
|
+
el = document.createElement("style");
|
|
353
|
+
el.id = STYLE_SHEET_ID;
|
|
354
|
+
document.head.appendChild(el);
|
|
355
|
+
}
|
|
356
|
+
const css = styleSheetCss();
|
|
357
|
+
if (el.textContent !== css) el.textContent = css;
|
|
358
|
+
}
|
|
359
|
+
function clearSectionAttrs(root) {
|
|
360
|
+
for (const attr of Object.values(SECTION_ATTRS)) {
|
|
361
|
+
for (const el of Array.from(root.querySelectorAll(`[${attr}]`))) el.removeAttribute(attr);
|
|
362
|
+
}
|
|
363
|
+
for (const el of Array.from(root.querySelectorAll("[data-ohw-style-bgcolor]"))) {
|
|
364
|
+
restoreInline(el, "background");
|
|
365
|
+
el.removeAttribute("data-ohw-style-bgcolor");
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
function clearNodeProps(root) {
|
|
369
|
+
for (const el of Array.from(root.querySelectorAll(`[${NODE_WROTE_ATTR}]`))) {
|
|
370
|
+
const h = el;
|
|
371
|
+
for (const prop of NODE_PROPS) restoreInline(h, prop);
|
|
372
|
+
h.removeAttribute(NODE_WROTE_ATTR);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
function buttonSurfaceOf(el) {
|
|
376
|
+
return el.closest("a, button") ?? el;
|
|
377
|
+
}
|
|
378
|
+
function applyStylesToDom(store) {
|
|
379
|
+
ensureStyleSheet();
|
|
380
|
+
clearSectionAttrs(document);
|
|
381
|
+
clearNodeProps(document);
|
|
382
|
+
loadStyleFonts(
|
|
383
|
+
store ? Object.values(store.nodes).flatMap((n) => n.fontFamily ? [n.fontFamily] : []) : []
|
|
384
|
+
);
|
|
385
|
+
if (!store) return;
|
|
386
|
+
for (const [sectionId, override] of Object.entries(store.sections)) {
|
|
387
|
+
const sections = document.querySelectorAll(
|
|
388
|
+
`[data-ohw-section="${CSS.escape(sectionId)}"]`
|
|
389
|
+
);
|
|
390
|
+
for (const section of Array.from(sections)) {
|
|
391
|
+
for (const [prop, attr] of Object.entries(SECTION_ATTRS)) {
|
|
392
|
+
const value = override[prop];
|
|
393
|
+
if (value === void 0) continue;
|
|
394
|
+
if (prop === "sectionBackground" && override.sectionBackgroundColor !== void 0) continue;
|
|
395
|
+
section.setAttribute(attr, String(value).replace(":", "-"));
|
|
396
|
+
}
|
|
397
|
+
if (override.sectionBackgroundColor !== void 0) {
|
|
398
|
+
saveInline(section, "background");
|
|
399
|
+
section.style.setProperty("background", override.sectionBackgroundColor, "important");
|
|
400
|
+
section.setAttribute("data-ohw-style-bgcolor", "");
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
for (const [key, override] of Object.entries(store.nodes)) {
|
|
405
|
+
const nodes = document.querySelectorAll(`[data-ohw-key="${CSS.escape(key)}"]`);
|
|
406
|
+
for (const el of Array.from(nodes)) {
|
|
407
|
+
if (override.color !== void 0) {
|
|
408
|
+
saveInline(el, "color");
|
|
409
|
+
el.style.setProperty("color", override.color, "important");
|
|
410
|
+
el.setAttribute(NODE_WROTE_ATTR, "");
|
|
411
|
+
}
|
|
412
|
+
if (override.fontFamily !== void 0) {
|
|
413
|
+
saveInline(el, "font-family");
|
|
414
|
+
el.style.setProperty("font-family", `'${override.fontFamily}'`, "important");
|
|
415
|
+
el.setAttribute(NODE_WROTE_ATTR, "");
|
|
416
|
+
}
|
|
417
|
+
if (override.fontSize !== void 0) {
|
|
418
|
+
saveInline(el, "font-size");
|
|
419
|
+
el.style.setProperty("font-size", `${override.fontSize}px`, "important");
|
|
420
|
+
el.setAttribute(NODE_WROTE_ATTR, "");
|
|
421
|
+
}
|
|
422
|
+
if (override.buttonBackground !== void 0 || override.buttonText !== void 0) {
|
|
423
|
+
const surface = buttonSurfaceOf(el);
|
|
424
|
+
if (override.buttonBackground !== void 0) {
|
|
425
|
+
saveInline(surface, "background");
|
|
426
|
+
surface.style.setProperty("background", override.buttonBackground, "important");
|
|
427
|
+
}
|
|
428
|
+
if (override.buttonText !== void 0) {
|
|
429
|
+
saveInline(surface, "color");
|
|
430
|
+
surface.style.setProperty("color", override.buttonText, "important");
|
|
431
|
+
}
|
|
432
|
+
surface.setAttribute(NODE_WROTE_ATTR, "");
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
121
438
|
// src/ui/ai-tree/aiSectionsManager.tsx
|
|
122
439
|
import { flushSync } from "react-dom";
|
|
123
440
|
import { createRoot } from "react-dom/client";
|
|
@@ -132,7 +449,8 @@ function lucideByName(name) {
|
|
|
132
449
|
}
|
|
133
450
|
var typeStyle = (spec, font) => ({
|
|
134
451
|
fontFamily: font,
|
|
135
|
-
|
|
452
|
+
// Headings shrink with the viewport (reaching full size around ~900px wide); body copy stays put.
|
|
453
|
+
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
454
|
lineHeight: spec.line,
|
|
137
455
|
fontWeight: spec.weight
|
|
138
456
|
});
|
|
@@ -141,12 +459,58 @@ var featureLines = (value) => value.split(/\n|<br\s*\/?>/i).map((line) => line.t
|
|
|
141
459
|
var CHECK_MASK = `data:image/svg+xml,${encodeURIComponent(
|
|
142
460
|
'<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
461
|
)}`;
|
|
462
|
+
var AI_MOBILE_CSS = [
|
|
463
|
+
"@media (max-width: 768px){",
|
|
464
|
+
"[data-ai-section]{overflow-x:hidden}",
|
|
465
|
+
"[data-ai-container]{padding:0 20px !important}",
|
|
466
|
+
"[data-ai-row]{display:flex !important;flex-direction:column !important;align-items:stretch !important}",
|
|
467
|
+
"[data-ai-cell]{width:100%;min-width:0}",
|
|
468
|
+
"[data-ai-grid]{grid-template-columns:1fr !important}",
|
|
469
|
+
// Group containers flatten to a column on phones; span placements come along for free.
|
|
470
|
+
"[data-ai-group]{display:flex !important;flex-direction:column !important}",
|
|
471
|
+
"[data-ai-group] > *{grid-column:auto !important}",
|
|
472
|
+
"[data-ai-section] img{max-width:100%}",
|
|
473
|
+
"}",
|
|
474
|
+
"@media (min-width: 769px) and (max-width: 1024px){",
|
|
475
|
+
"[data-ai-grid]{grid-template-columns:repeat(2, 1fr) !important}",
|
|
476
|
+
"}"
|
|
477
|
+
].join("");
|
|
144
478
|
var FEATURE_LINE_CSS = [
|
|
145
479
|
"[data-ai-features]>div{position:relative;padding-left:40px;min-height:24px}",
|
|
146
480
|
'[data-ai-features]>div::before{content:"";position:absolute;left:0;top:1px;width:24px;height:24px;',
|
|
147
481
|
`background-color:currentColor;-webkit-mask:url("${CHECK_MASK}") no-repeat 0 0/24px 24px;`,
|
|
148
482
|
`mask:url("${CHECK_MASK}") no-repeat 0 0/24px 24px}`
|
|
149
483
|
].join("");
|
|
484
|
+
function hexLuminance(color) {
|
|
485
|
+
const m = /^#([0-9a-f]{6})$/i.exec(color.trim());
|
|
486
|
+
if (!m) return null;
|
|
487
|
+
const [r2, g, b] = [0, 2, 4].map((i) => {
|
|
488
|
+
const c = parseInt(m[1].slice(i, i + 2), 16) / 255;
|
|
489
|
+
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
|
490
|
+
});
|
|
491
|
+
return 0.2126 * r2 + 0.7152 * g + 0.0722 * b;
|
|
492
|
+
}
|
|
493
|
+
function hexContrast(a, b) {
|
|
494
|
+
const la = hexLuminance(a);
|
|
495
|
+
const lb = hexLuminance(b);
|
|
496
|
+
if (la === null || lb === null) return null;
|
|
497
|
+
const [hi, lo] = la > lb ? [la, lb] : [lb, la];
|
|
498
|
+
return (hi + 0.05) / (lo + 0.05);
|
|
499
|
+
}
|
|
500
|
+
function accentBandContext(brand) {
|
|
501
|
+
const p = brand.palette;
|
|
502
|
+
const lightWins = (hexContrast(p.light, p.primary) ?? 99) >= (hexContrast(p.dark, p.primary) ?? 0);
|
|
503
|
+
if (lightWins) {
|
|
504
|
+
return {
|
|
505
|
+
brand: { ...brand, palette: { dark: p.light, primary: p.light, accent: p.light, light: p.primary } },
|
|
506
|
+
buttonLabel: p.primary
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
return {
|
|
510
|
+
brand: { ...brand, palette: { dark: p.dark, primary: p.dark, accent: p.dark, light: p.light } },
|
|
511
|
+
buttonLabel: p.light
|
|
512
|
+
};
|
|
513
|
+
}
|
|
150
514
|
function textAttrs(ctx, path) {
|
|
151
515
|
return ctx.keyFor ? { "data-ohw-key": ctx.keyFor(path), "data-ohw-editable": "text" } : {};
|
|
152
516
|
}
|
|
@@ -158,7 +522,12 @@ var AI_RESPONSIVE_CSS = [
|
|
|
158
522
|
"@media (max-width: 640px) {",
|
|
159
523
|
" [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
|
|
160
524
|
" [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
|
|
525
|
+
// Group containers flatten to a column on phones; span placements come along for free.
|
|
526
|
+
" [data-ai-responsive] [data-ai-group] { display: flex !important; flex-direction: column !important; }",
|
|
527
|
+
" [data-ai-responsive] [data-ai-group] > * { grid-column: auto !important; }",
|
|
161
528
|
" [data-ai-responsive] [data-ai-section-inner] { padding-left: 20px !important; padding-right: 20px !important; }",
|
|
529
|
+
" [data-ai-responsive] { overflow-x: hidden; }",
|
|
530
|
+
" [data-ai-responsive] img { max-width: 100%; }",
|
|
162
531
|
"}"
|
|
163
532
|
].join("\n");
|
|
164
533
|
var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
|
|
@@ -236,7 +605,7 @@ function ButtonEl({
|
|
|
236
605
|
}) {
|
|
237
606
|
const secondary = slots.variant === "secondary";
|
|
238
607
|
const href = str(slots.href);
|
|
239
|
-
const linkAttrs = ctx.keyFor && path ? { "data-ohw-href-key": ctx.keyFor(`${path}.href`) } : {};
|
|
608
|
+
const linkAttrs = ctx.keyFor && path ? { "data-ohw-href-key": ctx.keyFor(`${path}.href`), "data-ohw-role": "button" } : {};
|
|
240
609
|
return /* @__PURE__ */ jsx(
|
|
241
610
|
"a",
|
|
242
611
|
{
|
|
@@ -252,7 +621,7 @@ function ButtonEl({
|
|
|
252
621
|
textDecoration: "none",
|
|
253
622
|
cursor: "pointer",
|
|
254
623
|
...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 }
|
|
624
|
+
...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
625
|
},
|
|
257
626
|
children: /* @__PURE__ */ jsx("span", { ...path ? textAttrs(ctx, `${path}.label`) : {}, children: str(slots.label) })
|
|
258
627
|
}
|
|
@@ -758,7 +1127,24 @@ function CardBlock({ node, ctx, path }) {
|
|
|
758
1127
|
minWidth: 0
|
|
759
1128
|
},
|
|
760
1129
|
children: [
|
|
761
|
-
media && (horizontal ? /* @__PURE__ */ jsx(
|
|
1130
|
+
media && (horizontal ? /* @__PURE__ */ jsx(
|
|
1131
|
+
"div",
|
|
1132
|
+
{
|
|
1133
|
+
style: (
|
|
1134
|
+
// An icon hugs its glyph — flex:1 gave a 48px icon half the card and pushed the
|
|
1135
|
+
// text to the far side. Photos keep the half-and-half split. The inset has no
|
|
1136
|
+
// inner padding (the photo split absorbed that), so the icon carries its own gap.
|
|
1137
|
+
/^(lucide|simple):/.test(mediaRef) ? {
|
|
1138
|
+
flexShrink: 0,
|
|
1139
|
+
display: "flex",
|
|
1140
|
+
alignItems: "center",
|
|
1141
|
+
padding: mediaInset,
|
|
1142
|
+
[mediaPosition === "right" ? "marginLeft" : "marginRight"]: 20
|
|
1143
|
+
} : { flex: 1, minWidth: 0, display: "flex", alignItems: "center", padding: mediaInset }
|
|
1144
|
+
),
|
|
1145
|
+
children: media
|
|
1146
|
+
}
|
|
1147
|
+
) : /* @__PURE__ */ jsx(
|
|
762
1148
|
"div",
|
|
763
1149
|
{
|
|
764
1150
|
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 +1235,44 @@ function AccordionBlock({ node, ctx, path }) {
|
|
|
849
1235
|
) })
|
|
850
1236
|
] }, i)) });
|
|
851
1237
|
}
|
|
1238
|
+
function useIsMobile() {
|
|
1239
|
+
const [mobile, setMobile] = React.useState(
|
|
1240
|
+
() => typeof window !== "undefined" && window.matchMedia("(max-width: 768px)").matches
|
|
1241
|
+
);
|
|
1242
|
+
React.useEffect(() => {
|
|
1243
|
+
const mq = window.matchMedia("(max-width: 768px)");
|
|
1244
|
+
const update = () => setMobile(mq.matches);
|
|
1245
|
+
update();
|
|
1246
|
+
mq.addEventListener("change", update);
|
|
1247
|
+
return () => mq.removeEventListener("change", update);
|
|
1248
|
+
}, []);
|
|
1249
|
+
return mobile;
|
|
1250
|
+
}
|
|
852
1251
|
function Carousel({ items, itemsPerRow, ctx }) {
|
|
1252
|
+
const isMobile = useIsMobile();
|
|
1253
|
+
const perPage = isMobile ? 1 : itemsPerRow;
|
|
1254
|
+
const pages = Math.max(1, Math.ceil(items.length / perPage));
|
|
853
1255
|
const [page, setPage] = React.useState(0);
|
|
854
|
-
const pages = Math.max(1, Math.ceil(items.length / itemsPerRow));
|
|
855
1256
|
const current = Math.min(page, pages - 1);
|
|
1257
|
+
if (pages <= 1) {
|
|
1258
|
+
const cols = Math.max(1, Math.min(items.length, itemsPerRow));
|
|
1259
|
+
return /* @__PURE__ */ jsx(
|
|
1260
|
+
"div",
|
|
1261
|
+
{
|
|
1262
|
+
"data-ai-grid": String(cols),
|
|
1263
|
+
style: {
|
|
1264
|
+
display: "grid",
|
|
1265
|
+
gridTemplateColumns: `repeat(${cols}, 1fr)`,
|
|
1266
|
+
gap: AI_TREE_TOKENS.spacing8,
|
|
1267
|
+
alignItems: "start"
|
|
1268
|
+
},
|
|
1269
|
+
children: items
|
|
1270
|
+
}
|
|
1271
|
+
);
|
|
1272
|
+
}
|
|
856
1273
|
const pageGroups = Array.from(
|
|
857
1274
|
{ length: pages },
|
|
858
|
-
(_, p) => items.slice(p *
|
|
1275
|
+
(_, p) => items.slice(p * perPage, (p + 1) * perPage)
|
|
859
1276
|
);
|
|
860
1277
|
const chrome = (enabled) => ({
|
|
861
1278
|
border: `1px solid ${ctx.brand.palette.dark}`,
|
|
@@ -880,55 +1297,69 @@ function Carousel({ items, itemsPerRow, ctx }) {
|
|
|
880
1297
|
cursor: "pointer",
|
|
881
1298
|
padding: 0
|
|
882
1299
|
});
|
|
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(
|
|
1300
|
+
const viewport = /* @__PURE__ */ jsx("div", { style: { flex: isMobile ? "0 0 auto" : 1, minWidth: 0, width: "100%", overflow: "hidden" }, children: /* @__PURE__ */ jsx(
|
|
1301
|
+
"div",
|
|
1302
|
+
{
|
|
1303
|
+
style: {
|
|
1304
|
+
display: "flex",
|
|
1305
|
+
transform: `translateX(-${current * 100}%)`,
|
|
1306
|
+
transition: "transform 0.4s ease"
|
|
1307
|
+
},
|
|
1308
|
+
children: pageGroups.map((group, p) => /* @__PURE__ */ jsx(
|
|
896
1309
|
"div",
|
|
897
1310
|
{
|
|
1311
|
+
"data-ai-grid": String(perPage),
|
|
898
1312
|
style: {
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
1313
|
+
flex: "0 0 100%",
|
|
1314
|
+
display: "grid",
|
|
1315
|
+
gridTemplateColumns: `repeat(${perPage}, 1fr)`,
|
|
1316
|
+
gap: AI_TREE_TOKENS.spacing8,
|
|
1317
|
+
alignItems: "start"
|
|
902
1318
|
},
|
|
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
|
-
|
|
1319
|
+
children: group
|
|
1320
|
+
},
|
|
1321
|
+
p
|
|
1322
|
+
))
|
|
1323
|
+
}
|
|
1324
|
+
) });
|
|
1325
|
+
const prevBtn = /* @__PURE__ */ jsx(
|
|
1326
|
+
"button",
|
|
1327
|
+
{
|
|
1328
|
+
type: "button",
|
|
1329
|
+
"aria-label": "Previous",
|
|
1330
|
+
onClick: () => setPage((p) => Math.max(0, p - 1)),
|
|
1331
|
+
style: chrome(current > 0),
|
|
1332
|
+
children: /* @__PURE__ */ jsx(ArrowLeft, { size: 24, strokeWidth: 1.5, "aria-hidden": true })
|
|
1333
|
+
}
|
|
1334
|
+
);
|
|
1335
|
+
const nextBtn = /* @__PURE__ */ jsx(
|
|
1336
|
+
"button",
|
|
1337
|
+
{
|
|
1338
|
+
type: "button",
|
|
1339
|
+
"aria-label": "Next",
|
|
1340
|
+
onClick: () => setPage((p) => Math.min(pages - 1, p + 1)),
|
|
1341
|
+
style: chrome(current < pages - 1),
|
|
1342
|
+
children: /* @__PURE__ */ jsx(ArrowRight, { size: 24, strokeWidth: 1.5, "aria-hidden": true })
|
|
1343
|
+
}
|
|
1344
|
+
);
|
|
1345
|
+
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)) });
|
|
1346
|
+
if (isMobile) {
|
|
1347
|
+
return /* @__PURE__ */ jsxs("div", { "data-ai-carousel": "", style: { display: "flex", flexDirection: "column", gap: AI_TREE_TOKENS.spacing6, alignItems: "center" }, children: [
|
|
1348
|
+
viewport,
|
|
1349
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: AI_TREE_TOKENS.spacing6, justifyContent: "center" }, children: [
|
|
1350
|
+
prevBtn,
|
|
1351
|
+
nextBtn
|
|
1352
|
+
] }),
|
|
1353
|
+
dots
|
|
1354
|
+
] });
|
|
1355
|
+
}
|
|
1356
|
+
return /* @__PURE__ */ jsxs("div", { "data-ai-carousel": "", style: { display: "flex", flexDirection: "column", gap: AI_TREE_TOKENS.spacing8, alignItems: "center" }, children: [
|
|
1357
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: AI_TREE_TOKENS.spacing6, width: "100%" }, children: [
|
|
1358
|
+
prevBtn,
|
|
1359
|
+
viewport,
|
|
1360
|
+
nextBtn
|
|
930
1361
|
] }),
|
|
931
|
-
|
|
1362
|
+
dots
|
|
932
1363
|
] });
|
|
933
1364
|
}
|
|
934
1365
|
function CollectionBlock({ node, ctx, path }) {
|
|
@@ -1022,6 +1453,49 @@ function renderNode(node, ctx, path) {
|
|
|
1022
1453
|
switch (node.type) {
|
|
1023
1454
|
case "text":
|
|
1024
1455
|
return /* @__PURE__ */ jsx(TextBlock, { slots, ctx, path });
|
|
1456
|
+
// Layout container: arranges child blocks, contributes no content of its own. `grid` is a
|
|
1457
|
+
// nested 12-column grid the children span (a collage is 3–5 media on it, bottom-aligned so
|
|
1458
|
+
// mixed aspects read as a composition); `split` is exactly two children at a ratio; `stack`
|
|
1459
|
+
// is a column. Children render through this same dispatcher, so edit markers, media
|
|
1460
|
+
// resolution, and copy paths all work unchanged inside a group.
|
|
1461
|
+
case "group": {
|
|
1462
|
+
const layout = str(slots.layout);
|
|
1463
|
+
const gap = slots.spacing === "tight" ? AI_TREE_TOKENS.spacing3 : slots.spacing === "airy" ? AI_TREE_TOKENS.spacing8 : AI_TREE_TOKENS.spacing6;
|
|
1464
|
+
const kids = (node.children ?? []).map((child, i) => /* @__PURE__ */ jsx(
|
|
1465
|
+
"div",
|
|
1466
|
+
{
|
|
1467
|
+
style: layout === "grid" ? {
|
|
1468
|
+
gridColumn: `span ${typeof child.span === "number" ? Math.min(12, Math.max(1, child.span)) : 12}`,
|
|
1469
|
+
minWidth: 0
|
|
1470
|
+
} : { minWidth: 0 },
|
|
1471
|
+
children: renderNode(child, ctx, `${path}.c${i}`)
|
|
1472
|
+
},
|
|
1473
|
+
i
|
|
1474
|
+
));
|
|
1475
|
+
if (layout === "grid") {
|
|
1476
|
+
return /* @__PURE__ */ jsx(
|
|
1477
|
+
"div",
|
|
1478
|
+
{
|
|
1479
|
+
"data-ai-group": "grid",
|
|
1480
|
+
style: { display: "grid", gridTemplateColumns: "repeat(12, 1fr)", gap, alignItems: "end" },
|
|
1481
|
+
children: kids
|
|
1482
|
+
}
|
|
1483
|
+
);
|
|
1484
|
+
}
|
|
1485
|
+
if (layout === "split") {
|
|
1486
|
+
const ratio = str(slots.ratio);
|
|
1487
|
+
const cols = ratio === "3:5" ? "3fr 5fr" : ratio === "5:3" ? "5fr 3fr" : "1fr 1fr";
|
|
1488
|
+
return /* @__PURE__ */ jsx(
|
|
1489
|
+
"div",
|
|
1490
|
+
{
|
|
1491
|
+
"data-ai-group": "split",
|
|
1492
|
+
style: { display: "grid", gridTemplateColumns: cols, gap, alignItems: "center" },
|
|
1493
|
+
children: kids
|
|
1494
|
+
}
|
|
1495
|
+
);
|
|
1496
|
+
}
|
|
1497
|
+
return /* @__PURE__ */ jsx("div", { "data-ai-group": "stack", style: { display: "flex", flexDirection: "column", gap }, children: kids });
|
|
1498
|
+
}
|
|
1025
1499
|
case "button":
|
|
1026
1500
|
return /* @__PURE__ */ jsx(ButtonEl, { slots, ctx, path });
|
|
1027
1501
|
case "button-row":
|
|
@@ -1102,33 +1576,111 @@ function renderNode(node, ctx, path) {
|
|
|
1102
1576
|
}
|
|
1103
1577
|
);
|
|
1104
1578
|
}
|
|
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
|
-
|
|
1579
|
+
case "form": {
|
|
1580
|
+
const formAttrs = ctx.keyFor ? {
|
|
1581
|
+
"data-ohw-editable": "form",
|
|
1582
|
+
"data-ohw-key": ctx.keyFor(`${path}.form`),
|
|
1583
|
+
"data-ohw-success-text": "Thanks \u2014 we'll be in touch shortly."
|
|
1584
|
+
} : {};
|
|
1585
|
+
const fieldStyle = {
|
|
1586
|
+
width: "100%",
|
|
1587
|
+
boxSizing: "border-box",
|
|
1588
|
+
border: `1px solid color-mix(in srgb, ${ctx.brand.palette.dark} 45%, #ffffff)`,
|
|
1589
|
+
borderRadius: 0,
|
|
1590
|
+
padding: 12,
|
|
1591
|
+
background: "#fff",
|
|
1592
|
+
color: ctx.brand.palette.dark,
|
|
1593
|
+
outline: "none",
|
|
1594
|
+
...typeStyle(AI_TREE_TOKENS.type.bodyM, ctx.brand.fonts.body)
|
|
1595
|
+
};
|
|
1596
|
+
const labelStyle = {
|
|
1597
|
+
...typeStyle(AI_TREE_TOKENS.type.bodyMBold, ctx.brand.fonts.body),
|
|
1598
|
+
color: ctx.brand.palette.dark,
|
|
1599
|
+
textAlign: "left",
|
|
1600
|
+
width: "100%"
|
|
1601
|
+
};
|
|
1602
|
+
const centered = ctx.sectionAlignment === "center";
|
|
1603
|
+
const submitAlign = centered ? "center" : "flex-start";
|
|
1604
|
+
const children = node.children ?? [];
|
|
1605
|
+
return (
|
|
1606
|
+
// 32px between the field group and the submit. In a stacked (centered) section the form is
|
|
1607
|
+
// capped at 780px and centered — the section's 12-col grid would otherwise leave it hugging
|
|
1608
|
+
// the left edge; a split section lets it fill its own column.
|
|
1609
|
+
/* @__PURE__ */ jsxs(
|
|
1610
|
+
"form",
|
|
1611
|
+
{
|
|
1612
|
+
...formAttrs,
|
|
1613
|
+
"data-ai-form": "",
|
|
1614
|
+
style: {
|
|
1615
|
+
display: "flex",
|
|
1616
|
+
flexDirection: "column",
|
|
1617
|
+
gap: 32,
|
|
1618
|
+
width: "100%",
|
|
1619
|
+
...centered ? { maxWidth: 780, marginLeft: "auto", marginRight: "auto" } : {}
|
|
1620
|
+
},
|
|
1621
|
+
children: [
|
|
1622
|
+
/* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", gap: 24, width: "100%", alignItems: "flex-start" }, children: children.map((child, i) => {
|
|
1623
|
+
if (child.type !== "input") return null;
|
|
1624
|
+
const cs = child.slots ?? {};
|
|
1625
|
+
const kind = str(cs.kind);
|
|
1626
|
+
const label = str(cs.label);
|
|
1627
|
+
const placeholder = str(cs.placeholder);
|
|
1628
|
+
const required = cs.required === true;
|
|
1629
|
+
const name = label.toLowerCase().replace(/[^a-z0-9]+/gu, "-").replace(/^-+|-+$/gu, "") || `field-${i}`;
|
|
1630
|
+
const isTextarea = kind === "textarea";
|
|
1631
|
+
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8, width: "100%" }, children: [
|
|
1632
|
+
/* @__PURE__ */ jsx("label", { ...textAttrs(ctx, `${path}.c${i}.label`), style: labelStyle, children: label }),
|
|
1633
|
+
isTextarea ? /* @__PURE__ */ jsx(
|
|
1634
|
+
"textarea",
|
|
1635
|
+
{
|
|
1636
|
+
name,
|
|
1637
|
+
placeholder,
|
|
1638
|
+
required,
|
|
1639
|
+
style: { ...fieldStyle, height: 180, resize: "vertical" }
|
|
1640
|
+
}
|
|
1641
|
+
) : /* @__PURE__ */ jsx(
|
|
1642
|
+
"input",
|
|
1643
|
+
{
|
|
1644
|
+
name,
|
|
1645
|
+
type: kind === "email" ? "email" : "text",
|
|
1646
|
+
placeholder,
|
|
1647
|
+
required,
|
|
1648
|
+
style: { ...fieldStyle, height: 48 }
|
|
1649
|
+
}
|
|
1650
|
+
)
|
|
1651
|
+
] }, i);
|
|
1652
|
+
}) }),
|
|
1653
|
+
children.map((child, i) => {
|
|
1654
|
+
if (child.type === "input") return null;
|
|
1655
|
+
const cs = child.slots ?? {};
|
|
1656
|
+
return /* @__PURE__ */ jsx(
|
|
1657
|
+
"button",
|
|
1658
|
+
{
|
|
1659
|
+
type: "submit",
|
|
1660
|
+
style: {
|
|
1661
|
+
alignSelf: submitAlign,
|
|
1662
|
+
border: "none",
|
|
1663
|
+
cursor: "pointer",
|
|
1664
|
+
padding: "12px 24px",
|
|
1665
|
+
// Corner radius follows the host template's own buttons (measured from a template
|
|
1666
|
+
// CTA); 8px only when the page has no template button to match.
|
|
1667
|
+
borderRadius: ctx.buttonRadius ?? 8,
|
|
1668
|
+
// Brand-styled: primary fill, brand-derived label colour (not a fixed token) so it
|
|
1669
|
+
// reads correctly on custom palettes.
|
|
1670
|
+
background: ctx.brand.palette.primary,
|
|
1671
|
+
color: ctx.buttonLabel ?? ctx.brand.palette.light,
|
|
1672
|
+
...typeStyle(AI_TREE_TOKENS.type.button, ctx.brand.fonts.body)
|
|
1673
|
+
},
|
|
1674
|
+
children: /* @__PURE__ */ jsx("span", { ...textAttrs(ctx, `${path}.c${i}.label`), children: str(cs.label) })
|
|
1675
|
+
},
|
|
1676
|
+
i
|
|
1677
|
+
);
|
|
1678
|
+
})
|
|
1679
|
+
]
|
|
1680
|
+
}
|
|
1681
|
+
)
|
|
1682
|
+
);
|
|
1683
|
+
}
|
|
1132
1684
|
case "schedule-widget":
|
|
1133
1685
|
return /* @__PURE__ */ jsx(
|
|
1134
1686
|
"div",
|
|
@@ -1149,16 +1701,27 @@ function renderNode(node, ctx, path) {
|
|
|
1149
1701
|
return null;
|
|
1150
1702
|
}
|
|
1151
1703
|
}
|
|
1152
|
-
function AiTreeRenderer({
|
|
1704
|
+
function AiTreeRenderer({
|
|
1705
|
+
tree,
|
|
1706
|
+
brand,
|
|
1707
|
+
buttonRadius,
|
|
1708
|
+
resolveMedia,
|
|
1709
|
+
editKeyPrefix
|
|
1710
|
+
}) {
|
|
1153
1711
|
if (!isRenderableTree(tree)) {
|
|
1154
1712
|
return null;
|
|
1155
1713
|
}
|
|
1156
1714
|
const resolvedBrand = brand ?? AI_DEFAULT_BRAND;
|
|
1715
|
+
const band = (tree.settings ?? {}).sectionBackground === "accent" ? accentBandContext(resolvedBrand) : null;
|
|
1716
|
+
const blockBrand = band?.brand ?? resolvedBrand;
|
|
1157
1717
|
const ctx = {
|
|
1158
|
-
brand:
|
|
1718
|
+
brand: blockBrand,
|
|
1159
1719
|
resolveMedia: resolveMedia ?? (() => null),
|
|
1160
|
-
cardSurface:
|
|
1161
|
-
keyFor: editKeyPrefix ? (path) => `${editKeyPrefix}.${path}` : null
|
|
1720
|
+
cardSurface: blockBrand.palette.light.toUpperCase() === AI_DEFAULT_BRAND.palette.light.toUpperCase() ? "#ECECEB" : `color-mix(in srgb, ${blockBrand.palette.light} 90%, ${blockBrand.palette.dark})`,
|
|
1721
|
+
keyFor: editKeyPrefix ? (path) => `${editKeyPrefix}.${path}` : null,
|
|
1722
|
+
sectionAlignment: (tree.settings ?? {}).alignment === "center" ? "center" : "left",
|
|
1723
|
+
buttonRadius,
|
|
1724
|
+
...band ? { buttonLabel: band.buttonLabel } : {}
|
|
1162
1725
|
};
|
|
1163
1726
|
const settings = tree.settings ?? {};
|
|
1164
1727
|
const pad = AI_TREE_TOKENS.sectionPadding[settings.spacing ?? "balanced"];
|
|
@@ -1166,6 +1729,20 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
|
|
|
1166
1729
|
const overlayAlpha = { low: 0.25, medium: 0.45, high: 0.65 }[settings.overlayIntensity ?? "medium"];
|
|
1167
1730
|
const backgroundUrl = isOverlay && settings.backgroundMedia ? ctx.resolveMedia(settings.backgroundMedia) : null;
|
|
1168
1731
|
const bgAttrs = ctx.keyFor && isOverlay && settings.backgroundMedia ? { "data-ohw-key": ctx.keyFor("settings.backgroundMedia"), "data-ohw-editable": "bg-image" } : {};
|
|
1732
|
+
const toneBackground = (() => {
|
|
1733
|
+
const { dark, primary, light } = resolvedBrand.palette;
|
|
1734
|
+
switch (settings.sectionBackground) {
|
|
1735
|
+
case "surface":
|
|
1736
|
+
return `color-mix(in srgb, ${light} 94%, ${dark})`;
|
|
1737
|
+
case "accent":
|
|
1738
|
+
return primary;
|
|
1739
|
+
case "accent-soft":
|
|
1740
|
+
return `color-mix(in srgb, ${primary} 12%, ${light})`;
|
|
1741
|
+
default:
|
|
1742
|
+
return void 0;
|
|
1743
|
+
}
|
|
1744
|
+
})();
|
|
1745
|
+
const distributed = !isOverlay && settings.textDistribution;
|
|
1169
1746
|
return /* @__PURE__ */ jsxs(
|
|
1170
1747
|
"section",
|
|
1171
1748
|
{
|
|
@@ -1175,13 +1752,15 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
|
|
|
1175
1752
|
style: {
|
|
1176
1753
|
position: "relative",
|
|
1177
1754
|
padding: `${pad}px 0`,
|
|
1178
|
-
background: isOverlay && !backgroundUrl ? "#DBEAFE" : void 0,
|
|
1755
|
+
background: toneBackground ?? (isOverlay && !backgroundUrl ? "#DBEAFE" : void 0),
|
|
1179
1756
|
backgroundImage: backgroundUrl ? `url(${backgroundUrl})` : void 0,
|
|
1180
1757
|
backgroundSize: "cover",
|
|
1181
|
-
backgroundPosition: "center"
|
|
1758
|
+
backgroundPosition: "center",
|
|
1759
|
+
color: settings.sectionBackground === "accent" ? blockBrand.palette.dark : void 0
|
|
1182
1760
|
},
|
|
1183
1761
|
children: [
|
|
1184
1762
|
/* @__PURE__ */ jsx("style", { children: AI_RESPONSIVE_CSS }),
|
|
1763
|
+
/* @__PURE__ */ jsx("style", { children: AI_MOBILE_CSS }),
|
|
1185
1764
|
isOverlay && backgroundUrl && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
|
|
1186
1765
|
/* @__PURE__ */ jsx(
|
|
1187
1766
|
"div",
|
|
@@ -1202,10 +1781,24 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
|
|
|
1202
1781
|
display: "grid",
|
|
1203
1782
|
gridTemplateColumns: "repeat(12, 1fr)",
|
|
1204
1783
|
gap: AI_TREE_TOKENS.spacing6,
|
|
1205
|
-
alignItems: settings.verticalPosition === "top" ? "start" : "center",
|
|
1784
|
+
alignItems: distributed === "space-between" ? "stretch" : (distributed ?? settings.verticalPosition) === "top" ? "start" : "center",
|
|
1206
1785
|
marginTop: r2 > 0 ? AI_TREE_TOKENS.spacing8 : 0
|
|
1207
1786
|
},
|
|
1208
|
-
children: (row.blocks ?? []).map((block, b) => /* @__PURE__ */ jsx(
|
|
1787
|
+
children: (row.blocks ?? []).map((block, b) => /* @__PURE__ */ jsx(
|
|
1788
|
+
"div",
|
|
1789
|
+
{
|
|
1790
|
+
"data-ai-cell": "",
|
|
1791
|
+
style: {
|
|
1792
|
+
gridColumn: `span ${Math.min(12, Math.max(1, block.span))}`,
|
|
1793
|
+
minWidth: 0,
|
|
1794
|
+
// space-between: each column becomes a flex column whose content spreads over
|
|
1795
|
+
// the full row height instead of clumping at the top.
|
|
1796
|
+
...distributed === "space-between" ? { display: "flex", flexDirection: "column", justifyContent: "space-between" } : {}
|
|
1797
|
+
},
|
|
1798
|
+
children: renderNode(block, ctx, `r${r2}.b${b}`)
|
|
1799
|
+
},
|
|
1800
|
+
b
|
|
1801
|
+
))
|
|
1209
1802
|
},
|
|
1210
1803
|
r2
|
|
1211
1804
|
))
|
|
@@ -1221,17 +1814,36 @@ import { jsx as jsx2 } from "react/jsx-runtime";
|
|
|
1221
1814
|
var CONTAINER_ATTR = "data-ohw-ai-generated";
|
|
1222
1815
|
var REPLACED_ATTR = "data-ohw-ai-replaced-by";
|
|
1223
1816
|
var REMOVED_ATTR = "data-ohw-ai-removed";
|
|
1817
|
+
var TEMPLATE_HIDDEN_ATTR = "data-ohw-ai-template-hidden";
|
|
1818
|
+
var CHROME_SECTION_IDS = /* @__PURE__ */ new Set(["navbar", "footer"]);
|
|
1819
|
+
function readRootVar(name) {
|
|
1820
|
+
if (typeof document === "undefined") return "";
|
|
1821
|
+
return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
|
|
1822
|
+
}
|
|
1823
|
+
function deriveBrandOverride() {
|
|
1824
|
+
const dark = readRootVar("--ohw-brand-dark");
|
|
1825
|
+
const primary = readRootVar("--ohw-brand-primary");
|
|
1826
|
+
const light = readRootVar("--ohw-brand-light");
|
|
1827
|
+
if (!dark || !primary || !light) return null;
|
|
1828
|
+
const accent = readRootVar("--ohw-brand-accent");
|
|
1829
|
+
const heading = readRootVar("--font-heading") || readRootVar("--font-display");
|
|
1830
|
+
const body = readRootVar("--font-body");
|
|
1831
|
+
return {
|
|
1832
|
+
palette: { dark, primary, accent: accent || dark, light },
|
|
1833
|
+
fonts: {
|
|
1834
|
+
heading: heading || AI_DEFAULT_BRAND.fonts.heading,
|
|
1835
|
+
body: body || AI_DEFAULT_BRAND.fonts.body
|
|
1836
|
+
}
|
|
1837
|
+
};
|
|
1838
|
+
}
|
|
1224
1839
|
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");
|
|
1840
|
+
const dark = readRootVar("--color-dark");
|
|
1841
|
+
const primary = readRootVar("--color-primary");
|
|
1842
|
+
const light = readRootVar("--color-light");
|
|
1231
1843
|
if (!dark || !primary || !light) return null;
|
|
1232
|
-
const accent =
|
|
1233
|
-
const heading =
|
|
1234
|
-
const body =
|
|
1844
|
+
const accent = readRootVar("--color-accent");
|
|
1845
|
+
const heading = readRootVar("--font-heading") || readRootVar("--font-display");
|
|
1846
|
+
const body = readRootVar("--font-body");
|
|
1235
1847
|
return {
|
|
1236
1848
|
palette: { dark, primary, accent: accent || dark, light },
|
|
1237
1849
|
fonts: {
|
|
@@ -1240,6 +1852,13 @@ function deriveTemplateBrand() {
|
|
|
1240
1852
|
}
|
|
1241
1853
|
};
|
|
1242
1854
|
}
|
|
1855
|
+
function deriveTemplateButtonRadius() {
|
|
1856
|
+
if (typeof document === "undefined") return null;
|
|
1857
|
+
const btn = document.querySelector('[data-ohw-role="button"]');
|
|
1858
|
+
if (!btn) return null;
|
|
1859
|
+
const radius = getComputedStyle(btn).borderTopLeftRadius;
|
|
1860
|
+
return radius || null;
|
|
1861
|
+
}
|
|
1243
1862
|
var mounted = /* @__PURE__ */ new Map();
|
|
1244
1863
|
function findTemplateSection(id) {
|
|
1245
1864
|
for (const el of document.querySelectorAll(`[data-ohw-section="${CSS.escape(id)}"]`)) {
|
|
@@ -1303,6 +1922,24 @@ function syncRemovedSections(state) {
|
|
|
1303
1922
|
}
|
|
1304
1923
|
}
|
|
1305
1924
|
}
|
|
1925
|
+
function syncTemplateHidden(state, pageHasSections) {
|
|
1926
|
+
const hide = state.hideTemplate === true && pageHasSections;
|
|
1927
|
+
for (const el of Array.from(document.querySelectorAll(`[${TEMPLATE_HIDDEN_ATTR}]`))) {
|
|
1928
|
+
if (!hide) {
|
|
1929
|
+
el.style.removeProperty("display");
|
|
1930
|
+
el.removeAttribute(TEMPLATE_HIDDEN_ATTR);
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
if (!hide) return;
|
|
1934
|
+
for (const el of Array.from(document.querySelectorAll("[data-ohw-section]"))) {
|
|
1935
|
+
if (el.hasAttribute(CONTAINER_ATTR)) continue;
|
|
1936
|
+
if (CHROME_SECTION_IDS.has(el.getAttribute("data-ohw-section") ?? "")) continue;
|
|
1937
|
+
if (el.parentElement?.closest("[data-ohw-section]")) continue;
|
|
1938
|
+
if (el.hasAttribute(REPLACED_ATTR) || el.hasAttribute(REMOVED_ATTR)) continue;
|
|
1939
|
+
el.style.display = "none";
|
|
1940
|
+
el.setAttribute(TEMPLATE_HIDDEN_ATTR, "");
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1306
1943
|
function syncReplacedOriginals(state) {
|
|
1307
1944
|
for (const el of document.querySelectorAll(`[${REPLACED_ATTR}]`)) {
|
|
1308
1945
|
const byId = el.getAttribute(REPLACED_ATTR) ?? "";
|
|
@@ -1321,10 +1958,64 @@ function syncReplacedOriginals(state) {
|
|
|
1321
1958
|
}
|
|
1322
1959
|
}
|
|
1323
1960
|
}
|
|
1961
|
+
var sectionOrderIndex = /* @__PURE__ */ new Map();
|
|
1962
|
+
function setAiSectionOrder(raw, currentPath) {
|
|
1963
|
+
const next = /* @__PURE__ */ new Map();
|
|
1964
|
+
if (raw) {
|
|
1965
|
+
try {
|
|
1966
|
+
const entries = JSON.parse(raw);
|
|
1967
|
+
for (const entry of entries) {
|
|
1968
|
+
if (!entry.pagePath || entry.pagePath === currentPath) next.set(entry.instanceId, entry.order);
|
|
1969
|
+
}
|
|
1970
|
+
} catch {
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
sectionOrderIndex = next;
|
|
1974
|
+
}
|
|
1975
|
+
function applyExplicitOrder(entries) {
|
|
1976
|
+
if (sectionOrderIndex.size === 0) return entries;
|
|
1977
|
+
return entries.map((entry, index) => ({ entry, index, order: sectionOrderIndex.get(entry.id) })).sort((a, b) => {
|
|
1978
|
+
if (a.order === void 0 && b.order === void 0) return a.index - b.index;
|
|
1979
|
+
if (a.order === void 0) return 1;
|
|
1980
|
+
if (b.order === void 0) return -1;
|
|
1981
|
+
return a.order - b.order;
|
|
1982
|
+
}).map((item) => item.entry);
|
|
1983
|
+
}
|
|
1984
|
+
function orderByChain(sections) {
|
|
1985
|
+
const ids = new Set(sections.map((entry) => entry.id));
|
|
1986
|
+
const after = /* @__PURE__ */ new Map();
|
|
1987
|
+
const roots = [];
|
|
1988
|
+
for (const entry of sections) {
|
|
1989
|
+
const anchor = entry.replaces ?? entry.beforeSection ?? entry.afterSection ?? null;
|
|
1990
|
+
if (anchor && ids.has(anchor)) {
|
|
1991
|
+
const bucket = after.get(anchor);
|
|
1992
|
+
if (bucket) bucket.push(entry);
|
|
1993
|
+
else after.set(anchor, [entry]);
|
|
1994
|
+
} else {
|
|
1995
|
+
roots.push(entry);
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
const out = [];
|
|
1999
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2000
|
+
const visit = (entry) => {
|
|
2001
|
+
if (seen.has(entry.id)) return;
|
|
2002
|
+
seen.add(entry.id);
|
|
2003
|
+
out.push(entry);
|
|
2004
|
+
for (const child of after.get(entry.id) ?? []) visit(child);
|
|
2005
|
+
};
|
|
2006
|
+
for (const root of roots) visit(root);
|
|
2007
|
+
return out.length === sections.length ? out : sections;
|
|
2008
|
+
}
|
|
1324
2009
|
function applyAiSectionsToDom(state, options) {
|
|
1325
2010
|
if (typeof document === "undefined") return;
|
|
2011
|
+
const brandOverride = deriveBrandOverride();
|
|
1326
2012
|
const templateBrand = deriveTemplateBrand();
|
|
1327
|
-
const
|
|
2013
|
+
const templateButtonRadius = deriveTemplateButtonRadius();
|
|
2014
|
+
const brandKey = brandOverride ? JSON.stringify(brandOverride) : "";
|
|
2015
|
+
const pagePath = window.location.pathname;
|
|
2016
|
+
const pageSections = state.sections.filter((entry) => (entry.path ?? "/") === pagePath);
|
|
2017
|
+
const activeIds = new Set(pageSections.map((entry) => entry.id));
|
|
2018
|
+
const ordered = state.hideTemplate === true ? applyExplicitOrder(orderByChain(pageSections)) : pageSections;
|
|
1328
2019
|
for (const [id, section] of mounted) {
|
|
1329
2020
|
if (!activeIds.has(id)) {
|
|
1330
2021
|
section.root.unmount();
|
|
@@ -1332,8 +2023,8 @@ function applyAiSectionsToDom(state, options) {
|
|
|
1332
2023
|
mounted.delete(id);
|
|
1333
2024
|
}
|
|
1334
2025
|
}
|
|
1335
|
-
for (const entry of
|
|
1336
|
-
const serialized = JSON.stringify(entry);
|
|
2026
|
+
for (const entry of ordered) {
|
|
2027
|
+
const serialized = JSON.stringify(entry) + brandKey;
|
|
1337
2028
|
const existing = mounted.get(entry.id);
|
|
1338
2029
|
if (existing && existing.serialized === serialized && existing.container.isConnected) {
|
|
1339
2030
|
continue;
|
|
@@ -1358,7 +2049,8 @@ function applyAiSectionsToDom(state, options) {
|
|
|
1358
2049
|
AiTreeRenderer,
|
|
1359
2050
|
{
|
|
1360
2051
|
tree: entry.tree,
|
|
1361
|
-
brand: entry.brand ?? options?.brand ?? templateBrand ?? void 0,
|
|
2052
|
+
brand: brandOverride ?? entry.brand ?? options?.brand ?? templateBrand ?? void 0,
|
|
2053
|
+
buttonRadius: templateButtonRadius,
|
|
1362
2054
|
resolveMedia,
|
|
1363
2055
|
editKeyPrefix: `ai.${entry.id}`
|
|
1364
2056
|
}
|
|
@@ -1367,8 +2059,20 @@ function applyAiSectionsToDom(state, options) {
|
|
|
1367
2059
|
});
|
|
1368
2060
|
mounted.set(entry.id, { root, container, serialized });
|
|
1369
2061
|
}
|
|
2062
|
+
if (state.hideTemplate === true) {
|
|
2063
|
+
let prev = null;
|
|
2064
|
+
for (const entry of ordered) {
|
|
2065
|
+
const el = mounted.get(entry.id)?.container;
|
|
2066
|
+
if (!el) continue;
|
|
2067
|
+
if (prev && !(prev.compareDocumentPosition(el) & Node.DOCUMENT_POSITION_FOLLOWING)) {
|
|
2068
|
+
prev.insertAdjacentElement("afterend", el);
|
|
2069
|
+
}
|
|
2070
|
+
prev = el;
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
1370
2073
|
syncReplacedOriginals(state);
|
|
1371
2074
|
syncRemovedSections(state);
|
|
2075
|
+
syncTemplateHidden(state, pageSections.length > 0);
|
|
1372
2076
|
}
|
|
1373
2077
|
|
|
1374
2078
|
// src/useLinkHrefGuardian.ts
|
|
@@ -1495,6 +2199,7 @@ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
|
|
|
1495
2199
|
/* @__PURE__ */ jsx3(
|
|
1496
2200
|
Dialog.Overlay,
|
|
1497
2201
|
{
|
|
2202
|
+
"data-ohw-scheduling-modal": "",
|
|
1498
2203
|
className: "fixed inset-0 z-50",
|
|
1499
2204
|
style: { background: "rgba(0,0,0,0.45)" }
|
|
1500
2205
|
}
|
|
@@ -1502,6 +2207,7 @@ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
|
|
|
1502
2207
|
/* @__PURE__ */ jsxs2(
|
|
1503
2208
|
Dialog.Content,
|
|
1504
2209
|
{
|
|
2210
|
+
"data-ohw-scheduling-modal": "",
|
|
1505
2211
|
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
2212
|
style: { maxWidth: 400 },
|
|
1507
2213
|
children: [
|
|
@@ -1975,7 +2681,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
|
|
|
1975
2681
|
const autoId = useId();
|
|
1976
2682
|
const insertAfter = insertAfterProp ?? autoId;
|
|
1977
2683
|
const [schedule, setSchedule] = useState2(null);
|
|
1978
|
-
const [loading, setLoading] = useState2(
|
|
2684
|
+
const [loading, setLoading] = useState2(initialScheduleId !== null);
|
|
1979
2685
|
const [inEditor, setInEditor] = useState2(false);
|
|
1980
2686
|
const [isHovered, setIsHovered] = useState2(false);
|
|
1981
2687
|
const [modalState, setModalState] = useState2(null);
|
|
@@ -2149,8 +2855,10 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
|
|
|
2149
2855
|
"*"
|
|
2150
2856
|
);
|
|
2151
2857
|
};
|
|
2152
|
-
if (!inEditor && !loading && !schedule) return null;
|
|
2153
2858
|
const sectionId = `scheduling-${insertAfter}`;
|
|
2859
|
+
if (!inEditor && !loading && !schedule) {
|
|
2860
|
+
return /* @__PURE__ */ jsx4("section", { "data-ohw-section": sectionId, "data-ohw-scheduling-anchor": insertAfter, style: { display: "none" } });
|
|
2861
|
+
}
|
|
2154
2862
|
return /* @__PURE__ */ jsxs3(
|
|
2155
2863
|
"section",
|
|
2156
2864
|
{
|
|
@@ -7067,13 +7775,17 @@ function MediaOverlay({
|
|
|
7067
7775
|
hover,
|
|
7068
7776
|
isUploading,
|
|
7069
7777
|
fadingOut = false,
|
|
7778
|
+
selected = false,
|
|
7779
|
+
hovered = false,
|
|
7070
7780
|
onFadeOutComplete,
|
|
7071
7781
|
onReplace,
|
|
7782
|
+
onSelect,
|
|
7072
7783
|
onVideoSettingsChange
|
|
7073
7784
|
}) {
|
|
7074
7785
|
const { rect } = hover;
|
|
7075
7786
|
const skeletonRef = React8.useRef(null);
|
|
7076
7787
|
const isVideo = hover.elementType === "video";
|
|
7788
|
+
const showChrome = !selected || hovered;
|
|
7077
7789
|
const autoplay = hover.videoAutoplay ?? true;
|
|
7078
7790
|
const muted = hover.videoMuted ?? true;
|
|
7079
7791
|
const probeRef = React8.useRef(null);
|
|
@@ -7087,6 +7799,7 @@ function MediaOverlay({
|
|
|
7087
7799
|
(prev) => prev && prev.fullWidth === width && prev.height === height ? prev : { fullWidth: width, height }
|
|
7088
7800
|
);
|
|
7089
7801
|
}, [isVideo]);
|
|
7802
|
+
const replaceLabel = isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image";
|
|
7090
7803
|
const replaceMode = buttonSize ? replaceButtonMode({ width: rect.width, height: rect.height }, buttonSize) : "none";
|
|
7091
7804
|
const box = {
|
|
7092
7805
|
position: "fixed",
|
|
@@ -7120,7 +7833,7 @@ function MediaOverlay({
|
|
|
7120
7833
|
}
|
|
7121
7834
|
);
|
|
7122
7835
|
}
|
|
7123
|
-
const settingsBar = isVideo && !hover.isDragOver ? /* @__PURE__ */ jsxs7(
|
|
7836
|
+
const settingsBar = isVideo && !hover.isDragOver && showChrome ? /* @__PURE__ */ jsxs7(
|
|
7124
7837
|
"div",
|
|
7125
7838
|
{
|
|
7126
7839
|
"data-ohw-bridge": "",
|
|
@@ -7190,10 +7903,12 @@ function MediaOverlay({
|
|
|
7190
7903
|
// in-document, pointer-events does it natively. The button below opts back in, so
|
|
7191
7904
|
// Replace still works.
|
|
7192
7905
|
pointerEvents: hover.hasTextOverlap ? "none" : "auto",
|
|
7193
|
-
|
|
7194
|
-
|
|
7906
|
+
// Selected: a firm component ring with no wash, so the image reads as chosen rather
|
|
7907
|
+
// than hovered. Hover keeps the existing tinted preview.
|
|
7908
|
+
boxShadow: selected ? "inset 0 0 0 2px var(--color-primary)" : "inset 0 0 0 1.5px var(--color-primary)",
|
|
7909
|
+
background: selected ? "transparent" : "color-mix(in srgb, var(--color-primary) 20%, transparent)"
|
|
7195
7910
|
},
|
|
7196
|
-
onClick: () => onReplace(hover.key),
|
|
7911
|
+
onClick: () => onSelect && !selected ? onSelect(hover.key) : onReplace(hover.key),
|
|
7197
7912
|
children: [
|
|
7198
7913
|
/* @__PURE__ */ jsxs7(
|
|
7199
7914
|
Button,
|
|
@@ -7214,17 +7929,17 @@ function MediaOverlay({
|
|
|
7214
7929
|
},
|
|
7215
7930
|
children: [
|
|
7216
7931
|
isVideo ? /* @__PURE__ */ jsx15(Film, { size: 14 }) : /* @__PURE__ */ jsx15(ImageIcon, { size: 14 }),
|
|
7217
|
-
|
|
7932
|
+
replaceLabel
|
|
7218
7933
|
]
|
|
7219
7934
|
}
|
|
7220
7935
|
),
|
|
7221
|
-
replaceMode
|
|
7936
|
+
showChrome && replaceMode !== "none" && /* @__PURE__ */ jsxs7(
|
|
7222
7937
|
Button,
|
|
7223
7938
|
{
|
|
7224
7939
|
"data-ohw-media-overlay": "",
|
|
7225
7940
|
variant: "outline",
|
|
7226
7941
|
size: "sm",
|
|
7227
|
-
"aria-label":
|
|
7942
|
+
"aria-label": replaceLabel,
|
|
7228
7943
|
className: "gap-1.5 cursor-pointer hover:bg-background",
|
|
7229
7944
|
style: {
|
|
7230
7945
|
...OVERLAY_BUTTON_STYLE,
|
|
@@ -7247,7 +7962,7 @@ function MediaOverlay({
|
|
|
7247
7962
|
},
|
|
7248
7963
|
children: [
|
|
7249
7964
|
isVideo ? /* @__PURE__ */ jsx15(Film, { size: 14 }) : /* @__PURE__ */ jsx15(ImageIcon, { size: 14 }),
|
|
7250
|
-
replaceMode === "full" ?
|
|
7965
|
+
replaceMode === "full" ? replaceLabel : null
|
|
7251
7966
|
]
|
|
7252
7967
|
}
|
|
7253
7968
|
)
|
|
@@ -7331,6 +8046,8 @@ function parseSectionsFromRoot(root) {
|
|
|
7331
8046
|
const id = el.getAttribute("data-ohw-section") ?? "";
|
|
7332
8047
|
if (!id || seen.has(id) || LINK_PICKER_EXCLUDED_IDS.has(id)) continue;
|
|
7333
8048
|
if (el.parentElement?.closest("[data-ohw-section]")) continue;
|
|
8049
|
+
if (el.hasAttribute("data-ohw-ai-template-hidden") || el.hasAttribute("data-ohw-ai-removed") || el.hasAttribute("data-ohw-ai-replaced-by"))
|
|
8050
|
+
continue;
|
|
7334
8051
|
seen.add(id);
|
|
7335
8052
|
const label = el.getAttribute("data-ohw-section-label") ?? titleCaseSectionId(id);
|
|
7336
8053
|
sections.push({ id, label });
|
|
@@ -7360,6 +8077,10 @@ function topLevelSections() {
|
|
|
7360
8077
|
function instanceIdOf(el) {
|
|
7361
8078
|
return el.dataset.ohwInstance ?? el.dataset.ohwSection ?? "";
|
|
7362
8079
|
}
|
|
8080
|
+
function findByInstanceId(instanceId) {
|
|
8081
|
+
const escapedId = CSS.escape(instanceId);
|
|
8082
|
+
return document.querySelector(`[data-ohw-instance="${escapedId}"]`) ?? document.querySelector(`[data-ohw-section="${escapedId}"]:not([data-ohw-instance])`);
|
|
8083
|
+
}
|
|
7363
8084
|
function planSectionMove(instanceId, targetIndex, currentPath) {
|
|
7364
8085
|
const sections = topLevelSections();
|
|
7365
8086
|
const index = sections.findIndex((el) => instanceIdOf(el) === instanceId);
|
|
@@ -7395,7 +8116,7 @@ function syncRemovedFlags(entries) {
|
|
|
7395
8116
|
}
|
|
7396
8117
|
});
|
|
7397
8118
|
for (const id of removedIds) {
|
|
7398
|
-
const el =
|
|
8119
|
+
const el = findByInstanceId(id);
|
|
7399
8120
|
if (el) {
|
|
7400
8121
|
el.style.display = "none";
|
|
7401
8122
|
el.setAttribute(REMOVED_ATTR2, "");
|
|
@@ -7423,9 +8144,7 @@ function applyPersistedOrder(entries) {
|
|
|
7423
8144
|
}
|
|
7424
8145
|
}
|
|
7425
8146
|
function setSectionRemoved(instanceId, currentPath, existingEntries, removed) {
|
|
7426
|
-
|
|
7427
|
-
if (!document.querySelector(`[data-ohw-instance="${escapedId}"]`) && !document.querySelector(`[data-ohw-section="${escapedId}"]:not([data-ohw-instance])`))
|
|
7428
|
-
return null;
|
|
8147
|
+
if (!findByInstanceId(instanceId)) return null;
|
|
7429
8148
|
const byId = new Map(existingEntries.map((e) => [e.instanceId, e]));
|
|
7430
8149
|
const allSections = Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
|
|
7431
8150
|
(el) => !el.parentElement?.closest("[data-ohw-section]") && !isChromeSection(el)
|
|
@@ -7614,6 +8333,7 @@ function AiSectionOverlay({
|
|
|
7614
8333
|
}) {
|
|
7615
8334
|
const [selectedId, setSelectedId] = useState5(null);
|
|
7616
8335
|
const [reviewId, setReviewId] = useState5(null);
|
|
8336
|
+
const [reviewButtonsHidden, setReviewButtonsHidden] = useState5(false);
|
|
7617
8337
|
const reviewIdRef = useRef4(null);
|
|
7618
8338
|
reviewIdRef.current = reviewId;
|
|
7619
8339
|
const selectedIdRef = useRef4(null);
|
|
@@ -7675,6 +8395,7 @@ function AiSectionOverlay({
|
|
|
7675
8395
|
}
|
|
7676
8396
|
const found = readRect(sectionId) != null;
|
|
7677
8397
|
setReviewId(found ? sectionId : null);
|
|
8398
|
+
setReviewButtonsHidden(e.data.hideButtons === true);
|
|
7678
8399
|
postToParent2({ type: "ow:ai-review-started", sectionId, found });
|
|
7679
8400
|
if (found) {
|
|
7680
8401
|
document.querySelector(`[data-ohw-instance="${CSS.escape(sectionId)}"]`)?.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
|
@@ -7802,13 +8523,16 @@ function AiSectionOverlay({
|
|
|
7802
8523
|
border: `2px solid ${PRIMARY2}`,
|
|
7803
8524
|
borderRadius: edgeAwareRadius(reviewRect),
|
|
7804
8525
|
zIndex: 2147483200,
|
|
7805
|
-
// The veil itself: swallows clicks so the section stays locked until decided.
|
|
8526
|
+
// The veil itself: swallows clicks so the section stays locked until decided. This
|
|
8527
|
+
// stopPropagation only guards the bubble phase; the bridge's capture-phase click
|
|
8528
|
+
// handlers additionally bail on `[data-ohw-ai-review]`, without which a click on
|
|
8529
|
+
// Accept/Discard resolves to the media beneath and opens the file picker.
|
|
7806
8530
|
background: "rgba(8, 133, 254, 0.04)",
|
|
7807
8531
|
pointerEvents: "auto",
|
|
7808
8532
|
cursor: "default"
|
|
7809
8533
|
},
|
|
7810
8534
|
onClick: (e) => e.stopPropagation(),
|
|
7811
|
-
children: /* @__PURE__ */ jsxs9(
|
|
8535
|
+
children: !reviewButtonsHidden && /* @__PURE__ */ jsxs9(
|
|
7812
8536
|
"div",
|
|
7813
8537
|
{
|
|
7814
8538
|
style: {
|
|
@@ -10375,8 +11099,13 @@ function referenceBox(slot) {
|
|
|
10375
11099
|
const neighbour = row ? Array.from(row.querySelectorAll('[data-ohw-editable="icon"]')).find(
|
|
10376
11100
|
(el) => el !== slot && !el.hasAttribute("data-ohw-social-icon-placeholder")
|
|
10377
11101
|
) : null;
|
|
10378
|
-
|
|
10379
|
-
|
|
11102
|
+
if (neighbour) {
|
|
11103
|
+
const box2 = neighbour.querySelector(GLYPH_SELECTOR)?.getBoundingClientRect();
|
|
11104
|
+
if (box2?.width && box2.height) return box2;
|
|
11105
|
+
}
|
|
11106
|
+
const own = slot.getBoundingClientRect();
|
|
11107
|
+
if (own.width && own.height) return own;
|
|
11108
|
+
const box = slot.querySelector(GLYPH_SELECTOR)?.getBoundingClientRect() ?? null;
|
|
10380
11109
|
return box?.width && box.height ? box : null;
|
|
10381
11110
|
}
|
|
10382
11111
|
function iconMarkupSizedFor(slot, markup) {
|
|
@@ -12177,6 +12906,7 @@ function readLogoSizeState(content, placement) {
|
|
|
12177
12906
|
function getLogoElement(el) {
|
|
12178
12907
|
const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
|
|
12179
12908
|
if (marked) return marked;
|
|
12909
|
+
if (el.closest('[data-ohw-editable="icon"]')) return null;
|
|
12180
12910
|
const root = el.closest("nav, [data-ohw-nav-root], footer");
|
|
12181
12911
|
if (!root) return null;
|
|
12182
12912
|
const anchor = el.closest("a");
|
|
@@ -13251,6 +13981,7 @@ function useSectionDrag({
|
|
|
13251
13981
|
}
|
|
13252
13982
|
const orderJson = JSON.stringify(entries);
|
|
13253
13983
|
editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
|
|
13984
|
+
setAiSectionOrder(orderJson, window.location.pathname);
|
|
13254
13985
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
|
|
13255
13986
|
applyPersistedOrder(entries);
|
|
13256
13987
|
clearSectionDragVisuals();
|
|
@@ -14110,21 +14841,10 @@ function parseSchedulingInsertAfter(insertAfter) {
|
|
|
14110
14841
|
insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
|
|
14111
14842
|
};
|
|
14112
14843
|
}
|
|
14113
|
-
function
|
|
14114
|
-
|
|
14115
|
-
const
|
|
14116
|
-
|
|
14117
|
-
return { effectiveInsertAfter, insertBefore };
|
|
14118
|
-
}
|
|
14119
|
-
function getSchedulingMountPoint(insertAfter) {
|
|
14120
|
-
const { anchor } = parseSchedulingInsertAfter(insertAfter);
|
|
14121
|
-
let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
|
|
14122
|
-
if (!anchorEl && anchor === "scheduling") {
|
|
14123
|
-
const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
|
|
14124
|
-
anchorEl = widgets.at(-1) ?? null;
|
|
14125
|
-
}
|
|
14126
|
-
if (!anchorEl) return null;
|
|
14127
|
-
return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
|
|
14844
|
+
function resolveEntryAnchor(entry) {
|
|
14845
|
+
if (entry.anchorId !== void 0) return { anchorId: entry.anchorId, beforeId: entry.beforeId ?? null };
|
|
14846
|
+
const parsed = parseSchedulingInsertAfter(entry.insertAfter);
|
|
14847
|
+
return { anchorId: parsed.anchor, beforeId: parsed.insertBefore };
|
|
14128
14848
|
}
|
|
14129
14849
|
function schedulingMountDepth(insertAfter) {
|
|
14130
14850
|
if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
|
|
@@ -14141,8 +14861,7 @@ function getPageSchedulingEntries(raw) {
|
|
|
14141
14861
|
}
|
|
14142
14862
|
}
|
|
14143
14863
|
function isSchedulingWidgetMissing(entry) {
|
|
14144
|
-
|
|
14145
|
-
return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
|
|
14864
|
+
return !document.querySelector(`[data-ohw-section="${schedulingSectionId(entry.insertAfter)}"]`);
|
|
14146
14865
|
}
|
|
14147
14866
|
function hasMissingSchedulingWidgets(entries) {
|
|
14148
14867
|
return entries.some(isSchedulingWidgetMissing);
|
|
@@ -14172,16 +14891,17 @@ function initSectionsFromContent(content, removeExisting = false) {
|
|
|
14172
14891
|
} catch {
|
|
14173
14892
|
}
|
|
14174
14893
|
}
|
|
14175
|
-
function mountSchedulingWidget(
|
|
14176
|
-
const
|
|
14177
|
-
const sectionId = schedulingSectionId(
|
|
14894
|
+
function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, beforeId, existingWidgetId) {
|
|
14895
|
+
const widgetId = existingWidgetId ?? (beforeId ? `${anchorId}${INSERT_BEFORE_MARKER}${beforeId}` : anchorId);
|
|
14896
|
+
const sectionId = schedulingSectionId(widgetId);
|
|
14178
14897
|
if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
|
|
14179
|
-
const
|
|
14180
|
-
if (!
|
|
14898
|
+
const anchorEl = document.querySelector(`[data-ohw-section="${anchorId}"]`);
|
|
14899
|
+
if (!anchorEl) return false;
|
|
14900
|
+
const mountPoint = anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
|
|
14181
14901
|
const container = document.createElement("div");
|
|
14182
14902
|
container.dataset.ohwSectionContainer = "scheduling";
|
|
14183
|
-
if (
|
|
14184
|
-
const beforeAnchor = document.querySelector(`[data-ohw-section="${
|
|
14903
|
+
if (beforeId) {
|
|
14904
|
+
const beforeAnchor = document.querySelector(`[data-ohw-section="${beforeId}"]`);
|
|
14185
14905
|
const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
|
|
14186
14906
|
if (!beforePoint) return false;
|
|
14187
14907
|
beforePoint.insertAdjacentElement("beforebegin", container);
|
|
@@ -14192,19 +14912,25 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
|
|
|
14192
14912
|
}
|
|
14193
14913
|
tail.insertAdjacentElement("afterend", container);
|
|
14194
14914
|
}
|
|
14195
|
-
|
|
14196
|
-
|
|
14197
|
-
|
|
14198
|
-
|
|
14199
|
-
|
|
14200
|
-
|
|
14201
|
-
|
|
14202
|
-
|
|
14203
|
-
|
|
14204
|
-
|
|
14205
|
-
|
|
14206
|
-
|
|
14207
|
-
|
|
14915
|
+
try {
|
|
14916
|
+
const root = createRoot2(container);
|
|
14917
|
+
flushSync2(() => {
|
|
14918
|
+
root.render(
|
|
14919
|
+
/* @__PURE__ */ jsx33(
|
|
14920
|
+
SchedulingWidget,
|
|
14921
|
+
{
|
|
14922
|
+
notifyOnConnect,
|
|
14923
|
+
initialScheduleId: scheduleId,
|
|
14924
|
+
insertAfter: widgetId
|
|
14925
|
+
}
|
|
14926
|
+
)
|
|
14927
|
+
);
|
|
14928
|
+
});
|
|
14929
|
+
} catch (err) {
|
|
14930
|
+
console.error("[ow:scheduling] render threw", err);
|
|
14931
|
+
container.remove();
|
|
14932
|
+
return false;
|
|
14933
|
+
}
|
|
14208
14934
|
const tracker = getSectionsTracker();
|
|
14209
14935
|
let sections = [];
|
|
14210
14936
|
try {
|
|
@@ -14212,10 +14938,12 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
|
|
|
14212
14938
|
} catch {
|
|
14213
14939
|
}
|
|
14214
14940
|
const inEditor = typeof window !== "undefined" && window.self !== window.top;
|
|
14215
|
-
if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter ===
|
|
14941
|
+
if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === widgetId)) {
|
|
14216
14942
|
sections.push({
|
|
14217
14943
|
type: "scheduling",
|
|
14218
|
-
insertAfter:
|
|
14944
|
+
insertAfter: widgetId,
|
|
14945
|
+
anchorId,
|
|
14946
|
+
beforeId: beforeId ?? null,
|
|
14219
14947
|
pagePath: window.location.pathname,
|
|
14220
14948
|
...scheduleId ? { scheduleId } : {}
|
|
14221
14949
|
});
|
|
@@ -14229,7 +14957,8 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
|
|
|
14229
14957
|
for (let i = pending.length - 1; i >= 0; i--) {
|
|
14230
14958
|
const entry = pending[i];
|
|
14231
14959
|
const shouldNotify = typeof notifyOnConnect === "function" ? notifyOnConnect(entry) : notifyOnConnect;
|
|
14232
|
-
|
|
14960
|
+
const { anchorId, beforeId } = resolveEntryAnchor(entry);
|
|
14961
|
+
if (mountSchedulingWidget(anchorId, shouldNotify, entry.scheduleId ?? null, beforeId, entry.insertAfter)) {
|
|
14233
14962
|
pending.splice(i, 1);
|
|
14234
14963
|
}
|
|
14235
14964
|
}
|
|
@@ -14387,6 +15116,11 @@ function applyLinkByKey(key, val) {
|
|
|
14387
15116
|
hrefAnchors.forEach((el) => applyLinkHref(el, val));
|
|
14388
15117
|
}
|
|
14389
15118
|
}
|
|
15119
|
+
function isInsideLinkEditor(target) {
|
|
15120
|
+
return Boolean(
|
|
15121
|
+
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"]')
|
|
15122
|
+
);
|
|
15123
|
+
}
|
|
14390
15124
|
function isInsideFloatingPanel(target) {
|
|
14391
15125
|
return Boolean(target.closest("[data-ohw-floating-panel]"));
|
|
14392
15126
|
}
|
|
@@ -14394,11 +15128,6 @@ function isPointOverFloatingPanel(clientX, clientY) {
|
|
|
14394
15128
|
const el = document.elementFromPoint(clientX, clientY);
|
|
14395
15129
|
return Boolean(el instanceof Element && el.closest("[data-ohw-floating-panel]"));
|
|
14396
15130
|
}
|
|
14397
|
-
function isInsideLinkEditor(target) {
|
|
14398
|
-
return Boolean(
|
|
14399
|
-
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"]')
|
|
14400
|
-
);
|
|
14401
|
-
}
|
|
14402
15131
|
function getHrefKeyFromElement(el) {
|
|
14403
15132
|
if (!el) return null;
|
|
14404
15133
|
const anchor = el.closest("[data-ohw-href-key]");
|
|
@@ -14657,7 +15386,7 @@ function getNavigationSelectionParent(el) {
|
|
|
14657
15386
|
if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
|
|
14658
15387
|
return getFooterLinksContainer();
|
|
14659
15388
|
}
|
|
14660
|
-
if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
|
|
15389
|
+
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)) {
|
|
14661
15390
|
return getNavigationRoot(el);
|
|
14662
15391
|
}
|
|
14663
15392
|
return null;
|
|
@@ -14872,7 +15601,6 @@ var ICONS = {
|
|
|
14872
15601
|
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"/>',
|
|
14873
15602
|
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"/>'
|
|
14874
15603
|
};
|
|
14875
|
-
var HEIGHT_SETTLE_DELAYS = [150, 400, 800];
|
|
14876
15604
|
var SELECTION_CHROME_GAP2 = 4;
|
|
14877
15605
|
var TOOLBAR_STROKE_GAP2 = 4;
|
|
14878
15606
|
var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
|
|
@@ -15252,6 +15980,7 @@ function StateToggle({
|
|
|
15252
15980
|
);
|
|
15253
15981
|
}
|
|
15254
15982
|
var contentCache = /* @__PURE__ */ new Map();
|
|
15983
|
+
var fetchedContentPaths = /* @__PURE__ */ new Set();
|
|
15255
15984
|
var OHW_LOADER_STYLE = {
|
|
15256
15985
|
position: "fixed",
|
|
15257
15986
|
inset: 0,
|
|
@@ -15370,6 +16099,70 @@ function OhhwellsBridge() {
|
|
|
15370
16099
|
const hoveredImageHasTextOverlapRef = useRef10(false);
|
|
15371
16100
|
const dragOverElRef = useRef10(null);
|
|
15372
16101
|
const [mediaHover, setMediaHover] = useState13(null);
|
|
16102
|
+
const [selectedMedia, setSelectedMedia] = useState13(null);
|
|
16103
|
+
const selectedMediaElRef = useRef10(null);
|
|
16104
|
+
const clearMediaSelection = useCallback8(() => {
|
|
16105
|
+
const prev = selectedMediaElRef.current;
|
|
16106
|
+
selectedMediaElRef.current = null;
|
|
16107
|
+
setSelectedMedia(null);
|
|
16108
|
+
const sectionEl = prev?.closest("[data-ohw-section]") ?? null;
|
|
16109
|
+
if (sectionEl) {
|
|
16110
|
+
postToParentRef.current({
|
|
16111
|
+
type: "ow:section-selected",
|
|
16112
|
+
sectionId: sectionEl.dataset.ohwSection ?? null,
|
|
16113
|
+
sectionLabel: sectionEl.dataset.ohwSectionLabel ?? titleCaseSectionId(sectionEl.dataset.ohwSection ?? ""),
|
|
16114
|
+
key: null
|
|
16115
|
+
});
|
|
16116
|
+
}
|
|
16117
|
+
}, []);
|
|
16118
|
+
const clearMediaSelectionRef = useRef10(clearMediaSelection);
|
|
16119
|
+
clearMediaSelectionRef.current = clearMediaSelection;
|
|
16120
|
+
const selectMediaElement = useCallback8((el) => {
|
|
16121
|
+
const r2 = el.getBoundingClientRect();
|
|
16122
|
+
const video = el.dataset.ohwEditable === "video" ? el.querySelector("video") : null;
|
|
16123
|
+
selectedMediaElRef.current = el;
|
|
16124
|
+
setSelectedMedia({
|
|
16125
|
+
key: el.dataset.ohwKey ?? "",
|
|
16126
|
+
rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height },
|
|
16127
|
+
elementType: el.dataset.ohwEditable ?? "image",
|
|
16128
|
+
hasTextOverlap: false,
|
|
16129
|
+
isDragOver: false,
|
|
16130
|
+
...video ? { videoAutoplay: video.autoplay, videoMuted: video.muted } : {}
|
|
16131
|
+
});
|
|
16132
|
+
const sectionEl = el.closest("[data-ohw-section]");
|
|
16133
|
+
aiSectionApiRef.current?.selectFromElement(el, { report: false });
|
|
16134
|
+
postToParentRef.current({
|
|
16135
|
+
type: "ow:section-selected",
|
|
16136
|
+
sectionId: sectionEl?.dataset.ohwSection ?? null,
|
|
16137
|
+
sectionLabel: sectionEl ? sectionEl.dataset.ohwSectionLabel ?? titleCaseSectionId(sectionEl.dataset.ohwSection ?? "") : null,
|
|
16138
|
+
key: el.dataset.ohwKey ?? null,
|
|
16139
|
+
// Display name for the pill — the raw key prettifies into fragments ("Img"); the
|
|
16140
|
+
// bridge knows what the node IS, so it names it.
|
|
16141
|
+
keyLabel: el.dataset.ohwEditable === "video" ? "Video" : el.dataset.ohwEditable === "bg-image" ? "Background" : "Image"
|
|
16142
|
+
});
|
|
16143
|
+
}, []);
|
|
16144
|
+
const selectMediaElementRef = useRef10(selectMediaElement);
|
|
16145
|
+
selectMediaElementRef.current = selectMediaElement;
|
|
16146
|
+
useEffect13(() => {
|
|
16147
|
+
if (!selectedMedia) return;
|
|
16148
|
+
const update = () => {
|
|
16149
|
+
const el = selectedMediaElRef.current;
|
|
16150
|
+
if (!el || !el.isConnected) {
|
|
16151
|
+
clearMediaSelection();
|
|
16152
|
+
return;
|
|
16153
|
+
}
|
|
16154
|
+
const r2 = el.getBoundingClientRect();
|
|
16155
|
+
setSelectedMedia(
|
|
16156
|
+
(prev) => prev ? { ...prev, rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height } } : prev
|
|
16157
|
+
);
|
|
16158
|
+
};
|
|
16159
|
+
window.addEventListener("scroll", update, true);
|
|
16160
|
+
window.addEventListener("resize", update);
|
|
16161
|
+
return () => {
|
|
16162
|
+
window.removeEventListener("scroll", update, true);
|
|
16163
|
+
window.removeEventListener("resize", update);
|
|
16164
|
+
};
|
|
16165
|
+
}, [selectedMedia !== null]);
|
|
15373
16166
|
const [carouselHover, setCarouselHover] = useState13(null);
|
|
15374
16167
|
const [uploadingRects, setUploadingRects] = useState13({});
|
|
15375
16168
|
const hoveredGapRef = useRef10(null);
|
|
@@ -15632,13 +16425,6 @@ function OhhwellsBridge() {
|
|
|
15632
16425
|
const [isItemDragging, setIsItemDragging] = useState13(false);
|
|
15633
16426
|
const [isFooterFrameSelection, setIsFooterFrameSelection] = useState13(false);
|
|
15634
16427
|
isFooterFrameSelectionRef.current = isFooterFrameSelection;
|
|
15635
|
-
const [floatingPanel, setFloatingPanel] = useState13(null);
|
|
15636
|
-
const floatingPanelOpenRef = useRef10(false);
|
|
15637
|
-
floatingPanelOpenRef.current = floatingPanel !== null;
|
|
15638
|
-
const [floatingPanelPos, setFloatingPanelPos] = useState13(null);
|
|
15639
|
-
const [logoSizeDraft, setLogoSizeDraft] = useState13(null);
|
|
15640
|
-
const [editorViewport, setEditorViewport] = useState13("desktop");
|
|
15641
|
-
const [parentScrollSnap, setParentScrollSnap] = useState13(null);
|
|
15642
16428
|
const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = useState13(null);
|
|
15643
16429
|
const [footerHeadingVisible, setFooterHeadingVisible] = useState13(null);
|
|
15644
16430
|
const footerDragRef = useRef10(null);
|
|
@@ -15653,7 +16439,16 @@ function OhhwellsBridge() {
|
|
|
15653
16439
|
const addNavAfterAnchorRef = useRef10(null);
|
|
15654
16440
|
const editContentRef = useRef10({});
|
|
15655
16441
|
const aiSectionsRef = useRef10("");
|
|
16442
|
+
const brandKitRef = useRef10("");
|
|
16443
|
+
const stylesRef = useRef10("");
|
|
15656
16444
|
const pendingDeleteUndoRef = useRef10(null);
|
|
16445
|
+
const [floatingPanel, setFloatingPanel] = useState13(null);
|
|
16446
|
+
const floatingPanelOpenRef = useRef10(false);
|
|
16447
|
+
const setFloatingPanelRef = useRef10(setFloatingPanel);
|
|
16448
|
+
const [floatingPanelPos, setFloatingPanelPos] = useState13(null);
|
|
16449
|
+
const [logoSizeDraft, setLogoSizeDraft] = useState13(null);
|
|
16450
|
+
const [editorViewport, setEditorViewport] = useState13("desktop");
|
|
16451
|
+
const [parentScrollSnap, setParentScrollSnap] = useState13(null);
|
|
15657
16452
|
const [sitePages, setSitePages] = useState13([]);
|
|
15658
16453
|
const [sectionsByPath, setSectionsByPath] = useState13({});
|
|
15659
16454
|
const sectionsPrefetchGenRef = useRef10(0);
|
|
@@ -15662,7 +16457,18 @@ function OhhwellsBridge() {
|
|
|
15662
16457
|
const linkPopoverOpenRef = useRef10(false);
|
|
15663
16458
|
const linkPopoverGraceUntilRef = useRef10(0);
|
|
15664
16459
|
setLinkPopoverRef.current = setLinkPopover;
|
|
16460
|
+
setFloatingPanelRef.current = setFloatingPanel;
|
|
15665
16461
|
linkPopoverSessionRef.current = linkPopover;
|
|
16462
|
+
floatingPanelOpenRef.current = Boolean(floatingPanel);
|
|
16463
|
+
useEffect13(() => {
|
|
16464
|
+
const syncViewport = () => {
|
|
16465
|
+
const next = window.innerWidth <= 480 ? "mobile" : "desktop";
|
|
16466
|
+
setEditorViewport((prev) => prev === next ? prev : next);
|
|
16467
|
+
};
|
|
16468
|
+
syncViewport();
|
|
16469
|
+
window.addEventListener("resize", syncViewport);
|
|
16470
|
+
return () => window.removeEventListener("resize", syncViewport);
|
|
16471
|
+
}, []);
|
|
15666
16472
|
const {
|
|
15667
16473
|
navDragRef,
|
|
15668
16474
|
navDropSlots,
|
|
@@ -16973,15 +17779,31 @@ function OhhwellsBridge() {
|
|
|
16973
17779
|
}
|
|
16974
17780
|
const applyContent = (content) => {
|
|
16975
17781
|
const imageLoads = [];
|
|
17782
|
+
if (typeof content[BRAND_KIT_KEY] === "string") {
|
|
17783
|
+
brandKitRef.current = content[BRAND_KIT_KEY];
|
|
17784
|
+
applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
|
|
17785
|
+
} else {
|
|
17786
|
+
brandKitRef.current = "";
|
|
17787
|
+
applyBrandToDom(null);
|
|
17788
|
+
}
|
|
16976
17789
|
if (typeof content[AI_SECTIONS_KEY] === "string") {
|
|
16977
17790
|
aiSectionsRef.current = content[AI_SECTIONS_KEY];
|
|
17791
|
+
setAiSectionOrder(content[SECTION_ORDER_KEY], window.location.pathname);
|
|
16978
17792
|
applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
|
|
16979
17793
|
}
|
|
17794
|
+
if (typeof content[STYLE_STORE_KEY] === "string") {
|
|
17795
|
+
stylesRef.current = content[STYLE_STORE_KEY];
|
|
17796
|
+
applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
|
|
17797
|
+
}
|
|
17798
|
+
applyBrandChrome(content);
|
|
16980
17799
|
for (const [key, val] of Object.entries(content)) {
|
|
16981
17800
|
if (key === "__ohw_sections") continue;
|
|
16982
17801
|
if (key === AI_SECTIONS_KEY) continue;
|
|
16983
17802
|
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
16984
17803
|
if (LOGO_IMAGE_KEYS.includes(key)) continue;
|
|
17804
|
+
if (key === BRAND_KIT_KEY) continue;
|
|
17805
|
+
if (key === STYLE_STORE_KEY) continue;
|
|
17806
|
+
if (BRAND_CHROME_KEYS.has(key)) continue;
|
|
16985
17807
|
if (applyVideoSettingNode(key, val)) continue;
|
|
16986
17808
|
if (applyCarouselNode(key, val)) continue;
|
|
16987
17809
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
@@ -17047,7 +17869,9 @@ function OhhwellsBridge() {
|
|
|
17047
17869
|
let cancelled = false;
|
|
17048
17870
|
setFetchState("loading");
|
|
17049
17871
|
const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
|
|
17050
|
-
|
|
17872
|
+
const initialPath = pathname;
|
|
17873
|
+
fetchedContentPaths.add(`${subdomain}::${initialPath}`);
|
|
17874
|
+
fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
|
|
17051
17875
|
if (cancelled) return;
|
|
17052
17876
|
const content = data?.content ?? {};
|
|
17053
17877
|
contentCache.set(subdomain, content);
|
|
@@ -17167,10 +17991,28 @@ function OhhwellsBridge() {
|
|
|
17167
17991
|
initSectionInstancesFromContent(content, window.location.pathname);
|
|
17168
17992
|
observer?.disconnect();
|
|
17169
17993
|
try {
|
|
17994
|
+
applyBrandChrome(content);
|
|
17995
|
+
if (typeof content[BRAND_KIT_KEY] === "string") {
|
|
17996
|
+
applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
|
|
17997
|
+
} else {
|
|
17998
|
+
applyBrandToDom(null);
|
|
17999
|
+
}
|
|
18000
|
+
if (typeof content[AI_SECTIONS_KEY] === "string") {
|
|
18001
|
+
setAiSectionOrder(content[SECTION_ORDER_KEY], window.location.pathname);
|
|
18002
|
+
applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
|
|
18003
|
+
}
|
|
18004
|
+
if (typeof content[STYLE_STORE_KEY] === "string") {
|
|
18005
|
+
applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
|
|
18006
|
+
}
|
|
17170
18007
|
for (const [key, val] of Object.entries(content)) {
|
|
17171
18008
|
if (key === "__ohw_sections") continue;
|
|
18009
|
+
if (key === AI_SECTIONS_KEY) continue;
|
|
17172
18010
|
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
17173
18011
|
if (LOGO_IMAGE_KEYS.includes(key)) continue;
|
|
18012
|
+
if (key === BRAND_KIT_KEY) continue;
|
|
18013
|
+
if (key === STYLE_STORE_KEY) continue;
|
|
18014
|
+
if (key === STYLE_STORE_KEY) continue;
|
|
18015
|
+
if (BRAND_CHROME_KEYS.has(key)) continue;
|
|
17174
18016
|
if (applyVideoSettingNode(key, val)) continue;
|
|
17175
18017
|
if (applyCarouselNode(key, val)) continue;
|
|
17176
18018
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
@@ -17216,6 +18058,17 @@ function OhhwellsBridge() {
|
|
|
17216
18058
|
debounceTimer = setTimeout(applyFromCache, 150);
|
|
17217
18059
|
};
|
|
17218
18060
|
applyFromCache();
|
|
18061
|
+
const pathCacheKey = `${subdomain}::${pathname}`;
|
|
18062
|
+
if (!fetchedContentPaths.has(pathCacheKey)) {
|
|
18063
|
+
fetchedContentPaths.add(pathCacheKey);
|
|
18064
|
+
const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
|
|
18065
|
+
fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(pathname)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
|
|
18066
|
+
if (!data?.content) return;
|
|
18067
|
+
contentCache.set(subdomain, data.content);
|
|
18068
|
+
applyFromCache();
|
|
18069
|
+
}).catch(() => {
|
|
18070
|
+
});
|
|
18071
|
+
}
|
|
17219
18072
|
observer = new MutationObserver(scheduleApply);
|
|
17220
18073
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
17221
18074
|
return () => {
|
|
@@ -17311,30 +18164,31 @@ function OhhwellsBridge() {
|
|
|
17311
18164
|
}, [isEditMode, pathname, subdomain, fetchState, resyncSelectedNavigationItem]);
|
|
17312
18165
|
useEffect13(() => {
|
|
17313
18166
|
if (!isEditMode) return;
|
|
18167
|
+
let lastPosted = 0;
|
|
17314
18168
|
const measure = () => {
|
|
17315
18169
|
const h = document.body.scrollHeight;
|
|
17316
|
-
if (h > 50
|
|
18170
|
+
if (h > 50 && Math.abs(h - lastPosted) > 1) {
|
|
18171
|
+
lastPosted = h;
|
|
18172
|
+
postToParent2({ type: "ow:height", height: h });
|
|
18173
|
+
}
|
|
18174
|
+
};
|
|
18175
|
+
let raf = null;
|
|
18176
|
+
const schedule = () => {
|
|
18177
|
+
if (raf != null) return;
|
|
18178
|
+
raf = requestAnimationFrame(() => {
|
|
18179
|
+
raf = null;
|
|
18180
|
+
measure();
|
|
18181
|
+
});
|
|
17317
18182
|
};
|
|
17318
18183
|
const t1 = setTimeout(measure, 50);
|
|
17319
18184
|
const t2 = setTimeout(measure, 500);
|
|
17320
|
-
|
|
17321
|
-
|
|
17322
|
-
const clearResizeTimers = () => {
|
|
17323
|
-
resizeTimers.forEach(clearTimeout);
|
|
17324
|
-
resizeTimers = [];
|
|
17325
|
-
};
|
|
17326
|
-
const handleResize = () => {
|
|
17327
|
-
if (window.innerWidth === lastWidth) return;
|
|
17328
|
-
lastWidth = window.innerWidth;
|
|
17329
|
-
clearResizeTimers();
|
|
17330
|
-
resizeTimers = HEIGHT_SETTLE_DELAYS.map((delay) => setTimeout(measure, delay));
|
|
17331
|
-
};
|
|
17332
|
-
window.addEventListener("resize", handleResize);
|
|
18185
|
+
const ro = new ResizeObserver(schedule);
|
|
18186
|
+
ro.observe(document.body);
|
|
17333
18187
|
return () => {
|
|
17334
18188
|
clearTimeout(t1);
|
|
17335
18189
|
clearTimeout(t2);
|
|
17336
|
-
|
|
17337
|
-
|
|
18190
|
+
if (raf != null) cancelAnimationFrame(raf);
|
|
18191
|
+
ro.disconnect();
|
|
17338
18192
|
};
|
|
17339
18193
|
}, [pathname, isEditMode, postToParent2]);
|
|
17340
18194
|
useEffect13(() => {
|
|
@@ -17575,6 +18429,7 @@ function OhhwellsBridge() {
|
|
|
17575
18429
|
return;
|
|
17576
18430
|
}
|
|
17577
18431
|
const target = e.target;
|
|
18432
|
+
if (target.closest("[data-ohw-ai-review]")) return;
|
|
17578
18433
|
if (target.closest("[data-ohw-toolbar]")) return;
|
|
17579
18434
|
if (target.closest("[data-ohw-state-toggle]")) return;
|
|
17580
18435
|
if (target.closest("[data-ohw-max-badge]")) return;
|
|
@@ -17586,6 +18441,9 @@ function OhhwellsBridge() {
|
|
|
17586
18441
|
)) {
|
|
17587
18442
|
return;
|
|
17588
18443
|
}
|
|
18444
|
+
if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
|
|
18445
|
+
clearMediaSelectionRef.current();
|
|
18446
|
+
}
|
|
17589
18447
|
{
|
|
17590
18448
|
const formEl = getFormElement(target);
|
|
17591
18449
|
const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
|
|
@@ -17737,19 +18595,14 @@ function OhhwellsBridge() {
|
|
|
17737
18595
|
}
|
|
17738
18596
|
const clickedButton = findClosestButtonLike(target);
|
|
17739
18597
|
const buttonOnMedia = Boolean(clickedButton && isMediaEditable(editable) && editable.contains(clickedButton));
|
|
17740
|
-
console.log("[click-debug]", {
|
|
17741
|
-
editableType: editable.dataset.ohwEditable,
|
|
17742
|
-
editableTag: editable.tagName,
|
|
17743
|
-
targetTag: target.tagName,
|
|
17744
|
-
clickedButtonTag: clickedButton?.tagName ?? null,
|
|
17745
|
-
buttonOnMedia,
|
|
17746
|
-
isMediaEditableEditable: isMediaEditable(editable)
|
|
17747
|
-
});
|
|
17748
18598
|
if (isMediaEditable(editable) && !buttonOnMedia) {
|
|
17749
18599
|
e.preventDefault();
|
|
17750
18600
|
e.stopPropagation();
|
|
17751
|
-
|
|
17752
|
-
|
|
18601
|
+
if (selectedMediaElRef.current === editable) {
|
|
18602
|
+
postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "", elementType: editable.dataset.ohwEditable ?? "image" });
|
|
18603
|
+
} else {
|
|
18604
|
+
selectMediaElementRef.current(editable);
|
|
18605
|
+
}
|
|
17753
18606
|
return;
|
|
17754
18607
|
}
|
|
17755
18608
|
const socialItem = getSocialItem(editable);
|
|
@@ -17768,11 +18621,6 @@ function OhhwellsBridge() {
|
|
|
17768
18621
|
const hrefLookupTarget = buttonOnMedia ? clickedButton : editable;
|
|
17769
18622
|
const hrefCtx = getHrefKeyFromElement(hrefLookupTarget);
|
|
17770
18623
|
const navAnchor = hrefCtx ? getNavigationItemAnchor(hrefCtx.anchor) : null;
|
|
17771
|
-
console.log("[click-debug 2]", {
|
|
17772
|
-
hrefLookupTargetTag: hrefLookupTarget.tagName,
|
|
17773
|
-
hrefCtx: hrefCtx ? { key: hrefCtx.key } : null,
|
|
17774
|
-
navAnchorTag: navAnchor?.tagName ?? null
|
|
17775
|
-
});
|
|
17776
18624
|
if (navAnchor) {
|
|
17777
18625
|
e.preventDefault();
|
|
17778
18626
|
e.stopPropagation();
|
|
@@ -17890,6 +18738,7 @@ function OhhwellsBridge() {
|
|
|
17890
18738
|
};
|
|
17891
18739
|
const handleDblClick = (e) => {
|
|
17892
18740
|
const target = e.target;
|
|
18741
|
+
if (target.closest("[data-ohw-ai-review]")) return;
|
|
17893
18742
|
if (target.closest("[data-ohw-toolbar]")) return;
|
|
17894
18743
|
if (target.closest("[data-ohw-state-toggle]")) return;
|
|
17895
18744
|
if (target.closest("[data-ohw-max-badge]")) return;
|
|
@@ -17941,6 +18790,9 @@ function OhhwellsBridge() {
|
|
|
17941
18790
|
setHoveredItemRect(null);
|
|
17942
18791
|
hoveredNavContainerRef.current = null;
|
|
17943
18792
|
setHoveredNavContainerRect(null);
|
|
18793
|
+
siblingHintElRef.current = null;
|
|
18794
|
+
setSiblingHintRect(null);
|
|
18795
|
+
setSiblingHintRects([]);
|
|
17944
18796
|
return;
|
|
17945
18797
|
}
|
|
17946
18798
|
{
|
|
@@ -18059,7 +18911,6 @@ function OhhwellsBridge() {
|
|
|
18059
18911
|
hoveredNavContainerRef.current = null;
|
|
18060
18912
|
setHoveredNavContainerRect(null);
|
|
18061
18913
|
hoveredItemElRef.current = editable;
|
|
18062
|
-
setHoveredItemRect(isSelectedForHoverRef.current(editable) ? null : editable.getBoundingClientRect());
|
|
18063
18914
|
}
|
|
18064
18915
|
}
|
|
18065
18916
|
}
|
|
@@ -18356,7 +19207,7 @@ function OhhwellsBridge() {
|
|
|
18356
19207
|
}
|
|
18357
19208
|
};
|
|
18358
19209
|
const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
|
|
18359
|
-
if (linkPopoverOpenRef.current) {
|
|
19210
|
+
if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
|
|
18360
19211
|
if (hoveredImageRef.current) {
|
|
18361
19212
|
hoveredImageRef.current = null;
|
|
18362
19213
|
hoveredImageHasTextOverlapRef.current = false;
|
|
@@ -18690,7 +19541,9 @@ function OhhwellsBridge() {
|
|
|
18690
19541
|
return;
|
|
18691
19542
|
}
|
|
18692
19543
|
const { y } = toProbeCoords(clientX, clientY, fromParentViewport);
|
|
18693
|
-
const sections = Array.from(document.querySelectorAll("[data-ohw-section]")).
|
|
19544
|
+
const sections = Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
|
|
19545
|
+
(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
|
|
19546
|
+
).sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);
|
|
18694
19547
|
const ZONE = 20;
|
|
18695
19548
|
for (let i = 0; i < sections.length; i++) {
|
|
18696
19549
|
const a = sections[i];
|
|
@@ -18719,8 +19572,7 @@ function OhhwellsBridge() {
|
|
|
18719
19572
|
};
|
|
18720
19573
|
const handleMouseMove = (e) => {
|
|
18721
19574
|
const { clientX, clientY } = e;
|
|
18722
|
-
if (
|
|
18723
|
-
if (isOverEditorChrome(clientX, clientY)) {
|
|
19575
|
+
if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY) || isOverEditorChrome(clientX, clientY)) {
|
|
18724
19576
|
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
18725
19577
|
formHoverElRef.current = null;
|
|
18726
19578
|
setFormHoverRect(null);
|
|
@@ -18728,6 +19580,12 @@ function OhhwellsBridge() {
|
|
|
18728
19580
|
setHoveredItemRect(null);
|
|
18729
19581
|
hoveredNavContainerRef.current = null;
|
|
18730
19582
|
setHoveredNavContainerRect(null);
|
|
19583
|
+
siblingHintElRef.current = null;
|
|
19584
|
+
setSiblingHintRect(null);
|
|
19585
|
+
setSiblingHintRects([]);
|
|
19586
|
+
dismissImageHover();
|
|
19587
|
+
clearImageHover();
|
|
19588
|
+
setSectionGap(null);
|
|
18731
19589
|
return;
|
|
18732
19590
|
}
|
|
18733
19591
|
if (probeSocialsRowAt(clientX, clientY)) return;
|
|
@@ -18739,7 +19597,11 @@ function OhhwellsBridge() {
|
|
|
18739
19597
|
if (e.data?.type !== "ow:pointer-sync") return;
|
|
18740
19598
|
const { clientX, clientY } = e.data;
|
|
18741
19599
|
if (typeof clientX !== "number" || typeof clientY !== "number") return;
|
|
18742
|
-
if (
|
|
19600
|
+
if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
|
|
19601
|
+
dismissImageHover();
|
|
19602
|
+
clearImageHover();
|
|
19603
|
+
return;
|
|
19604
|
+
}
|
|
18743
19605
|
if (probeSocialsRowAt(clientX, clientY)) return;
|
|
18744
19606
|
probeSectionGapAt(clientX, clientY);
|
|
18745
19607
|
probeImageAt(clientX, clientY);
|
|
@@ -19022,10 +19884,23 @@ function OhhwellsBridge() {
|
|
|
19022
19884
|
if (e.data?.type !== "ow:hydrate") return;
|
|
19023
19885
|
const content = e.data.content;
|
|
19024
19886
|
if (!content) return;
|
|
19887
|
+
if (typeof content[BRAND_KIT_KEY] === "string") {
|
|
19888
|
+
brandKitRef.current = content[BRAND_KIT_KEY];
|
|
19889
|
+
applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
|
|
19890
|
+
} else {
|
|
19891
|
+
brandKitRef.current = "";
|
|
19892
|
+
applyBrandToDom(null);
|
|
19893
|
+
}
|
|
19025
19894
|
if (typeof content[AI_SECTIONS_KEY] === "string") {
|
|
19026
19895
|
aiSectionsRef.current = content[AI_SECTIONS_KEY];
|
|
19896
|
+
setAiSectionOrder(content[SECTION_ORDER_KEY], window.location.pathname);
|
|
19027
19897
|
applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
|
|
19028
19898
|
}
|
|
19899
|
+
if (typeof content[STYLE_STORE_KEY] === "string") {
|
|
19900
|
+
stylesRef.current = content[STYLE_STORE_KEY];
|
|
19901
|
+
applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
|
|
19902
|
+
}
|
|
19903
|
+
applyBrandChrome(content);
|
|
19029
19904
|
let sectionsJson = null;
|
|
19030
19905
|
for (const [key, val] of Object.entries(content)) {
|
|
19031
19906
|
if (key === "__ohw_sections") {
|
|
@@ -19035,6 +19910,9 @@ function OhhwellsBridge() {
|
|
|
19035
19910
|
if (key === AI_SECTIONS_KEY) continue;
|
|
19036
19911
|
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
19037
19912
|
if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
|
|
19913
|
+
if (key === BRAND_KIT_KEY) continue;
|
|
19914
|
+
if (key === STYLE_STORE_KEY) continue;
|
|
19915
|
+
if (BRAND_CHROME_KEYS.has(key)) continue;
|
|
19038
19916
|
if (applyVideoSettingNode(key, val)) continue;
|
|
19039
19917
|
if (applyCarouselNode(key, val)) continue;
|
|
19040
19918
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
@@ -19048,6 +19926,8 @@ function OhhwellsBridge() {
|
|
|
19048
19926
|
if (video && video.src !== val) applyVideoSrc(video, val);
|
|
19049
19927
|
} else if (el.dataset.ohwEditable === "link") {
|
|
19050
19928
|
applyLinkHref(el, val);
|
|
19929
|
+
} else if (el.dataset.ohwEditable === "icon") {
|
|
19930
|
+
applyIconMarkup(el, val);
|
|
19051
19931
|
} else if (isIconMarkupValue(val)) {
|
|
19052
19932
|
} else {
|
|
19053
19933
|
el.innerHTML = val;
|
|
@@ -19132,12 +20012,21 @@ function OhhwellsBridge() {
|
|
|
19132
20012
|
nodes: collectEditableNodes(editContentRef.current)
|
|
19133
20013
|
});
|
|
19134
20014
|
};
|
|
20015
|
+
const clearInteractionChrome = () => {
|
|
20016
|
+
deactivateRef.current();
|
|
20017
|
+
deselectRef.current();
|
|
20018
|
+
clearMediaSelectionRef.current();
|
|
20019
|
+
};
|
|
19135
20020
|
const handleAiApplyTree = (e) => {
|
|
19136
20021
|
if (e.data?.type !== "ow:ai-apply-tree") return;
|
|
19137
20022
|
const payload = e.data.payload;
|
|
19138
20023
|
if (!payload || typeof payload.id !== "string" || !isRenderableTree(payload.tree)) return;
|
|
20024
|
+
clearInteractionChrome();
|
|
19139
20025
|
const previous = aiSectionsRef.current;
|
|
19140
|
-
const nextState = applyTreeToState(parseAiSectionsState(previous),
|
|
20026
|
+
const nextState = applyTreeToState(parseAiSectionsState(previous), {
|
|
20027
|
+
...payload,
|
|
20028
|
+
path: payload.path ?? window.location.pathname
|
|
20029
|
+
});
|
|
19141
20030
|
const nextValue = serializeAiSectionsState(nextState);
|
|
19142
20031
|
aiSectionsRef.current = nextValue;
|
|
19143
20032
|
applyAiSectionsToDom(nextState);
|
|
@@ -19158,6 +20047,7 @@ function OhhwellsBridge() {
|
|
|
19158
20047
|
const exists = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
|
|
19159
20048
|
if (!exists) return;
|
|
19160
20049
|
if (isPageFrameSection(exists)) return;
|
|
20050
|
+
clearInteractionChrome();
|
|
19161
20051
|
const previous = aiSectionsRef.current;
|
|
19162
20052
|
const nextState = deleteSectionFromState(parseAiSectionsState(previous), sectionId);
|
|
19163
20053
|
const nextValue = serializeAiSectionsState(nextState);
|
|
@@ -19173,8 +20063,10 @@ function OhhwellsBridge() {
|
|
|
19173
20063
|
const handleAiSetSections = (e) => {
|
|
19174
20064
|
if (e.data?.type !== "ow:ai-set-sections") return;
|
|
19175
20065
|
const value = typeof e.data.value === "string" ? e.data.value : "";
|
|
20066
|
+
clearInteractionChrome();
|
|
19176
20067
|
aiSectionsRef.current = value;
|
|
19177
20068
|
applyAiSectionsToDom(parseAiSectionsState(value));
|
|
20069
|
+
applyStylesToDom(parseStyleStore(stylesRef.current));
|
|
19178
20070
|
const restoredHeight = document.body.scrollHeight;
|
|
19179
20071
|
if (restoredHeight > 50) postToParentRef.current({ type: "ow:height", height: restoredHeight });
|
|
19180
20072
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: AI_SECTIONS_KEY, text: value }] });
|
|
@@ -19190,10 +20082,40 @@ function OhhwellsBridge() {
|
|
|
19190
20082
|
if (!entries) return;
|
|
19191
20083
|
const orderJson = JSON.stringify(entries);
|
|
19192
20084
|
editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
|
|
20085
|
+
setAiSectionOrder(orderJson, window.location.pathname);
|
|
19193
20086
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
|
|
19194
20087
|
window.dispatchEvent(new Event("resize"));
|
|
19195
20088
|
};
|
|
19196
20089
|
window.addEventListener("message", handleMoveSection);
|
|
20090
|
+
const handleAiSetBrand = (e) => {
|
|
20091
|
+
if (e.data?.type !== "ow:ai-set-brand") return;
|
|
20092
|
+
const value = typeof e.data.value === "string" ? e.data.value : "";
|
|
20093
|
+
const previous = brandKitRef.current;
|
|
20094
|
+
brandKitRef.current = value;
|
|
20095
|
+
applyBrandToDom(parseBrandKit(value));
|
|
20096
|
+
if (aiSectionsRef.current) applyAiSectionsToDom(parseAiSectionsState(aiSectionsRef.current));
|
|
20097
|
+
applyStylesToDom(parseStyleStore(stylesRef.current));
|
|
20098
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: BRAND_KIT_KEY, text: value }] });
|
|
20099
|
+
postToParentRef.current({ type: "ow:ai-brand-applied", previous, value });
|
|
20100
|
+
};
|
|
20101
|
+
window.addEventListener("message", handleAiSetBrand);
|
|
20102
|
+
const handleAiSetStyles = (e) => {
|
|
20103
|
+
if (e.data?.type !== "ow:ai-set-styles") return;
|
|
20104
|
+
const value = typeof e.data.value === "string" ? e.data.value : "";
|
|
20105
|
+
const previous = stylesRef.current;
|
|
20106
|
+
stylesRef.current = value;
|
|
20107
|
+
applyStylesToDom(parseStyleStore(value));
|
|
20108
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: STYLE_STORE_KEY, text: value }] });
|
|
20109
|
+
postToParentRef.current({ type: "ow:ai-styles-applied", previous, value });
|
|
20110
|
+
};
|
|
20111
|
+
window.addEventListener("message", handleAiSetStyles);
|
|
20112
|
+
const handleGetBrand = (e) => {
|
|
20113
|
+
if (e.data?.type !== "ow:get-brand") return;
|
|
20114
|
+
const template = deriveTemplateBrand();
|
|
20115
|
+
const value = brandKitRef.current || (template ? JSON.stringify(template) : "");
|
|
20116
|
+
postToParentRef.current({ type: "ow:brand-value", value });
|
|
20117
|
+
};
|
|
20118
|
+
window.addEventListener("message", handleGetBrand);
|
|
19197
20119
|
const handlePanelDragging = (e) => {
|
|
19198
20120
|
if (e.data?.type !== "ow:panel-dragging") return;
|
|
19199
20121
|
if (e.data.dragging) document.documentElement.setAttribute("data-ohw-panel-dragging", "");
|
|
@@ -19251,8 +20173,15 @@ function OhhwellsBridge() {
|
|
|
19251
20173
|
closeLinkPopoverRef.current();
|
|
19252
20174
|
return;
|
|
19253
20175
|
}
|
|
20176
|
+
if (floatingPanelOpenRef.current) {
|
|
20177
|
+
setFloatingPanelRef.current(null);
|
|
20178
|
+
deselectRef.current();
|
|
20179
|
+
deactivateRef.current();
|
|
20180
|
+
return;
|
|
20181
|
+
}
|
|
19254
20182
|
deselectRef.current();
|
|
19255
20183
|
deactivateRef.current();
|
|
20184
|
+
clearMediaSelectionRef.current();
|
|
19256
20185
|
};
|
|
19257
20186
|
window.addEventListener("message", handleDeactivate);
|
|
19258
20187
|
const handleToastAction = (e) => {
|
|
@@ -19338,6 +20267,10 @@ function OhhwellsBridge() {
|
|
|
19338
20267
|
const handleKeyDown = (e) => {
|
|
19339
20268
|
if (e.key === "Escape" && document.querySelector("[data-ohw-section-picker]")) return;
|
|
19340
20269
|
if (e.key === "Escape" && document.querySelector("[data-ohw-more-menu]")) return;
|
|
20270
|
+
if (e.key === "Escape" && selectedMediaElRef.current) {
|
|
20271
|
+
clearMediaSelectionRef.current();
|
|
20272
|
+
return;
|
|
20273
|
+
}
|
|
19341
20274
|
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "a" && activeElRef.current) {
|
|
19342
20275
|
e.preventDefault();
|
|
19343
20276
|
selectAllTextInEditable(activeElRef.current);
|
|
@@ -19497,6 +20430,12 @@ function OhhwellsBridge() {
|
|
|
19497
20430
|
if (aiSectionsRef.current) {
|
|
19498
20431
|
nodes.push({ key: AI_SECTIONS_KEY, type: "sections", text: aiSectionsRef.current });
|
|
19499
20432
|
}
|
|
20433
|
+
if (stylesRef.current) {
|
|
20434
|
+
nodes.push({ key: STYLE_STORE_KEY, type: "styles", text: stylesRef.current });
|
|
20435
|
+
}
|
|
20436
|
+
if (brandKitRef.current) {
|
|
20437
|
+
nodes.push({ key: BRAND_KIT_KEY, type: "brand", text: brandKitRef.current });
|
|
20438
|
+
}
|
|
19500
20439
|
document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
|
|
19501
20440
|
const formKey = formKeyOf(form);
|
|
19502
20441
|
if (!formKey) return;
|
|
@@ -19514,8 +20453,12 @@ function OhhwellsBridge() {
|
|
|
19514
20453
|
if (inserted) {
|
|
19515
20454
|
const tracker = getSectionsTracker();
|
|
19516
20455
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
|
|
19517
|
-
const
|
|
19518
|
-
|
|
20456
|
+
const reportHeight = () => {
|
|
20457
|
+
const h = document.body.scrollHeight;
|
|
20458
|
+
if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
|
|
20459
|
+
};
|
|
20460
|
+
reportHeight();
|
|
20461
|
+
setTimeout(reportHeight, 500);
|
|
19519
20462
|
}
|
|
19520
20463
|
};
|
|
19521
20464
|
const handleSwitchSchedule = (e) => {
|
|
@@ -19912,13 +20855,16 @@ function OhhwellsBridge() {
|
|
|
19912
20855
|
window.removeEventListener("message", handleAiDeleteSection);
|
|
19913
20856
|
window.removeEventListener("message", handleAiSetSections);
|
|
19914
20857
|
window.removeEventListener("message", handleMoveSection);
|
|
20858
|
+
window.removeEventListener("message", handleAiSetBrand);
|
|
20859
|
+
window.removeEventListener("message", handleAiSetStyles);
|
|
20860
|
+
window.removeEventListener("message", handleGetBrand);
|
|
19915
20861
|
window.removeEventListener("message", handlePanelDragging);
|
|
19916
20862
|
window.removeEventListener("message", handleDeleteSection);
|
|
19917
20863
|
window.removeEventListener("message", handleDeactivate);
|
|
19918
|
-
document.documentElement.removeAttribute("data-ohw-panel-dragging");
|
|
19919
20864
|
window.removeEventListener("message", handleToastAction);
|
|
19920
20865
|
window.removeEventListener("message", handleFormCount);
|
|
19921
20866
|
window.removeEventListener("message", handleUiEscape);
|
|
20867
|
+
document.documentElement.removeAttribute("data-ohw-panel-dragging");
|
|
19922
20868
|
autoSaveTimers.current.forEach(clearTimeout);
|
|
19923
20869
|
autoSaveTimers.current.clear();
|
|
19924
20870
|
if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
|
|
@@ -20121,7 +21067,7 @@ function OhhwellsBridge() {
|
|
|
20121
21067
|
postToParent2({
|
|
20122
21068
|
type: "ow:ready",
|
|
20123
21069
|
version: "1",
|
|
20124
|
-
bridgeVersion: "0.1.
|
|
21070
|
+
bridgeVersion: "0.1.78",
|
|
20125
21071
|
path: pathname,
|
|
20126
21072
|
nodes: collectEditableNodes(editContentRef.current),
|
|
20127
21073
|
sections
|
|
@@ -20528,11 +21474,22 @@ function OhhwellsBridge() {
|
|
|
20528
21474
|
const showEditLink = toolbarShowEditLink;
|
|
20529
21475
|
const currentSections = sectionsByPath[pathname] ?? [];
|
|
20530
21476
|
linkPopoverOpenRef.current = linkPopover !== null;
|
|
21477
|
+
const handleMediaSelect = useCallback8((key) => {
|
|
21478
|
+
const el = hoveredImageRef.current?.dataset.ohwKey === key ? hoveredImageRef.current : Array.from(document.querySelectorAll(MEDIA_SELECTOR)).find(
|
|
21479
|
+
(m) => (m.dataset.ohwKey ?? "") === key
|
|
21480
|
+
) ?? null;
|
|
21481
|
+
if (!el) return;
|
|
21482
|
+
selectMediaElementRef.current(el);
|
|
21483
|
+
}, []);
|
|
20531
21484
|
const handleMediaReplace = useCallback8(
|
|
20532
21485
|
(key) => {
|
|
20533
|
-
postToParent2({
|
|
21486
|
+
postToParent2({
|
|
21487
|
+
type: "ow:image-pick",
|
|
21488
|
+
key,
|
|
21489
|
+
elementType: mediaHover?.elementType ?? selectedMedia?.elementType ?? "image"
|
|
21490
|
+
});
|
|
20534
21491
|
},
|
|
20535
|
-
[postToParent2, mediaHover?.elementType]
|
|
21492
|
+
[postToParent2, mediaHover?.elementType, selectedMedia?.elementType]
|
|
20536
21493
|
);
|
|
20537
21494
|
const handleEditCarousel = useCallback8(
|
|
20538
21495
|
(key) => {
|
|
@@ -20604,12 +21561,25 @@ function OhhwellsBridge() {
|
|
|
20604
21561
|
},
|
|
20605
21562
|
`uploading-${key}`
|
|
20606
21563
|
)),
|
|
20607
|
-
mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ jsx33(
|
|
21564
|
+
mediaHover && !(mediaHover.key in uploadingRects) && mediaHover.key !== selectedMedia?.key && /* @__PURE__ */ jsx33(
|
|
20608
21565
|
MediaOverlay,
|
|
20609
21566
|
{
|
|
20610
21567
|
hover: mediaHover,
|
|
20611
21568
|
isUploading: false,
|
|
20612
21569
|
onReplace: handleMediaReplace,
|
|
21570
|
+
onSelect: handleMediaSelect,
|
|
21571
|
+
onVideoSettingsChange: handleVideoSettingsChange
|
|
21572
|
+
}
|
|
21573
|
+
),
|
|
21574
|
+
selectedMedia && !(selectedMedia.key in uploadingRects) && !linkPopover && /* @__PURE__ */ jsx33(
|
|
21575
|
+
MediaOverlay,
|
|
21576
|
+
{
|
|
21577
|
+
hover: selectedMedia,
|
|
21578
|
+
selected: true,
|
|
21579
|
+
hovered: mediaHover?.key === selectedMedia.key,
|
|
21580
|
+
isUploading: false,
|
|
21581
|
+
onReplace: handleMediaReplace,
|
|
21582
|
+
onSelect: handleMediaSelect,
|
|
20613
21583
|
onVideoSettingsChange: handleVideoSettingsChange
|
|
20614
21584
|
}
|
|
20615
21585
|
),
|
|
@@ -21015,6 +21985,59 @@ function OhhwellsBridge() {
|
|
|
21015
21985
|
) : null
|
|
21016
21986
|
] });
|
|
21017
21987
|
}
|
|
21988
|
+
|
|
21989
|
+
// src/ui/EmptySection.tsx
|
|
21990
|
+
import Link3 from "next/link";
|
|
21991
|
+
import { Fragment as Fragment9, jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
21992
|
+
function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey }) {
|
|
21993
|
+
return /* @__PURE__ */ jsxs21(Fragment9, { children: [
|
|
21994
|
+
/* @__PURE__ */ jsx34(
|
|
21995
|
+
"p",
|
|
21996
|
+
{
|
|
21997
|
+
style: {
|
|
21998
|
+
fontFamily: "var(--brand-font-body)",
|
|
21999
|
+
fontSize: "0.75rem",
|
|
22000
|
+
fontWeight: 500,
|
|
22001
|
+
letterSpacing: "0.15em",
|
|
22002
|
+
textTransform: "uppercase",
|
|
22003
|
+
color: "var(--brand-accent)",
|
|
22004
|
+
marginBottom: "1.5rem"
|
|
22005
|
+
},
|
|
22006
|
+
children: /* @__PURE__ */ jsx34(Link3, { href: homeHref, style: { color: "inherit" }, children: /* @__PURE__ */ jsx34("span", { ...eyebrowKey ? { "data-ohw-editable": "text", "data-ohw-key": eyebrowKey } : {}, children: "Home" }) })
|
|
22007
|
+
}
|
|
22008
|
+
),
|
|
22009
|
+
/* @__PURE__ */ jsx34(
|
|
22010
|
+
"h1",
|
|
22011
|
+
{
|
|
22012
|
+
style: {
|
|
22013
|
+
fontFamily: "var(--brand-font-heading)",
|
|
22014
|
+
fontSize: "clamp(2rem, 3.5vw, 2.75rem)",
|
|
22015
|
+
lineHeight: 1.1,
|
|
22016
|
+
letterSpacing: "-0.025em",
|
|
22017
|
+
color: "var(--brand-text)",
|
|
22018
|
+
marginBottom: "1rem"
|
|
22019
|
+
},
|
|
22020
|
+
...titleKey ? { "data-ohw-editable": "text", "data-ohw-key": titleKey, "data-ohw-max-length": 80 } : {},
|
|
22021
|
+
children: title
|
|
22022
|
+
}
|
|
22023
|
+
),
|
|
22024
|
+
/* @__PURE__ */ jsx34(
|
|
22025
|
+
"p",
|
|
22026
|
+
{
|
|
22027
|
+
style: {
|
|
22028
|
+
fontFamily: "var(--brand-font-body)",
|
|
22029
|
+
fontSize: "1rem",
|
|
22030
|
+
lineHeight: 1.7,
|
|
22031
|
+
fontWeight: 300,
|
|
22032
|
+
color: "var(--brand-text-muted)",
|
|
22033
|
+
maxWidth: "340px"
|
|
22034
|
+
},
|
|
22035
|
+
...subtitleKey ? { "data-ohw-editable": "text", "data-ohw-key": subtitleKey, "data-ohw-max-length": 160 } : {},
|
|
22036
|
+
children: "This page doesn't have any content yet."
|
|
22037
|
+
}
|
|
22038
|
+
)
|
|
22039
|
+
] });
|
|
22040
|
+
}
|
|
21018
22041
|
export {
|
|
21019
22042
|
AI_DEFAULT_BRAND,
|
|
21020
22043
|
AI_TREE_SCHEMA_VERSIONS,
|
|
@@ -21031,6 +22054,7 @@ export {
|
|
|
21031
22054
|
DropdownMenuItem,
|
|
21032
22055
|
DropdownMenuSeparator,
|
|
21033
22056
|
DropdownMenuTrigger,
|
|
22057
|
+
EmptySection,
|
|
21034
22058
|
ItemActionToolbar,
|
|
21035
22059
|
ItemInteractionLayer,
|
|
21036
22060
|
LinkEditorPanel,
|