@ohhwells/bridge 0.1.126-next.349 → 0.1.127-next.351
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 +720 -635
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -4
- package/dist/index.d.ts +18 -4
- package/dist/index.js +720 -635
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -120,6 +120,12 @@ function enforceLinkHrefs() {
|
|
|
120
120
|
|
|
121
121
|
// src/lib/ai-tree.ts
|
|
122
122
|
var AI_TREE_SCHEMA_VERSIONS = [1];
|
|
123
|
+
function legacyVerticalPaddingAlias(spacing) {
|
|
124
|
+
if (spacing === "tight") return "s";
|
|
125
|
+
if (spacing === "balanced") return "m";
|
|
126
|
+
if (spacing === "airy") return "l";
|
|
127
|
+
return void 0;
|
|
128
|
+
}
|
|
123
129
|
var BUTTON_FILL_TOKENS = ["dark", "primary", "accent", "light"];
|
|
124
130
|
var BUTTON_STYLES = ["solid", "outline", "ghost"];
|
|
125
131
|
var BUTTON_RADII = ["square", "rounded", "pill"];
|
|
@@ -142,7 +148,10 @@ var AI_TREE_TOKENS = {
|
|
|
142
148
|
radiusButton: 10,
|
|
143
149
|
radiusCard: 18,
|
|
144
150
|
sectionMaxWidth: 1280,
|
|
145
|
-
|
|
151
|
+
/** 6-step vertical-padding scale (OHH-789) — replaces the deprecated tight/balanced/airy
|
|
152
|
+
* map; `m` keeps the old `balanced` value (64) so already-generated sites render unchanged.
|
|
153
|
+
* `none` is the true zero floor for an explicit "remove the padding" ask. */
|
|
154
|
+
sectionPaddingByScale: { none: 0, xs: 24, s: 40, m: 64, l: 96, xl: 128 },
|
|
146
155
|
type: {
|
|
147
156
|
display: { size: 56, line: 1.1, weight: 700 },
|
|
148
157
|
h2: { size: 40, line: 1.15, weight: 700 },
|
|
@@ -160,177 +169,538 @@ function isRenderableTree(value) {
|
|
|
160
169
|
return AI_TREE_SCHEMA_VERSIONS.includes(tree.schemaVersion) && tree.type === "section" && Array.isArray(tree.rows);
|
|
161
170
|
}
|
|
162
171
|
|
|
163
|
-
// src/lib/
|
|
164
|
-
var
|
|
165
|
-
var
|
|
166
|
-
function
|
|
167
|
-
return
|
|
168
|
-
}
|
|
169
|
-
var EMPTY_AI_SECTIONS = { v: 1, sections: [] };
|
|
170
|
-
function parseAiSectionsState(raw) {
|
|
171
|
-
if (!raw) return EMPTY_AI_SECTIONS;
|
|
172
|
+
// src/lib/section-styles.ts
|
|
173
|
+
var STYLE_STORE_KEY = "__ohw_styles";
|
|
174
|
+
var STYLE_SHEET_ID = "ohw-section-styles";
|
|
175
|
+
function parseStyleStore(raw) {
|
|
176
|
+
if (!raw) return null;
|
|
172
177
|
try {
|
|
173
178
|
const parsed = JSON.parse(raw);
|
|
174
|
-
if (parsed?.v !== 1
|
|
175
|
-
const sections = parsed.sections.filter(
|
|
176
|
-
(entry) => typeof entry === "object" && entry !== null && typeof entry.id === "string" && entry.id.length > 0 && isRenderableTree(entry.tree)
|
|
177
|
-
).map((entry) => ({
|
|
178
|
-
...entry,
|
|
179
|
-
label: typeof entry.label === "string" ? entry.label : "Generated section",
|
|
180
|
-
media: entry.media && typeof entry.media === "object" ? entry.media : {}
|
|
181
|
-
}));
|
|
182
|
-
const removed = Array.isArray(parsed.removed) ? parsed.removed.filter((id) => typeof id === "string" && id.length > 0) : [];
|
|
179
|
+
if (parsed?.v !== 1) return null;
|
|
183
180
|
return {
|
|
184
181
|
v: 1,
|
|
185
|
-
sections,
|
|
186
|
-
|
|
187
|
-
...parsed.hideTemplate === true ? { hideTemplate: true } : {}
|
|
182
|
+
sections: typeof parsed.sections === "object" && parsed.sections ? parsed.sections : {},
|
|
183
|
+
nodes: typeof parsed.nodes === "object" && parsed.nodes ? parsed.nodes : {}
|
|
188
184
|
};
|
|
189
185
|
} catch {
|
|
190
|
-
return
|
|
186
|
+
return null;
|
|
191
187
|
}
|
|
192
188
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
...selfEdit ? priorEntry?.beforeSection ? { beforeSection: priorEntry.beforeSection } : {} : insertBefore && payload.targetSectionId ? { beforeSection: payload.targetSectionId } : {},
|
|
206
|
-
...selfEdit ? priorEntry?.replaces ? { replaces: priorEntry.replaces } : {} : payload.mode === "replace" && payload.targetSectionId ? { replaces: payload.targetSectionId } : {},
|
|
207
|
-
tree: payload.tree,
|
|
208
|
-
media: payload.media ?? {},
|
|
209
|
-
...payload.brand && typeof payload.brand === "object" ? { brand: payload.brand } : {}
|
|
189
|
+
var BG_VALUES = {
|
|
190
|
+
surface: "color-mix(in srgb, var(--ohw-brand-light, var(--color-light, #ECECEB)) 94%, var(--ohw-brand-dark, var(--color-dark, #0C0A09)))",
|
|
191
|
+
accent: "var(--ohw-brand-primary, var(--color-primary, #0078E5))",
|
|
192
|
+
"accent-soft": "color-mix(in srgb, var(--ohw-brand-primary, var(--color-primary, #0078E5)) 12%, var(--ohw-brand-light, var(--color-light, #FFFFFF)))"
|
|
193
|
+
};
|
|
194
|
+
var NAVBAR_COLOR_TOKENS = ["dark", "primary", "accent", "light"];
|
|
195
|
+
function navbarColorVar(token) {
|
|
196
|
+
const fallback = {
|
|
197
|
+
dark: "#0C0A09",
|
|
198
|
+
primary: "#0078E5",
|
|
199
|
+
accent: "#0078E5",
|
|
200
|
+
light: "#FFFFFF"
|
|
210
201
|
};
|
|
211
|
-
|
|
212
|
-
return { ...state, v: 1, sections: [...others, entry] };
|
|
202
|
+
return `var(--ohw-brand-${token}, var(--color-${token}, ${fallback[token]}))`;
|
|
213
203
|
}
|
|
214
|
-
function
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
...entry.tree,
|
|
224
|
-
rows: entry.tree.rows.map((row) => ({ ...row, blocks: row.blocks.map((block) => ({ ...block })) }))
|
|
225
|
-
};
|
|
226
|
-
nextTrees.set(id, fresh);
|
|
227
|
-
return fresh;
|
|
228
|
-
};
|
|
229
|
-
const nodes = {};
|
|
230
|
-
for (const [key, override] of Object.entries(store.nodes)) {
|
|
231
|
-
const match = override.align !== void 0 && key.startsWith(AI_SLOT_KEY_PREFIX) ? key.match(/^ai\.(.+?)\.r(\d+)\.b(\d+)(?:\.|$)/) : null;
|
|
232
|
-
const tree = match ? treeFor(match[1]) : void 0;
|
|
233
|
-
const block = match && tree ? tree.rows[Number(match[2])]?.blocks[Number(match[3])] : void 0;
|
|
234
|
-
if (!block) {
|
|
235
|
-
nodes[key] = override;
|
|
236
|
-
continue;
|
|
237
|
-
}
|
|
238
|
-
block.align = override.align;
|
|
239
|
-
const rest = { ...override };
|
|
240
|
-
delete rest.align;
|
|
241
|
-
if (Object.keys(rest).length > 0) nodes[key] = rest;
|
|
204
|
+
function resolveNavbarValue(value) {
|
|
205
|
+
if (value === void 0) return void 0;
|
|
206
|
+
return NAVBAR_COLOR_TOKENS.includes(value) ? navbarColorVar(value) : value;
|
|
207
|
+
}
|
|
208
|
+
var HEADLINE_SIZES = { md: 24, lg: 32, xl: 40, display: 56 };
|
|
209
|
+
function styleSheetCss() {
|
|
210
|
+
const rules = [];
|
|
211
|
+
for (const [tone, value] of Object.entries(BG_VALUES)) {
|
|
212
|
+
rules.push(`[data-ohw-style-bg="${tone}"] { background: ${value} !important; }`);
|
|
242
213
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
delete rest.align;
|
|
255
|
-
if (Object.keys(rest).length > 0) sections[sectionId] = rest;
|
|
214
|
+
rules.push(
|
|
215
|
+
`[data-ohw-style-bg="accent"] { color: var(--ohw-brand-light, var(--color-light, #FFFFFF)) !important; }`
|
|
216
|
+
);
|
|
217
|
+
rules.push(
|
|
218
|
+
`[data-ohw-style-distribution="space-between"] { display: flex !important; flex-direction: column; justify-content: space-between; }`,
|
|
219
|
+
`[data-ohw-style-distribution="center"] { display: flex !important; flex-direction: column; justify-content: center; }`
|
|
220
|
+
);
|
|
221
|
+
for (const [scale, size] of Object.entries(HEADLINE_SIZES)) {
|
|
222
|
+
rules.push(
|
|
223
|
+
`[data-ohw-style-headline="${scale}"] :is(h1, h2, h3) { font-size: ${size}px !important; line-height: 1.15 !important; }`
|
|
224
|
+
);
|
|
256
225
|
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
return tree ? { ...entry, tree } : entry;
|
|
265
|
-
})
|
|
266
|
-
},
|
|
267
|
-
store: { v: 1, sections, nodes },
|
|
268
|
-
changed: true
|
|
269
|
-
};
|
|
270
|
-
}
|
|
271
|
-
function removeFromState(state, id) {
|
|
272
|
-
return { ...state, v: 1, sections: state.sections.filter((entry) => entry.id !== id) };
|
|
273
|
-
}
|
|
274
|
-
function deleteSectionFromState(state, sectionId) {
|
|
275
|
-
if (state.sections.some((entry) => entry.id === sectionId)) {
|
|
276
|
-
return removeFromState(state, sectionId);
|
|
226
|
+
rules.push(
|
|
227
|
+
`[data-ohw-style-aspect="fill-height"] img { height: 100% !important; aspect-ratio: auto !important; object-fit: cover; }`
|
|
228
|
+
);
|
|
229
|
+
for (const aspect of ["1:1", "4:5", "3:4", "3:2", "16:9", "2:1", "3:1"]) {
|
|
230
|
+
rules.push(
|
|
231
|
+
`[data-ohw-style-aspect="${aspect.replace(":", "-")}"] img { aspect-ratio: ${aspect.replace(":", " / ")} !important; height: auto !important; object-fit: cover; }`
|
|
232
|
+
);
|
|
277
233
|
}
|
|
278
|
-
const
|
|
279
|
-
|
|
280
|
-
|
|
234
|
+
const pad = { tight: 40, balanced: 64, airy: 96 };
|
|
235
|
+
for (const [spacing, px2] of Object.entries(pad)) {
|
|
236
|
+
rules.push(
|
|
237
|
+
`[data-ohw-style-spacing="${spacing}"] { padding-top: ${px2}px !important; padding-bottom: ${px2}px !important; }`
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
for (const [step, px2] of Object.entries(AI_TREE_TOKENS.sectionPaddingByScale)) {
|
|
241
|
+
const vpCss = px2 === 0 ? "0px" : `clamp(${Math.max(16, Math.round(px2 * 0.5))}px, ${(px2 / 9).toFixed(2)}vw, ${px2}px)`;
|
|
242
|
+
rules.push(
|
|
243
|
+
`[data-ohw-style-vpadding="${step}"] { padding-top: ${vpCss} !important; padding-bottom: ${vpCss} !important; }`
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
rules.push(
|
|
247
|
+
`[data-ohw-style-corners="sharp"] :is(.card, [data-ohw-card], img, picture, figure, [data-ohw-editable="bg-image"]) { border-radius: 0 !important; }`,
|
|
248
|
+
`[data-ohw-style-corners="sharp"] :has(> img) { border-radius: 0 !important; }`,
|
|
249
|
+
`[data-ohw-style-corners="rounded"] :is(.card, [data-ohw-card], img, picture, figure, [data-ohw-editable="bg-image"]) { border-radius: max(0.75rem, var(--radius, 0px)) !important; }`,
|
|
250
|
+
`[data-ohw-style-corners="rounded"] :has(> img) { border-radius: max(0.75rem, var(--radius, 0px)) !important; }`
|
|
251
|
+
);
|
|
252
|
+
for (const align of ["left", "center", "right"]) {
|
|
253
|
+
rules.push(`[data-ohw-style-align="${align}"] { text-align: ${align} !important; }`);
|
|
254
|
+
}
|
|
255
|
+
return rules.join("\n");
|
|
281
256
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
const
|
|
285
|
-
|
|
286
|
-
|
|
257
|
+
var STYLE_FONT_LINK_ID = "ohw-style-fonts";
|
|
258
|
+
function loadStyleFonts(families) {
|
|
259
|
+
const unique = [...new Set(families.map((f) => f.trim()).filter(Boolean))];
|
|
260
|
+
const existing = document.getElementById(STYLE_FONT_LINK_ID);
|
|
261
|
+
if (unique.length === 0) {
|
|
262
|
+
existing?.remove();
|
|
263
|
+
return;
|
|
287
264
|
}
|
|
288
|
-
const
|
|
289
|
-
const
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
if (store) {
|
|
297
|
-
const sections = {};
|
|
298
|
-
for (const [key, override] of Object.entries(store.sections)) {
|
|
299
|
-
if (!reaped.has(key)) sections[key] = override;
|
|
300
|
-
}
|
|
301
|
-
const nodes = {};
|
|
302
|
-
for (const [key, override] of Object.entries(store.nodes)) {
|
|
303
|
-
if (!slotPrefixes.some((prefix) => key.startsWith(prefix))) nodes[key] = override;
|
|
304
|
-
}
|
|
305
|
-
nextStore = { v: 1, sections, nodes };
|
|
265
|
+
const spec = unique.map((f) => `${f.replace(/ /g, "+")}:400,500,600,700`).join("|");
|
|
266
|
+
const href = `https://fonts.googleapis.com/css?family=${spec}&display=swap`;
|
|
267
|
+
let link = existing;
|
|
268
|
+
if (!link) {
|
|
269
|
+
link = document.createElement("link");
|
|
270
|
+
link.id = STYLE_FONT_LINK_ID;
|
|
271
|
+
link.rel = "stylesheet";
|
|
272
|
+
document.head.appendChild(link);
|
|
306
273
|
}
|
|
307
|
-
|
|
274
|
+
if (link.href !== href) link.href = href;
|
|
308
275
|
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
"
|
|
314
|
-
"
|
|
315
|
-
"
|
|
316
|
-
"
|
|
317
|
-
"
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
var
|
|
321
|
-
"
|
|
322
|
-
"
|
|
323
|
-
"
|
|
324
|
-
"
|
|
325
|
-
"
|
|
326
|
-
"
|
|
327
|
-
"threads",
|
|
328
|
-
"pinterest",
|
|
329
|
-
"whatsapp"
|
|
276
|
+
var SECTION_ATTRS = {
|
|
277
|
+
sectionBackground: "data-ohw-style-bg",
|
|
278
|
+
textDistribution: "data-ohw-style-distribution",
|
|
279
|
+
headlineScale: "data-ohw-style-headline",
|
|
280
|
+
imageAspect: "data-ohw-style-aspect",
|
|
281
|
+
spacing: "data-ohw-style-spacing",
|
|
282
|
+
verticalPadding: "data-ohw-style-vpadding",
|
|
283
|
+
cornerStyle: "data-ohw-style-corners",
|
|
284
|
+
align: "data-ohw-style-align"
|
|
285
|
+
};
|
|
286
|
+
var NODE_WROTE_ATTR = "data-ohw-style-node";
|
|
287
|
+
var NODE_PROPS = [
|
|
288
|
+
"color",
|
|
289
|
+
"font-family",
|
|
290
|
+
"font-size",
|
|
291
|
+
"text-align",
|
|
292
|
+
"justify-content",
|
|
293
|
+
"align-items"
|
|
330
294
|
];
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
295
|
+
var ALIGN_JUSTIFY = {
|
|
296
|
+
left: "flex-start",
|
|
297
|
+
center: "center",
|
|
298
|
+
right: "flex-end"
|
|
299
|
+
};
|
|
300
|
+
function saveInline(el, prop) {
|
|
301
|
+
const attr = `data-ohw-style-prev-${prop}`;
|
|
302
|
+
if (el.hasAttribute(attr)) return;
|
|
303
|
+
const value = el.style.getPropertyValue(prop) || (prop === "background" ? el.style.getPropertyValue("background-color") : "");
|
|
304
|
+
el.setAttribute(attr, value);
|
|
305
|
+
}
|
|
306
|
+
function restoreInline(el, prop) {
|
|
307
|
+
const attr = `data-ohw-style-prev-${prop}`;
|
|
308
|
+
if (!el.hasAttribute(attr)) return;
|
|
309
|
+
const prev = el.getAttribute(attr) ?? "";
|
|
310
|
+
if (prev) el.style.setProperty(prop, prev);
|
|
311
|
+
else el.style.removeProperty(prop);
|
|
312
|
+
el.removeAttribute(attr);
|
|
313
|
+
}
|
|
314
|
+
function ensureStyleSheet() {
|
|
315
|
+
let el = document.getElementById(STYLE_SHEET_ID);
|
|
316
|
+
if (!el) {
|
|
317
|
+
el = document.createElement("style");
|
|
318
|
+
el.id = STYLE_SHEET_ID;
|
|
319
|
+
document.head.appendChild(el);
|
|
320
|
+
}
|
|
321
|
+
const css = styleSheetCss();
|
|
322
|
+
if (el.textContent !== css) el.textContent = css;
|
|
323
|
+
}
|
|
324
|
+
function clearSectionAttrs(root) {
|
|
325
|
+
for (const attr of Object.values(SECTION_ATTRS)) {
|
|
326
|
+
for (const el of Array.from(root.querySelectorAll(`[${attr}]`))) el.removeAttribute(attr);
|
|
327
|
+
}
|
|
328
|
+
for (const el of Array.from(root.querySelectorAll("[data-ohw-style-bgcolor]"))) {
|
|
329
|
+
restoreInline(el, "background");
|
|
330
|
+
el.removeAttribute("data-ohw-style-bgcolor");
|
|
331
|
+
}
|
|
332
|
+
for (const el of Array.from(root.querySelectorAll("[data-ohw-style-minheight]"))) {
|
|
333
|
+
restoreInline(el, "min-height");
|
|
334
|
+
el.removeAttribute("data-ohw-style-minheight");
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
function clearNodeProps(root) {
|
|
338
|
+
for (const el of Array.from(root.querySelectorAll(`[${NODE_WROTE_ATTR}]`))) {
|
|
339
|
+
const h = el;
|
|
340
|
+
for (const prop of NODE_PROPS) restoreInline(h, prop);
|
|
341
|
+
h.removeAttribute(NODE_WROTE_ATTR);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
function alignSubjectOf(el) {
|
|
345
|
+
const button = el.closest('[data-ohw-role="button"]');
|
|
346
|
+
if (button?.parentElement) return button.parentElement;
|
|
347
|
+
let subject = el;
|
|
348
|
+
while (subject.parentElement && !subject.parentElement.hasAttribute("data-ohw-section") && /^inline/.test(getComputedStyle(subject).display)) {
|
|
349
|
+
subject = subject.parentElement;
|
|
350
|
+
}
|
|
351
|
+
const TEXT_ROW = /^(P|LI|H[1-6]|BLOCKQUOTE|FIGCAPTION|DT|DD)$/;
|
|
352
|
+
const parent = subject.parentElement;
|
|
353
|
+
if (parent && TEXT_ROW.test(parent.tagName) && /^(inline-)?flex$/.test(getComputedStyle(parent).display)) {
|
|
354
|
+
return parent;
|
|
355
|
+
}
|
|
356
|
+
if (parent && !parent.hasAttribute("data-ohw-section")) {
|
|
357
|
+
const pcs = getComputedStyle(parent);
|
|
358
|
+
const children = Array.from(parent.children);
|
|
359
|
+
const textRowChild = (c) => c.hasAttribute("data-ohw-editable") || (c.textContent ?? "").trim().length === 0 || c.getAttribute("aria-hidden") === "true";
|
|
360
|
+
if (/^(inline-)?flex$/.test(pcs.display) && pcs.flexDirection.startsWith("row") && children.length > 0 && children.some((c) => c.hasAttribute("data-ohw-editable")) && children.every(textRowChild)) {
|
|
361
|
+
return parent;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
const row = subject.closest("li");
|
|
365
|
+
if (row && row !== subject && /^(inline-)?flex$/.test(getComputedStyle(row).display)) {
|
|
366
|
+
return row;
|
|
367
|
+
}
|
|
368
|
+
return subject;
|
|
369
|
+
}
|
|
370
|
+
function applyStylesToDom(store) {
|
|
371
|
+
ensureStyleSheet();
|
|
372
|
+
clearSectionAttrs(document);
|
|
373
|
+
clearNodeProps(document);
|
|
374
|
+
loadStyleFonts(
|
|
375
|
+
store ? [
|
|
376
|
+
...Object.values(store.nodes).flatMap((n) => n.fontFamily ? [n.fontFamily] : []),
|
|
377
|
+
...Object.values(store.sections).flatMap((s) => s.fontFamily ? [s.fontFamily] : [])
|
|
378
|
+
] : []
|
|
379
|
+
);
|
|
380
|
+
if (!store) return;
|
|
381
|
+
for (const [sectionId, override] of Object.entries(store.sections)) {
|
|
382
|
+
const escaped = CSS.escape(sectionId);
|
|
383
|
+
const byInstance = document.querySelectorAll(`[data-ohw-instance="${escaped}"]`);
|
|
384
|
+
const sections = Array.from(
|
|
385
|
+
byInstance.length ? byInstance : document.querySelectorAll(`[data-ohw-section="${escaped}"]`)
|
|
386
|
+
);
|
|
387
|
+
const isNavbar = sectionId === "navbar";
|
|
388
|
+
if (isNavbar) {
|
|
389
|
+
const drawer = document.querySelector("[data-ohw-nav-drawer]");
|
|
390
|
+
if (drawer && !sections.includes(drawer)) sections.push(drawer);
|
|
391
|
+
const header = document.querySelector("[data-ohw-nav-root]");
|
|
392
|
+
if (header && !sections.includes(header)) sections.push(header);
|
|
393
|
+
}
|
|
394
|
+
for (const marker of Array.from(sections)) {
|
|
395
|
+
const isDrawerMarker = isNavbar && marker.hasAttribute("data-ohw-nav-drawer");
|
|
396
|
+
const section = marker.querySelector(":scope > [data-ai-section]") ?? marker;
|
|
397
|
+
for (const [prop, attr] of Object.entries(SECTION_ATTRS)) {
|
|
398
|
+
const value = override[prop];
|
|
399
|
+
if (value === void 0) continue;
|
|
400
|
+
if (prop === "sectionBackground" && override.sectionBackgroundColor !== void 0) continue;
|
|
401
|
+
section.setAttribute(attr, String(value).replace(":", "-"));
|
|
402
|
+
}
|
|
403
|
+
const bgOverride = isDrawerMarker ? override.navbarMobileBackground : override.sectionBackgroundColor;
|
|
404
|
+
const textOverride = isDrawerMarker ? override.navbarMobileTextColor : override.textColor;
|
|
405
|
+
const bgValue = isNavbar ? resolveNavbarValue(bgOverride) : bgOverride;
|
|
406
|
+
const textValue = isNavbar ? resolveNavbarValue(textOverride) : textOverride;
|
|
407
|
+
if (bgValue !== void 0) {
|
|
408
|
+
saveInline(section, "background");
|
|
409
|
+
section.style.setProperty("background", bgValue, "important");
|
|
410
|
+
section.setAttribute("data-ohw-style-bgcolor", "");
|
|
411
|
+
}
|
|
412
|
+
if (override.minHeight !== void 0 && !isDrawerMarker) {
|
|
413
|
+
const minHeightCss = override.minHeight === "full" ? "var(--ohw-canvas-h, calc(100vh - 80px))" : override.minHeight === "half" ? "calc(var(--ohw-canvas-h, 100vh) / 2)" : void 0;
|
|
414
|
+
saveInline(section, "min-height");
|
|
415
|
+
if (minHeightCss) section.style.setProperty("min-height", minHeightCss, "important");
|
|
416
|
+
else section.style.removeProperty("min-height");
|
|
417
|
+
section.setAttribute("data-ohw-style-minheight", "");
|
|
418
|
+
}
|
|
419
|
+
if (textValue !== void 0 || override.fontFamily !== void 0) {
|
|
420
|
+
const textEls = marker.querySelectorAll(
|
|
421
|
+
'[data-ohw-editable="text"], [data-ohw-editable="plain"], [data-ohw-field-label]'
|
|
422
|
+
);
|
|
423
|
+
for (const el of Array.from(textEls)) {
|
|
424
|
+
if (textValue !== void 0) {
|
|
425
|
+
saveInline(el, "color");
|
|
426
|
+
el.style.setProperty("color", textValue, "important");
|
|
427
|
+
el.setAttribute(NODE_WROTE_ATTR, "");
|
|
428
|
+
}
|
|
429
|
+
if (override.fontFamily !== void 0) {
|
|
430
|
+
saveInline(el, "font-family");
|
|
431
|
+
el.style.setProperty("font-family", `'${override.fontFamily}'`, "important");
|
|
432
|
+
el.setAttribute(NODE_WROTE_ATTR, "");
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
for (const [key, override] of Object.entries(store.nodes)) {
|
|
439
|
+
const nodes = document.querySelectorAll(`[data-ohw-key="${CSS.escape(key)}"]`);
|
|
440
|
+
for (const el of Array.from(nodes)) {
|
|
441
|
+
if (override.color !== void 0) {
|
|
442
|
+
saveInline(el, "color");
|
|
443
|
+
el.style.setProperty("color", override.color, "important");
|
|
444
|
+
el.setAttribute(NODE_WROTE_ATTR, "");
|
|
445
|
+
}
|
|
446
|
+
if (override.fontFamily !== void 0) {
|
|
447
|
+
saveInline(el, "font-family");
|
|
448
|
+
el.style.setProperty("font-family", `'${override.fontFamily}'`, "important");
|
|
449
|
+
el.setAttribute(NODE_WROTE_ATTR, "");
|
|
450
|
+
}
|
|
451
|
+
if (override.fontSize !== void 0) {
|
|
452
|
+
saveInline(el, "font-size");
|
|
453
|
+
el.style.setProperty("font-size", `${override.fontSize}px`, "important");
|
|
454
|
+
el.setAttribute(NODE_WROTE_ATTR, "");
|
|
455
|
+
}
|
|
456
|
+
if (override.align !== void 0) {
|
|
457
|
+
const subject = alignSubjectOf(el);
|
|
458
|
+
saveInline(subject, "text-align");
|
|
459
|
+
saveInline(subject, "justify-content");
|
|
460
|
+
subject.style.setProperty("text-align", override.align, "important");
|
|
461
|
+
subject.style.setProperty(
|
|
462
|
+
"justify-content",
|
|
463
|
+
ALIGN_JUSTIFY[override.align] ?? "flex-start",
|
|
464
|
+
"important"
|
|
465
|
+
);
|
|
466
|
+
subject.setAttribute(NODE_WROTE_ATTR, "");
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
for (const [sectionId, override] of Object.entries(store.sections)) {
|
|
471
|
+
if (override.headingColor === void 0) continue;
|
|
472
|
+
const escaped = CSS.escape(sectionId);
|
|
473
|
+
const byInstance = document.querySelectorAll(`[data-ohw-instance="${escaped}"]`);
|
|
474
|
+
const sections = byInstance.length ? byInstance : document.querySelectorAll(`[data-ohw-section="${escaped}"]`);
|
|
475
|
+
for (const marker of Array.from(sections)) {
|
|
476
|
+
const headings = marker.querySelectorAll(
|
|
477
|
+
":is(h1, h2, h3, h4, h5, h6)[data-ohw-editable], :is(h1, h2, h3, h4, h5, h6) > [data-ohw-editable]"
|
|
478
|
+
);
|
|
479
|
+
for (const el of Array.from(headings)) {
|
|
480
|
+
saveInline(el, "color");
|
|
481
|
+
el.style.setProperty("color", override.headingColor, "important");
|
|
482
|
+
el.setAttribute(NODE_WROTE_ATTR, "");
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// src/lib/ai-sections-store.ts
|
|
489
|
+
var AI_SECTIONS_KEY = "__ohw_ai_sections";
|
|
490
|
+
var AI_SLOT_KEY_PREFIX = "ai.";
|
|
491
|
+
function aiSlotKeyPrefixFor(sectionId) {
|
|
492
|
+
return `${AI_SLOT_KEY_PREFIX}${sectionId}.`;
|
|
493
|
+
}
|
|
494
|
+
var EMPTY_AI_SECTIONS = { v: 1, sections: [] };
|
|
495
|
+
function parseAiSectionsState(raw) {
|
|
496
|
+
if (!raw) return EMPTY_AI_SECTIONS;
|
|
497
|
+
try {
|
|
498
|
+
const parsed = JSON.parse(raw);
|
|
499
|
+
if (parsed?.v !== 1 || !Array.isArray(parsed.sections)) return EMPTY_AI_SECTIONS;
|
|
500
|
+
const sections = parsed.sections.filter(
|
|
501
|
+
(entry) => typeof entry === "object" && entry !== null && typeof entry.id === "string" && entry.id.length > 0 && isRenderableTree(entry.tree)
|
|
502
|
+
).map((entry) => ({
|
|
503
|
+
...entry,
|
|
504
|
+
label: typeof entry.label === "string" ? entry.label : "Generated section",
|
|
505
|
+
media: entry.media && typeof entry.media === "object" ? entry.media : {}
|
|
506
|
+
}));
|
|
507
|
+
const removed = Array.isArray(parsed.removed) ? parsed.removed.filter((id) => typeof id === "string" && id.length > 0) : [];
|
|
508
|
+
return {
|
|
509
|
+
v: 1,
|
|
510
|
+
sections,
|
|
511
|
+
...removed.length ? { removed } : {},
|
|
512
|
+
...parsed.hideTemplate === true ? { hideTemplate: true } : {}
|
|
513
|
+
};
|
|
514
|
+
} catch {
|
|
515
|
+
return EMPTY_AI_SECTIONS;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
function serializeAiSectionsState(state) {
|
|
519
|
+
return JSON.stringify(state);
|
|
520
|
+
}
|
|
521
|
+
function resolveSectionSettingsContext(sectionId, aiSectionsRaw, stylesRaw) {
|
|
522
|
+
const genSettings = parseAiSectionsState(aiSectionsRaw).sections.find(
|
|
523
|
+
(entry) => entry.id === sectionId
|
|
524
|
+
)?.tree.settings;
|
|
525
|
+
if (genSettings) return genSettings;
|
|
526
|
+
const styleOverride = parseStyleStore(stylesRaw)?.sections[sectionId];
|
|
527
|
+
if (!styleOverride?.verticalPadding && !styleOverride?.minHeight && !styleOverride?.spacing) {
|
|
528
|
+
return void 0;
|
|
529
|
+
}
|
|
530
|
+
return {
|
|
531
|
+
verticalPadding: styleOverride.verticalPadding ?? legacyVerticalPaddingAlias(styleOverride.spacing),
|
|
532
|
+
minHeight: styleOverride.minHeight
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
function applyTreeToState(state, payload) {
|
|
536
|
+
const insertBefore = payload.mode === "insert" && payload.position === "before";
|
|
537
|
+
const priorEntry = state.sections.find((existing) => existing.id === payload.id);
|
|
538
|
+
const selfEdit = payload.mode === "replace" && payload.targetSectionId === payload.id;
|
|
539
|
+
const entry = {
|
|
540
|
+
id: payload.id,
|
|
541
|
+
label: payload.label ?? "Generated section",
|
|
542
|
+
...typeof payload.path === "string" && payload.path ? { path: payload.path } : {},
|
|
543
|
+
afterSection: selfEdit ? priorEntry?.afterSection ?? null : payload.mode === "insert" && !insertBefore ? payload.targetSectionId ?? null : null,
|
|
544
|
+
...selfEdit ? priorEntry?.beforeSection ? { beforeSection: priorEntry.beforeSection } : {} : insertBefore && payload.targetSectionId ? { beforeSection: payload.targetSectionId } : {},
|
|
545
|
+
...selfEdit ? priorEntry?.replaces ? { replaces: priorEntry.replaces } : {} : payload.mode === "replace" && payload.targetSectionId ? { replaces: payload.targetSectionId } : {},
|
|
546
|
+
tree: payload.tree,
|
|
547
|
+
media: payload.media ?? {},
|
|
548
|
+
...payload.brand && typeof payload.brand === "object" ? { brand: payload.brand } : {}
|
|
549
|
+
};
|
|
550
|
+
const others = state.sections.filter((existing) => existing.id !== entry.id);
|
|
551
|
+
return { ...state, v: 1, sections: [...others, entry] };
|
|
552
|
+
}
|
|
553
|
+
function foldAlignIntoTrees(state, store) {
|
|
554
|
+
const byId = new Map(state.sections.map((entry) => [entry.id, entry]));
|
|
555
|
+
const nextTrees = /* @__PURE__ */ new Map();
|
|
556
|
+
const treeFor = (id) => {
|
|
557
|
+
const cloned = nextTrees.get(id);
|
|
558
|
+
if (cloned) return cloned;
|
|
559
|
+
const entry = byId.get(id);
|
|
560
|
+
if (!entry) return void 0;
|
|
561
|
+
const fresh = {
|
|
562
|
+
...entry.tree,
|
|
563
|
+
rows: entry.tree.rows.map((row) => ({ ...row, blocks: row.blocks.map((block) => ({ ...block })) }))
|
|
564
|
+
};
|
|
565
|
+
nextTrees.set(id, fresh);
|
|
566
|
+
return fresh;
|
|
567
|
+
};
|
|
568
|
+
const nodes = {};
|
|
569
|
+
for (const [key, override] of Object.entries(store.nodes)) {
|
|
570
|
+
const match = override.align !== void 0 && key.startsWith(AI_SLOT_KEY_PREFIX) ? key.match(/^ai\.(.+?)\.r(\d+)\.b(\d+)(?:\.|$)/) : null;
|
|
571
|
+
const tree = match ? treeFor(match[1]) : void 0;
|
|
572
|
+
const block = match && tree ? tree.rows[Number(match[2])]?.blocks[Number(match[3])] : void 0;
|
|
573
|
+
if (!block) {
|
|
574
|
+
nodes[key] = override;
|
|
575
|
+
continue;
|
|
576
|
+
}
|
|
577
|
+
block.align = override.align;
|
|
578
|
+
const rest = { ...override };
|
|
579
|
+
delete rest.align;
|
|
580
|
+
if (Object.keys(rest).length > 0) nodes[key] = rest;
|
|
581
|
+
}
|
|
582
|
+
const sections = {};
|
|
583
|
+
for (const [sectionId, override] of Object.entries(store.sections)) {
|
|
584
|
+
const tree = override.align !== void 0 ? treeFor(sectionId) : void 0;
|
|
585
|
+
if (!tree) {
|
|
586
|
+
sections[sectionId] = override;
|
|
587
|
+
continue;
|
|
588
|
+
}
|
|
589
|
+
for (const row of tree.rows) {
|
|
590
|
+
for (const block of row.blocks) block.align = override.align;
|
|
591
|
+
}
|
|
592
|
+
const rest = { ...override };
|
|
593
|
+
delete rest.align;
|
|
594
|
+
if (Object.keys(rest).length > 0) sections[sectionId] = rest;
|
|
595
|
+
}
|
|
596
|
+
if (nextTrees.size === 0) return { state, store, changed: false };
|
|
597
|
+
return {
|
|
598
|
+
state: {
|
|
599
|
+
...state,
|
|
600
|
+
v: 1,
|
|
601
|
+
sections: state.sections.map((entry) => {
|
|
602
|
+
const tree = nextTrees.get(entry.id);
|
|
603
|
+
return tree ? { ...entry, tree } : entry;
|
|
604
|
+
})
|
|
605
|
+
},
|
|
606
|
+
store: { v: 1, sections, nodes },
|
|
607
|
+
changed: true
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
function foldSectionHeightIntoTrees(state, store) {
|
|
611
|
+
const byId = new Map(state.sections.map((entry) => [entry.id, entry]));
|
|
612
|
+
const nextEntries = /* @__PURE__ */ new Map();
|
|
613
|
+
const sections = {};
|
|
614
|
+
for (const [sectionId, override] of Object.entries(store.sections)) {
|
|
615
|
+
const entry = byId.get(sectionId);
|
|
616
|
+
const touchesHeight = override.verticalPadding !== void 0 || override.minHeight !== void 0;
|
|
617
|
+
if (!entry || !touchesHeight) {
|
|
618
|
+
sections[sectionId] = override;
|
|
619
|
+
continue;
|
|
620
|
+
}
|
|
621
|
+
const nextSettings = { ...entry.tree.settings ?? {} };
|
|
622
|
+
if (override.verticalPadding !== void 0) nextSettings.verticalPadding = override.verticalPadding;
|
|
623
|
+
if (override.minHeight !== void 0) nextSettings.minHeight = override.minHeight;
|
|
624
|
+
nextEntries.set(sectionId, { ...entry, tree: { ...entry.tree, settings: nextSettings } });
|
|
625
|
+
const rest = { ...override };
|
|
626
|
+
delete rest.verticalPadding;
|
|
627
|
+
delete rest.minHeight;
|
|
628
|
+
if (Object.keys(rest).length > 0) sections[sectionId] = rest;
|
|
629
|
+
}
|
|
630
|
+
if (nextEntries.size === 0) return { state, store, changed: false };
|
|
631
|
+
return {
|
|
632
|
+
state: {
|
|
633
|
+
...state,
|
|
634
|
+
v: 1,
|
|
635
|
+
sections: state.sections.map((entry) => nextEntries.get(entry.id) ?? entry)
|
|
636
|
+
},
|
|
637
|
+
store: { v: 1, sections, nodes: store.nodes },
|
|
638
|
+
changed: true
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
function removeFromState(state, id) {
|
|
642
|
+
return { ...state, v: 1, sections: state.sections.filter((entry) => entry.id !== id) };
|
|
643
|
+
}
|
|
644
|
+
function deleteSectionFromState(state, sectionId) {
|
|
645
|
+
if (state.sections.some((entry) => entry.id === sectionId)) {
|
|
646
|
+
return removeFromState(state, sectionId);
|
|
647
|
+
}
|
|
648
|
+
const removed = state.removed ?? [];
|
|
649
|
+
if (removed.includes(sectionId)) return state;
|
|
650
|
+
return { ...state, v: 1, sections: state.sections, removed: [...removed, sectionId] };
|
|
651
|
+
}
|
|
652
|
+
function reapRemovedAiSections(state, store, removedIds, excludeIds = /* @__PURE__ */ new Set()) {
|
|
653
|
+
const generated = new Set(state.sections.map((entry) => entry.id));
|
|
654
|
+
const reapedIds = [...new Set(removedIds)].filter((id) => generated.has(id) && !excludeIds.has(id));
|
|
655
|
+
if (reapedIds.length === 0) {
|
|
656
|
+
return { state, store, reapedIds: [], slotPrefixes: [], changed: false };
|
|
657
|
+
}
|
|
658
|
+
const reaped = new Set(reapedIds);
|
|
659
|
+
const slotPrefixes = reapedIds.map(aiSlotKeyPrefixFor);
|
|
660
|
+
const nextState = {
|
|
661
|
+
...state,
|
|
662
|
+
v: 1,
|
|
663
|
+
sections: state.sections.filter((entry) => !reaped.has(entry.id))
|
|
664
|
+
};
|
|
665
|
+
let nextStore = store;
|
|
666
|
+
if (store) {
|
|
667
|
+
const sections = {};
|
|
668
|
+
for (const [key, override] of Object.entries(store.sections)) {
|
|
669
|
+
if (!reaped.has(key)) sections[key] = override;
|
|
670
|
+
}
|
|
671
|
+
const nodes = {};
|
|
672
|
+
for (const [key, override] of Object.entries(store.nodes)) {
|
|
673
|
+
if (!slotPrefixes.some((prefix) => key.startsWith(prefix))) nodes[key] = override;
|
|
674
|
+
}
|
|
675
|
+
nextStore = { v: 1, sections, nodes };
|
|
676
|
+
}
|
|
677
|
+
return { state: nextState, store: nextStore, reapedIds, slotPrefixes, changed: true };
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// src/lib/ai-footer-tree.ts
|
|
681
|
+
var FOOTER_COLUMN_SPANS = [12, 6, 4, 3];
|
|
682
|
+
var FOOTER_COLUMN_BLOCKS = [
|
|
683
|
+
"link-list",
|
|
684
|
+
"text",
|
|
685
|
+
"hours",
|
|
686
|
+
"logo",
|
|
687
|
+
"social-row",
|
|
688
|
+
"media-strip"
|
|
689
|
+
];
|
|
690
|
+
var FOOTER_SOCIAL_PLATFORMS = [
|
|
691
|
+
"facebook",
|
|
692
|
+
"instagram",
|
|
693
|
+
"youtube",
|
|
694
|
+
"linkedin",
|
|
695
|
+
"twitter",
|
|
696
|
+
"tiktok",
|
|
697
|
+
"threads",
|
|
698
|
+
"pinterest",
|
|
699
|
+
"whatsapp"
|
|
700
|
+
];
|
|
701
|
+
function resolveFooterCopyright(copyright, year = (/* @__PURE__ */ new Date()).getFullYear()) {
|
|
702
|
+
return copyright.replace(/\{year\}/g, String(year));
|
|
703
|
+
}
|
|
334
704
|
function isRenderableFooterTree(value) {
|
|
335
705
|
if (typeof value !== "object" || value === null) return false;
|
|
336
706
|
const tree = value;
|
|
@@ -613,520 +983,222 @@ function isButtonStyle(value) {
|
|
|
613
983
|
const v = value;
|
|
614
984
|
return BUTTON_FILL_TOKENS.includes(v.fill) && BUTTON_FILL_TOKENS.includes(v.textColor) && BUTTON_STYLES.includes(v.style) && BUTTON_RADII.includes(v.radius) && BUTTON_SIZES.includes(v.size);
|
|
615
985
|
}
|
|
616
|
-
function parseCustomFontWeight(raw) {
|
|
617
|
-
if (typeof raw !== "object" || raw === null) return null;
|
|
618
|
-
const w = raw;
|
|
619
|
-
if (typeof w.weight !== "number" || !Number.isFinite(w.weight)) return null;
|
|
620
|
-
if (typeof w.url !== "string" || !w.url) return null;
|
|
621
|
-
if (typeof w.format !== "string" || !CUSTOM_FONT_FORMATS.has(w.format)) return null;
|
|
622
|
-
const weightEnd = typeof w.weightEnd === "number" && Number.isFinite(w.weightEnd) && w.weightEnd > w.weight ? w.weightEnd : void 0;
|
|
623
|
-
return { weight: w.weight, ...weightEnd !== void 0 ? { weightEnd } : {}, url: w.url, format: w.format };
|
|
624
|
-
}
|
|
625
|
-
function parseCustomFont(raw) {
|
|
626
|
-
if (typeof raw !== "object" || raw === null) return null;
|
|
627
|
-
const f = raw;
|
|
628
|
-
if (typeof f.family !== "string" || !f.family) return null;
|
|
629
|
-
if (typeof f.label !== "string" || !f.label) return null;
|
|
630
|
-
if (!Array.isArray(f.weights)) return null;
|
|
631
|
-
const weights = f.weights.map(parseCustomFontWeight).filter((w) => w !== null);
|
|
632
|
-
if (weights.length === 0) return null;
|
|
633
|
-
return { family: f.family, label: f.label, weights };
|
|
634
|
-
}
|
|
635
|
-
var BUTTON_FIELDS = Object.keys(ROLES_BY_FIELD);
|
|
636
|
-
function parseTouchedButtons(raw) {
|
|
637
|
-
const out = {};
|
|
638
|
-
for (const variant of ["primary", "secondary"]) {
|
|
639
|
-
const list = raw?.[variant];
|
|
640
|
-
if (Array.isArray(list)) out[variant] = BUTTON_FIELDS.filter((f) => list.includes(f));
|
|
641
|
-
}
|
|
642
|
-
return out;
|
|
643
|
-
}
|
|
644
|
-
function parseBrandKit(raw) {
|
|
645
|
-
if (!raw) return null;
|
|
646
|
-
try {
|
|
647
|
-
const parsed = JSON.parse(raw);
|
|
648
|
-
const p = parsed?.palette;
|
|
649
|
-
const f = parsed?.fonts;
|
|
650
|
-
const palette = p && typeof p.dark === "string" && typeof p.primary === "string" && typeof p.accent === "string" && typeof p.light === "string" ? { dark: p.dark, primary: p.primary, accent: p.accent, light: p.light } : void 0;
|
|
651
|
-
const fonts = f && typeof f.heading === "string" && typeof f.body === "string" ? { heading: f.heading, body: f.body } : void 0;
|
|
652
|
-
const customFonts = Array.isArray(parsed?.customFonts) ? parsed.customFonts.map(parseCustomFont).filter((c) => c !== null) : void 0;
|
|
653
|
-
const b = parsed?.buttons;
|
|
654
|
-
const buttons = b && isButtonStyle(b.primary) && isButtonStyle(b.secondary) ? { primary: b.primary, secondary: b.secondary } : void 0;
|
|
655
|
-
if (!palette && !fonts && !customFonts && !buttons) return null;
|
|
656
|
-
return {
|
|
657
|
-
...palette ? { palette } : {},
|
|
658
|
-
...fonts ? { fonts } : {},
|
|
659
|
-
...customFonts ? { customFonts } : {},
|
|
660
|
-
...buttons ? { buttons } : {},
|
|
661
|
-
...parsed.touched && typeof parsed.touched === "object" ? {
|
|
662
|
-
touched: {
|
|
663
|
-
palette: parsed.touched.palette === true,
|
|
664
|
-
fonts: parsed.touched.fonts === true,
|
|
665
|
-
buttons: parseTouchedButtons(parsed.touched.buttons)
|
|
666
|
-
}
|
|
667
|
-
} : {}
|
|
668
|
-
};
|
|
669
|
-
} catch {
|
|
670
|
-
return null;
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
function familyOf(stack) {
|
|
674
|
-
const first = stack.split(",")[0]?.trim() ?? "";
|
|
675
|
-
return first.replace(/^['"]|['"]$/g, "");
|
|
676
|
-
}
|
|
677
|
-
function quoteFamily(family) {
|
|
678
|
-
return `'${family.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
|
|
679
|
-
}
|
|
680
|
-
var WEIGHT_SCALE = [100, 200, 300, 400, 500, 600, 700, 800, 900];
|
|
681
|
-
function loadBrandFonts(families) {
|
|
682
|
-
const unique = [...new Set(families.map((f) => f.trim()).filter(Boolean))];
|
|
683
|
-
if (unique.length === 0) return;
|
|
684
|
-
const weights = WEIGHT_SCALE.join(";");
|
|
685
|
-
const spec = unique.map((f) => `family=${f.replace(/ /g, "+")}:wght@${weights}`).join("&");
|
|
686
|
-
const href = `https://fonts.googleapis.com/css2?${spec}&display=swap`;
|
|
687
|
-
let link = document.getElementById(BRAND_FONT_LINK_ID);
|
|
688
|
-
if (!link) {
|
|
689
|
-
link = document.createElement("link");
|
|
690
|
-
link.id = BRAND_FONT_LINK_ID;
|
|
691
|
-
link.rel = "stylesheet";
|
|
692
|
-
document.head.appendChild(link);
|
|
693
|
-
}
|
|
694
|
-
if (link.href !== href) link.href = href;
|
|
695
|
-
}
|
|
696
|
-
var BUTTON_RULES_STYLE_ID = "ohw-brand-button-rules";
|
|
697
|
-
function buttonRulesCss(kit) {
|
|
698
|
-
const rule = (variant) => {
|
|
699
|
-
const roles = touchedButtonRoles(variant, kit);
|
|
700
|
-
const v = (role) => `var(--brand-button-${variant}-${role})`;
|
|
701
|
-
const decls = [];
|
|
702
|
-
if (roles.has("bg")) decls.push(`background:${v("bg")} !important`);
|
|
703
|
-
if (roles.has("text")) decls.push(`color:${v("text")} !important`);
|
|
704
|
-
if (roles.has("border")) decls.push(`border:1.5px solid ${v("border")} !important`);
|
|
705
|
-
if (roles.has("radius")) decls.push(`border-radius:${v("radius")} !important`);
|
|
706
|
-
if (roles.has("padding-x")) decls.push(`padding:var(--brand-button-${variant}-padding, ${v("padding-y")} ${v("padding-x")}) !important`);
|
|
707
|
-
if (roles.has("font-size")) decls.push(`font-size:${v("font-size")} !important`);
|
|
708
|
-
if (decls.length === 0) return "";
|
|
709
|
-
const selectors = [`html .btn--${variant}`, `html .btn-${variant}`];
|
|
710
|
-
const labelRule = roles.has("text") ? `${selectors.map((x) => `${x} *`).join(",")}{color:inherit !important}` : "";
|
|
711
|
-
return `${selectors.join(",")}{${decls.join(";")}}` + labelRule + (roles.has("bg") ? `${selectors.map((x) => `${x}:hover`).join(",")}{opacity:0.88}` : "");
|
|
712
|
-
};
|
|
713
|
-
return rule("primary") + rule("secondary");
|
|
714
|
-
}
|
|
715
|
-
function syncButtonRules(kit) {
|
|
716
|
-
let el = document.getElementById(BUTTON_RULES_STYLE_ID);
|
|
717
|
-
const css = kit ? buttonRulesCss(kit) : "";
|
|
718
|
-
if (!css) {
|
|
719
|
-
el?.remove();
|
|
720
|
-
return;
|
|
721
|
-
}
|
|
722
|
-
if (!el) {
|
|
723
|
-
el = document.createElement("style");
|
|
724
|
-
el.id = BUTTON_RULES_STYLE_ID;
|
|
725
|
-
document.head.appendChild(el);
|
|
726
|
-
}
|
|
727
|
-
if (el.textContent !== css) el.textContent = css;
|
|
728
|
-
}
|
|
729
|
-
function escapeCssString(value) {
|
|
730
|
-
return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
731
|
-
}
|
|
732
|
-
function loadCustomFonts(fonts) {
|
|
733
|
-
const list = fonts ?? [];
|
|
734
|
-
let style = document.getElementById(CUSTOM_FONT_STYLE_ID);
|
|
735
|
-
if (list.length === 0) {
|
|
736
|
-
style?.remove();
|
|
737
|
-
return;
|
|
738
|
-
}
|
|
739
|
-
if (!style) {
|
|
740
|
-
style = document.createElement("style");
|
|
741
|
-
style.id = CUSTOM_FONT_STYLE_ID;
|
|
742
|
-
document.head.appendChild(style);
|
|
743
|
-
}
|
|
744
|
-
style.textContent = list.flatMap(
|
|
745
|
-
(font) => font.weights.map((w) => {
|
|
746
|
-
const weightDescriptor = w.weightEnd ? `${w.weight} ${w.weightEnd}` : `${w.weight}`;
|
|
747
|
-
return `@font-face { font-family: '${escapeCssString(font.family)}'; src: url('${escapeCssString(w.url)}') format('${w.format}'); font-weight: ${weightDescriptor}; font-display: swap; }`;
|
|
748
|
-
})
|
|
749
|
-
).join("\n");
|
|
750
|
-
}
|
|
751
|
-
function applyBrandToDom(kit) {
|
|
752
|
-
const root = document.documentElement;
|
|
753
|
-
if (!kit) {
|
|
754
|
-
for (const name of [...BRAND_VAR_NAMES, ...LEGACY_BRAND_VAR_NAMES, ...BUTTON_VAR_NAMES]) {
|
|
755
|
-
root.style.removeProperty(name);
|
|
756
|
-
}
|
|
757
|
-
for (const name of [...FONT_VARS.heading, ...FONT_VARS.body]) root.style.removeProperty(name);
|
|
758
|
-
document.getElementById(BRAND_FONT_LINK_ID)?.remove();
|
|
759
|
-
document.getElementById(CUSTOM_FONT_STYLE_ID)?.remove();
|
|
760
|
-
syncButtonRules(null);
|
|
761
|
-
return;
|
|
762
|
-
}
|
|
763
|
-
const applyPalette = kit.touched ? kit.touched.palette === true : true;
|
|
764
|
-
const applyFonts = kit.touched ? kit.touched.fonts === true : true;
|
|
765
|
-
if (kit.palette) {
|
|
766
|
-
if (applyPalette) {
|
|
767
|
-
for (const [name, value] of Object.entries(brandColorVars(kit.palette))) root.style.setProperty(name, value);
|
|
768
|
-
} else {
|
|
769
|
-
for (const name of [...BRAND_VAR_NAMES, ...LEGACY_BRAND_VAR_NAMES]) root.style.removeProperty(name);
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
if (kit.buttons) {
|
|
773
|
-
for (const name of BUTTON_VAR_NAMES) root.style.removeProperty(name);
|
|
774
|
-
for (const [name, value] of Object.entries(buttonStyleVars({ ...kit, buttons: kit.buttons }))) root.style.setProperty(name, value);
|
|
775
|
-
}
|
|
776
|
-
syncButtonRules(kit.buttons ? kit : null);
|
|
777
|
-
if (kit.customFonts) {
|
|
778
|
-
loadCustomFonts(kit.customFonts);
|
|
779
|
-
}
|
|
780
|
-
if (kit.fonts) {
|
|
781
|
-
if (applyFonts) {
|
|
782
|
-
const heading = quoteFamily(kit.fonts.heading);
|
|
783
|
-
const body = quoteFamily(kit.fonts.body);
|
|
784
|
-
for (const name of FONT_VARS.heading) root.style.setProperty(name, heading);
|
|
785
|
-
for (const name of FONT_VARS.body) root.style.setProperty(name, body);
|
|
786
|
-
const customFamilies = new Set((kit.customFonts ?? []).map((f) => f.family));
|
|
787
|
-
const googleFamilies = [familyOf(kit.fonts.heading), familyOf(kit.fonts.body)].filter(
|
|
788
|
-
(f) => !customFamilies.has(f)
|
|
789
|
-
);
|
|
790
|
-
loadBrandFonts(googleFamilies);
|
|
791
|
-
} else {
|
|
792
|
-
for (const name of [...FONT_VARS.heading, ...FONT_VARS.body]) root.style.removeProperty(name);
|
|
793
|
-
document.getElementById(BRAND_FONT_LINK_ID)?.remove();
|
|
794
|
-
}
|
|
795
|
-
}
|
|
986
|
+
function parseCustomFontWeight(raw) {
|
|
987
|
+
if (typeof raw !== "object" || raw === null) return null;
|
|
988
|
+
const w = raw;
|
|
989
|
+
if (typeof w.weight !== "number" || !Number.isFinite(w.weight)) return null;
|
|
990
|
+
if (typeof w.url !== "string" || !w.url) return null;
|
|
991
|
+
if (typeof w.format !== "string" || !CUSTOM_FONT_FORMATS.has(w.format)) return null;
|
|
992
|
+
const weightEnd = typeof w.weightEnd === "number" && Number.isFinite(w.weightEnd) && w.weightEnd > w.weight ? w.weightEnd : void 0;
|
|
993
|
+
return { weight: w.weight, ...weightEnd !== void 0 ? { weightEnd } : {}, url: w.url, format: w.format };
|
|
796
994
|
}
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
995
|
+
function parseCustomFont(raw) {
|
|
996
|
+
if (typeof raw !== "object" || raw === null) return null;
|
|
997
|
+
const f = raw;
|
|
998
|
+
if (typeof f.family !== "string" || !f.family) return null;
|
|
999
|
+
if (typeof f.label !== "string" || !f.label) return null;
|
|
1000
|
+
if (!Array.isArray(f.weights)) return null;
|
|
1001
|
+
const weights = f.weights.map(parseCustomFontWeight).filter((w) => w !== null);
|
|
1002
|
+
if (weights.length === 0) return null;
|
|
1003
|
+
return { family: f.family, label: f.label, weights };
|
|
804
1004
|
}
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
const
|
|
808
|
-
const
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
if (node.nodeType !== Node.ELEMENT_NODE) continue;
|
|
812
|
-
const el = node;
|
|
813
|
-
const tag = el.tagName.toLowerCase();
|
|
814
|
-
if (!ALLOWED_HEAD_TAGS.has(tag)) continue;
|
|
815
|
-
const built = document.createElement(tag);
|
|
816
|
-
for (const attr of Array.from(el.attributes)) built.setAttribute(attr.name, attr.value);
|
|
817
|
-
if (tag === "script" || tag === "style" || tag === "noscript") built.textContent = el.textContent;
|
|
818
|
-
built.dataset.ohwCustomCode = slot;
|
|
819
|
-
nodes.push(built);
|
|
1005
|
+
var BUTTON_FIELDS = Object.keys(ROLES_BY_FIELD);
|
|
1006
|
+
function parseTouchedButtons(raw) {
|
|
1007
|
+
const out = {};
|
|
1008
|
+
for (const variant of ["primary", "secondary"]) {
|
|
1009
|
+
const list = raw?.[variant];
|
|
1010
|
+
if (Array.isArray(list)) out[variant] = BUTTON_FIELDS.filter((f) => list.includes(f));
|
|
820
1011
|
}
|
|
821
|
-
return
|
|
822
|
-
}
|
|
823
|
-
function applyCustomCodeToHead(content) {
|
|
824
|
-
clearExisting("after");
|
|
825
|
-
clearExisting("before");
|
|
826
|
-
const afterNodes = buildNodes(content[CUSTOM_CODE_AFTER_HEAD_KEY], "after");
|
|
827
|
-
if (afterNodes.length > 0) document.head.prepend(...afterNodes);
|
|
828
|
-
const beforeNodes = buildNodes(content[CUSTOM_CODE_BEFORE_HEAD_KEY], "before");
|
|
829
|
-
if (beforeNodes.length > 0) document.head.append(...beforeNodes);
|
|
1012
|
+
return out;
|
|
830
1013
|
}
|
|
831
|
-
|
|
832
|
-
// src/lib/section-styles.ts
|
|
833
|
-
var STYLE_STORE_KEY = "__ohw_styles";
|
|
834
|
-
var STYLE_SHEET_ID = "ohw-section-styles";
|
|
835
|
-
function parseStyleStore(raw) {
|
|
1014
|
+
function parseBrandKit(raw) {
|
|
836
1015
|
if (!raw) return null;
|
|
837
1016
|
try {
|
|
838
1017
|
const parsed = JSON.parse(raw);
|
|
839
|
-
|
|
1018
|
+
const p = parsed?.palette;
|
|
1019
|
+
const f = parsed?.fonts;
|
|
1020
|
+
const palette = p && typeof p.dark === "string" && typeof p.primary === "string" && typeof p.accent === "string" && typeof p.light === "string" ? { dark: p.dark, primary: p.primary, accent: p.accent, light: p.light } : void 0;
|
|
1021
|
+
const fonts = f && typeof f.heading === "string" && typeof f.body === "string" ? { heading: f.heading, body: f.body } : void 0;
|
|
1022
|
+
const customFonts = Array.isArray(parsed?.customFonts) ? parsed.customFonts.map(parseCustomFont).filter((c) => c !== null) : void 0;
|
|
1023
|
+
const b = parsed?.buttons;
|
|
1024
|
+
const buttons = b && isButtonStyle(b.primary) && isButtonStyle(b.secondary) ? { primary: b.primary, secondary: b.secondary } : void 0;
|
|
1025
|
+
if (!palette && !fonts && !customFonts && !buttons) return null;
|
|
840
1026
|
return {
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
1027
|
+
...palette ? { palette } : {},
|
|
1028
|
+
...fonts ? { fonts } : {},
|
|
1029
|
+
...customFonts ? { customFonts } : {},
|
|
1030
|
+
...buttons ? { buttons } : {},
|
|
1031
|
+
...parsed.touched && typeof parsed.touched === "object" ? {
|
|
1032
|
+
touched: {
|
|
1033
|
+
palette: parsed.touched.palette === true,
|
|
1034
|
+
fonts: parsed.touched.fonts === true,
|
|
1035
|
+
buttons: parseTouchedButtons(parsed.touched.buttons)
|
|
1036
|
+
}
|
|
1037
|
+
} : {}
|
|
844
1038
|
};
|
|
845
1039
|
} catch {
|
|
846
1040
|
return null;
|
|
847
1041
|
}
|
|
848
1042
|
}
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
"accent-soft": "color-mix(in srgb, var(--ohw-brand-primary, var(--color-primary, #0078E5)) 12%, var(--ohw-brand-light, var(--color-light, #FFFFFF)))"
|
|
853
|
-
};
|
|
854
|
-
var NAVBAR_COLOR_TOKENS = ["dark", "primary", "accent", "light"];
|
|
855
|
-
function navbarColorVar(token) {
|
|
856
|
-
const fallback = {
|
|
857
|
-
dark: "#0C0A09",
|
|
858
|
-
primary: "#0078E5",
|
|
859
|
-
accent: "#0078E5",
|
|
860
|
-
light: "#FFFFFF"
|
|
861
|
-
};
|
|
862
|
-
return `var(--ohw-brand-${token}, var(--color-${token}, ${fallback[token]}))`;
|
|
863
|
-
}
|
|
864
|
-
function resolveNavbarValue(value) {
|
|
865
|
-
if (value === void 0) return void 0;
|
|
866
|
-
return NAVBAR_COLOR_TOKENS.includes(value) ? navbarColorVar(value) : value;
|
|
1043
|
+
function familyOf(stack) {
|
|
1044
|
+
const first = stack.split(",")[0]?.trim() ?? "";
|
|
1045
|
+
return first.replace(/^['"]|['"]$/g, "");
|
|
867
1046
|
}
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
const rules = [];
|
|
871
|
-
for (const [tone, value] of Object.entries(BG_VALUES)) {
|
|
872
|
-
rules.push(`[data-ohw-style-bg="${tone}"] { background: ${value} !important; }`);
|
|
873
|
-
}
|
|
874
|
-
rules.push(
|
|
875
|
-
`[data-ohw-style-bg="accent"] { color: var(--ohw-brand-light, var(--color-light, #FFFFFF)) !important; }`
|
|
876
|
-
);
|
|
877
|
-
rules.push(
|
|
878
|
-
`[data-ohw-style-distribution="space-between"] { display: flex !important; flex-direction: column; justify-content: space-between; }`,
|
|
879
|
-
`[data-ohw-style-distribution="center"] { display: flex !important; flex-direction: column; justify-content: center; }`
|
|
880
|
-
);
|
|
881
|
-
for (const [scale, size] of Object.entries(HEADLINE_SIZES)) {
|
|
882
|
-
rules.push(
|
|
883
|
-
`[data-ohw-style-headline="${scale}"] :is(h1, h2, h3) { font-size: ${size}px !important; line-height: 1.15 !important; }`
|
|
884
|
-
);
|
|
885
|
-
}
|
|
886
|
-
rules.push(
|
|
887
|
-
`[data-ohw-style-aspect="fill-height"] img { height: 100% !important; aspect-ratio: auto !important; object-fit: cover; }`
|
|
888
|
-
);
|
|
889
|
-
for (const aspect of ["1:1", "4:5", "3:4", "3:2", "16:9", "2:1", "3:1"]) {
|
|
890
|
-
rules.push(
|
|
891
|
-
`[data-ohw-style-aspect="${aspect.replace(":", "-")}"] img { aspect-ratio: ${aspect.replace(":", " / ")} !important; height: auto !important; object-fit: cover; }`
|
|
892
|
-
);
|
|
893
|
-
}
|
|
894
|
-
const pad = { tight: 40, balanced: 64, airy: 96 };
|
|
895
|
-
for (const [spacing, px2] of Object.entries(pad)) {
|
|
896
|
-
rules.push(
|
|
897
|
-
`[data-ohw-style-spacing="${spacing}"] { padding-top: ${px2}px !important; padding-bottom: ${px2}px !important; }`
|
|
898
|
-
);
|
|
899
|
-
}
|
|
900
|
-
rules.push(
|
|
901
|
-
`[data-ohw-style-corners="sharp"] :is(.card, [data-ohw-card], img, picture, figure, [data-ohw-editable="bg-image"]) { border-radius: 0 !important; }`,
|
|
902
|
-
`[data-ohw-style-corners="sharp"] :has(> img) { border-radius: 0 !important; }`,
|
|
903
|
-
`[data-ohw-style-corners="rounded"] :is(.card, [data-ohw-card], img, picture, figure, [data-ohw-editable="bg-image"]) { border-radius: max(0.75rem, var(--radius, 0px)) !important; }`,
|
|
904
|
-
`[data-ohw-style-corners="rounded"] :has(> img) { border-radius: max(0.75rem, var(--radius, 0px)) !important; }`
|
|
905
|
-
);
|
|
906
|
-
for (const align of ["left", "center", "right"]) {
|
|
907
|
-
rules.push(`[data-ohw-style-align="${align}"] { text-align: ${align} !important; }`);
|
|
908
|
-
}
|
|
909
|
-
return rules.join("\n");
|
|
1047
|
+
function quoteFamily(family) {
|
|
1048
|
+
return `'${family.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
|
|
910
1049
|
}
|
|
911
|
-
var
|
|
912
|
-
function
|
|
1050
|
+
var WEIGHT_SCALE = [100, 200, 300, 400, 500, 600, 700, 800, 900];
|
|
1051
|
+
function loadBrandFonts(families) {
|
|
913
1052
|
const unique = [...new Set(families.map((f) => f.trim()).filter(Boolean))];
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
const spec = unique.map((f) => `${f.replace(/ /g, "+")}:400,500,600,700`).join("|");
|
|
920
|
-
const href = `https://fonts.googleapis.com/css?family=${spec}&display=swap`;
|
|
921
|
-
let link = existing;
|
|
1053
|
+
if (unique.length === 0) return;
|
|
1054
|
+
const weights = WEIGHT_SCALE.join(";");
|
|
1055
|
+
const spec = unique.map((f) => `family=${f.replace(/ /g, "+")}:wght@${weights}`).join("&");
|
|
1056
|
+
const href = `https://fonts.googleapis.com/css2?${spec}&display=swap`;
|
|
1057
|
+
let link = document.getElementById(BRAND_FONT_LINK_ID);
|
|
922
1058
|
if (!link) {
|
|
923
1059
|
link = document.createElement("link");
|
|
924
|
-
link.id =
|
|
1060
|
+
link.id = BRAND_FONT_LINK_ID;
|
|
925
1061
|
link.rel = "stylesheet";
|
|
926
1062
|
document.head.appendChild(link);
|
|
927
1063
|
}
|
|
928
1064
|
if (link.href !== href) link.href = href;
|
|
929
1065
|
}
|
|
930
|
-
var
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
};
|
|
939
|
-
|
|
940
|
-
var
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
var ALIGN_JUSTIFY = {
|
|
949
|
-
left: "flex-start",
|
|
950
|
-
center: "center",
|
|
951
|
-
right: "flex-end"
|
|
952
|
-
};
|
|
953
|
-
function saveInline(el, prop) {
|
|
954
|
-
const attr = `data-ohw-style-prev-${prop}`;
|
|
955
|
-
if (el.hasAttribute(attr)) return;
|
|
956
|
-
const value = el.style.getPropertyValue(prop) || (prop === "background" ? el.style.getPropertyValue("background-color") : "");
|
|
957
|
-
el.setAttribute(attr, value);
|
|
958
|
-
}
|
|
959
|
-
function restoreInline(el, prop) {
|
|
960
|
-
const attr = `data-ohw-style-prev-${prop}`;
|
|
961
|
-
if (!el.hasAttribute(attr)) return;
|
|
962
|
-
const prev = el.getAttribute(attr) ?? "";
|
|
963
|
-
if (prev) el.style.setProperty(prop, prev);
|
|
964
|
-
else el.style.removeProperty(prop);
|
|
965
|
-
el.removeAttribute(attr);
|
|
1066
|
+
var BUTTON_RULES_STYLE_ID = "ohw-brand-button-rules";
|
|
1067
|
+
function buttonRulesCss(kit) {
|
|
1068
|
+
const rule = (variant) => {
|
|
1069
|
+
const roles = touchedButtonRoles(variant, kit);
|
|
1070
|
+
const v = (role) => `var(--brand-button-${variant}-${role})`;
|
|
1071
|
+
const decls = [];
|
|
1072
|
+
if (roles.has("bg")) decls.push(`background:${v("bg")} !important`);
|
|
1073
|
+
if (roles.has("text")) decls.push(`color:${v("text")} !important`);
|
|
1074
|
+
if (roles.has("border")) decls.push(`border:1.5px solid ${v("border")} !important`);
|
|
1075
|
+
if (roles.has("radius")) decls.push(`border-radius:${v("radius")} !important`);
|
|
1076
|
+
if (roles.has("padding-x")) decls.push(`padding:var(--brand-button-${variant}-padding, ${v("padding-y")} ${v("padding-x")}) !important`);
|
|
1077
|
+
if (roles.has("font-size")) decls.push(`font-size:${v("font-size")} !important`);
|
|
1078
|
+
if (decls.length === 0) return "";
|
|
1079
|
+
const selectors = [`html .btn--${variant}`, `html .btn-${variant}`];
|
|
1080
|
+
const labelRule = roles.has("text") ? `${selectors.map((x) => `${x} *`).join(",")}{color:inherit !important}` : "";
|
|
1081
|
+
return `${selectors.join(",")}{${decls.join(";")}}` + labelRule + (roles.has("bg") ? `${selectors.map((x) => `${x}:hover`).join(",")}{opacity:0.88}` : "");
|
|
1082
|
+
};
|
|
1083
|
+
return rule("primary") + rule("secondary");
|
|
966
1084
|
}
|
|
967
|
-
function
|
|
968
|
-
let el = document.getElementById(
|
|
1085
|
+
function syncButtonRules(kit) {
|
|
1086
|
+
let el = document.getElementById(BUTTON_RULES_STYLE_ID);
|
|
1087
|
+
const css = kit ? buttonRulesCss(kit) : "";
|
|
1088
|
+
if (!css) {
|
|
1089
|
+
el?.remove();
|
|
1090
|
+
return;
|
|
1091
|
+
}
|
|
969
1092
|
if (!el) {
|
|
970
1093
|
el = document.createElement("style");
|
|
971
|
-
el.id =
|
|
1094
|
+
el.id = BUTTON_RULES_STYLE_ID;
|
|
972
1095
|
document.head.appendChild(el);
|
|
973
1096
|
}
|
|
974
|
-
const css = styleSheetCss();
|
|
975
1097
|
if (el.textContent !== css) el.textContent = css;
|
|
976
1098
|
}
|
|
977
|
-
function
|
|
978
|
-
|
|
979
|
-
for (const el of Array.from(root.querySelectorAll(`[${attr}]`))) el.removeAttribute(attr);
|
|
980
|
-
}
|
|
981
|
-
for (const el of Array.from(root.querySelectorAll("[data-ohw-style-bgcolor]"))) {
|
|
982
|
-
restoreInline(el, "background");
|
|
983
|
-
el.removeAttribute("data-ohw-style-bgcolor");
|
|
984
|
-
}
|
|
985
|
-
}
|
|
986
|
-
function clearNodeProps(root) {
|
|
987
|
-
for (const el of Array.from(root.querySelectorAll(`[${NODE_WROTE_ATTR}]`))) {
|
|
988
|
-
const h = el;
|
|
989
|
-
for (const prop of NODE_PROPS) restoreInline(h, prop);
|
|
990
|
-
h.removeAttribute(NODE_WROTE_ATTR);
|
|
991
|
-
}
|
|
1099
|
+
function escapeCssString(value) {
|
|
1100
|
+
return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
992
1101
|
}
|
|
993
|
-
function
|
|
994
|
-
const
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
}
|
|
1000
|
-
const TEXT_ROW = /^(P|LI|H[1-6]|BLOCKQUOTE|FIGCAPTION|DT|DD)$/;
|
|
1001
|
-
const parent = subject.parentElement;
|
|
1002
|
-
if (parent && TEXT_ROW.test(parent.tagName) && /^(inline-)?flex$/.test(getComputedStyle(parent).display)) {
|
|
1003
|
-
return parent;
|
|
1004
|
-
}
|
|
1005
|
-
if (parent && !parent.hasAttribute("data-ohw-section")) {
|
|
1006
|
-
const pcs = getComputedStyle(parent);
|
|
1007
|
-
const children = Array.from(parent.children);
|
|
1008
|
-
const textRowChild = (c) => c.hasAttribute("data-ohw-editable") || (c.textContent ?? "").trim().length === 0 || c.getAttribute("aria-hidden") === "true";
|
|
1009
|
-
if (/^(inline-)?flex$/.test(pcs.display) && pcs.flexDirection.startsWith("row") && children.length > 0 && children.some((c) => c.hasAttribute("data-ohw-editable")) && children.every(textRowChild)) {
|
|
1010
|
-
return parent;
|
|
1011
|
-
}
|
|
1102
|
+
function loadCustomFonts(fonts) {
|
|
1103
|
+
const list = fonts ?? [];
|
|
1104
|
+
let style = document.getElementById(CUSTOM_FONT_STYLE_ID);
|
|
1105
|
+
if (list.length === 0) {
|
|
1106
|
+
style?.remove();
|
|
1107
|
+
return;
|
|
1012
1108
|
}
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1109
|
+
if (!style) {
|
|
1110
|
+
style = document.createElement("style");
|
|
1111
|
+
style.id = CUSTOM_FONT_STYLE_ID;
|
|
1112
|
+
document.head.appendChild(style);
|
|
1016
1113
|
}
|
|
1017
|
-
|
|
1114
|
+
style.textContent = list.flatMap(
|
|
1115
|
+
(font) => font.weights.map((w) => {
|
|
1116
|
+
const weightDescriptor = w.weightEnd ? `${w.weight} ${w.weightEnd}` : `${w.weight}`;
|
|
1117
|
+
return `@font-face { font-family: '${escapeCssString(font.family)}'; src: url('${escapeCssString(w.url)}') format('${w.format}'); font-weight: ${weightDescriptor}; font-display: swap; }`;
|
|
1118
|
+
})
|
|
1119
|
+
).join("\n");
|
|
1018
1120
|
}
|
|
1019
|
-
function
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
store ? [
|
|
1025
|
-
...Object.values(store.nodes).flatMap((n) => n.fontFamily ? [n.fontFamily] : []),
|
|
1026
|
-
...Object.values(store.sections).flatMap((s) => s.fontFamily ? [s.fontFamily] : [])
|
|
1027
|
-
] : []
|
|
1028
|
-
);
|
|
1029
|
-
if (!store) return;
|
|
1030
|
-
for (const [sectionId, override] of Object.entries(store.sections)) {
|
|
1031
|
-
const escaped = CSS.escape(sectionId);
|
|
1032
|
-
const byInstance = document.querySelectorAll(`[data-ohw-instance="${escaped}"]`);
|
|
1033
|
-
const sections = Array.from(
|
|
1034
|
-
byInstance.length ? byInstance : document.querySelectorAll(`[data-ohw-section="${escaped}"]`)
|
|
1035
|
-
);
|
|
1036
|
-
const isNavbar = sectionId === "navbar";
|
|
1037
|
-
if (isNavbar) {
|
|
1038
|
-
const drawer = document.querySelector("[data-ohw-nav-drawer]");
|
|
1039
|
-
if (drawer && !sections.includes(drawer)) sections.push(drawer);
|
|
1040
|
-
const header = document.querySelector("[data-ohw-nav-root]");
|
|
1041
|
-
if (header && !sections.includes(header)) sections.push(header);
|
|
1042
|
-
}
|
|
1043
|
-
for (const marker of Array.from(sections)) {
|
|
1044
|
-
const isDrawerMarker = isNavbar && marker.hasAttribute("data-ohw-nav-drawer");
|
|
1045
|
-
const section = marker.querySelector(":scope > [data-ai-section]") ?? marker;
|
|
1046
|
-
for (const [prop, attr] of Object.entries(SECTION_ATTRS)) {
|
|
1047
|
-
const value = override[prop];
|
|
1048
|
-
if (value === void 0) continue;
|
|
1049
|
-
if (prop === "sectionBackground" && override.sectionBackgroundColor !== void 0) continue;
|
|
1050
|
-
section.setAttribute(attr, String(value).replace(":", "-"));
|
|
1051
|
-
}
|
|
1052
|
-
const bgOverride = isDrawerMarker ? override.navbarMobileBackground : override.sectionBackgroundColor;
|
|
1053
|
-
const textOverride = isDrawerMarker ? override.navbarMobileTextColor : override.textColor;
|
|
1054
|
-
const bgValue = isNavbar ? resolveNavbarValue(bgOverride) : bgOverride;
|
|
1055
|
-
const textValue = isNavbar ? resolveNavbarValue(textOverride) : textOverride;
|
|
1056
|
-
if (bgValue !== void 0) {
|
|
1057
|
-
saveInline(section, "background");
|
|
1058
|
-
section.style.setProperty("background", bgValue, "important");
|
|
1059
|
-
section.setAttribute("data-ohw-style-bgcolor", "");
|
|
1060
|
-
}
|
|
1061
|
-
if (textValue !== void 0 || override.fontFamily !== void 0) {
|
|
1062
|
-
const textEls = marker.querySelectorAll(
|
|
1063
|
-
'[data-ohw-editable="text"], [data-ohw-editable="plain"], [data-ohw-field-label]'
|
|
1064
|
-
);
|
|
1065
|
-
for (const el of Array.from(textEls)) {
|
|
1066
|
-
if (textValue !== void 0) {
|
|
1067
|
-
saveInline(el, "color");
|
|
1068
|
-
el.style.setProperty("color", textValue, "important");
|
|
1069
|
-
el.setAttribute(NODE_WROTE_ATTR, "");
|
|
1070
|
-
}
|
|
1071
|
-
if (override.fontFamily !== void 0) {
|
|
1072
|
-
saveInline(el, "font-family");
|
|
1073
|
-
el.style.setProperty("font-family", `'${override.fontFamily}'`, "important");
|
|
1074
|
-
el.setAttribute(NODE_WROTE_ATTR, "");
|
|
1075
|
-
}
|
|
1076
|
-
}
|
|
1077
|
-
}
|
|
1121
|
+
function applyBrandToDom(kit) {
|
|
1122
|
+
const root = document.documentElement;
|
|
1123
|
+
if (!kit) {
|
|
1124
|
+
for (const name of [...BRAND_VAR_NAMES, ...LEGACY_BRAND_VAR_NAMES, ...BUTTON_VAR_NAMES]) {
|
|
1125
|
+
root.style.removeProperty(name);
|
|
1078
1126
|
}
|
|
1127
|
+
for (const name of [...FONT_VARS.heading, ...FONT_VARS.body]) root.style.removeProperty(name);
|
|
1128
|
+
document.getElementById(BRAND_FONT_LINK_ID)?.remove();
|
|
1129
|
+
document.getElementById(CUSTOM_FONT_STYLE_ID)?.remove();
|
|
1130
|
+
syncButtonRules(null);
|
|
1131
|
+
return;
|
|
1079
1132
|
}
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
}
|
|
1088
|
-
if (override.fontFamily !== void 0) {
|
|
1089
|
-
saveInline(el, "font-family");
|
|
1090
|
-
el.style.setProperty("font-family", `'${override.fontFamily}'`, "important");
|
|
1091
|
-
el.setAttribute(NODE_WROTE_ATTR, "");
|
|
1092
|
-
}
|
|
1093
|
-
if (override.fontSize !== void 0) {
|
|
1094
|
-
saveInline(el, "font-size");
|
|
1095
|
-
el.style.setProperty("font-size", `${override.fontSize}px`, "important");
|
|
1096
|
-
el.setAttribute(NODE_WROTE_ATTR, "");
|
|
1097
|
-
}
|
|
1098
|
-
if (override.align !== void 0) {
|
|
1099
|
-
const subject = alignSubjectOf(el);
|
|
1100
|
-
saveInline(subject, "text-align");
|
|
1101
|
-
saveInline(subject, "justify-content");
|
|
1102
|
-
subject.style.setProperty("text-align", override.align, "important");
|
|
1103
|
-
subject.style.setProperty(
|
|
1104
|
-
"justify-content",
|
|
1105
|
-
ALIGN_JUSTIFY[override.align] ?? "flex-start",
|
|
1106
|
-
"important"
|
|
1107
|
-
);
|
|
1108
|
-
subject.setAttribute(NODE_WROTE_ATTR, "");
|
|
1109
|
-
}
|
|
1133
|
+
const applyPalette = kit.touched ? kit.touched.palette === true : true;
|
|
1134
|
+
const applyFonts = kit.touched ? kit.touched.fonts === true : true;
|
|
1135
|
+
if (kit.palette) {
|
|
1136
|
+
if (applyPalette) {
|
|
1137
|
+
for (const [name, value] of Object.entries(brandColorVars(kit.palette))) root.style.setProperty(name, value);
|
|
1138
|
+
} else {
|
|
1139
|
+
for (const name of [...BRAND_VAR_NAMES, ...LEGACY_BRAND_VAR_NAMES]) root.style.removeProperty(name);
|
|
1110
1140
|
}
|
|
1111
1141
|
}
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
const
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1142
|
+
if (kit.buttons) {
|
|
1143
|
+
for (const name of BUTTON_VAR_NAMES) root.style.removeProperty(name);
|
|
1144
|
+
for (const [name, value] of Object.entries(buttonStyleVars({ ...kit, buttons: kit.buttons }))) root.style.setProperty(name, value);
|
|
1145
|
+
}
|
|
1146
|
+
syncButtonRules(kit.buttons ? kit : null);
|
|
1147
|
+
if (kit.customFonts) {
|
|
1148
|
+
loadCustomFonts(kit.customFonts);
|
|
1149
|
+
}
|
|
1150
|
+
if (kit.fonts) {
|
|
1151
|
+
if (applyFonts) {
|
|
1152
|
+
const heading = quoteFamily(kit.fonts.heading);
|
|
1153
|
+
const body = quoteFamily(kit.fonts.body);
|
|
1154
|
+
for (const name of FONT_VARS.heading) root.style.setProperty(name, heading);
|
|
1155
|
+
for (const name of FONT_VARS.body) root.style.setProperty(name, body);
|
|
1156
|
+
const customFamilies = new Set((kit.customFonts ?? []).map((f) => f.family));
|
|
1157
|
+
const googleFamilies = [familyOf(kit.fonts.heading), familyOf(kit.fonts.body)].filter(
|
|
1158
|
+
(f) => !customFamilies.has(f)
|
|
1120
1159
|
);
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
}
|
|
1160
|
+
loadBrandFonts(googleFamilies);
|
|
1161
|
+
} else {
|
|
1162
|
+
for (const name of [...FONT_VARS.heading, ...FONT_VARS.body]) root.style.removeProperty(name);
|
|
1163
|
+
document.getElementById(BRAND_FONT_LINK_ID)?.remove();
|
|
1126
1164
|
}
|
|
1127
1165
|
}
|
|
1128
1166
|
}
|
|
1129
1167
|
|
|
1168
|
+
// src/lib/custom-code.ts
|
|
1169
|
+
var ALLOWED_HEAD_TAGS = /* @__PURE__ */ new Set(["style", "meta", "link", "script", "noscript", "base"]);
|
|
1170
|
+
var CUSTOM_CODE_AFTER_HEAD_KEY = "__ohw_custom_code_after_head";
|
|
1171
|
+
var CUSTOM_CODE_BEFORE_HEAD_KEY = "__ohw_custom_code_before_head";
|
|
1172
|
+
function clearExisting(slot) {
|
|
1173
|
+
document.querySelectorAll(`[data-ohw-custom-code="${slot}"]`).forEach((el) => el.remove());
|
|
1174
|
+
}
|
|
1175
|
+
function buildNodes(raw, slot) {
|
|
1176
|
+
if (!raw || !raw.trim()) return [];
|
|
1177
|
+
const parsed = new DOMParser().parseFromString(raw, "text/html");
|
|
1178
|
+
const source = [...parsed.head.childNodes, ...parsed.body.childNodes];
|
|
1179
|
+
const nodes = [];
|
|
1180
|
+
for (const node of source) {
|
|
1181
|
+
if (node.nodeType !== Node.ELEMENT_NODE) continue;
|
|
1182
|
+
const el = node;
|
|
1183
|
+
const tag = el.tagName.toLowerCase();
|
|
1184
|
+
if (!ALLOWED_HEAD_TAGS.has(tag)) continue;
|
|
1185
|
+
const built = document.createElement(tag);
|
|
1186
|
+
for (const attr of Array.from(el.attributes)) built.setAttribute(attr.name, attr.value);
|
|
1187
|
+
if (tag === "script" || tag === "style" || tag === "noscript") built.textContent = el.textContent;
|
|
1188
|
+
built.dataset.ohwCustomCode = slot;
|
|
1189
|
+
nodes.push(built);
|
|
1190
|
+
}
|
|
1191
|
+
return nodes;
|
|
1192
|
+
}
|
|
1193
|
+
function applyCustomCodeToHead(content) {
|
|
1194
|
+
clearExisting("after");
|
|
1195
|
+
clearExisting("before");
|
|
1196
|
+
const afterNodes = buildNodes(content[CUSTOM_CODE_AFTER_HEAD_KEY], "after");
|
|
1197
|
+
if (afterNodes.length > 0) document.head.prepend(...afterNodes);
|
|
1198
|
+
const beforeNodes = buildNodes(content[CUSTOM_CODE_BEFORE_HEAD_KEY], "before");
|
|
1199
|
+
if (beforeNodes.length > 0) document.head.append(...beforeNodes);
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1130
1202
|
// src/ui/ai-tree/aiSectionsManager.tsx
|
|
1131
1203
|
var import_react_dom = require("react-dom");
|
|
1132
1204
|
var import_client = require("react-dom/client");
|
|
@@ -3751,7 +3823,9 @@ function AiTreeRenderer({
|
|
|
3751
3823
|
...band ? { buttonLabel: band.buttonLabel } : {}
|
|
3752
3824
|
};
|
|
3753
3825
|
const settings = tree.settings ?? {};
|
|
3754
|
-
const pad = AI_TREE_TOKENS.
|
|
3826
|
+
const pad = AI_TREE_TOKENS.sectionPaddingByScale[settings.verticalPadding ?? legacyVerticalPaddingAlias(settings.spacing) ?? "m"];
|
|
3827
|
+
const padCss = pad === 0 ? "0px" : `clamp(${Math.max(16, Math.round(pad * 0.5))}px, ${(pad / 9).toFixed(2)}vw, ${pad}px)`;
|
|
3828
|
+
const minHeightCss = settings.minHeight === "full" ? "var(--ohw-canvas-h, calc(100vh - 80px))" : settings.minHeight === "half" ? "calc(var(--ohw-canvas-h, 100vh) / 2)" : void 0;
|
|
3755
3829
|
const isOverlay = tree.scaffold === "overlay";
|
|
3756
3830
|
const overlayAlpha = { low: 0.25, medium: 0.45, high: 0.65 }[settings.overlayIntensity ?? "medium"];
|
|
3757
3831
|
const backgroundUrl = isOverlay && settings.backgroundMedia ? ctx.resolveMedia(settings.backgroundMedia) : null;
|
|
@@ -3791,7 +3865,9 @@ function AiTreeRenderer({
|
|
|
3791
3865
|
"data-ai-responsive": "",
|
|
3792
3866
|
style: {
|
|
3793
3867
|
position: "relative",
|
|
3794
|
-
padding: `${
|
|
3868
|
+
padding: `${padCss} 0`,
|
|
3869
|
+
minHeight: minHeightCss,
|
|
3870
|
+
boxSizing: "border-box",
|
|
3795
3871
|
background: toneBackground ?? (isOverlay && !backgroundUrl ? "#DBEAFE" : void 0),
|
|
3796
3872
|
backgroundImage: backgroundUrl ? `url(${backgroundUrl})` : void 0,
|
|
3797
3873
|
backgroundSize: "cover",
|
|
@@ -3915,7 +3991,7 @@ function AiFooterRenderer({
|
|
|
3915
3991
|
style: {
|
|
3916
3992
|
background: bandBg,
|
|
3917
3993
|
color: bandText,
|
|
3918
|
-
padding: `${AI_TREE_TOKENS.
|
|
3994
|
+
padding: `${AI_TREE_TOKENS.sectionPaddingByScale.m}px 0 ${AI_TREE_TOKENS.spacing8}px`
|
|
3919
3995
|
},
|
|
3920
3996
|
children: [
|
|
3921
3997
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_FOOTER_MOBILE_CSS }),
|
|
@@ -27841,19 +27917,20 @@ function OhhwellsBridge() {
|
|
|
27841
27917
|
let previousSections;
|
|
27842
27918
|
const store = parseStyleStore(value);
|
|
27843
27919
|
if (store) {
|
|
27844
|
-
const
|
|
27845
|
-
|
|
27846
|
-
|
|
27920
|
+
const foldedAlign = foldAlignIntoTrees(parseAiSectionsState(aiSectionsRef.current), store);
|
|
27921
|
+
const foldedHeight = foldSectionHeightIntoTrees(foldedAlign.state, foldedAlign.store);
|
|
27922
|
+
if (foldedAlign.changed || foldedHeight.changed) {
|
|
27923
|
+
const nextSections = serializeAiSectionsState(foldedHeight.state);
|
|
27847
27924
|
if (nextSections !== aiSectionsRef.current) {
|
|
27848
27925
|
previousSections = aiSectionsRef.current;
|
|
27849
27926
|
aiSectionsRef.current = nextSections;
|
|
27850
|
-
applyAiSectionsToDom(
|
|
27927
|
+
applyAiSectionsToDom(foldedHeight.state);
|
|
27851
27928
|
postToParentRef.current({
|
|
27852
27929
|
type: "ow:change",
|
|
27853
27930
|
nodes: [{ key: AI_SECTIONS_KEY, text: nextSections }]
|
|
27854
27931
|
});
|
|
27855
27932
|
}
|
|
27856
|
-
value = JSON.stringify(
|
|
27933
|
+
value = JSON.stringify(foldedHeight.store);
|
|
27857
27934
|
}
|
|
27858
27935
|
}
|
|
27859
27936
|
stylesRef.current = value;
|
|
@@ -28478,7 +28555,15 @@ function OhhwellsBridge() {
|
|
|
28478
28555
|
width: box.width,
|
|
28479
28556
|
height: box.height
|
|
28480
28557
|
} : null;
|
|
28481
|
-
|
|
28558
|
+
const settings = resolveSectionSettingsContext(sectionId, aiSectionsRef.current, stylesRef.current);
|
|
28559
|
+
postToParentRef.current({
|
|
28560
|
+
type: "ow:section-nodes",
|
|
28561
|
+
sectionId,
|
|
28562
|
+
found: !!sectionEl,
|
|
28563
|
+
nodes,
|
|
28564
|
+
rect,
|
|
28565
|
+
...settings ? { settings } : {}
|
|
28566
|
+
});
|
|
28482
28567
|
};
|
|
28483
28568
|
let selectionChangeRaf = null;
|
|
28484
28569
|
const runSelectionChange = () => {
|
|
@@ -29145,7 +29230,7 @@ function OhhwellsBridge() {
|
|
|
29145
29230
|
postToParent2({
|
|
29146
29231
|
type: "ow:ready",
|
|
29147
29232
|
version: "1",
|
|
29148
|
-
bridgeVersion: "0.1.
|
|
29233
|
+
bridgeVersion: "0.1.127",
|
|
29149
29234
|
path: pathname,
|
|
29150
29235
|
nodes: collectEditableNodes(editContentRef.current),
|
|
29151
29236
|
sections
|