@reddoorla/maintenance 0.72.0 → 0.75.0
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/airtable-6L3JD7FY.js +9 -0
- package/dist/{announce-HJCGEVJA.js → announce-V6EYPPUR.js} +4 -4
- package/dist/{blux-FKZYMKLF.js → blux-C24OVDDW.js} +1044 -69
- package/dist/blux-C24OVDDW.js.map +1 -0
- package/dist/{bump-deps-GZEJAUKI.js → bump-deps-OQR3IXLN.js} +2 -2
- package/dist/chunk-2OP4JKIQ.js +19 -0
- package/dist/chunk-2OP4JKIQ.js.map +1 -0
- package/dist/{chunk-BJCA7O4A.js → chunk-3PU2QWOJ.js} +2 -2
- package/dist/{chunk-GI4M2AMO.js → chunk-4BHNP6Y2.js} +2 -2
- package/dist/{chunk-KRHPS3R4.js → chunk-6OYICQSJ.js} +47 -3
- package/dist/chunk-6OYICQSJ.js.map +1 -0
- package/dist/{chunk-BWKK2ZXS.js → chunk-GXVX4HUH.js} +32 -2
- package/dist/chunk-GXVX4HUH.js.map +1 -0
- package/dist/{chunk-KLB2OLPW.js → chunk-HBFMTM2E.js} +3 -3
- package/dist/{chunk-SQJ53Z7R.js → chunk-I55YWV6T.js} +3 -3
- package/dist/{chunk-7P7YETHG.js → chunk-IDBMYDVH.js} +2 -2
- package/dist/{chunk-JGLAWXUC.js → chunk-JR67FZVC.js} +7 -3
- package/dist/chunk-JR67FZVC.js.map +1 -0
- package/dist/{chunk-APTAITXZ.js → chunk-KKL3RYFQ.js} +2 -2
- package/dist/{chunk-NRISG4XD.js → chunk-NUIBQPB3.js} +42 -11
- package/dist/chunk-NUIBQPB3.js.map +1 -0
- package/dist/{chunk-BHE5CUWI.js → chunk-PERP7LSS.js} +269 -18
- package/dist/chunk-PERP7LSS.js.map +1 -0
- package/dist/chunk-RHPDQW6D.js +29 -0
- package/dist/chunk-RHPDQW6D.js.map +1 -0
- package/dist/{chunk-HR5SONJC.js → chunk-SK44IFZN.js} +2 -2
- package/dist/cli/bin.js +31 -19
- package/dist/cli/bin.js.map +1 -1
- package/dist/cli/commands/audit.js +6 -6
- package/dist/{convert-to-pnpm-SVFDLXZS.js → convert-to-pnpm-OVMASATZ.js} +2 -2
- package/dist/{digest-VX7Q4MNF.js → digest-LEMZS5ZK.js} +99 -7
- package/dist/digest-LEMZS5ZK.js.map +1 -0
- package/dist/{ensure-site-O7G5ZJS7.js → ensure-site-3SOAWTZF.js} +2 -2
- package/dist/forms/index.js +1 -1
- package/dist/{github-signals-QVW2LB7N.js → github-signals-TF3VSNIN.js} +4 -4
- package/dist/{health-endpoint-IYR5VBUT.js → health-endpoint-JZGYCMTV.js} +2 -2
- package/dist/index.d.ts +104 -19
- package/dist/index.js +122 -30
- package/dist/index.js.map +1 -1
- package/dist/{init-CU6PVZRG.js → init-7PTP4O2K.js} +4 -4
- package/dist/{launch-JWXCTJRG.js → launch-W33FWWOZ.js} +6 -6
- package/dist/{lighthouse-airtable-J5TLASJC.js → lighthouse-airtable-ZXNMDEPE.js} +3 -3
- package/dist/{onboard-6UQE5QIO.js → onboard-PX36YDZA.js} +2 -2
- package/dist/{orchestrate-ZDH24Z6Y.js → orchestrate-NYRYEKQM.js} +3 -3
- package/dist/{preflight-BNTMWTPG.js → preflight-JCQFKB6M.js} +4 -4
- package/dist/{renovate-dispatch-C3ALLACX.js → renovate-dispatch-6II2JVOX.js} +29 -27
- package/dist/renovate-dispatch-6II2JVOX.js.map +1 -0
- package/dist/{report-YLFSGNWR.js → report-POHQEV6F.js} +21 -5
- package/dist/report-POHQEV6F.js.map +1 -0
- package/dist/screenouts-7P5GOZMV.js +57 -0
- package/dist/screenouts-7P5GOZMV.js.map +1 -0
- package/dist/{self-updating-64FNRQB5.js → self-updating-2YSVNMPP.js} +2 -2
- package/dist/{selftest-7DPNGL6R.js → selftest-OO2WDSDM.js} +5 -5
- package/dist/{smoke-suite-TWILWDSF.js → smoke-suite-CSP23NZM.js} +2 -2
- package/dist/spam-classifier-VG4ICIWS.js +217 -0
- package/dist/spam-classifier-VG4ICIWS.js.map +1 -0
- package/dist/submissions-BNRGY3JQ.js +122 -0
- package/dist/submissions-BNRGY3JQ.js.map +1 -0
- package/dist/submissions-MDKWJYMM.js +281 -0
- package/dist/submissions-MDKWJYMM.js.map +1 -0
- package/dist/{svelte-codemods-RVDTTXFD.js → svelte-codemods-V54KOBWU.js} +2 -2
- package/dist/{sync-configs-BUUD6L5Q.js → sync-configs-UBCJRASZ.js} +2 -2
- package/dist/{upgrade-HHKWYJRN.js → upgrade-WR6ICPAG.js} +2 -2
- package/dist/{websites-EH2ULELZ.js → websites-U6642XSE.js} +10 -2
- package/dist/{write-audits-to-airtable-LZDYTNX5.js → write-audits-to-airtable-22T6D733.js} +4 -4
- package/package.json +1 -1
- package/dist/airtable-U2GTDTK5.js +0 -9
- package/dist/blux-FKZYMKLF.js.map +0 -1
- package/dist/chunk-BHE5CUWI.js.map +0 -1
- package/dist/chunk-BWKK2ZXS.js.map +0 -1
- package/dist/chunk-JGLAWXUC.js.map +0 -1
- package/dist/chunk-KRHPS3R4.js.map +0 -1
- package/dist/chunk-NRISG4XD.js.map +0 -1
- package/dist/chunk-QTHKOIFJ.js +0 -13
- package/dist/chunk-QTHKOIFJ.js.map +0 -1
- package/dist/digest-VX7Q4MNF.js.map +0 -1
- package/dist/renovate-dispatch-C3ALLACX.js.map +0 -1
- package/dist/report-YLFSGNWR.js.map +0 -1
- /package/dist/{airtable-U2GTDTK5.js.map → airtable-6L3JD7FY.js.map} +0 -0
- /package/dist/{announce-HJCGEVJA.js.map → announce-V6EYPPUR.js.map} +0 -0
- /package/dist/{bump-deps-GZEJAUKI.js.map → bump-deps-OQR3IXLN.js.map} +0 -0
- /package/dist/{chunk-BJCA7O4A.js.map → chunk-3PU2QWOJ.js.map} +0 -0
- /package/dist/{chunk-GI4M2AMO.js.map → chunk-4BHNP6Y2.js.map} +0 -0
- /package/dist/{chunk-KLB2OLPW.js.map → chunk-HBFMTM2E.js.map} +0 -0
- /package/dist/{chunk-SQJ53Z7R.js.map → chunk-I55YWV6T.js.map} +0 -0
- /package/dist/{chunk-7P7YETHG.js.map → chunk-IDBMYDVH.js.map} +0 -0
- /package/dist/{chunk-APTAITXZ.js.map → chunk-KKL3RYFQ.js.map} +0 -0
- /package/dist/{chunk-HR5SONJC.js.map → chunk-SK44IFZN.js.map} +0 -0
- /package/dist/{convert-to-pnpm-SVFDLXZS.js.map → convert-to-pnpm-OVMASATZ.js.map} +0 -0
- /package/dist/{ensure-site-O7G5ZJS7.js.map → ensure-site-3SOAWTZF.js.map} +0 -0
- /package/dist/{github-signals-QVW2LB7N.js.map → github-signals-TF3VSNIN.js.map} +0 -0
- /package/dist/{health-endpoint-IYR5VBUT.js.map → health-endpoint-JZGYCMTV.js.map} +0 -0
- /package/dist/{init-CU6PVZRG.js.map → init-7PTP4O2K.js.map} +0 -0
- /package/dist/{launch-JWXCTJRG.js.map → launch-W33FWWOZ.js.map} +0 -0
- /package/dist/{lighthouse-airtable-J5TLASJC.js.map → lighthouse-airtable-ZXNMDEPE.js.map} +0 -0
- /package/dist/{onboard-6UQE5QIO.js.map → onboard-PX36YDZA.js.map} +0 -0
- /package/dist/{orchestrate-ZDH24Z6Y.js.map → orchestrate-NYRYEKQM.js.map} +0 -0
- /package/dist/{preflight-BNTMWTPG.js.map → preflight-JCQFKB6M.js.map} +0 -0
- /package/dist/{self-updating-64FNRQB5.js.map → self-updating-2YSVNMPP.js.map} +0 -0
- /package/dist/{selftest-7DPNGL6R.js.map → selftest-OO2WDSDM.js.map} +0 -0
- /package/dist/{smoke-suite-TWILWDSF.js.map → smoke-suite-CSP23NZM.js.map} +0 -0
- /package/dist/{svelte-codemods-RVDTTXFD.js.map → svelte-codemods-V54KOBWU.js.map} +0 -0
- /package/dist/{sync-configs-BUUD6L5Q.js.map → sync-configs-UBCJRASZ.js.map} +0 -0
- /package/dist/{upgrade-HHKWYJRN.js.map → upgrade-WR6ICPAG.js.map} +0 -0
- /package/dist/{websites-EH2ULELZ.js.map → websites-U6642XSE.js.map} +0 -0
- /package/dist/{write-audits-to-airtable-LZDYTNX5.js.map → write-audits-to-airtable-22T6D733.js.map} +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
CDN_HOSTS,
|
|
2
3
|
collectAssetUrls
|
|
3
4
|
} from "./chunk-SL24DCGC.js";
|
|
4
5
|
|
|
@@ -188,12 +189,26 @@ function slugify(s) {
|
|
|
188
189
|
}
|
|
189
190
|
function normalizePages(raw) {
|
|
190
191
|
const diagnostics = [];
|
|
191
|
-
const
|
|
192
|
-
|
|
192
|
+
const seen = /* @__PURE__ */ new Set();
|
|
193
|
+
const pages = raw.pages.map((p, i) => {
|
|
194
|
+
const urlSlug = String(p.url ?? "").trim();
|
|
195
|
+
const path = i === 0 ? "" : urlSlug ? slugify(urlSlug) : slugify(String(p.title ?? ""));
|
|
196
|
+
let uid = i === 0 ? "home" : path;
|
|
197
|
+
if (seen.has(uid)) {
|
|
198
|
+
const base2 = uid;
|
|
199
|
+
for (let n = 2; seen.has(uid); n++) uid = `${base2}-${n}`;
|
|
200
|
+
diagnostics.push({
|
|
201
|
+
kind: "duplicate-page-uid",
|
|
202
|
+
where: base2,
|
|
203
|
+
message: `page ${i} ("${String(p.title ?? "")}") slugs to "${base2}" already used \u2014 renamed to "${uid}"`
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
seen.add(uid);
|
|
193
207
|
return {
|
|
194
208
|
uid,
|
|
195
209
|
title: String(p.title ?? ""),
|
|
196
210
|
description: String(p.description ?? ""),
|
|
211
|
+
path,
|
|
197
212
|
sections: (p.items ?? []).map((b) => sectionFromBlock(b, uid, diagnostics))
|
|
198
213
|
};
|
|
199
214
|
});
|
|
@@ -212,6 +227,7 @@ function normalizeTheme(raw) {
|
|
|
212
227
|
const m = entry[innerKey] ?? {};
|
|
213
228
|
const transform = cleanCssValue(m["text-transform"]);
|
|
214
229
|
const tracking = cleanCssValue(m["letter-spacing"]);
|
|
230
|
+
const margin = cleanCssValue(m["margin"]);
|
|
215
231
|
const mobileSize = cleanCssValue(m["__media_mobile_font-size"]);
|
|
216
232
|
const mobileLineHeight = cleanCssValue(m["__media_mobile_line-height"]);
|
|
217
233
|
textStyles.push({
|
|
@@ -224,10 +240,38 @@ function normalizeTheme(raw) {
|
|
|
224
240
|
lineHeight: cleanCssValue(m["line-height"]) || "1.5",
|
|
225
241
|
...transform && transform !== "none" ? { transform } : {},
|
|
226
242
|
...tracking ? { letterSpacing: tracking } : {},
|
|
243
|
+
...margin && margin !== "0" ? { margin } : {},
|
|
227
244
|
...mobileSize ? { mobileSize } : {},
|
|
228
245
|
...mobileLineHeight ? { mobileLineHeight } : {}
|
|
229
246
|
});
|
|
230
247
|
}
|
|
248
|
+
const cleanCssMap = (v) => {
|
|
249
|
+
const out = {};
|
|
250
|
+
for (const [k, val] of Object.entries(v ?? {})) {
|
|
251
|
+
if (k === "font-ident") continue;
|
|
252
|
+
const s = cleanCssValue(typeof val === "number" ? String(val) : val);
|
|
253
|
+
if (s) out[k] = s;
|
|
254
|
+
}
|
|
255
|
+
return out;
|
|
256
|
+
};
|
|
257
|
+
const buttonStyles = [];
|
|
258
|
+
for (const v of Object.values(raw.styles.buttons ?? {})) {
|
|
259
|
+
const entry = v ?? {};
|
|
260
|
+
const innerKey = Object.keys(entry).find((k) => /^\.buttons\d+$/.test(k));
|
|
261
|
+
if (!innerKey) continue;
|
|
262
|
+
const css = cleanCssMap(entry[innerKey]);
|
|
263
|
+
if (Object.keys(css).length === 0) continue;
|
|
264
|
+
const hover = cleanCssMap(entry[`${innerKey}:hover`]);
|
|
265
|
+
const active = cleanCssMap(entry[`${innerKey}:active`]);
|
|
266
|
+
buttonStyles.push({
|
|
267
|
+
role: innerKey.slice(1),
|
|
268
|
+
// ".buttons2" -> "buttons2"
|
|
269
|
+
label: str(entry._label),
|
|
270
|
+
css,
|
|
271
|
+
...Object.keys(hover).length ? { hover } : {},
|
|
272
|
+
...Object.keys(active).length ? { active } : {}
|
|
273
|
+
});
|
|
274
|
+
}
|
|
231
275
|
const fonts = raw.settings.fonts ?? {};
|
|
232
276
|
const roleFont = (r) => textStyles.find((t) => t.role === r)?.fontFamily ?? "";
|
|
233
277
|
return {
|
|
@@ -240,7 +284,8 @@ function normalizeTheme(raw) {
|
|
|
240
284
|
parseGoogleFonts(str(fonts.google)),
|
|
241
285
|
typekitFontLoads(str(fonts.string))
|
|
242
286
|
),
|
|
243
|
-
textStyles
|
|
287
|
+
textStyles,
|
|
288
|
+
buttonStyles
|
|
244
289
|
};
|
|
245
290
|
}
|
|
246
291
|
|
|
@@ -330,8 +375,17 @@ function assembleIR(input) {
|
|
|
330
375
|
alt: String(m.name ?? "")
|
|
331
376
|
};
|
|
332
377
|
});
|
|
378
|
+
const faviconId = raw.settings.favicon?.media;
|
|
379
|
+
const favicon = faviconId ? { assetId: faviconId, sourceUrl: urlMap.get(faviconId) ?? null } : void 0;
|
|
380
|
+
if (favicon && !favicon.sourceUrl) {
|
|
381
|
+
diagnostics.push({
|
|
382
|
+
kind: "unresolved-asset",
|
|
383
|
+
where: favicon.assetId,
|
|
384
|
+
message: `no CDN url for favicon ${favicon.assetId}`
|
|
385
|
+
});
|
|
386
|
+
}
|
|
333
387
|
return {
|
|
334
|
-
meta: raw.meta,
|
|
388
|
+
meta: { ...raw.meta, ...favicon ? { favicon } : {} },
|
|
335
389
|
theme,
|
|
336
390
|
pages,
|
|
337
391
|
collections,
|
|
@@ -644,6 +698,7 @@ function emitThemeCss(theme) {
|
|
|
644
698
|
if (t.fontFamily) lines.push(` --text-${t.role}--font-family: ${t.fontFamily};`);
|
|
645
699
|
if (t.transform) lines.push(` --text-${t.role}--text-transform: ${t.transform};`);
|
|
646
700
|
if (t.letterSpacing) lines.push(` --text-${t.role}--letter-spacing: ${t.letterSpacing};`);
|
|
701
|
+
if (t.margin) lines.push(` --text-${t.role}--margin: ${t.margin};`);
|
|
647
702
|
if (t.mobileSize) lines.push(` --text-${t.role}--mobile-font-size: ${t.mobileSize};`);
|
|
648
703
|
if (t.mobileLineHeight)
|
|
649
704
|
lines.push(` --text-${t.role}--mobile-line-height: ${t.mobileLineHeight};`);
|
|
@@ -667,12 +722,40 @@ function emitRolesCss(theme) {
|
|
|
667
722
|
` line-height: var(--text-${r}--line-height);`,
|
|
668
723
|
` letter-spacing: var(--text-${r}--letter-spacing, normal);`,
|
|
669
724
|
` text-transform: var(--text-${r}--text-transform, none);`,
|
|
670
|
-
` margin: 0;`,
|
|
725
|
+
` margin: var(--text-${r}--margin, 0);`,
|
|
671
726
|
`}`
|
|
672
727
|
);
|
|
673
728
|
}
|
|
674
729
|
return lines.join("\n") + "\n";
|
|
675
730
|
}
|
|
731
|
+
function emitButtonsCss(theme) {
|
|
732
|
+
if (!theme.buttonStyles.length) return "";
|
|
733
|
+
const lines = [
|
|
734
|
+
"/* Button skins \u2014 one .buttonsN per Blux button style; raw anchors carry",
|
|
735
|
+
" `ib middle buttonsN` verbatim. Generated by blux emit. */",
|
|
736
|
+
".ib {",
|
|
737
|
+
" display: inline-block;",
|
|
738
|
+
"}",
|
|
739
|
+
"/* Text links (`ib middle links`) are underlined by the Blux platform CSS;",
|
|
740
|
+
" an inline-block box does not inherit an ancestor's text-decoration, so",
|
|
741
|
+
" the affordance must be declared on the anchor itself. */",
|
|
742
|
+
".links {",
|
|
743
|
+
" text-decoration: underline;",
|
|
744
|
+
"}"
|
|
745
|
+
];
|
|
746
|
+
const rule = (selector, css) => {
|
|
747
|
+
lines.push(`${selector} {`);
|
|
748
|
+
for (const [k, v] of Object.entries(css)) lines.push(` ${k}: ${v};`);
|
|
749
|
+
lines.push("}");
|
|
750
|
+
};
|
|
751
|
+
for (const b of theme.buttonStyles) {
|
|
752
|
+
if (b.label) lines.push(`/* ${b.role} \u2014 ${b.label} */`);
|
|
753
|
+
rule(`.${b.role}`, b.css);
|
|
754
|
+
if (b.hover) rule(`.${b.role}:hover`, b.hover);
|
|
755
|
+
if (b.active) rule(`.${b.role}:active`, b.active);
|
|
756
|
+
}
|
|
757
|
+
return lines.join("\n") + "\n";
|
|
758
|
+
}
|
|
676
759
|
|
|
677
760
|
// src/blux/emit/review.ts
|
|
678
761
|
function buildReviewManifest(ir, opts) {
|
|
@@ -769,6 +852,28 @@ function cssProp(style, prop) {
|
|
|
769
852
|
const m = new RegExp(`(?:^|;|\\s)${prop}\\s*:\\s*([^;]+)`, "i").exec(style);
|
|
770
853
|
return m?.[1]?.trim();
|
|
771
854
|
}
|
|
855
|
+
var MARGIN_UTIL_RE = /\bmargin-(\d+)(r|l|t|b)\b/g;
|
|
856
|
+
var MARGIN_SIDES = { r: "right", l: "left", t: "top", b: "bottom" };
|
|
857
|
+
function utilityStylesFromClass(className) {
|
|
858
|
+
const out = {};
|
|
859
|
+
for (const m of className.matchAll(MARGIN_UTIL_RE)) {
|
|
860
|
+
const [, n, side] = m;
|
|
861
|
+
if (n && side) out[`margin-${MARGIN_SIDES[side]}`] = `${n}%`;
|
|
862
|
+
}
|
|
863
|
+
return out;
|
|
864
|
+
}
|
|
865
|
+
function textLeafStyle(el) {
|
|
866
|
+
const out = utilityStylesFromClass(el.classNames ?? "");
|
|
867
|
+
for (const decl of (el.getAttribute("style") ?? "").split(";")) {
|
|
868
|
+
const colon = decl.indexOf(":");
|
|
869
|
+
if (colon < 0) continue;
|
|
870
|
+
const prop = decl.slice(0, colon).trim().toLowerCase();
|
|
871
|
+
const value = decl.slice(colon + 1).trim();
|
|
872
|
+
if (!value) continue;
|
|
873
|
+
if (prop === "color" || prop === "padding" || prop.startsWith("margin")) out[prop] = value;
|
|
874
|
+
}
|
|
875
|
+
return Object.keys(out).length ? out : null;
|
|
876
|
+
}
|
|
772
877
|
var CENTERED = /* @__PURE__ */ new Set(["center", "center center", "50% 50%", "50%"]);
|
|
773
878
|
function readBgSizing(el) {
|
|
774
879
|
const style = el.getAttribute("style") ?? "";
|
|
@@ -860,17 +965,93 @@ function mediaFromElement(el) {
|
|
|
860
965
|
var DEFAULT_TOKEN = { cols: 1, raw: "grid-1" };
|
|
861
966
|
var isElement = (n) => n.tagName !== void 0 && n.tagName !== null;
|
|
862
967
|
var hasClass = (el, c) => el.classNames.split(/\s+/).includes(c);
|
|
968
|
+
var isFeedTemplate = (el) => {
|
|
969
|
+
const id = el.getAttribute("id") ?? "";
|
|
970
|
+
const style = el.getAttribute("style") ?? "";
|
|
971
|
+
const hidden = /display\s*:\s*none/i.test(style);
|
|
972
|
+
return hidden && (/-template$/.test(id) || /\{\{[a-z_]+\}\}/i.test(el.innerHTML));
|
|
973
|
+
};
|
|
863
974
|
var isMediaHolder = (el) => hasClass(el, "block-media-holder") || hasClass(el, "camediaload") && !!el.getAttribute("data-media") || el.tagName === "VIDEO";
|
|
864
975
|
var isLeafElement = (el) => hasClass(el, "block-title") || hasClass(el, "block-body") || hasClass(el, "block-subtitle") || isMediaHolder(el);
|
|
865
976
|
var isLeafAnchor = (el) => el.tagName === "A" && collectStructuralChildren(el).length === 0;
|
|
866
977
|
var isStructural = (el) => isLeafElement(el) || el.hasAttribute("data-exec") || // Blux custom-code embed (e.g. map mount)
|
|
867
978
|
hasClass(el, "cagrid") || isLeafAnchor(el) || parseGridToken(el.classNames) !== null;
|
|
868
|
-
|
|
979
|
+
var isCellBoundary = (el) => hasClass(el, "block-grid-container") || hasClass(el, "block-subcontent");
|
|
980
|
+
function inlineBg(el) {
|
|
981
|
+
const c = cssProp(el.getAttribute("style") ?? "", "background-color")?.trim();
|
|
982
|
+
if (!c) return void 0;
|
|
983
|
+
const low = c.toLowerCase().replace(/\s+/g, "");
|
|
984
|
+
if (low === "transparent" || low === "rgba(0,0,0,0)") return void 0;
|
|
985
|
+
return c;
|
|
986
|
+
}
|
|
987
|
+
function inlinePadding(el) {
|
|
988
|
+
const c = cssProp(el.getAttribute("style") ?? "", "padding")?.trim();
|
|
989
|
+
if (!c) return void 0;
|
|
990
|
+
if (/^(0(px|%|em|rem)?\s*)+$/i.test(c)) return void 0;
|
|
991
|
+
return c;
|
|
992
|
+
}
|
|
993
|
+
function inlineMinHeight(el) {
|
|
994
|
+
const c = cssProp(el.getAttribute("style") ?? "", "min-height")?.trim();
|
|
995
|
+
if (!c) return void 0;
|
|
996
|
+
if (/^(0(px|%|em|rem|vh|vw)?|auto)$/i.test(c)) return void 0;
|
|
997
|
+
return c;
|
|
998
|
+
}
|
|
999
|
+
function layerBackground(el) {
|
|
1000
|
+
for (const child of el.childNodes) {
|
|
1001
|
+
if (!isElement(child) || !hasClass(child, "block-background-layer")) continue;
|
|
1002
|
+
const s = child.getAttribute("style") ?? "";
|
|
1003
|
+
const bg = (cssProp(s, "background") ?? cssProp(s, "background-color"))?.trim();
|
|
1004
|
+
if (bg && !/^(none|transparent)$/i.test(bg)) return bg;
|
|
1005
|
+
}
|
|
1006
|
+
return void 0;
|
|
1007
|
+
}
|
|
1008
|
+
function collectStructuralChildren(el, inherited = {}) {
|
|
869
1009
|
const out = [];
|
|
1010
|
+
const layerBg = inherited.nested === true ? layerBackground(el) : void 0;
|
|
1011
|
+
const base2 = layerBg !== void 0 ? { ...inherited, layerBackground: layerBg } : inherited;
|
|
870
1012
|
for (const child of el.childNodes) {
|
|
871
1013
|
if (!isElement(child)) continue;
|
|
872
|
-
if (
|
|
873
|
-
|
|
1014
|
+
if (hasClass(child, "block-background-layer")) continue;
|
|
1015
|
+
if (isFeedTemplate(child)) continue;
|
|
1016
|
+
const nested = base2.nested === true || isCellBoundary(child);
|
|
1017
|
+
const background = inlineBg(child) ?? base2.background;
|
|
1018
|
+
const padding = (nested ? inlinePadding(child) : void 0) ?? base2.padding;
|
|
1019
|
+
const minHeight = (nested ? inlineMinHeight(child) : void 0) ?? base2.minHeight;
|
|
1020
|
+
const valign = nested && hasClass(child, "valignmiddle") || base2.valign === true;
|
|
1021
|
+
const card = {
|
|
1022
|
+
...background !== void 0 ? { background } : {},
|
|
1023
|
+
...padding !== void 0 ? { padding } : {},
|
|
1024
|
+
...minHeight !== void 0 ? { minHeight } : {},
|
|
1025
|
+
...base2.layerBackground !== void 0 ? { layerBackground: base2.layerBackground } : {},
|
|
1026
|
+
...valign ? { valign } : {},
|
|
1027
|
+
...base2.fill === true ? { fill: true } : {},
|
|
1028
|
+
...nested ? { nested } : {}
|
|
1029
|
+
};
|
|
1030
|
+
const group = !isStructural(child) && (hasClass(child, "block-subcontent") && parseGridToken(child.classNames) === null || nested && (inlinePadding(child) !== void 0 || inlineMinHeight(child) !== void 0 || layerBackground(child) !== void 0)) && collectStructuralChildren(child).length >= 2;
|
|
1031
|
+
if (isStructural(child)) {
|
|
1032
|
+
const inheritedCard = {
|
|
1033
|
+
...base2.background !== void 0 ? { background: base2.background } : {},
|
|
1034
|
+
...base2.padding !== void 0 ? { padding: base2.padding } : {},
|
|
1035
|
+
...base2.minHeight !== void 0 ? { minHeight: base2.minHeight } : {},
|
|
1036
|
+
...base2.layerBackground !== void 0 ? { layerBackground: base2.layerBackground } : {},
|
|
1037
|
+
...base2.valign === true ? { valign: true } : {},
|
|
1038
|
+
...base2.fill === true ? { fill: true } : {},
|
|
1039
|
+
...nested ? { nested } : {}
|
|
1040
|
+
};
|
|
1041
|
+
out.push({
|
|
1042
|
+
el: child,
|
|
1043
|
+
...Object.keys(inheritedCard).length > 0 ? { card: inheritedCard } : {}
|
|
1044
|
+
});
|
|
1045
|
+
} else if (group) {
|
|
1046
|
+
const ownLayer = !isCellBoundary(child) ? layerBackground(child) : void 0;
|
|
1047
|
+
const groupCard = ownLayer !== void 0 ? { ...card, layerBackground: ownLayer } : card;
|
|
1048
|
+
out.push({
|
|
1049
|
+
el: child,
|
|
1050
|
+
...Object.keys(groupCard).length > 0 ? { card: groupCard } : {}
|
|
1051
|
+
});
|
|
1052
|
+
} else {
|
|
1053
|
+
out.push(...collectStructuralChildren(child, card));
|
|
1054
|
+
}
|
|
874
1055
|
}
|
|
875
1056
|
return out;
|
|
876
1057
|
}
|
|
@@ -888,23 +1069,37 @@ function isDisabledWithin(caption, holder) {
|
|
|
888
1069
|
}
|
|
889
1070
|
return false;
|
|
890
1071
|
}
|
|
891
|
-
function parseNode(el) {
|
|
1072
|
+
function parseNode(el, nested = false) {
|
|
892
1073
|
if (hasClass(el, "block-title") && /^H[1-6]$/.test(el.tagName ?? "")) {
|
|
893
1074
|
const role = textRoleFromClass(el.classNames);
|
|
1075
|
+
const style = textLeafStyle(el);
|
|
894
1076
|
return {
|
|
895
1077
|
kind: "heading",
|
|
896
1078
|
...role ? { role } : {},
|
|
1079
|
+
...style ? { style } : {},
|
|
897
1080
|
level: headingLevel(el),
|
|
898
1081
|
html: el.innerHTML
|
|
899
1082
|
};
|
|
900
1083
|
}
|
|
901
1084
|
if (hasClass(el, "block-body")) {
|
|
902
1085
|
const role = textRoleFromClass(el.classNames);
|
|
903
|
-
|
|
1086
|
+
const style = textLeafStyle(el);
|
|
1087
|
+
return {
|
|
1088
|
+
kind: "body",
|
|
1089
|
+
...role ? { role } : {},
|
|
1090
|
+
...style ? { style } : {},
|
|
1091
|
+
html: el.innerHTML
|
|
1092
|
+
};
|
|
904
1093
|
}
|
|
905
1094
|
if (hasClass(el, "block-subtitle")) {
|
|
906
1095
|
const role = textRoleFromClass(el.classNames);
|
|
907
|
-
|
|
1096
|
+
const style = textLeafStyle(el);
|
|
1097
|
+
return {
|
|
1098
|
+
kind: "subtitle",
|
|
1099
|
+
...role ? { role } : {},
|
|
1100
|
+
...style ? { style } : {},
|
|
1101
|
+
text: blockPlainText(el.innerHTML)
|
|
1102
|
+
};
|
|
908
1103
|
}
|
|
909
1104
|
if (isMediaHolder(el)) {
|
|
910
1105
|
const media = mediaFromElement(el);
|
|
@@ -922,14 +1117,52 @@ function parseNode(el) {
|
|
|
922
1117
|
if (el.tagName === "A") {
|
|
923
1118
|
return { kind: "raw", html: el.outerHTML };
|
|
924
1119
|
}
|
|
925
|
-
return parseContainer(el);
|
|
926
|
-
}
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
1120
|
+
return parseContainer(el, nested);
|
|
1121
|
+
}
|
|
1122
|
+
var nodePainted = (style) => "background" in style || "background-color" in style || "min-height" in style;
|
|
1123
|
+
function withCardStyle(node, card) {
|
|
1124
|
+
if (!card || card.background === void 0 && card.padding === void 0 && card.minHeight === void 0 && card.layerBackground === void 0 && !card.valign && !card.fill)
|
|
1125
|
+
return node;
|
|
1126
|
+
if (node.kind === "row" || node.kind === "stack") {
|
|
1127
|
+
const style2 = { ...node.style ?? {} };
|
|
1128
|
+
if (card.background !== void 0) style2["background-color"] = card.background;
|
|
1129
|
+
if (card.layerBackground !== void 0) style2["background"] = card.layerBackground;
|
|
1130
|
+
if (card.padding !== void 0) style2.padding = card.padding;
|
|
1131
|
+
if (card.minHeight !== void 0) style2["min-height"] = card.minHeight;
|
|
1132
|
+
if (card.valign) style2["_valign"] = "middle";
|
|
1133
|
+
if (card.fill && nodePainted(style2)) style2["_fill"] = "column";
|
|
1134
|
+
if (Object.keys(style2).length === 0) return node;
|
|
1135
|
+
return { ...node, style: style2 };
|
|
1136
|
+
}
|
|
1137
|
+
if (node.kind === "media" && card.padding === void 0 && !card.valign) {
|
|
1138
|
+
if (card.minHeight !== void 0 && node.media.minHeight === void 0)
|
|
1139
|
+
return { ...node, media: { ...node.media, minHeight: card.minHeight } };
|
|
1140
|
+
return node;
|
|
1141
|
+
}
|
|
1142
|
+
if (card.padding === void 0 && card.minHeight === void 0 && card.layerBackground === void 0 && !card.valign)
|
|
1143
|
+
return node;
|
|
1144
|
+
const style = {};
|
|
1145
|
+
if (card.padding !== void 0) style.padding = card.padding;
|
|
1146
|
+
if (card.background !== void 0) style["background-color"] = card.background;
|
|
1147
|
+
if (card.layerBackground !== void 0) style["background"] = card.layerBackground;
|
|
1148
|
+
if (card.minHeight !== void 0) style["min-height"] = card.minHeight;
|
|
1149
|
+
if (card.valign) style["_valign"] = "middle";
|
|
1150
|
+
if (card.fill && nodePainted(style)) style["_fill"] = "column";
|
|
1151
|
+
return { kind: "stack", children: [node], style };
|
|
1152
|
+
}
|
|
1153
|
+
function parseContainer(el, nested = false) {
|
|
1154
|
+
const kids = collectStructuralChildren(
|
|
1155
|
+
el,
|
|
1156
|
+
isCellBoundary(el) || hasClass(el, "cagrid") ? { nested: true, ...hasClass(el, "cagridFlexHeight") ? { fill: true } : {} } : nested ? { nested: true } : {}
|
|
1157
|
+
);
|
|
1158
|
+
const parsed = kids.map((k) => ({
|
|
1159
|
+
token: parseGridToken(k.el.classNames),
|
|
1160
|
+
node: withCardStyle(parseNode(k.el, k.card?.nested === true), k.card)
|
|
1161
|
+
})).filter((p) => !(p.node.kind === "raw" && p.node.html.trim() === ""));
|
|
930
1162
|
const isGrid = hasClass(el, "cagrid");
|
|
931
1163
|
const tokenCount = parsed.filter((p) => p.token).length;
|
|
932
|
-
|
|
1164
|
+
const hasWidthToken = parsed.some((p) => p.token && p.token.cols !== 1 && p.token.cols !== "any");
|
|
1165
|
+
if ((isGrid || tokenCount >= 2 || hasWidthToken) && parsed.length > 0) {
|
|
933
1166
|
const cells = parsed.map((p) => ({ token: p.token ?? DEFAULT_TOKEN, node: p.node }));
|
|
934
1167
|
if (hasClass(el, "caslider")) {
|
|
935
1168
|
const cols = Number(el.getAttribute("data-columns"));
|
|
@@ -940,10 +1173,15 @@ function parseContainer(el) {
|
|
|
940
1173
|
}
|
|
941
1174
|
const [only] = parsed;
|
|
942
1175
|
if (parsed.length === 1 && only) return only.node;
|
|
943
|
-
if (parsed.length === 0) return { kind: "raw", html: el
|
|
1176
|
+
if (parsed.length === 0) return { kind: "raw", html: rawInnerHtml(el) };
|
|
944
1177
|
return { kind: "stack", children: parsed.map((p) => p.node) };
|
|
945
1178
|
}
|
|
1179
|
+
function rawInnerHtml(el) {
|
|
1180
|
+
const html = el.innerHTML;
|
|
1181
|
+
return /\{\{[a-z_]+\}\}/i.test(html) ? "" : html;
|
|
1182
|
+
}
|
|
946
1183
|
var BAND_ID_RE = /^page-block-(\d+)$/;
|
|
1184
|
+
var BLOCK_CLASS_RE = /\bblocks\d+\b/;
|
|
947
1185
|
function bandBackground(el) {
|
|
948
1186
|
if (!hasClass(el, "camediaload")) return void 0;
|
|
949
1187
|
const rawId = el.getAttribute("data-media");
|
|
@@ -972,8 +1210,10 @@ function parseGridBands(html) {
|
|
|
972
1210
|
const idStr = m[1];
|
|
973
1211
|
if (idStr === void 0) continue;
|
|
974
1212
|
const background = bandBackground(child);
|
|
1213
|
+
const blockClass = BLOCK_CLASS_RE.exec(child.classNames)?.[0];
|
|
975
1214
|
bands.push({
|
|
976
1215
|
index: Number(idStr),
|
|
1216
|
+
...blockClass ? { blockClass } : {},
|
|
977
1217
|
...background ? { background } : {},
|
|
978
1218
|
root: parseContainer(child)
|
|
979
1219
|
});
|
|
@@ -1060,7 +1300,11 @@ function isEmptyRaw(node) {
|
|
|
1060
1300
|
return text.length === 0;
|
|
1061
1301
|
}
|
|
1062
1302
|
function base(band) {
|
|
1063
|
-
return {
|
|
1303
|
+
return {
|
|
1304
|
+
index: band.index,
|
|
1305
|
+
...band.blockClass ? { blockClass: band.blockClass } : {},
|
|
1306
|
+
...band.background ? { background: band.background } : {}
|
|
1307
|
+
};
|
|
1064
1308
|
}
|
|
1065
1309
|
function nodeText(node) {
|
|
1066
1310
|
switch (node.kind) {
|
|
@@ -1077,8 +1321,18 @@ function nodeText(node) {
|
|
|
1077
1321
|
return "";
|
|
1078
1322
|
}
|
|
1079
1323
|
}
|
|
1324
|
+
function unboxed(node) {
|
|
1325
|
+
let n = node;
|
|
1326
|
+
while (n.kind === "stack" && n.children.length === 1) {
|
|
1327
|
+
const only = n.children[0];
|
|
1328
|
+
if (!only) break;
|
|
1329
|
+
n = only;
|
|
1330
|
+
}
|
|
1331
|
+
return n;
|
|
1332
|
+
}
|
|
1080
1333
|
function pureCellMedia(cell) {
|
|
1081
|
-
|
|
1334
|
+
const n = unboxed(cell.node);
|
|
1335
|
+
return n.kind === "media" ? n.media : null;
|
|
1082
1336
|
}
|
|
1083
1337
|
function textRoleMeta(heading, subtitle) {
|
|
1084
1338
|
return {
|
|
@@ -1093,6 +1347,12 @@ function cellRatio(cell) {
|
|
|
1093
1347
|
if (t.cols === "any") return 50;
|
|
1094
1348
|
return Math.round(100 / t.cols);
|
|
1095
1349
|
}
|
|
1350
|
+
var isTextNode = (n) => n.kind === "heading" || n.kind === "body" || n.kind === "subtitle";
|
|
1351
|
+
var isBlankText = (n) => {
|
|
1352
|
+
if (n.kind === "heading" || n.kind === "body") return blockPlainText(n.html) === "";
|
|
1353
|
+
if (n.kind === "subtitle") return n.text.trim() === "";
|
|
1354
|
+
return false;
|
|
1355
|
+
};
|
|
1096
1356
|
function carouselSlides(cells) {
|
|
1097
1357
|
const out = [];
|
|
1098
1358
|
for (const c of cells) {
|
|
@@ -1101,12 +1361,18 @@ function carouselSlides(cells) {
|
|
|
1101
1361
|
out.push({ media: n.media });
|
|
1102
1362
|
continue;
|
|
1103
1363
|
}
|
|
1104
|
-
if (n.kind === "stack" && n.children.length
|
|
1105
|
-
const [m,
|
|
1106
|
-
|
|
1364
|
+
if (n.kind === "stack" && n.children.length >= 2) {
|
|
1365
|
+
const [m, ...rest] = n.children;
|
|
1366
|
+
const h = rest.find((r) => r.kind === "heading");
|
|
1367
|
+
if (m?.kind === "media" && h?.kind === "heading" && rest.every(isTextNode)) {
|
|
1368
|
+
const sub = rest.find(
|
|
1369
|
+
(r) => r !== h && (r.kind === "body" || r.kind === "subtitle") && !isBlankText(r)
|
|
1370
|
+
);
|
|
1371
|
+
const subcaption = sub?.kind === "body" ? { html: sub.html, ...sub.role ? { role: sub.role } : {} } : sub?.kind === "subtitle" ? { html: sub.text, ...sub.role ? { role: sub.role } : {} } : void 0;
|
|
1107
1372
|
out.push({
|
|
1108
1373
|
media: m.media,
|
|
1109
|
-
caption: { html: h.html, level: h.level, ...h.role ? { role: h.role } : {} }
|
|
1374
|
+
caption: { html: h.html, level: h.level, ...h.role ? { role: h.role } : {} },
|
|
1375
|
+
...subcaption ? { subcaption } : {}
|
|
1110
1376
|
});
|
|
1111
1377
|
continue;
|
|
1112
1378
|
}
|
|
@@ -1134,12 +1400,18 @@ function rewriteMapMounts(node, isMapMount) {
|
|
|
1134
1400
|
token: c.token,
|
|
1135
1401
|
node: rewriteMapMounts(c.node, isMapMount)
|
|
1136
1402
|
})),
|
|
1137
|
-
//
|
|
1138
|
-
// Carousel to Grid
|
|
1139
|
-
|
|
1403
|
+
// Preserve the row's own markers when rebuilding: dropping the slider
|
|
1404
|
+
// would silently demote a Carousel to Grid, and dropping the style would
|
|
1405
|
+
// lose a card background — for every band whenever a map config exists.
|
|
1406
|
+
...node.slider ? { slider: node.slider } : {},
|
|
1407
|
+
...node.style ? { style: node.style } : {}
|
|
1140
1408
|
};
|
|
1141
1409
|
case "stack":
|
|
1142
|
-
return {
|
|
1410
|
+
return {
|
|
1411
|
+
kind: "stack",
|
|
1412
|
+
children: node.children.map((n) => rewriteMapMounts(n, isMapMount)),
|
|
1413
|
+
...node.style ? { style: node.style } : {}
|
|
1414
|
+
};
|
|
1143
1415
|
case "heading":
|
|
1144
1416
|
case "body":
|
|
1145
1417
|
case "subtitle":
|
|
@@ -1375,10 +1647,272 @@ function extractToggles(html) {
|
|
|
1375
1647
|
return groups.length === labels.length && groups.every((g) => g !== void 0) ? groups : [];
|
|
1376
1648
|
}
|
|
1377
1649
|
|
|
1650
|
+
// src/blux/grid/feed-grid.ts
|
|
1651
|
+
var termMatchesTag = (term, tag) => term === tag || term.endsWith("s") && term.slice(0, -1) === tag || tag.endsWith("s") && tag.slice(0, -1) === term;
|
|
1652
|
+
function tagFilter(expr) {
|
|
1653
|
+
const groups = (expr ?? "").split("||").map(
|
|
1654
|
+
(g) => g.split("&&").map((t) => t.trim().toLowerCase()).filter(Boolean)
|
|
1655
|
+
).filter((g) => g.length > 0);
|
|
1656
|
+
if (!groups.length) return () => true;
|
|
1657
|
+
return (tags) => {
|
|
1658
|
+
const set = tags.map((t) => t.toLowerCase());
|
|
1659
|
+
return groups.some((g) => g.every((term) => set.some((tag) => termMatchesTag(term, tag))));
|
|
1660
|
+
};
|
|
1661
|
+
}
|
|
1662
|
+
function sortRecords(records, sort) {
|
|
1663
|
+
if (sort === "title") {
|
|
1664
|
+
return [...records].sort(
|
|
1665
|
+
(a, b) => String(a["title"] ?? "").localeCompare(String(b["title"] ?? ""))
|
|
1666
|
+
);
|
|
1667
|
+
}
|
|
1668
|
+
if (sort === "fdate" || sort === "date") {
|
|
1669
|
+
return [...records].sort(
|
|
1670
|
+
(a, b) => String(b["date"] ?? "").localeCompare(String(a["date"] ?? ""))
|
|
1671
|
+
);
|
|
1672
|
+
}
|
|
1673
|
+
return records;
|
|
1674
|
+
}
|
|
1675
|
+
function cropRatioOf(v) {
|
|
1676
|
+
if (typeof v !== "string") return void 0;
|
|
1677
|
+
const m = /^\s*(\d+(?:\.\d+)?)\s*:\s*(\d+(?:\.\d+)?)\s*$/.exec(v);
|
|
1678
|
+
if (!m) return void 0;
|
|
1679
|
+
const w = Number(m[1]);
|
|
1680
|
+
const h = Number(m[2]);
|
|
1681
|
+
return w > 0 && h > 0 ? `${w}:${h}` : void 0;
|
|
1682
|
+
}
|
|
1683
|
+
var isDisabled = (r) => r["disabled"] === true || r["disable"] === true;
|
|
1684
|
+
var styleDisabled = (cfg) => !!cfg && typeof cfg === "object" && cfg["class"] === "disable";
|
|
1685
|
+
function resolveFeedTiles(bandDef, resolvers) {
|
|
1686
|
+
const sources = Array.isArray(bandDef.sources) ? bandDef.sources.map(String) : [];
|
|
1687
|
+
const source = sources[0];
|
|
1688
|
+
if (!source) return null;
|
|
1689
|
+
const cfg = bandDef.sourceConfig ?? {};
|
|
1690
|
+
const filterExpr = cfg["filters"]?.tag;
|
|
1691
|
+
const match = tagFilter(filterExpr);
|
|
1692
|
+
const sort = cfg["sort"];
|
|
1693
|
+
const bodyOff = styleDisabled(cfg["_body"]);
|
|
1694
|
+
const titleOff = styleDisabled(cfg["_title"]);
|
|
1695
|
+
const ratio = cropRatioOf(cfg["mediaRatio"] ?? cfg["ratio"]);
|
|
1696
|
+
const framed = (m) => m && ratio ? { ...m, cropRatio: ratio, fit: "cover" } : m;
|
|
1697
|
+
if (source === "__media") {
|
|
1698
|
+
const matched = [];
|
|
1699
|
+
for (const [uuid, entry] of resolvers.media) {
|
|
1700
|
+
const type = String(entry["type"] ?? "");
|
|
1701
|
+
if (!type.startsWith("image/")) continue;
|
|
1702
|
+
if (!match(entry["tags"] ?? [])) continue;
|
|
1703
|
+
matched.push({ ...entry, __uuid: uuid });
|
|
1704
|
+
}
|
|
1705
|
+
const tiles2 = sortRecords(matched, sort).map((entry) => {
|
|
1706
|
+
const media = framed(
|
|
1707
|
+
resolvers.mediaFor(
|
|
1708
|
+
String(entry["__uuid"]),
|
|
1709
|
+
entry["type"],
|
|
1710
|
+
entry["name"]
|
|
1711
|
+
)
|
|
1712
|
+
);
|
|
1713
|
+
const tile = {};
|
|
1714
|
+
if (media) tile.media = media;
|
|
1715
|
+
if (!titleOff && entry["name"]) tile.title = escapeHtml(String(entry["name"]));
|
|
1716
|
+
if (!bodyOff && entry["description"]) tile.body = plainToHtml(String(entry["description"]));
|
|
1717
|
+
return tile;
|
|
1718
|
+
}).filter((t) => t.media || t.title || t.body);
|
|
1719
|
+
return tiles2.length ? tiles2 : null;
|
|
1720
|
+
}
|
|
1721
|
+
const records = resolvers.feeds.get(source);
|
|
1722
|
+
if (!records) return null;
|
|
1723
|
+
const enabled = records.filter((r) => !isDisabled(r));
|
|
1724
|
+
const filtered = filterExpr ? enabled.filter((r) => match(r["tags"] ?? [])) : enabled;
|
|
1725
|
+
const ordered = sortRecords(filtered, sort);
|
|
1726
|
+
const tiles = ordered.map((r) => {
|
|
1727
|
+
const tile = {};
|
|
1728
|
+
const m = r["media"];
|
|
1729
|
+
if (m?.media) {
|
|
1730
|
+
const media = framed(resolvers.mediaFor(m.media, m.type));
|
|
1731
|
+
if (media) tile.media = media;
|
|
1732
|
+
}
|
|
1733
|
+
if (!titleOff && r["title"]) tile.title = String(r["title"]);
|
|
1734
|
+
if (!bodyOff && r["body"]) tile.body = String(r["body"]);
|
|
1735
|
+
return tile;
|
|
1736
|
+
});
|
|
1737
|
+
return tiles.length ? tiles : null;
|
|
1738
|
+
}
|
|
1739
|
+
function materializeFeedGrid(opts) {
|
|
1740
|
+
const { heading, tiles, columns, spacing, overlay } = opts;
|
|
1741
|
+
const headingNode = heading ? {
|
|
1742
|
+
kind: "heading",
|
|
1743
|
+
level: heading.level,
|
|
1744
|
+
html: heading.html,
|
|
1745
|
+
...heading.role ? { role: heading.role } : {}
|
|
1746
|
+
} : null;
|
|
1747
|
+
if (!tiles || !tiles.length) return headingNode;
|
|
1748
|
+
const cols = Math.max(1, Math.round(columns));
|
|
1749
|
+
const cells = tiles.map((t) => {
|
|
1750
|
+
const parts = [];
|
|
1751
|
+
if (t.media) parts.push({ kind: "media", media: t.media });
|
|
1752
|
+
if (t.title) parts.push({ kind: "heading", level: 6, html: t.title, role: "text6" });
|
|
1753
|
+
if (t.body) parts.push({ kind: "body", html: t.body });
|
|
1754
|
+
let node;
|
|
1755
|
+
if (overlay && t.media) {
|
|
1756
|
+
node = {
|
|
1757
|
+
kind: "stack",
|
|
1758
|
+
children: parts,
|
|
1759
|
+
style: {
|
|
1760
|
+
_overlay: overlay.ratio,
|
|
1761
|
+
...overlay.color ? { _overlayColor: overlay.color } : {},
|
|
1762
|
+
...overlay.valign ? { _overlayValign: overlay.valign } : {}
|
|
1763
|
+
}
|
|
1764
|
+
};
|
|
1765
|
+
} else {
|
|
1766
|
+
node = parts.length === 1 ? parts[0] : { kind: "stack", children: parts };
|
|
1767
|
+
}
|
|
1768
|
+
return {
|
|
1769
|
+
token: {
|
|
1770
|
+
cols,
|
|
1771
|
+
raw: `grid-${cols}${spacing ? `-s${spacing}` : ""}`,
|
|
1772
|
+
...spacing ? { spacing } : {}
|
|
1773
|
+
},
|
|
1774
|
+
node
|
|
1775
|
+
};
|
|
1776
|
+
});
|
|
1777
|
+
const row = { kind: "row", cells };
|
|
1778
|
+
return headingNode ? { kind: "stack", children: [headingNode, row] } : row;
|
|
1779
|
+
}
|
|
1780
|
+
function escapeHtml(s) {
|
|
1781
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
1782
|
+
}
|
|
1783
|
+
function plainToHtml(s) {
|
|
1784
|
+
const inner = escapeHtml(s.trim()).replace(/\r?\n/g, "<br>");
|
|
1785
|
+
return `<p>${inner}</p>`;
|
|
1786
|
+
}
|
|
1787
|
+
function extFor(mime, name) {
|
|
1788
|
+
const byMime = {
|
|
1789
|
+
"image/jpeg": "jpg",
|
|
1790
|
+
"image/jpg": "jpg",
|
|
1791
|
+
"image/png": "png",
|
|
1792
|
+
"image/svg+xml": "svg",
|
|
1793
|
+
"image/gif": "gif",
|
|
1794
|
+
"image/webp": "webp",
|
|
1795
|
+
"image/avif": "avif",
|
|
1796
|
+
"image/bmp": "bmp",
|
|
1797
|
+
"image/tiff": "tiff"
|
|
1798
|
+
};
|
|
1799
|
+
if (mime && byMime[mime]) return byMime[mime];
|
|
1800
|
+
const m = /\.([a-z0-9]{2,5})$/i.exec(name ?? "");
|
|
1801
|
+
const ext = m?.[1]?.toLowerCase();
|
|
1802
|
+
const IMG = /* @__PURE__ */ new Set(["jpg", "jpeg", "png", "svg", "gif", "webp", "avif", "bmp", "tif", "tiff"]);
|
|
1803
|
+
return ext && IMG.has(ext) ? ext === "jpeg" ? "jpg" : ext : null;
|
|
1804
|
+
}
|
|
1805
|
+
function isFeedBand(item) {
|
|
1806
|
+
return !!item && typeof item === "object" && Array.isArray(item.sources) && item.sources.length > 0;
|
|
1807
|
+
}
|
|
1808
|
+
function feedAssetBase(htmls, siteId) {
|
|
1809
|
+
for (const html of htmls) {
|
|
1810
|
+
const m = /data-base="(https?:\/\/[^"]+?\/)"/i.exec(html);
|
|
1811
|
+
if (m?.[1]) return m[1].replace(/^http:/, "https:");
|
|
1812
|
+
}
|
|
1813
|
+
return `https://${CDN_HOSTS[0]}/${siteId}/`;
|
|
1814
|
+
}
|
|
1815
|
+
function buildFeedResolvers(feeds, mediaLibrary, base2) {
|
|
1816
|
+
const feedMap = /* @__PURE__ */ new Map();
|
|
1817
|
+
for (const [id, f] of Object.entries(feeds)) {
|
|
1818
|
+
if (Array.isArray(f?.items)) feedMap.set(id, f.items);
|
|
1819
|
+
}
|
|
1820
|
+
const mediaMap = new Map(Object.entries(mediaLibrary));
|
|
1821
|
+
return {
|
|
1822
|
+
feeds: feedMap,
|
|
1823
|
+
media: mediaMap,
|
|
1824
|
+
mediaFor: (uuid, type, name) => {
|
|
1825
|
+
const ext = extFor(type, name);
|
|
1826
|
+
if (!ext) return null;
|
|
1827
|
+
return { kind: "image", assetId: uuid, base: base2, ext };
|
|
1828
|
+
}
|
|
1829
|
+
};
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
// src/blux/products.ts
|
|
1833
|
+
var CANONICAL_CATEGORIES = ["Upholstered", "Case", "Exterior", "Metal", "Finishes"];
|
|
1834
|
+
var CATEGORY_ALIASES = {
|
|
1835
|
+
upholstrered: "Upholstered",
|
|
1836
|
+
upholsered: "Upholstered"
|
|
1837
|
+
};
|
|
1838
|
+
var SUBCATEGORY_ALIASES = {
|
|
1839
|
+
banuette: "Banquette",
|
|
1840
|
+
"benches & ottomans": "Ottomans & Benches",
|
|
1841
|
+
miscellaneous: "Misc."
|
|
1842
|
+
};
|
|
1843
|
+
function cleanWs(s) {
|
|
1844
|
+
return String(s ?? "").replace(/\s+/g, " ").trim();
|
|
1845
|
+
}
|
|
1846
|
+
function titleCase(s) {
|
|
1847
|
+
return s.replace(/\b\w/g, (c) => c.toUpperCase());
|
|
1848
|
+
}
|
|
1849
|
+
function normalizeCategory(raw) {
|
|
1850
|
+
const cleaned = cleanWs(raw);
|
|
1851
|
+
if (!cleaned) return "";
|
|
1852
|
+
const lower = cleaned.toLowerCase();
|
|
1853
|
+
if (CATEGORY_ALIASES[lower]) return CATEGORY_ALIASES[lower];
|
|
1854
|
+
const canon = CANONICAL_CATEGORIES.find((c) => c.toLowerCase() === lower);
|
|
1855
|
+
return canon ?? titleCase(lower);
|
|
1856
|
+
}
|
|
1857
|
+
function normalizeSubCategory(raw) {
|
|
1858
|
+
const cleaned = cleanWs(raw);
|
|
1859
|
+
if (!cleaned) return "";
|
|
1860
|
+
const lower = cleaned.toLowerCase();
|
|
1861
|
+
if (SUBCATEGORY_ALIASES[lower]) return SUBCATEGORY_ALIASES[lower];
|
|
1862
|
+
return titleCase(lower);
|
|
1863
|
+
}
|
|
1864
|
+
function productSlug(record) {
|
|
1865
|
+
const url = typeof record.url === "string" ? record.url.trim() : "";
|
|
1866
|
+
if (url) return url.replace(/^\/+products\/+/i, "").replace(/^\/+|\/+$/g, "");
|
|
1867
|
+
return String(record.title ?? "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
1868
|
+
}
|
|
1869
|
+
function mediaUuid(media) {
|
|
1870
|
+
if (media && typeof media === "object") {
|
|
1871
|
+
const m = media.media;
|
|
1872
|
+
if (typeof m === "string" && m) return m;
|
|
1873
|
+
}
|
|
1874
|
+
return void 0;
|
|
1875
|
+
}
|
|
1876
|
+
function imageFor(uuid, resolveImage) {
|
|
1877
|
+
if (!uuid) return void 0;
|
|
1878
|
+
const url = resolveImage(uuid);
|
|
1879
|
+
return url ? { assetId: uuid, url } : void 0;
|
|
1880
|
+
}
|
|
1881
|
+
function materializeProducts(records, resolveImage) {
|
|
1882
|
+
const bySlug = /* @__PURE__ */ new Map();
|
|
1883
|
+
for (const r of records) {
|
|
1884
|
+
const slug = productSlug(r);
|
|
1885
|
+
if (!slug) continue;
|
|
1886
|
+
const gallery = [];
|
|
1887
|
+
if (Array.isArray(r.items)) {
|
|
1888
|
+
for (const it of r.items) {
|
|
1889
|
+
const img2 = imageFor(mediaUuid(it?.media), resolveImage);
|
|
1890
|
+
if (img2) gallery.push(img2);
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
const image = imageFor(mediaUuid(r.media), resolveImage);
|
|
1894
|
+
const product = {
|
|
1895
|
+
slug,
|
|
1896
|
+
title: cleanWs(r.title),
|
|
1897
|
+
category: normalizeCategory(r.category),
|
|
1898
|
+
subCategory: normalizeSubCategory(r.sub_category),
|
|
1899
|
+
dimensions: String(r.dimensions ?? ""),
|
|
1900
|
+
tags: Array.isArray(r.tags) ? r.tags.filter((t) => typeof t === "string") : [],
|
|
1901
|
+
disabled: r.disabled === true,
|
|
1902
|
+
gallery,
|
|
1903
|
+
// `exactOptionalPropertyTypes`: omit the key rather than set undefined.
|
|
1904
|
+
...image ? { image } : {}
|
|
1905
|
+
};
|
|
1906
|
+
const existing = bySlug.get(slug);
|
|
1907
|
+
if (!existing || existing.disabled && !product.disabled) bySlug.set(slug, product);
|
|
1908
|
+
}
|
|
1909
|
+
return [...bySlug.values()];
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1378
1912
|
// src/blux/emit/block-styles.ts
|
|
1379
|
-
function blockStylesByIndex(siteJson) {
|
|
1913
|
+
function blockStylesByIndex(siteJson, pageIndex = 0) {
|
|
1380
1914
|
const out = /* @__PURE__ */ new Map();
|
|
1381
|
-
const blocks = siteJson?.content?.pages?.[
|
|
1915
|
+
const blocks = siteJson?.content?.pages?.[pageIndex]?.items;
|
|
1382
1916
|
if (!Array.isArray(blocks)) return out;
|
|
1383
1917
|
blocks.forEach((b, i) => {
|
|
1384
1918
|
const styles = b?.styles ?? {};
|
|
@@ -1391,6 +1925,30 @@ function blockStylesByIndex(siteJson) {
|
|
|
1391
1925
|
});
|
|
1392
1926
|
return out;
|
|
1393
1927
|
}
|
|
1928
|
+
var CONTAINER_RE = /^\.(blocks\d+)container$/;
|
|
1929
|
+
function blockClassDefaults(siteJson) {
|
|
1930
|
+
const out = /* @__PURE__ */ new Map();
|
|
1931
|
+
const blocks = siteJson?.styles?.blocks;
|
|
1932
|
+
if (!Array.isArray(blocks)) return out;
|
|
1933
|
+
for (const slot of blocks) {
|
|
1934
|
+
if (slot === null || typeof slot !== "object") continue;
|
|
1935
|
+
for (const [selector, css] of Object.entries(slot)) {
|
|
1936
|
+
const blockClass = CONTAINER_RE.exec(selector)?.[1];
|
|
1937
|
+
if (!blockClass || css === null || typeof css !== "object") continue;
|
|
1938
|
+
const rec = css;
|
|
1939
|
+
const padding = cleanCssValue(rec["padding"]);
|
|
1940
|
+
const mobilePadding = cleanCssValue(rec["__media_mobile_padding"]);
|
|
1941
|
+
const maxWidth = cleanCssValue(rec["max-width"]);
|
|
1942
|
+
const defaults = {
|
|
1943
|
+
...padding ? { padding } : {},
|
|
1944
|
+
...mobilePadding ? { mobilePadding } : {},
|
|
1945
|
+
...maxWidth ? { maxWidth } : {}
|
|
1946
|
+
};
|
|
1947
|
+
if (Object.keys(defaults).length) out.set(blockClass, defaults);
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
return out;
|
|
1951
|
+
}
|
|
1394
1952
|
|
|
1395
1953
|
// src/blux/emit/grid-slice.ts
|
|
1396
1954
|
function stripTags(html) {
|
|
@@ -1446,9 +2004,10 @@ function sliceSpecToPlanSlice(spec) {
|
|
|
1446
2004
|
return {
|
|
1447
2005
|
slice_type: "carousel",
|
|
1448
2006
|
variation: "default",
|
|
1449
|
-
items: spec.slides.map(
|
|
1450
|
-
|
|
1451
|
-
|
|
2007
|
+
items: spec.slides.map((s) => ({
|
|
2008
|
+
...s.caption ? { caption: blockPlainText(s.caption.html) } : {},
|
|
2009
|
+
...s.subcaption ? { subcaption: blockPlainText(s.subcaption.html) } : {}
|
|
2010
|
+
})),
|
|
1452
2011
|
primary: { band: spec.index }
|
|
1453
2012
|
};
|
|
1454
2013
|
case "MediaFull":
|
|
@@ -1526,13 +2085,17 @@ function collectPlanAssets(specs, resolve, diagnostics) {
|
|
|
1526
2085
|
}
|
|
1527
2086
|
function buildGridPlan(specs, ir) {
|
|
1528
2087
|
const page = ir.pages[0];
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
2088
|
+
return buildGridSitePlan(
|
|
2089
|
+
[{ uid: page?.uid ?? "home", title: page?.title ?? page?.uid ?? "home", specs }],
|
|
2090
|
+
ir
|
|
2091
|
+
);
|
|
2092
|
+
}
|
|
2093
|
+
function buildGridSitePlan(pages, ir) {
|
|
2094
|
+
const documents = pages.map((p) => ({
|
|
1532
2095
|
type: "page",
|
|
1533
|
-
uid,
|
|
1534
|
-
data: { title: richText(`<h1>${title}</h1>`), slices: specs.map(sliceSpecToPlanSlice) }
|
|
1535
|
-
};
|
|
2096
|
+
uid: p.uid,
|
|
2097
|
+
data: { title: richText(`<h1>${p.title}</h1>`), slices: p.specs.map(sliceSpecToPlanSlice) }
|
|
2098
|
+
}));
|
|
1536
2099
|
const assetById = new Map(ir.assets.map((a) => [a.id, a]));
|
|
1537
2100
|
const sourceUrlById = new Map(ir.assets.map((a) => [a.id, a.sourceUrl]));
|
|
1538
2101
|
const resolve = (m) => {
|
|
@@ -1541,9 +2104,13 @@ function buildGridPlan(specs, ir) {
|
|
|
1541
2104
|
return url ? { id: m.assetId, url, alt: asset?.alt ?? "" } : null;
|
|
1542
2105
|
};
|
|
1543
2106
|
const diagnostics = [...ir.diagnostics ?? []];
|
|
1544
|
-
const assets = collectPlanAssets(
|
|
2107
|
+
const assets = collectPlanAssets(
|
|
2108
|
+
pages.flatMap((p) => p.specs),
|
|
2109
|
+
resolve,
|
|
2110
|
+
diagnostics
|
|
2111
|
+
);
|
|
1545
2112
|
const customTypes = ir.collections.map(buildCustomType);
|
|
1546
|
-
return { customTypes, documents
|
|
2113
|
+
return { customTypes, documents, assets, stylesManifest: [], diagnostics };
|
|
1547
2114
|
}
|
|
1548
2115
|
|
|
1549
2116
|
// src/blux/emit/presentation.ts
|
|
@@ -1562,7 +2129,7 @@ function renderNode(node, resolve) {
|
|
|
1562
2129
|
const rn = renderNode(c.node, resolve);
|
|
1563
2130
|
if (rn) cells.push({ token: renderToken(c.token), node: rn });
|
|
1564
2131
|
}
|
|
1565
|
-
return { kind: "row", cells };
|
|
2132
|
+
return { kind: "row", cells, ...node.style ? { style: node.style } : {} };
|
|
1566
2133
|
}
|
|
1567
2134
|
case "stack": {
|
|
1568
2135
|
const children = [];
|
|
@@ -1570,19 +2137,30 @@ function renderNode(node, resolve) {
|
|
|
1570
2137
|
const rn = renderNode(c, resolve);
|
|
1571
2138
|
if (rn) children.push(rn);
|
|
1572
2139
|
}
|
|
1573
|
-
return { kind: "stack", children };
|
|
2140
|
+
return { kind: "stack", children, ...node.style ? { style: node.style } : {} };
|
|
1574
2141
|
}
|
|
1575
2142
|
case "heading":
|
|
1576
2143
|
return {
|
|
1577
2144
|
kind: "heading",
|
|
1578
2145
|
level: node.level,
|
|
1579
2146
|
html: node.html,
|
|
1580
|
-
...node.role ? { role: node.role } : {}
|
|
2147
|
+
...node.role ? { role: node.role } : {},
|
|
2148
|
+
...node.style ? { style: node.style } : {}
|
|
1581
2149
|
};
|
|
1582
2150
|
case "body":
|
|
1583
|
-
return {
|
|
2151
|
+
return {
|
|
2152
|
+
kind: "body",
|
|
2153
|
+
html: node.html,
|
|
2154
|
+
...node.role ? { role: node.role } : {},
|
|
2155
|
+
...node.style ? { style: node.style } : {}
|
|
2156
|
+
};
|
|
1584
2157
|
case "subtitle":
|
|
1585
|
-
return {
|
|
2158
|
+
return {
|
|
2159
|
+
kind: "subtitle",
|
|
2160
|
+
text: node.text,
|
|
2161
|
+
...node.role ? { role: node.role } : {},
|
|
2162
|
+
...node.style ? { style: node.style } : {}
|
|
2163
|
+
};
|
|
1586
2164
|
case "media": {
|
|
1587
2165
|
const m = resolve(node.media);
|
|
1588
2166
|
return m ? { kind: "media", media: m } : null;
|
|
@@ -1599,12 +2177,45 @@ function hasMapWidget(node) {
|
|
|
1599
2177
|
if (node.kind === "stack") return node.children.some(hasMapWidget);
|
|
1600
2178
|
return false;
|
|
1601
2179
|
}
|
|
2180
|
+
function markPanelRows(node, toggleCount) {
|
|
2181
|
+
if (node.kind === "row") {
|
|
2182
|
+
return {
|
|
2183
|
+
...node,
|
|
2184
|
+
cells: node.cells.map((c) => ({ ...c, node: markPanelRows(c.node, toggleCount) }))
|
|
2185
|
+
};
|
|
2186
|
+
}
|
|
2187
|
+
if (node.kind === "stack") {
|
|
2188
|
+
const children = node.children.map((c) => markPanelRows(c, toggleCount));
|
|
2189
|
+
for (let i = 0; i + 1 < children.length; i++) {
|
|
2190
|
+
const cur = children[i];
|
|
2191
|
+
const next = children[i + 1];
|
|
2192
|
+
if (cur?.kind === "widget" && cur.widget.type === "map" && next?.kind === "row" && next.cells.length === toggleCount) {
|
|
2193
|
+
children[i + 1] = { ...next, panels: true };
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
return { ...node, children };
|
|
2197
|
+
}
|
|
2198
|
+
return node;
|
|
2199
|
+
}
|
|
1602
2200
|
function buildPresentation(specs, deps) {
|
|
1603
2201
|
const bands = {};
|
|
1604
2202
|
for (const spec of specs) {
|
|
1605
2203
|
const bp = {};
|
|
1606
2204
|
const style = deps.styleFor(spec.index);
|
|
1607
2205
|
if (style) bp.style = style;
|
|
2206
|
+
const defaults = spec.blockClass ? deps.defaultsFor(spec.blockClass) : void 0;
|
|
2207
|
+
if (defaults) {
|
|
2208
|
+
const own = bp.style ?? {};
|
|
2209
|
+
const fill = {};
|
|
2210
|
+
if (own["_contentPadding"] === void 0 && defaults.padding) {
|
|
2211
|
+
fill["_contentPadding"] = defaults.padding;
|
|
2212
|
+
if (defaults.mobilePadding) fill["_contentPaddingMobile"] = defaults.mobilePadding;
|
|
2213
|
+
}
|
|
2214
|
+
if (own["_max-content-width"] === void 0 && defaults.maxWidth) {
|
|
2215
|
+
fill["_max-content-width"] = defaults.maxWidth;
|
|
2216
|
+
}
|
|
2217
|
+
if (Object.keys(fill).length) bp.style = { ...own, ...fill };
|
|
2218
|
+
}
|
|
1608
2219
|
if (spec.background) {
|
|
1609
2220
|
const bg = deps.resolveMedia(spec.background);
|
|
1610
2221
|
if (bg) bp.background = bg;
|
|
@@ -1633,14 +2244,15 @@ function buildPresentation(specs, deps) {
|
|
|
1633
2244
|
for (const s of spec.slides) {
|
|
1634
2245
|
const media = deps.resolveMedia(s.media);
|
|
1635
2246
|
if (!media) break;
|
|
1636
|
-
const slide = {
|
|
1637
|
-
media
|
|
1638
|
-
};
|
|
2247
|
+
const slide = { media };
|
|
1639
2248
|
if (s.caption) {
|
|
1640
2249
|
const caption = { level: s.caption.level };
|
|
1641
2250
|
if (s.caption.role !== void 0) caption.role = s.caption.role;
|
|
1642
2251
|
slide.caption = caption;
|
|
1643
2252
|
}
|
|
2253
|
+
if (s.subcaption) {
|
|
2254
|
+
slide.subcaption = s.subcaption.role !== void 0 ? { role: s.subcaption.role } : {};
|
|
2255
|
+
}
|
|
1644
2256
|
slides.push(slide);
|
|
1645
2257
|
}
|
|
1646
2258
|
if (slides.length > 0) {
|
|
@@ -1665,8 +2277,9 @@ function buildPresentation(specs, deps) {
|
|
|
1665
2277
|
break;
|
|
1666
2278
|
case "Grid": {
|
|
1667
2279
|
const tree = renderNode(spec.root, deps.resolveMedia);
|
|
1668
|
-
|
|
1669
|
-
if (
|
|
2280
|
+
const co = deps.map && hasMapWidget(spec.root) ? deps.map : null;
|
|
2281
|
+
if (co) bp.map = co;
|
|
2282
|
+
if (tree) bp.tree = co ? markPanelRows(tree, co.toggles.length) : tree;
|
|
1670
2283
|
break;
|
|
1671
2284
|
}
|
|
1672
2285
|
}
|
|
@@ -1699,6 +2312,7 @@ function convertExport({
|
|
|
1699
2312
|
const assetsById = new Map(ir.assets.map((a) => [a.id, a]));
|
|
1700
2313
|
const sourceUrlById = new Map(ir.assets.map((a) => [a.id, a.sourceUrl]));
|
|
1701
2314
|
const styles = blockStylesByIndex(siteJson);
|
|
2315
|
+
const defaults = blockClassDefaults(siteJson);
|
|
1702
2316
|
const deps = {
|
|
1703
2317
|
resolveMedia: (m) => {
|
|
1704
2318
|
const url = mediaUrl(m, sourceUrlById);
|
|
@@ -1713,17 +2327,284 @@ function convertExport({
|
|
|
1713
2327
|
...m.fit ? { fit: m.fit } : {},
|
|
1714
2328
|
...m.position ? { position: m.position } : {},
|
|
1715
2329
|
...m.minHeight ? { minHeight: m.minHeight } : {},
|
|
2330
|
+
...m.cropRatio ? { cropRatio: m.cropRatio } : {},
|
|
1716
2331
|
...m.playback ? { playback: m.playback } : {}
|
|
1717
2332
|
};
|
|
1718
2333
|
return rm;
|
|
1719
2334
|
},
|
|
1720
2335
|
styleFor: (i) => styles.get(i),
|
|
2336
|
+
defaultsFor: (blockClass) => defaults.get(blockClass),
|
|
1721
2337
|
map: mapConfig ? mapRenderFromConfig(mapConfig) : null
|
|
1722
2338
|
};
|
|
1723
2339
|
const plan = buildGridPlan(specs, ir);
|
|
1724
2340
|
const presentation = buildPresentation(specs, deps);
|
|
1725
2341
|
return { bands, specs, ir, mapConfig, plan, presentation };
|
|
1726
2342
|
}
|
|
2343
|
+
function leadingHeadings(root) {
|
|
2344
|
+
if (root.kind === "heading" || root.kind === "subtitle") return [root];
|
|
2345
|
+
if (root.kind === "stack") {
|
|
2346
|
+
const out = [];
|
|
2347
|
+
for (const c of root.children) {
|
|
2348
|
+
if (c.kind === "heading" || c.kind === "subtitle") out.push(c);
|
|
2349
|
+
else break;
|
|
2350
|
+
}
|
|
2351
|
+
return out;
|
|
2352
|
+
}
|
|
2353
|
+
return [];
|
|
2354
|
+
}
|
|
2355
|
+
function feedColumns(item) {
|
|
2356
|
+
const n = Number(item.columns);
|
|
2357
|
+
return Number.isFinite(n) && n > 0 ? n : 3;
|
|
2358
|
+
}
|
|
2359
|
+
function tileOverlay(item) {
|
|
2360
|
+
const cfg = item?.sourceConfig;
|
|
2361
|
+
if (!cfg || cfg["overlay"] !== true) return null;
|
|
2362
|
+
const ratio = cropRatioOf(cfg["mediaRatio"] ?? cfg["ratio"]);
|
|
2363
|
+
if (!ratio) return null;
|
|
2364
|
+
const color = typeof cfg["overlayColor"] === "string" ? cfg["overlayColor"] : void 0;
|
|
2365
|
+
const valign = typeof cfg["contentvalign"] === "string" ? cfg["contentvalign"] : void 0;
|
|
2366
|
+
return { ratio, ...color ? { color } : {}, ...valign ? { valign } : {} };
|
|
2367
|
+
}
|
|
2368
|
+
function isEmptyish(root) {
|
|
2369
|
+
switch (root.kind) {
|
|
2370
|
+
case "heading":
|
|
2371
|
+
case "subtitle":
|
|
2372
|
+
return true;
|
|
2373
|
+
case "raw":
|
|
2374
|
+
return root.html.trim() === "";
|
|
2375
|
+
case "media":
|
|
2376
|
+
case "widget":
|
|
2377
|
+
return false;
|
|
2378
|
+
case "row":
|
|
2379
|
+
return root.cells.length === 0;
|
|
2380
|
+
case "stack":
|
|
2381
|
+
return root.children.every(isEmptyish);
|
|
2382
|
+
default:
|
|
2383
|
+
return false;
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
function materializeFeedBands(bands, pageItems, resolvers, diagnostics) {
|
|
2387
|
+
if (!Array.isArray(pageItems)) return;
|
|
2388
|
+
for (const band of bands) {
|
|
2389
|
+
const item = pageItems[band.index];
|
|
2390
|
+
if (!isFeedBand(item)) continue;
|
|
2391
|
+
if (!isEmptyish(band.root)) {
|
|
2392
|
+
diagnostics.push({
|
|
2393
|
+
kind: "empty-feed-grid",
|
|
2394
|
+
where: String(band.index),
|
|
2395
|
+
message: `band ${band.index} has parsed content but site.json item is a feed source \u2014 positional join misaligned, left as parsed`
|
|
2396
|
+
});
|
|
2397
|
+
continue;
|
|
2398
|
+
}
|
|
2399
|
+
const tiles = resolveFeedTiles(item, resolvers);
|
|
2400
|
+
if (!tiles) {
|
|
2401
|
+
diagnostics.push({
|
|
2402
|
+
kind: "empty-feed-grid",
|
|
2403
|
+
where: String(band.index),
|
|
2404
|
+
message: `feed band ${band.index} (source ${String(item.sources[0])}) resolved to no tiles`
|
|
2405
|
+
});
|
|
2406
|
+
continue;
|
|
2407
|
+
}
|
|
2408
|
+
const spacing = parseInt(String(item.spacing ?? ""), 10);
|
|
2409
|
+
const row = materializeFeedGrid({
|
|
2410
|
+
tiles,
|
|
2411
|
+
columns: feedColumns(item),
|
|
2412
|
+
...Number.isFinite(spacing) && spacing > 0 ? { spacing } : {},
|
|
2413
|
+
...tileOverlay(item) ? { overlay: tileOverlay(item) } : {}
|
|
2414
|
+
});
|
|
2415
|
+
if (!row) continue;
|
|
2416
|
+
const headings = leadingHeadings(band.root);
|
|
2417
|
+
band.root = headings.length ? { kind: "stack", children: [...headings, row] } : row;
|
|
2418
|
+
}
|
|
2419
|
+
}
|
|
2420
|
+
function sitePages(siteJson) {
|
|
2421
|
+
const { pages } = normalizePages(parseBluxSite(siteJson));
|
|
2422
|
+
return pages.map((p) => ({ uid: p.uid, path: p.path, title: p.title }));
|
|
2423
|
+
}
|
|
2424
|
+
function convertSite({
|
|
2425
|
+
siteJson,
|
|
2426
|
+
htmlByUid
|
|
2427
|
+
}) {
|
|
2428
|
+
const ir = assembleIR({ siteJson, htmls: [...htmlByUid.values()] });
|
|
2429
|
+
const assetsById = new Map(ir.assets.map((a) => [a.id, a]));
|
|
2430
|
+
const sourceUrlById = new Map(ir.assets.map((a) => [a.id, a.sourceUrl]));
|
|
2431
|
+
const defaults = blockClassDefaults(siteJson);
|
|
2432
|
+
const raw = parseBluxSite(siteJson);
|
|
2433
|
+
const assetBase = feedAssetBase([...htmlByUid.values()], ir.meta.bluxSiteId);
|
|
2434
|
+
const feedResolvers = buildFeedResolvers(raw.feeds, raw.media, assetBase);
|
|
2435
|
+
const pageItemsByIndex = siteJson?.content?.pages;
|
|
2436
|
+
const pages = [];
|
|
2437
|
+
const presentation = { pages: {} };
|
|
2438
|
+
ir.pages.forEach((page, pageIndex) => {
|
|
2439
|
+
const html = htmlByUid.get(page.uid);
|
|
2440
|
+
if (html === void 0) {
|
|
2441
|
+
ir.diagnostics.push({
|
|
2442
|
+
kind: "missing-page-html",
|
|
2443
|
+
where: page.uid,
|
|
2444
|
+
message: `no rendered html for page "${page.uid}" (${page.path || "/"}) \u2014 page skipped`
|
|
2445
|
+
});
|
|
2446
|
+
return;
|
|
2447
|
+
}
|
|
2448
|
+
const bands = parseGridBands(html);
|
|
2449
|
+
materializeFeedBands(
|
|
2450
|
+
bands,
|
|
2451
|
+
pageItemsByIndex?.[pageIndex]?.items,
|
|
2452
|
+
feedResolvers,
|
|
2453
|
+
ir.diagnostics
|
|
2454
|
+
);
|
|
2455
|
+
const mapConfig = extractMapConfig(html);
|
|
2456
|
+
const specs = classifyBands(bands, mapConfig ? { isMapMount: makeIsMapMount(mapConfig) } : {});
|
|
2457
|
+
const styles = blockStylesByIndex(siteJson, pageIndex);
|
|
2458
|
+
const deps = {
|
|
2459
|
+
resolveMedia: (m) => {
|
|
2460
|
+
const url = mediaUrl(m, sourceUrlById);
|
|
2461
|
+
if (!url) return null;
|
|
2462
|
+
const alt = assetsById.get(m.assetId)?.alt;
|
|
2463
|
+
const rm = {
|
|
2464
|
+
kind: m.kind,
|
|
2465
|
+
url,
|
|
2466
|
+
...alt ? { alt } : {},
|
|
2467
|
+
...m.width !== void 0 ? { width: m.width } : {},
|
|
2468
|
+
...m.aspect !== void 0 ? { aspect: m.aspect } : {},
|
|
2469
|
+
...m.fit ? { fit: m.fit } : {},
|
|
2470
|
+
...m.position ? { position: m.position } : {},
|
|
2471
|
+
...m.minHeight ? { minHeight: m.minHeight } : {},
|
|
2472
|
+
...m.cropRatio ? { cropRatio: m.cropRatio } : {},
|
|
2473
|
+
...m.playback ? { playback: m.playback } : {}
|
|
2474
|
+
};
|
|
2475
|
+
return rm;
|
|
2476
|
+
},
|
|
2477
|
+
styleFor: (i) => styles.get(i),
|
|
2478
|
+
defaultsFor: (blockClass) => defaults.get(blockClass),
|
|
2479
|
+
map: mapConfig ? mapRenderFromConfig(mapConfig) : null
|
|
2480
|
+
};
|
|
2481
|
+
presentation.pages[page.uid] = buildPresentation(specs, deps);
|
|
2482
|
+
pages.push({ uid: page.uid, title: page.title, path: page.path, bands, specs, mapConfig });
|
|
2483
|
+
});
|
|
2484
|
+
const plan = buildGridSitePlan(
|
|
2485
|
+
pages.map((p) => ({ uid: p.uid, title: p.title, specs: p.specs })),
|
|
2486
|
+
ir
|
|
2487
|
+
);
|
|
2488
|
+
return { pages, ir, plan, presentation };
|
|
2489
|
+
}
|
|
2490
|
+
|
|
2491
|
+
// src/blux/emit/site-config.ts
|
|
2492
|
+
function parseNavItems(items) {
|
|
2493
|
+
if (!Array.isArray(items)) return [];
|
|
2494
|
+
const out = [];
|
|
2495
|
+
for (const raw of items) {
|
|
2496
|
+
const label = typeof raw?.title === "string" ? raw.title.trim() : "";
|
|
2497
|
+
if (!label) continue;
|
|
2498
|
+
const href = typeof raw?.link === "string" ? raw.link : "";
|
|
2499
|
+
const children = parseNavItems(raw?.items);
|
|
2500
|
+
out.push({ label, href, ...children.length ? { children } : {} });
|
|
2501
|
+
}
|
|
2502
|
+
return out;
|
|
2503
|
+
}
|
|
2504
|
+
var SOCIAL_DOMAINS = {
|
|
2505
|
+
facebook: ["facebook.com", "fb.com"],
|
|
2506
|
+
twitter: ["twitter.com", "x.com"],
|
|
2507
|
+
instagram: ["instagram.com"],
|
|
2508
|
+
pinterest: ["pinterest.com"],
|
|
2509
|
+
linkedin: ["linkedin.com"],
|
|
2510
|
+
"linkedin-company": ["linkedin.com"],
|
|
2511
|
+
youtube: ["youtube.com", "youtu.be"],
|
|
2512
|
+
reddit: ["reddit.com"]
|
|
2513
|
+
};
|
|
2514
|
+
function hostMatches(host, domain) {
|
|
2515
|
+
return host === domain || host.endsWith("." + domain);
|
|
2516
|
+
}
|
|
2517
|
+
function hostOf(url) {
|
|
2518
|
+
try {
|
|
2519
|
+
return new URL(url).hostname.toLowerCase();
|
|
2520
|
+
} catch {
|
|
2521
|
+
return "";
|
|
2522
|
+
}
|
|
2523
|
+
}
|
|
2524
|
+
function footerRegion(html) {
|
|
2525
|
+
const lower = html.toLowerCase();
|
|
2526
|
+
const start = lower.lastIndexOf("<footer");
|
|
2527
|
+
if (start === -1) return html;
|
|
2528
|
+
const end = lower.indexOf("</footer>", start);
|
|
2529
|
+
return end === -1 ? html.slice(start) : html.slice(start, end + "</footer>".length);
|
|
2530
|
+
}
|
|
2531
|
+
function socialHrefResolverFromHtml(htmls) {
|
|
2532
|
+
const hrefs = [];
|
|
2533
|
+
const re = /href\s*=\s*["']([^"']+)["']/gi;
|
|
2534
|
+
for (const html of htmls) {
|
|
2535
|
+
if (typeof html !== "string") continue;
|
|
2536
|
+
const region = footerRegion(html);
|
|
2537
|
+
let m;
|
|
2538
|
+
while (m = re.exec(region)) {
|
|
2539
|
+
const url = m[1];
|
|
2540
|
+
if (url && /^https?:\/\//i.test(url)) hrefs.push(url);
|
|
2541
|
+
}
|
|
2542
|
+
}
|
|
2543
|
+
return (network) => {
|
|
2544
|
+
const domains = SOCIAL_DOMAINS[network];
|
|
2545
|
+
if (!domains) return void 0;
|
|
2546
|
+
return hrefs.find((u) => {
|
|
2547
|
+
const host = hostOf(u);
|
|
2548
|
+
return host && domains.some((d) => hostMatches(host, d));
|
|
2549
|
+
});
|
|
2550
|
+
};
|
|
2551
|
+
}
|
|
2552
|
+
function parseSocials(raw, resolveSocialHref) {
|
|
2553
|
+
const out = [];
|
|
2554
|
+
const items = Array.isArray(raw) ? raw : [];
|
|
2555
|
+
for (const it of items) {
|
|
2556
|
+
const media = it?.media;
|
|
2557
|
+
if (!media || media.type !== "social") continue;
|
|
2558
|
+
const networks = media.networks;
|
|
2559
|
+
if (!networks || typeof networks !== "object") continue;
|
|
2560
|
+
const urls = media.urls ?? {};
|
|
2561
|
+
for (const [network, on] of Object.entries(networks)) {
|
|
2562
|
+
if (on !== true) continue;
|
|
2563
|
+
const fromExport = typeof urls[network] === "string" ? urls[network] : void 0;
|
|
2564
|
+
const url = fromExport ?? resolveSocialHref?.(network);
|
|
2565
|
+
out.push({ network, ...url ? { href: url } : {} });
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2568
|
+
return out;
|
|
2569
|
+
}
|
|
2570
|
+
function collectTitles(node, acc) {
|
|
2571
|
+
if (Array.isArray(node)) {
|
|
2572
|
+
for (const n of node) collectTitles(n, acc);
|
|
2573
|
+
return acc;
|
|
2574
|
+
}
|
|
2575
|
+
if (node && typeof node === "object") {
|
|
2576
|
+
const rec = node;
|
|
2577
|
+
if (typeof rec.title === "string" && rec.title.trim()) acc.push(rec.title.trim());
|
|
2578
|
+
for (const k of Object.keys(rec)) if (k !== "title") collectTitles(rec[k], acc);
|
|
2579
|
+
}
|
|
2580
|
+
return acc;
|
|
2581
|
+
}
|
|
2582
|
+
var COPYRIGHT_RE = /©|\(c\)|\bcopyright\b|rights reserved/i;
|
|
2583
|
+
function parseFooterText(raw) {
|
|
2584
|
+
return collectTitles(raw, []).find((t) => COPYRIGHT_RE.test(t));
|
|
2585
|
+
}
|
|
2586
|
+
function buildSiteConfig(siteJson, resolveLogo, resolveSocialHref) {
|
|
2587
|
+
const j = siteJson;
|
|
2588
|
+
const navRoot = Array.isArray(j.navigation) ? j.navigation[0] : j.navigation;
|
|
2589
|
+
const footRoot = Array.isArray(j.footer) ? j.footer[0] : j.footer;
|
|
2590
|
+
const items = parseNavItems(navRoot?.items);
|
|
2591
|
+
const logoMedia = navRoot?.logo?.media;
|
|
2592
|
+
const logoUuid = typeof logoMedia?.media === "string" ? logoMedia.media : void 0;
|
|
2593
|
+
const logoUrl = logoUuid ? resolveLogo(logoUuid) : null;
|
|
2594
|
+
const maxWidth = typeof logoMedia?.["max-width"] === "string" ? logoMedia["max-width"] : void 0;
|
|
2595
|
+
const socials = parseSocials(footRoot?.items, resolveSocialHref);
|
|
2596
|
+
const text = parseFooterText(footRoot?.items);
|
|
2597
|
+
return {
|
|
2598
|
+
nav: {
|
|
2599
|
+
...logoUrl ? { logo: { url: logoUrl, ...maxWidth ? { maxWidth } : {} } } : {},
|
|
2600
|
+
items
|
|
2601
|
+
},
|
|
2602
|
+
footer: {
|
|
2603
|
+
socials,
|
|
2604
|
+
...text ? { text } : {}
|
|
2605
|
+
}
|
|
2606
|
+
};
|
|
2607
|
+
}
|
|
1727
2608
|
|
|
1728
2609
|
// src/blux/emit/validate-layout.ts
|
|
1729
2610
|
function tokKey(t) {
|
|
@@ -1963,11 +2844,19 @@ function walkBand(bp, map) {
|
|
|
1963
2844
|
};
|
|
1964
2845
|
return out;
|
|
1965
2846
|
}
|
|
1966
|
-
function
|
|
2847
|
+
function rewritePage(p, map) {
|
|
1967
2848
|
const bands = {};
|
|
1968
|
-
for (const [k, bp] of Object.entries(
|
|
2849
|
+
for (const [k, bp] of Object.entries(p.bands)) bands[k] = walkBand(bp, map);
|
|
1969
2850
|
return { bands };
|
|
1970
2851
|
}
|
|
2852
|
+
function rewriteManifestUrls(manifest, urlMap) {
|
|
2853
|
+
if ("pages" in manifest && manifest.pages) {
|
|
2854
|
+
const pages = {};
|
|
2855
|
+
for (const [uid, p] of Object.entries(manifest.pages)) pages[uid] = rewritePage(p, urlMap);
|
|
2856
|
+
return { pages };
|
|
2857
|
+
}
|
|
2858
|
+
return rewritePage(manifest, urlMap);
|
|
2859
|
+
}
|
|
1971
2860
|
|
|
1972
2861
|
// src/cli/commands/blux.ts
|
|
1973
2862
|
async function runBluxCommand(action, dir, opts) {
|
|
@@ -2021,9 +2910,10 @@ async function runBluxCommand(action, dir, opts) {
|
|
|
2021
2910
|
await writeFile(join(out, "ir.json"), JSON.stringify(ir, null, 2));
|
|
2022
2911
|
await writeFile(join(out, "migration-plan.json"), JSON.stringify(plan, null, 2));
|
|
2023
2912
|
const rolesCss = emitRolesCss(ir.theme);
|
|
2913
|
+
const buttonsCss = emitButtonsCss(ir.theme);
|
|
2024
2914
|
await writeFile(
|
|
2025
2915
|
join(out, "theme.css"),
|
|
2026
|
-
emitThemeCss(ir.theme) + (rolesCss ? "\n" + rolesCss : "")
|
|
2916
|
+
emitThemeCss(ir.theme) + (rolesCss ? "\n" + rolesCss : "") + (buttonsCss ? "\n" + buttonsCss : "")
|
|
2027
2917
|
);
|
|
2028
2918
|
await writeFile(join(out, "review-manifest.json"), JSON.stringify(manifest, null, 2));
|
|
2029
2919
|
await writeFile(
|
|
@@ -2068,12 +2958,16 @@ async function runBluxCommand(action, dir, opts) {
|
|
|
2068
2958
|
WARNING missing assets: ${r.missingAssets.join(", ")}` : "";
|
|
2069
2959
|
let manifestNote = "";
|
|
2070
2960
|
const manifestPath = join(dirname(planPath), "blux-presentation.json");
|
|
2961
|
+
let manifestRaw = null;
|
|
2071
2962
|
try {
|
|
2072
|
-
|
|
2963
|
+
manifestRaw = await readFile(manifestPath, "utf-8");
|
|
2964
|
+
} catch {
|
|
2965
|
+
}
|
|
2966
|
+
if (manifestRaw !== null) {
|
|
2967
|
+
const manifest = JSON.parse(manifestRaw);
|
|
2073
2968
|
const rewritten = rewriteManifestUrls(manifest, r.assetUrlByCdn);
|
|
2074
2969
|
await writeFile(manifestPath, JSON.stringify(rewritten, null, 2) + "\n");
|
|
2075
2970
|
manifestNote = "\nmanifest media rewritten to Prismic urls";
|
|
2076
|
-
} catch {
|
|
2077
2971
|
}
|
|
2078
2972
|
return {
|
|
2079
2973
|
output: `custom types pushed: ${pushed.join(", ") || "none"}
|
|
@@ -2163,15 +3057,24 @@ assets: ${r.assetsUploaded} uploaded, ${r.assetsReused} reused | documents: ${r.
|
|
|
2163
3057
|
}
|
|
2164
3058
|
if (action === "convert") {
|
|
2165
3059
|
if (!dir) return { output: "blux convert needs a Blux export directory.", code: 1 };
|
|
2166
|
-
let html;
|
|
2167
3060
|
let siteJson;
|
|
2168
3061
|
try {
|
|
2169
|
-
html = await readFile(join(dir, "index.html"), "utf-8");
|
|
2170
3062
|
siteJson = JSON.parse(await readFile(join(dir, "site.json"), "utf-8"));
|
|
2171
3063
|
} catch (err) {
|
|
2172
3064
|
return { output: `could not read export in ${dir}: ${err.message}`, code: 1 };
|
|
2173
3065
|
}
|
|
2174
|
-
const
|
|
3066
|
+
const htmlByUid = /* @__PURE__ */ new Map();
|
|
3067
|
+
for (const p of sitePages(siteJson)) {
|
|
3068
|
+
const file = p.path ? join(dir, p.path, "index.html") : join(dir, "index.html");
|
|
3069
|
+
try {
|
|
3070
|
+
htmlByUid.set(p.uid, await readFile(file, "utf-8"));
|
|
3071
|
+
} catch {
|
|
3072
|
+
}
|
|
3073
|
+
}
|
|
3074
|
+
if (!htmlByUid.size) {
|
|
3075
|
+
return { output: `could not read any page html in ${dir}`, code: 1 };
|
|
3076
|
+
}
|
|
3077
|
+
const { pages, ir, plan, presentation } = convertSite({ siteJson, htmlByUid });
|
|
2175
3078
|
const outDir = opts.out ?? join(dir, "blux-out");
|
|
2176
3079
|
await mkdir(outDir, { recursive: true });
|
|
2177
3080
|
await writeFile(join(outDir, "migration-plan.json"), JSON.stringify(plan, null, 2));
|
|
@@ -2179,18 +3082,90 @@ assets: ${r.assetsUploaded} uploaded, ${r.assetsReused} reused | documents: ${r.
|
|
|
2179
3082
|
join(outDir, "blux-presentation.json"),
|
|
2180
3083
|
JSON.stringify(presentation, null, 2) + "\n"
|
|
2181
3084
|
);
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
3085
|
+
{
|
|
3086
|
+
const buttonsCss = emitButtonsCss(ir.theme);
|
|
3087
|
+
await writeFile(
|
|
3088
|
+
join(outDir, "theme.css"),
|
|
3089
|
+
emitThemeCss(ir.theme) + "\n" + emitRolesCss(ir.theme) + (buttonsCss ? "\n" + buttonsCss : "")
|
|
3090
|
+
);
|
|
3091
|
+
}
|
|
3092
|
+
const mapConfigs = Object.fromEntries(
|
|
3093
|
+
pages.filter((p) => p.mapConfig).map((p) => [p.uid, p.mapConfig])
|
|
2185
3094
|
);
|
|
2186
|
-
if (
|
|
2187
|
-
await writeFile(join(outDir, "map-config.json"), JSON.stringify(
|
|
3095
|
+
if (Object.keys(mapConfigs).length) {
|
|
3096
|
+
await writeFile(join(outDir, "map-config.json"), JSON.stringify(mapConfigs, null, 2) + "\n");
|
|
2188
3097
|
}
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
3098
|
+
{
|
|
3099
|
+
const sourceUrlById = new Map(ir.assets.map((a) => [a.id, a.sourceUrl]));
|
|
3100
|
+
const base2 = feedAssetBase([...htmlByUid.values()], ir.meta.bluxSiteId);
|
|
3101
|
+
const mediaDict = siteJson.media ?? {};
|
|
3102
|
+
const extByMime = {
|
|
3103
|
+
"image/png": "png",
|
|
3104
|
+
"image/jpeg": "jpg",
|
|
3105
|
+
"image/svg+xml": "svg",
|
|
3106
|
+
"image/gif": "gif",
|
|
3107
|
+
"image/webp": "webp"
|
|
3108
|
+
};
|
|
3109
|
+
const resolveLogo = (uuid) => {
|
|
3110
|
+
const scraped = sourceUrlById.get(uuid);
|
|
3111
|
+
if (scraped) return scraped;
|
|
3112
|
+
const ext = extByMime[String(mediaDict[uuid]?.type ?? "")];
|
|
3113
|
+
return ext ? `${base2}${uuid}.${ext}` : null;
|
|
3114
|
+
};
|
|
3115
|
+
const resolveSocialHref = socialHrefResolverFromHtml([...htmlByUid.values()]);
|
|
3116
|
+
const siteConfig = buildSiteConfig(siteJson, resolveLogo, resolveSocialHref);
|
|
3117
|
+
await writeFile(join(outDir, "site-config.json"), JSON.stringify(siteConfig, null, 2) + "\n");
|
|
3118
|
+
}
|
|
3119
|
+
{
|
|
3120
|
+
const feeds = siteJson.feeds ?? {};
|
|
3121
|
+
const productFeed = Object.values(feeds).find((f) => f?.publish === "products");
|
|
3122
|
+
if (productFeed && Array.isArray(productFeed.items)) {
|
|
3123
|
+
const base2 = feedAssetBase([...htmlByUid.values()], ir.meta.bluxSiteId);
|
|
3124
|
+
const mediaMeta = siteJson.media ?? {};
|
|
3125
|
+
const resolveImage = (uuid) => {
|
|
3126
|
+
const ext = extFor(mediaMeta[uuid]?.type, mediaMeta[uuid]?.name);
|
|
3127
|
+
return ext ? `${base2}${uuid}.${ext}` : null;
|
|
3128
|
+
};
|
|
3129
|
+
const products = materializeProducts(productFeed.items, resolveImage);
|
|
3130
|
+
await writeFile(join(outDir, "products.json"), JSON.stringify(products, null, 2) + "\n");
|
|
3131
|
+
}
|
|
3132
|
+
}
|
|
3133
|
+
let faviconLine = "";
|
|
3134
|
+
if (ir.meta.favicon?.sourceUrl) {
|
|
3135
|
+
const { assetId, sourceUrl } = ir.meta.favicon;
|
|
3136
|
+
try {
|
|
3137
|
+
const res = await (opts.fetchImpl ?? fetch)(sourceUrl);
|
|
3138
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
3139
|
+
await writeFile(join(outDir, "favicon.png"), new Uint8Array(await res.arrayBuffer()));
|
|
3140
|
+
faviconLine = `
|
|
3141
|
+
favicon \u2192 ${join(outDir, "favicon.png")}`;
|
|
3142
|
+
} catch (err) {
|
|
3143
|
+
await writeFile(
|
|
3144
|
+
join(outDir, "favicon.json"),
|
|
3145
|
+
JSON.stringify({ assetId, url: sourceUrl }, null, 2) + "\n"
|
|
3146
|
+
);
|
|
3147
|
+
faviconLine = `
|
|
3148
|
+
favicon fetch failed (${err.message}) \u2014 url preserved in ${join(outDir, "favicon.json")}`;
|
|
3149
|
+
}
|
|
3150
|
+
}
|
|
3151
|
+
const layoutByUid = {};
|
|
3152
|
+
const reportLines = [];
|
|
3153
|
+
for (const p of pages) {
|
|
3154
|
+
const pagePresentation = presentation.pages[p.uid];
|
|
3155
|
+
if (!pagePresentation) continue;
|
|
3156
|
+
const layout = validateLayout(p.specs, pagePresentation);
|
|
3157
|
+
layoutByUid[p.uid] = layout;
|
|
3158
|
+
reportLines.push(`[${p.uid}] ${formatLayoutReport(layout)}`);
|
|
3159
|
+
}
|
|
3160
|
+
await writeFile(
|
|
3161
|
+
join(outDir, "layout-report.json"),
|
|
3162
|
+
JSON.stringify({ pages: layoutByUid }, null, 2) + "\n"
|
|
3163
|
+
);
|
|
3164
|
+
const totalBands = pages.reduce((n, p) => n + p.bands.length, 0);
|
|
3165
|
+
const missing = ir.diagnostics.filter((d) => d.kind === "missing-page-html");
|
|
2192
3166
|
return {
|
|
2193
|
-
output: `Converted ${
|
|
3167
|
+
output: `Converted ${pages.length} pages / ${totalBands} bands \u2192 ${outDir} (${plan.documents.length} page documents` + (Object.keys(mapConfigs).length ? `, map config on ${Object.keys(mapConfigs).join(", ")}` : "") + ")" + (missing.length ? `
|
|
3168
|
+
skipped (no html): ${missing.map((d) => d.where).join(", ")}` : "") + faviconLine + "\n" + reportLines.join("\n"),
|
|
2194
3169
|
code: 0
|
|
2195
3170
|
};
|
|
2196
3171
|
}
|
|
@@ -2202,4 +3177,4 @@ assets: ${r.assetsUploaded} uploaded, ${r.assetsReused} reused | documents: ${r.
|
|
|
2202
3177
|
export {
|
|
2203
3178
|
runBluxCommand
|
|
2204
3179
|
};
|
|
2205
|
-
//# sourceMappingURL=blux-
|
|
3180
|
+
//# sourceMappingURL=blux-C24OVDDW.js.map
|