@omniharmonic/openspace-cli 0.1.13 → 0.1.14
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/{agents-md-QBMFXTIQ.js → agents-md-63S3T7CX.js} +2 -2
- package/dist/bin.js +3 -3
- package/dist/{chunk-EFSGZWAO.js → chunk-N7WFQYFE.js} +132 -16
- package/dist/chunk-N7WFQYFE.js.map +1 -0
- package/dist/{chunk-IXN2CNGL.js → chunk-SY5NDWS2.js} +81 -16
- package/dist/{chunk-IXN2CNGL.js.map → chunk-SY5NDWS2.js.map} +1 -1
- package/dist/{chunk-B5C7S4S5.js → chunk-TDLUJHWB.js} +817 -69
- package/dist/chunk-TDLUJHWB.js.map +1 -0
- package/dist/{crawl-TAADG5BR.js → crawl-YPDLNAN7.js} +2 -2
- package/dist/index.js +3 -3
- package/dist/starter/site/AGENTS.md +3 -3
- package/package.json +8 -8
- package/dist/chunk-B5C7S4S5.js.map +0 -1
- package/dist/chunk-EFSGZWAO.js.map +0 -1
- /package/dist/{agents-md-QBMFXTIQ.js.map → agents-md-63S3T7CX.js.map} +0 -0
- /package/dist/{crawl-TAADG5BR.js.map → crawl-YPDLNAN7.js.map} +0 -0
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
text,
|
|
30
30
|
themeFromCss,
|
|
31
31
|
videoUrl
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-N7WFQYFE.js";
|
|
33
33
|
import {
|
|
34
34
|
BREAKPOINT_INFO,
|
|
35
35
|
BUTTON_STYLES,
|
|
@@ -147,7 +147,7 @@ import {
|
|
|
147
147
|
toHex,
|
|
148
148
|
toJsonSchema,
|
|
149
149
|
toMarkdown
|
|
150
|
-
} from "./chunk-
|
|
150
|
+
} from "./chunk-SY5NDWS2.js";
|
|
151
151
|
import {
|
|
152
152
|
__commonJS,
|
|
153
153
|
__export,
|
|
@@ -15069,7 +15069,7 @@ function nearestOpenFont(family, metrics) {
|
|
|
15069
15069
|
let best;
|
|
15070
15070
|
for (const [name, cat, xHeight, capHeight, width] of list5) {
|
|
15071
15071
|
let d = metricDistance(metrics, { xHeight, capHeight, width });
|
|
15072
|
-
if (eq.includes(name)) d -=
|
|
15072
|
+
if (eq.includes(name)) d -= 4e-3 * (eq.length - eq.indexOf(name));
|
|
15073
15073
|
if (!best || d < (best.distance ?? Number.POSITIVE_INFINITY))
|
|
15074
15074
|
best = { family: name, category: cat, via: "metrics", distance: Math.max(0, d) };
|
|
15075
15075
|
}
|
|
@@ -15169,6 +15169,11 @@ async function fitGoogleWeights(font, fetchImpl = fetch) {
|
|
|
15169
15169
|
// ../import/src/fidelity/look.ts
|
|
15170
15170
|
var norm2 = (s2) => (s2 ?? "").toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
15171
15171
|
var GENERIC = /^(?:serif|sans-serif|monospace|system-ui|ui-\w+|-apple-system|blinkmacsystemfont)$/i;
|
|
15172
|
+
var colorGap = (a, b) => {
|
|
15173
|
+
const p = (h) => [1, 3, 5].map((i) => Number.parseInt(h.slice(i, i + 2), 16) || 0);
|
|
15174
|
+
const [x, y] = [p(a), p(b)];
|
|
15175
|
+
return Math.sqrt(x.reduce((n, v, i) => n + (v - (y[i] ?? 0)) ** 2, 0));
|
|
15176
|
+
};
|
|
15172
15177
|
var round = (n, d = 1) => Math.round(n * 10 ** d) / 10 ** d;
|
|
15173
15178
|
function stepOf(t, mobile, opts = {}) {
|
|
15174
15179
|
return {
|
|
@@ -15241,7 +15246,12 @@ function lookFromProbe(d, m) {
|
|
|
15241
15246
|
scale[p] = { ...scale[p], font: "heading", ...w >= 100 && w <= 900 ? { weight: w } : {} };
|
|
15242
15247
|
}
|
|
15243
15248
|
}
|
|
15244
|
-
if (
|
|
15249
|
+
if (P.display)
|
|
15250
|
+
scale.display = {
|
|
15251
|
+
...stepOf(P.display, M.display, { weight: true }),
|
|
15252
|
+
font: roleOf(P.display)
|
|
15253
|
+
};
|
|
15254
|
+
else if (scale.h1) scale.display = { ...scale.h1 };
|
|
15245
15255
|
if (P.lead) scale.lead = { ...stepOf(P.lead, M.lead), font: roleOf(P.lead) };
|
|
15246
15256
|
if (P.small) scale.small = stepOf(P.small, M.small);
|
|
15247
15257
|
if (P.eyebrow)
|
|
@@ -15263,6 +15273,17 @@ function lookFromProbe(d, m) {
|
|
|
15263
15273
|
const mobileLeft = m?.layout.left;
|
|
15264
15274
|
if (L2.contentWidth && L2.contentWidth >= 480 && L2.contentWidth <= 2400 && L2.contentWidth < d.viewport - 40)
|
|
15265
15275
|
layout.contentWidth = Math.round(L2.contentWidth / 8) * 8;
|
|
15276
|
+
const page = d.pageBackground;
|
|
15277
|
+
const panels = d.sections.filter(
|
|
15278
|
+
(s2) => page && s2.background && s2.left !== void 0 && s2.width !== void 0 && s2.left >= 24 && s2.left + s2.width <= d.viewport - 24 && s2.raised === true && colorGap(s2.background, page) >= 10
|
|
15279
|
+
);
|
|
15280
|
+
if (page && d.viewport >= 768 && panels.length && panels.length * 2 >= d.sections.length) {
|
|
15281
|
+
layout.surface = "card";
|
|
15282
|
+
layout.pageBackground = page;
|
|
15283
|
+
const measures = panels.map((s2) => s2.textWidth).filter((w2) => !!w2);
|
|
15284
|
+
const w = measures.sort((a, b) => a - b)[Math.floor(measures.length / 2)];
|
|
15285
|
+
if (w && w >= 480 && w <= 1600) layout.contentWidth = Math.round(w / 8) * 8;
|
|
15286
|
+
}
|
|
15266
15287
|
if (L2.sectionPadding && L2.sectionPadding >= 16 && L2.sectionPadding <= 320)
|
|
15267
15288
|
layout.sectionPadding = Math.round(L2.sectionPadding);
|
|
15268
15289
|
if (layout.sectionPadding && m?.layout.sectionPadding && m.layout.sectionPadding <= 240)
|
|
@@ -15304,6 +15325,17 @@ function lookFromProbe(d, m) {
|
|
|
15304
15325
|
...d.eyebrowRule ? { eyebrowRule: true } : {},
|
|
15305
15326
|
...d.fonts && Object.keys(d.fonts).length ? { metrics: { ...m?.fonts, ...d.fonts } } : {},
|
|
15306
15327
|
...hosts.length ? { fontHosts: hosts } : {},
|
|
15328
|
+
...d.logoHeight && d.logoHeight >= 16 ? {
|
|
15329
|
+
logoHeight: {
|
|
15330
|
+
d: Math.min(240, d.logoHeight),
|
|
15331
|
+
...m?.logoHeight && m.logoHeight >= 16 ? { m: Math.min(240, m.logoHeight) } : {}
|
|
15332
|
+
}
|
|
15333
|
+
} : {},
|
|
15334
|
+
...d.footerBackground ? { footerBackground: d.footerBackground } : {},
|
|
15335
|
+
...d.footerColumns && d.footerColumns >= 2 && d.footerColumns <= 4 ? {
|
|
15336
|
+
footerColumns: d.footerColumns,
|
|
15337
|
+
...d.footerText && d.footerText.size <= 14 && (d.footerText.case === "uppercase" || d.footerText.letterSpacing >= 0.08) ? { footerLabels: true } : {}
|
|
15338
|
+
} : {},
|
|
15307
15339
|
...d.sidebar ? {
|
|
15308
15340
|
sidebar: {
|
|
15309
15341
|
side: d.sidebar.side,
|
|
@@ -15331,6 +15363,13 @@ function applyLook(theme, look, source, mode = "override") {
|
|
|
15331
15363
|
theme.headerLayout = "sidebar";
|
|
15332
15364
|
if (look.sidebar.background) theme.headerBackground = look.sidebar.background;
|
|
15333
15365
|
}
|
|
15366
|
+
if (look.logoHeight && !keep(theme.logoHeight)) theme.logoHeight = look.logoHeight;
|
|
15367
|
+
if (look.footerBackground && !keep(theme.footerBackground))
|
|
15368
|
+
theme.footerBackground = look.footerBackground;
|
|
15369
|
+
if (look.footerColumns && !keep(theme.footerColumns)) {
|
|
15370
|
+
theme.footerColumns = look.footerColumns;
|
|
15371
|
+
if (look.footerLabels) theme.footerLabels = true;
|
|
15372
|
+
}
|
|
15334
15373
|
if (look.metrics) theme.fontMetrics = { ...look.metrics, ...theme.fontMetrics ?? {} };
|
|
15335
15374
|
if (look.fontHosts)
|
|
15336
15375
|
theme.fontHosts = [.../* @__PURE__ */ new Set([...theme.fontHosts ?? [], ...look.fontHosts])];
|
|
@@ -15541,7 +15580,15 @@ var PROBE_SCRIPT = String.raw`(async () => {
|
|
|
15541
15580
|
const er = e.getBoundingClientRect();
|
|
15542
15581
|
return visible(e) && er.width >= r.width * 0.9 && er.height >= r.height * 0.7;
|
|
15543
15582
|
});
|
|
15544
|
-
const res = { index, top: Math.round(r.top), height: Math.round(r.height), background: bgOf(sec.els ? sec.els[0] : sec), columns: 1, italicLines: 0, text: ownText(sec).slice(0, 120), words: ownText(sec).slice(0, 20000) };
|
|
15583
|
+
const res = { index, top: Math.round(r.top), height: Math.round(r.height), left: Math.round(r.left), width: Math.round(r.width), background: bgOf(sec.els ? sec.els[0] : sec), columns: 1, italicLines: 0, text: ownText(sec).slice(0, 120), words: ownText(sec).slice(0, 20000) };
|
|
15584
|
+
// A raised panel: the section's own box draws a shadow or a border (a theme's content card).
|
|
15585
|
+
// (Its own box, or a panel around it narrower than the page: an article card around a band.)
|
|
15586
|
+
for (let e = sec.els ? sec.els[0] : sec, k = 0; e && e !== document.body && k < 5; e = e.parentElement, k++) {
|
|
15587
|
+
const st = cs(e);
|
|
15588
|
+
const er = e.getBoundingClientRect();
|
|
15589
|
+
if (k > 0 && er.width >= vw * 0.95) break;
|
|
15590
|
+
if ((st.boxShadow && st.boxShadow !== "none") || (k === 0 && !sec.els && parseFloat(st.borderTopWidth) >= 1)) { res.raised = true; break; }
|
|
15591
|
+
}
|
|
15545
15592
|
if (bgEl || coverImg) res.backgroundImage = true;
|
|
15546
15593
|
const h = [...sec.querySelectorAll(headSel)].find((e) => visible(e) && ownText(e).length > 1);
|
|
15547
15594
|
if (h) {
|
|
@@ -15600,6 +15647,12 @@ var PROBE_SCRIPT = String.raw`(async () => {
|
|
|
15600
15647
|
res.body = best.t;
|
|
15601
15648
|
if (best.t.color) res.textColor = best.t.color;
|
|
15602
15649
|
}
|
|
15650
|
+
// How wide the running text is set (the measure), and where it starts.
|
|
15651
|
+
const measures = paras.map((p) => rect(p)).filter((x) => x.width > 120);
|
|
15652
|
+
if (measures.length) {
|
|
15653
|
+
res.textWidth = Math.round(Math.max(...measures.map((x) => x.width)));
|
|
15654
|
+
res.textLeft = Math.round(Math.min(...measures.map((x) => x.left)));
|
|
15655
|
+
}
|
|
15603
15656
|
// Columns: text items whose vertical spans overlap, at distinct left edges.
|
|
15604
15657
|
const items = [...sec.querySelectorAll(textSel + ", h2, img")].filter((e) => visible(e) && (e.tagName === "IMG" ? e.getBoundingClientRect().width > 60 : ownText(e).length >= 12)).map(rect);
|
|
15605
15658
|
for (const a of items) {
|
|
@@ -15612,6 +15665,24 @@ var PROBE_SCRIPT = String.raw`(async () => {
|
|
|
15612
15665
|
if (!sec.querySelector("form, input, textarea, select, iframe, video, canvas, embed, object")) {
|
|
15613
15666
|
const parts = [];
|
|
15614
15667
|
const inPart = (e) => parts.some((p) => p.el.contains(e));
|
|
15668
|
+
// Inline links and emphasis inside a paragraph (kept as rich text when it's recreated).
|
|
15669
|
+
const runsOf = (el) => {
|
|
15670
|
+
const out = [];
|
|
15671
|
+
const walk = (n, m) => {
|
|
15672
|
+
for (const c of n.childNodes) {
|
|
15673
|
+
if (c.nodeType === 3) { const t = (c.textContent || "").replace(/\s+/g, " "); if (t) out.push({ t, ...m }); }
|
|
15674
|
+
else if (c.nodeType === 1 && cs(c).display !== "none") {
|
|
15675
|
+
const mm = { ...m };
|
|
15676
|
+
if (c.tagName === "A" && c.getAttribute("href")) mm.href = c.href;
|
|
15677
|
+
if (/^(STRONG|B)$/.test(c.tagName)) mm.b = 1;
|
|
15678
|
+
if (/^(EM|I)$/.test(c.tagName)) mm.i = 1;
|
|
15679
|
+
if (c.tagName === "BR") out.push({ t: "\n" }); else walk(c, mm);
|
|
15680
|
+
}
|
|
15681
|
+
}
|
|
15682
|
+
};
|
|
15683
|
+
walk(el, {});
|
|
15684
|
+
return out.length <= 80 && out.some((r) => r.href || r.b || r.i) ? out : undefined;
|
|
15685
|
+
};
|
|
15615
15686
|
const btnLike = (e) => { const st = cs(e); return (toHex(st.backgroundColor) || parseFloat(st.borderTopWidth) >= 1) && parseFloat(st.paddingLeft) >= 8; };
|
|
15616
15687
|
const els = [...sec.querySelectorAll("h1, h2, h3, h4, h5, h6, p, li, blockquote, img, a, button")].filter((e) => visible(e));
|
|
15617
15688
|
for (const e of els) {
|
|
@@ -15630,10 +15701,34 @@ var PROBE_SCRIPT = String.raw`(async () => {
|
|
|
15630
15701
|
parts.push({ el: e, item: { kind: btnLike(e) ? "button" : "link", text: t, href: e.getAttribute("href") || "", box: at, style: { ...textOf(e), align: cs(e).textAlign } } });
|
|
15631
15702
|
} else {
|
|
15632
15703
|
if (!t || e.querySelector("p, li, h1, h2, h3, h4, h5, h6, img")) continue;
|
|
15633
|
-
|
|
15704
|
+
const runs = runsOf(e);
|
|
15705
|
+
parts.push({ el: e, item: { kind: "text", text: t.slice(0, 1500), box: at, style: { ...textOf(e), align: cs(e).textAlign }, ...(runs ? { runs } : {}) } });
|
|
15706
|
+
}
|
|
15707
|
+
}
|
|
15708
|
+
// Accent rules (thin bars, <hr>) and panels (boxes with their own fill or border) behind
|
|
15709
|
+
// the words: recreated on theme colors.
|
|
15710
|
+
const secBg = res.background;
|
|
15711
|
+
let decor = 0;
|
|
15712
|
+
for (const e of sec.querySelectorAll("hr, div, span, section, article, aside, figure, li")) {
|
|
15713
|
+
if (decor >= 6 || !visible(e) || parts.some((p) => p.el === e)) continue;
|
|
15714
|
+
const b = rect(e);
|
|
15715
|
+
const st = cs(e);
|
|
15716
|
+
const fill = toHex(st.backgroundColor);
|
|
15717
|
+
const bw = parseFloat(st.borderTopWidth) || 0;
|
|
15718
|
+
const line = e.tagName === "HR" || (b.height <= 4 && b.width >= 16 && (fill || bw >= 1));
|
|
15719
|
+
if (line) {
|
|
15720
|
+
parts.push({ el: e, item: { kind: "rule", box: { x: Math.round(b.left), y: Math.round(b.top), w: Math.round(b.width), h: Math.max(1, Math.round(e.tagName === "HR" ? bw || 1 : b.height)) }, color: fill || toHex(st.borderTopColor) } });
|
|
15721
|
+
decor++;
|
|
15722
|
+
continue;
|
|
15723
|
+
}
|
|
15724
|
+
const own = fill && fill !== secBg;
|
|
15725
|
+
const framed = bw >= 1 && st.borderTopStyle !== "none" && parseFloat(st.borderLeftWidth) >= 1;
|
|
15726
|
+
if ((own || framed) && b.width >= 120 && b.height >= 60 && b.width < r.width * 0.95 && !e.querySelector("img, video, iframe") && ownText(e)) {
|
|
15727
|
+
parts.push({ el: e, item: { kind: "panel", box: { x: Math.round(b.left), y: Math.round(b.top), w: Math.round(b.width), h: Math.round(b.height) }, ...(own ? { color: fill } : {}), ...(framed ? { border: { width: Math.round(bw), color: toHex(st.borderTopColor) } } : {}), radius: parseFloat(st.borderTopLeftRadius) || 0 } });
|
|
15728
|
+
decor++;
|
|
15634
15729
|
}
|
|
15635
15730
|
}
|
|
15636
|
-
if (parts.length && parts.length <=
|
|
15731
|
+
if (parts.length && parts.length <= 36) res.blueprint = parts.map((p) => p.item);
|
|
15637
15732
|
}
|
|
15638
15733
|
// Where the section's own content starts and ends (for the spacing between sections).
|
|
15639
15734
|
const content = [...sec.querySelectorAll(textSel + ", h1, h2, img, picture, video, iframe, form, button, a")].filter((e) => visible(e) && (/^(IMG|PICTURE|VIDEO|IFRAME|FORM)$/.test(e.tagName) || ownText(e).length > 1)).map(rect);
|
|
@@ -15667,6 +15762,20 @@ var PROBE_SCRIPT = String.raw`(async () => {
|
|
|
15667
15762
|
if (h2) presets.h2 = h2;
|
|
15668
15763
|
const h3 = median(styleOfAll("h3"));
|
|
15669
15764
|
if (h3) presets.h3 = h3;
|
|
15765
|
+
// A display line: the page's largest short text, well above its headings (a banner's title
|
|
15766
|
+
// set in a paragraph). Read when importing; the comparison doesn't check it.
|
|
15767
|
+
{
|
|
15768
|
+
const ref = Math.max(presets.h1 ? presets.h1.size : 0, presets.h2 ? presets.h2.size : 0, 16);
|
|
15769
|
+
let best;
|
|
15770
|
+
for (const e of document.querySelectorAll("p, strong, b, span, h1, h2, h3, div")) {
|
|
15771
|
+
if (!visible(e) || !inMain(e)) continue;
|
|
15772
|
+
const t = ownText(e);
|
|
15773
|
+
if (t.length < 3 || t.length > 60 || [...e.children].some((k) => ownText(k) === t && k.tagName !== "BR" && !/^(STRONG|B|EM|I|SPAN)$/.test(k.tagName))) continue;
|
|
15774
|
+
const st = textOf(e);
|
|
15775
|
+
if (st.size >= ref * 1.3 && (!best || st.size > best.size)) best = st;
|
|
15776
|
+
}
|
|
15777
|
+
if (best) presets.display = best;
|
|
15778
|
+
}
|
|
15670
15779
|
const tally = new Map();
|
|
15671
15780
|
for (const { el, t } of all) {
|
|
15672
15781
|
const k = t.family + "|" + t.size;
|
|
@@ -15717,7 +15826,7 @@ var PROBE_SCRIPT = String.raw`(async () => {
|
|
|
15717
15826
|
if (btn) {
|
|
15718
15827
|
presets.button = textOf(btn);
|
|
15719
15828
|
const s = cs(btn);
|
|
15720
|
-
buttons = { radius: parseFloat(s.borderTopLeftRadius) || 0, background: toHex(s.backgroundColor), border: parseFloat(s.borderTopWidth) || 0, paddingX: parseFloat(s.paddingLeft) || 0, text: ownText(btn).slice(0, 40), element: btn.tagName.toLowerCase() + (btn.className && typeof btn.className === "string" ? "." + btn.className.trim().split(/\s+/).slice(0, 2).join(".") : "") };
|
|
15829
|
+
buttons = { radius: parseFloat(s.borderTopLeftRadius) || 0, background: toHex(s.backgroundColor), border: parseFloat(s.borderTopWidth) || 0, paddingX: parseFloat(s.paddingLeft) || 0, text: ownText(btn).slice(0, 40), element: btn.tagName.toLowerCase() + (btn.className && typeof btn.className === "string" ? "." + btn.className.trim().split(/\s+/).slice(0, 2).join(".") : ""), ...(inMain(btn) ? {} : { chrome: true }) };
|
|
15721
15830
|
}
|
|
15722
15831
|
|
|
15723
15832
|
// --- layout ---------------------------------------------------------------------------------
|
|
@@ -15810,6 +15919,35 @@ var PROBE_SCRIPT = String.raw`(async () => {
|
|
|
15810
15919
|
height: doc.scrollHeight,
|
|
15811
15920
|
fonts,
|
|
15812
15921
|
fontRequests,
|
|
15922
|
+
pageBackground: toHex(cs(document.body).backgroundColor) || toHex(cs(doc).backgroundColor) || "#ffffff",
|
|
15923
|
+
...(() => {
|
|
15924
|
+
// The header's logo picture (its shown height) and the footer's own color.
|
|
15925
|
+
const out = {};
|
|
15926
|
+
const head = document.querySelector(".os-header, header, [role=banner], #header");
|
|
15927
|
+
const logo = head && [...head.querySelectorAll("img, svg")].find((e) => visible(e) && e.getBoundingClientRect().height >= 16 && e.getBoundingClientRect().width >= 24);
|
|
15928
|
+
if (logo) out.logoHeight = Math.round(logo.getBoundingClientRect().height);
|
|
15929
|
+
const foot = [...document.querySelectorAll(".os-footer, footer, [role=contentinfo], #footer")].filter(visible).pop();
|
|
15930
|
+
if (foot) {
|
|
15931
|
+
const c = toHex(cs(foot).backgroundColor) || [...foot.querySelectorAll("*")].map((e) => toHex(cs(e).backgroundColor)).find(Boolean);
|
|
15932
|
+
if (c) out.footerBackground = c;
|
|
15933
|
+
// Its first line of words: how many groups sit side by side, and their style.
|
|
15934
|
+
const leaves = [...foot.querySelectorAll("p, li, a, span, small, div, h2, h3, h4")].filter((e) => visible(e) && ownText(e) && ![...e.children].some((k) => ownText(k) && /^(P|DIV|UL|LI|H2|H3|H4)$/.test(k.tagName)));
|
|
15935
|
+
const lines = leaves.map((e) => ({ e, r: rect(e) }));
|
|
15936
|
+
if (lines.length) {
|
|
15937
|
+
const top = Math.min(...lines.map((l) => l.r.top));
|
|
15938
|
+
const row = lines.filter((l) => l.r.top <= top + 24);
|
|
15939
|
+
const groups = [];
|
|
15940
|
+
for (const l of row.sort((a, b) => a.r.left - b.r.left)) {
|
|
15941
|
+
const g = groups[groups.length - 1];
|
|
15942
|
+
if (g && l.r.left - g.right < 48) g.right = Math.max(g.right, l.r.right);
|
|
15943
|
+
else groups.push({ left: l.r.left, right: l.r.right });
|
|
15944
|
+
}
|
|
15945
|
+
out.footerColumns = groups.length;
|
|
15946
|
+
out.footerText = textOf(row[0].e);
|
|
15947
|
+
}
|
|
15948
|
+
}
|
|
15949
|
+
return out;
|
|
15950
|
+
})(),
|
|
15813
15951
|
...(sidebar ? { sidebar } : {}),
|
|
15814
15952
|
};
|
|
15815
15953
|
})()`;
|
|
@@ -42058,13 +42196,19 @@ async function mapBlock(ctx, sq, opts = {}) {
|
|
|
42058
42196
|
const href = sq.href ?? sq.card?.button?.href;
|
|
42059
42197
|
const link2 = href ? await linkFor(ctx, href) : void 0;
|
|
42060
42198
|
return one(
|
|
42061
|
-
makeBlock(
|
|
42062
|
-
|
|
42063
|
-
|
|
42064
|
-
|
|
42065
|
-
|
|
42066
|
-
|
|
42067
|
-
|
|
42199
|
+
makeBlock(
|
|
42200
|
+
ctx,
|
|
42201
|
+
"image",
|
|
42202
|
+
{
|
|
42203
|
+
image: image2,
|
|
42204
|
+
aspect: "original",
|
|
42205
|
+
lightbox: false,
|
|
42206
|
+
...caption ? { caption: clip2(caption, 200) } : {},
|
|
42207
|
+
...link2 ? { link: link2 } : {},
|
|
42208
|
+
...sq.natural ? { size: "original" } : {}
|
|
42209
|
+
},
|
|
42210
|
+
sq.natural ? { style: { align: { d: "start" } } } : {}
|
|
42211
|
+
)
|
|
42068
42212
|
);
|
|
42069
42213
|
}
|
|
42070
42214
|
case "gallery": {
|
|
@@ -42091,7 +42235,8 @@ async function mapBlock(ctx, sq, opts = {}) {
|
|
|
42091
42235
|
"gallery",
|
|
42092
42236
|
{
|
|
42093
42237
|
images,
|
|
42094
|
-
aspect: sq.layout === "grid" ? "1:1" : "original",
|
|
42238
|
+
aspect: sq.layout === "grid" || sq.fit === "contain" ? "1:1" : "original",
|
|
42239
|
+
...sq.fit === "contain" ? { fit: "contain" } : {},
|
|
42095
42240
|
columns: images.length >= 4 && sq.layout === "grid" ? "4" : "3",
|
|
42096
42241
|
lightbox: true,
|
|
42097
42242
|
showCaptions: images.some((i) => i.caption)
|
|
@@ -42298,7 +42443,12 @@ async function mapBlock(ctx, sq, opts = {}) {
|
|
|
42298
42443
|
makeBlock(
|
|
42299
42444
|
ctx,
|
|
42300
42445
|
"product",
|
|
42301
|
-
{
|
|
42446
|
+
{
|
|
42447
|
+
product: { collection: item2.collection, items: [item2.slug] },
|
|
42448
|
+
...sq.show?.excerpt === false ? { showDescription: false } : {},
|
|
42449
|
+
...sq.show?.price === false ? { showPrice: false } : {},
|
|
42450
|
+
...sq.show?.buy === false ? { showBuy: false, imageAspect: "1:1" } : {}
|
|
42451
|
+
},
|
|
42302
42452
|
{ variant: "card" }
|
|
42303
42453
|
)
|
|
42304
42454
|
);
|
|
@@ -42543,7 +42693,8 @@ function stackSection(blocks2, spans, style, width, align) {
|
|
|
42543
42693
|
layout: {
|
|
42544
42694
|
mode: "stack",
|
|
42545
42695
|
columns: cols > 1 ? { d: spans, m: [12] } : { d: [12] },
|
|
42546
|
-
|
|
42696
|
+
// Words beside a picture sit centred on it; columns of words start at the top together.
|
|
42697
|
+
...cols > 1 && blocks2.some(({ block: block4 }) => ["image", "gallery", "video", "map", "embed"].includes(block4.type)) ? { align: "center" } : {},
|
|
42547
42698
|
...width && width !== "content" ? { width } : {}
|
|
42548
42699
|
},
|
|
42549
42700
|
style,
|
|
@@ -42553,6 +42704,75 @@ function stackSection(blocks2, spans, style, width, align) {
|
|
|
42553
42704
|
if (isStarter) section.preset = starterRef({ columns: spans, ...align ? { align } : {} });
|
|
42554
42705
|
return section;
|
|
42555
42706
|
}
|
|
42707
|
+
var headingLevelOf = (b) => b.type === "heading" ? Number(String(b.fields.level ?? "h2").slice(1)) || 2 : void 0;
|
|
42708
|
+
var textOfNodes = (nodes) => JSON.stringify(nodes ?? "").length;
|
|
42709
|
+
function splitAtHeadings(blocks2) {
|
|
42710
|
+
const count2 = (level2) => {
|
|
42711
|
+
let n = 0;
|
|
42712
|
+
for (const { block: block4 } of blocks2) {
|
|
42713
|
+
if (headingLevelOf(block4) === level2) n++;
|
|
42714
|
+
if (block4.type === "text") {
|
|
42715
|
+
const doc = block4.fields.content;
|
|
42716
|
+
for (const node2 of doc?.content ?? [])
|
|
42717
|
+
if (node2.type === "heading" && node2.attrs?.level === level2) n++;
|
|
42718
|
+
}
|
|
42719
|
+
}
|
|
42720
|
+
return n;
|
|
42721
|
+
};
|
|
42722
|
+
const size2 = blocks2.reduce(
|
|
42723
|
+
(n, { block: block4 }) => n + (block4.type === "text" ? textOfNodes(block4.fields.content) : textOfNodes(block4.fields)),
|
|
42724
|
+
0
|
|
42725
|
+
);
|
|
42726
|
+
const media = blocks2.filter(
|
|
42727
|
+
({ block: block4 }) => block4.type === "image" && block4.fields.size === "original"
|
|
42728
|
+
).length;
|
|
42729
|
+
const rules = blocks2.filter(({ block: block4 }) => block4.type === "line").length;
|
|
42730
|
+
if (size2 < 2400 && media < 2 && rules < 1) return [blocks2];
|
|
42731
|
+
const level = count2(2) >= 2 ? 2 : count2(3) >= 3 ? 3 : void 0;
|
|
42732
|
+
if (!level) return [blocks2];
|
|
42733
|
+
const out = [[]];
|
|
42734
|
+
const cur = () => out[out.length - 1];
|
|
42735
|
+
const start = () => {
|
|
42736
|
+
if (!cur().length) return;
|
|
42737
|
+
const last2 = cur().at(-1);
|
|
42738
|
+
const carry = last2.block.type === "image" && last2.block.fields.size === "original";
|
|
42739
|
+
if (!carry) out.push([]);
|
|
42740
|
+
else if (cur().length > 1) {
|
|
42741
|
+
cur().pop();
|
|
42742
|
+
out.push([last2]);
|
|
42743
|
+
}
|
|
42744
|
+
};
|
|
42745
|
+
for (const p of blocks2) {
|
|
42746
|
+
if (headingLevelOf(p.block) === level) {
|
|
42747
|
+
start();
|
|
42748
|
+
cur().push(p);
|
|
42749
|
+
continue;
|
|
42750
|
+
}
|
|
42751
|
+
if (p.block.type !== "text") {
|
|
42752
|
+
cur().push(p);
|
|
42753
|
+
continue;
|
|
42754
|
+
}
|
|
42755
|
+
const doc = p.block.fields.content;
|
|
42756
|
+
const parts = [[]];
|
|
42757
|
+
for (const node2 of doc.content ?? []) {
|
|
42758
|
+
if (node2.type === "heading" && node2.attrs?.level === level && parts.at(-1).length)
|
|
42759
|
+
parts.push([]);
|
|
42760
|
+
parts.at(-1).push(node2);
|
|
42761
|
+
}
|
|
42762
|
+
parts.forEach((nodes, i) => {
|
|
42763
|
+
if (i > 0 || nodes[0]?.type === "heading" && nodes[0].attrs?.level === level) start();
|
|
42764
|
+
cur().push({
|
|
42765
|
+
...p,
|
|
42766
|
+
block: {
|
|
42767
|
+
...p.block,
|
|
42768
|
+
...i > 0 ? { id: newId("blk") } : {},
|
|
42769
|
+
fields: { ...p.block.fields, content: { ...doc, content: nodes } }
|
|
42770
|
+
}
|
|
42771
|
+
});
|
|
42772
|
+
});
|
|
42773
|
+
}
|
|
42774
|
+
return out.filter((c) => c.length);
|
|
42775
|
+
}
|
|
42556
42776
|
async function mapBlocks(ctx, blocks2, inGrid = false) {
|
|
42557
42777
|
const out = [];
|
|
42558
42778
|
for (const b of blocks2) out.push(...await mapBlock(ctx, b, { inGrid }));
|
|
@@ -42651,6 +42871,15 @@ async function mapSectionParts(ctx, sq) {
|
|
|
42651
42871
|
}
|
|
42652
42872
|
return [section];
|
|
42653
42873
|
}
|
|
42874
|
+
if (sq.together && sq.layout.rows.length > 1) {
|
|
42875
|
+
const filled2 = sq.layout.rows.map((r) => r.columns.filter((c) => c.blocks.length));
|
|
42876
|
+
const multi = filled2.some((cols) => cols.length > 1);
|
|
42877
|
+
const shapes = new Set(filled2.map((cols) => cols.map((c) => c.span).join("-")));
|
|
42878
|
+
if (multi && shapes.size > 1) {
|
|
42879
|
+
const grid = await rowsAsGrid(ctx, sq.layout.rows, style, width);
|
|
42880
|
+
if (grid) return [grid];
|
|
42881
|
+
}
|
|
42882
|
+
}
|
|
42654
42883
|
const out = [];
|
|
42655
42884
|
let run2;
|
|
42656
42885
|
const flush = () => {
|
|
@@ -42662,20 +42891,22 @@ async function mapSectionParts(ctx, sq) {
|
|
|
42662
42891
|
run2 = void 0;
|
|
42663
42892
|
return;
|
|
42664
42893
|
}
|
|
42665
|
-
|
|
42666
|
-
|
|
42667
|
-
|
|
42668
|
-
|
|
42669
|
-
|
|
42670
|
-
|
|
42671
|
-
|
|
42672
|
-
|
|
42673
|
-
|
|
42674
|
-
|
|
42675
|
-
|
|
42676
|
-
|
|
42677
|
-
|
|
42678
|
-
|
|
42894
|
+
const parts = run2.spans.length === 1 ? splitAtHeadings(run2.blocks) : [run2.blocks];
|
|
42895
|
+
for (const part of parts)
|
|
42896
|
+
out.push(
|
|
42897
|
+
stackSection(
|
|
42898
|
+
part,
|
|
42899
|
+
run2.spans,
|
|
42900
|
+
out.length ? {
|
|
42901
|
+
...style,
|
|
42902
|
+
// A photo stays with the first part; a plain color continues behind every part.
|
|
42903
|
+
background: style.background?.type === "color" ? style.background : void 0,
|
|
42904
|
+
minHeight: void 0
|
|
42905
|
+
} : style,
|
|
42906
|
+
width,
|
|
42907
|
+
align
|
|
42908
|
+
)
|
|
42909
|
+
);
|
|
42679
42910
|
run2 = void 0;
|
|
42680
42911
|
};
|
|
42681
42912
|
for (const row of sq.layout.rows) {
|
|
@@ -42704,6 +42935,11 @@ async function mapSectionParts(ctx, sq) {
|
|
|
42704
42935
|
flush();
|
|
42705
42936
|
if (!out.length && bg) out.push(stackSection([], [12], style, width, align));
|
|
42706
42937
|
if (sq.role && out.length === 1) applyNearestPreset(out[0], sq.role, !sq.theme);
|
|
42938
|
+
for (const s2 of out)
|
|
42939
|
+
if (s2.layout.mode === "stack" && s2.blocks.length === 1 && s2.blocks[0]?.type === "gallery" && s2.blocks[0].fields.fit === "contain" && (!s2.layout.width || s2.layout.width === "wide" || s2.layout.width === "full")) {
|
|
42940
|
+
s2.layout.width = "narrow";
|
|
42941
|
+
delete s2.preset;
|
|
42942
|
+
}
|
|
42707
42943
|
for (const s2 of out)
|
|
42708
42944
|
for (const k of Object.keys(s2.style))
|
|
42709
42945
|
if (s2.style[k] === void 0) delete s2.style[k];
|
|
@@ -42852,6 +43088,57 @@ function applyNearestPreset(section, role, themeUnknown) {
|
|
|
42852
43088
|
if (model.style?.align && !b.style?.align) b.style = { ...b.style, align: model.style.align };
|
|
42853
43089
|
}
|
|
42854
43090
|
}
|
|
43091
|
+
async function rowsAsGrid(ctx, rows, style, width) {
|
|
43092
|
+
const ROW_PX = 24;
|
|
43093
|
+
const contentPx = width === "wide" || width === "full" ? 1440 : width === "narrow" ? 720 : 1280;
|
|
43094
|
+
const blocks2 = [];
|
|
43095
|
+
const items = {};
|
|
43096
|
+
let y = 1;
|
|
43097
|
+
for (const row of rows) {
|
|
43098
|
+
const cols = row.columns.filter((c) => c.blocks.length);
|
|
43099
|
+
if (!cols.length) continue;
|
|
43100
|
+
const total = row.columns.reduce((n, c) => n + c.span, 0) || 12;
|
|
43101
|
+
let x = 1;
|
|
43102
|
+
let rowH = 1;
|
|
43103
|
+
for (const col of row.columns) {
|
|
43104
|
+
const w = Math.max(
|
|
43105
|
+
1,
|
|
43106
|
+
Math.min(DESKTOP_COLS - x + 1, Math.round(col.span / Math.max(12, total) * DESKTOP_COLS))
|
|
43107
|
+
);
|
|
43108
|
+
let cy = y;
|
|
43109
|
+
for (const b of await mapBlocks(ctx, col.blocks, true)) {
|
|
43110
|
+
const img2 = b.type === "image" ? b.fields.image : void 0;
|
|
43111
|
+
const ratio = img2?.width && img2?.height ? img2.width / img2.height : void 0;
|
|
43112
|
+
const h = b.type === "image" ? Math.max(4, Math.round(w / DESKTOP_COLS * contentPx / (ratio ?? 1.5) / ROW_PX)) : 1;
|
|
43113
|
+
blocks2.push(b);
|
|
43114
|
+
items[b.id] = { d: { x, y: cy, w, h } };
|
|
43115
|
+
cy += h;
|
|
43116
|
+
}
|
|
43117
|
+
rowH = Math.max(rowH, cy - y);
|
|
43118
|
+
x = Math.min(DESKTOP_COLS, x + w);
|
|
43119
|
+
}
|
|
43120
|
+
y += rowH;
|
|
43121
|
+
}
|
|
43122
|
+
if (blocks2.length < 2) return void 0;
|
|
43123
|
+
const section = {
|
|
43124
|
+
id: newId("sec"),
|
|
43125
|
+
layout: {
|
|
43126
|
+
mode: "grid",
|
|
43127
|
+
rows: { d: y - 1 },
|
|
43128
|
+
items,
|
|
43129
|
+
...width && width !== "content" ? { width } : {}
|
|
43130
|
+
},
|
|
43131
|
+
style,
|
|
43132
|
+
blocks: blocks2
|
|
43133
|
+
};
|
|
43134
|
+
const mobile = autoMobileLayout(section, { reset: true });
|
|
43135
|
+
if (mobile && section.layout.mode === "grid") {
|
|
43136
|
+
section.layout.items = mobile;
|
|
43137
|
+
const mRows = Math.max(1, ...Object.values(mobile).map((i) => i.m ? i.m.y + i.m.h - 1 : 1));
|
|
43138
|
+
section.layout.rows = { d: y - 1, m: mRows };
|
|
43139
|
+
}
|
|
43140
|
+
return section;
|
|
43141
|
+
}
|
|
42855
43142
|
|
|
42856
43143
|
// ../import/src/map/site.ts
|
|
42857
43144
|
var clip3 = (s2, n) => s2.length > n ? `${s2.slice(0, n - 1).trimEnd()}\u2026` : s2;
|
|
@@ -43151,6 +43438,12 @@ function headerFor(site2, siteDark, brand) {
|
|
|
43151
43438
|
}
|
|
43152
43439
|
return {
|
|
43153
43440
|
variant,
|
|
43441
|
+
...t.logoHeight ? {
|
|
43442
|
+
logoHeight: {
|
|
43443
|
+
d: Math.round(t.logoHeight.d),
|
|
43444
|
+
...t.logoHeight.m ? { m: Math.round(t.logoHeight.m) } : {}
|
|
43445
|
+
}
|
|
43446
|
+
} : {},
|
|
43154
43447
|
sticky: t.headerFixed === true,
|
|
43155
43448
|
transparentOnHero: t.headerTransparent === true,
|
|
43156
43449
|
mobileMenu: "fullscreen",
|
|
@@ -43393,6 +43686,18 @@ async function buildSite(sq, opts) {
|
|
|
43393
43686
|
blocks: text7 ? [text7] : []
|
|
43394
43687
|
};
|
|
43395
43688
|
}
|
|
43689
|
+
if (footer) splitFooterLine(footer, sq.theme.footerColumns, sq.theme.footerLabels === true);
|
|
43690
|
+
const footBg = sq.theme.footerBackground;
|
|
43691
|
+
if (footer && footBg && /^#[0-9a-f]{6}$/i.test(footBg) && !footer.style.background) {
|
|
43692
|
+
const dark = relativeLuminance(footBg) < 0.18;
|
|
43693
|
+
if (dark || relativeLuminance(footBg) < 0.97) {
|
|
43694
|
+
footer.style = {
|
|
43695
|
+
...footer.style,
|
|
43696
|
+
colorTheme: dark ? "dark" : footer.style.colorTheme ?? "light",
|
|
43697
|
+
background: { type: "color", color: footBg }
|
|
43698
|
+
};
|
|
43699
|
+
}
|
|
43700
|
+
}
|
|
43396
43701
|
const logo = sq.logo ? await opts.media.image(sq.logo, "logo") : void 0;
|
|
43397
43702
|
const favicon = sq.favicon && /^https?:\/\//.test(sq.favicon) && !/\.ico(?:[?#]|$)/i.test(sq.favicon) ? await opts.media.image({ url: sq.favicon, alt: "" }, "favicon") : void 0;
|
|
43398
43703
|
const homeSeoImage = sq.pages.find((p) => p.path === "/")?.seo.image;
|
|
@@ -43485,6 +43790,35 @@ async function buildSite(sq, opts) {
|
|
|
43485
43790
|
linkFixes
|
|
43486
43791
|
};
|
|
43487
43792
|
}
|
|
43793
|
+
function splitFooterLine(footer, columns, labels = false) {
|
|
43794
|
+
if (!columns || columns < 2 || columns > 4) return;
|
|
43795
|
+
if (footer.layout.mode !== "stack" || footer.layout.columns.d.length !== 1) return;
|
|
43796
|
+
const texts = footer.blocks.filter((b) => b.type === "text");
|
|
43797
|
+
if (footer.blocks.length !== 1 || texts.length !== 1) return;
|
|
43798
|
+
const block4 = texts[0];
|
|
43799
|
+
const doc = block4.fields.content;
|
|
43800
|
+
const paras = doc.content ?? [];
|
|
43801
|
+
if (paras.length !== columns || paras.some((p) => p.type !== "paragraph")) return;
|
|
43802
|
+
const span = Math.floor(12 / columns);
|
|
43803
|
+
const spans = paras.map((_, i) => i === paras.length - 1 ? 12 - span * (columns - 1) : span);
|
|
43804
|
+
const aligns = paras.map(
|
|
43805
|
+
(_, i) => i === 0 ? "start" : i === paras.length - 1 ? "end" : "center"
|
|
43806
|
+
);
|
|
43807
|
+
footer.blocks = paras.map((p, i) => ({
|
|
43808
|
+
...block4,
|
|
43809
|
+
id: i === 0 ? block4.id : newId("blk"),
|
|
43810
|
+
column: i,
|
|
43811
|
+
fields: {
|
|
43812
|
+
...block4.fields,
|
|
43813
|
+
content: {
|
|
43814
|
+
...doc,
|
|
43815
|
+
content: [labels ? { ...p, attrs: { ...p.attrs ?? {}, preset: "eyebrow" } } : p]
|
|
43816
|
+
}
|
|
43817
|
+
},
|
|
43818
|
+
style: { ...block4.style ?? {}, align: { d: aligns[i], m: "start" } }
|
|
43819
|
+
}));
|
|
43820
|
+
footer.layout = { ...footer.layout, columns: { d: spans, m: [12] }, align: "center" };
|
|
43821
|
+
}
|
|
43488
43822
|
|
|
43489
43823
|
// ../import/src/media.ts
|
|
43490
43824
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -48440,7 +48774,7 @@ async function crawlWordPress(opts) {
|
|
|
48440
48774
|
});
|
|
48441
48775
|
}
|
|
48442
48776
|
if (!rest && !wxr && home) {
|
|
48443
|
-
const { crawlGeneric: crawlGeneric2 } = await import("./crawl-
|
|
48777
|
+
const { crawlGeneric: crawlGeneric2 } = await import("./crawl-YPDLNAN7.js");
|
|
48444
48778
|
const site2 = await crawlGeneric2({
|
|
48445
48779
|
url: base,
|
|
48446
48780
|
fetcher: f,
|
|
@@ -48496,6 +48830,9 @@ async function crawlWordPress(opts) {
|
|
|
48496
48830
|
}
|
|
48497
48831
|
const pages = [];
|
|
48498
48832
|
const builders = /* @__PURE__ */ new Set();
|
|
48833
|
+
const showsTitles = /<h1[^>]*class="[^"]*\b(?:entry-title|page-title|wp-block-post-title)\b/.test(
|
|
48834
|
+
homeHtml
|
|
48835
|
+
);
|
|
48499
48836
|
for (const e of pageEntries.sort((a, b) => (a.menuOrder ?? 0) - (b.menuOrder ?? 0))) {
|
|
48500
48837
|
if (e === postsPage) continue;
|
|
48501
48838
|
if (COMMERCE_SLUGS.test(e.slug) && e.path.split("/").filter(Boolean).length === 1) {
|
|
@@ -48513,7 +48850,11 @@ async function crawlWordPress(opts) {
|
|
|
48513
48850
|
path: e.path,
|
|
48514
48851
|
title: e.title,
|
|
48515
48852
|
seo: { ...e.image && !e.seo.image ? { image: e.image } : {}, ...e.seo },
|
|
48516
|
-
sections:
|
|
48853
|
+
sections: withPageTitle(
|
|
48854
|
+
bodySections(e.html, base, postsPath, notices, e.path),
|
|
48855
|
+
e.title,
|
|
48856
|
+
showsTitles
|
|
48857
|
+
),
|
|
48517
48858
|
...nav.length && !navPaths.has(e.path) && e.path !== "/" ? { hidden: true } : {}
|
|
48518
48859
|
});
|
|
48519
48860
|
}
|
|
@@ -48660,6 +49001,32 @@ function wxrNav(wxr, base, entries) {
|
|
|
48660
49001
|
}
|
|
48661
49002
|
return out;
|
|
48662
49003
|
}
|
|
49004
|
+
function withPageTitle(sections, title, shows) {
|
|
49005
|
+
if (!shows || !title.trim()) return sections;
|
|
49006
|
+
const norm7 = (t) => t.toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
49007
|
+
const first2 = sections[0];
|
|
49008
|
+
const blocks2 = first2?.layout.mode === "classic" ? first2.layout.rows.flatMap((r) => r.columns.flatMap((c) => c.blocks)) : [];
|
|
49009
|
+
const opening = blocks2[0];
|
|
49010
|
+
if (opening && (opening.kind === "heading" && norm7(opening.text) === norm7(title) || opening.kind === "text" && /^\s*<h1/i.test(opening.html)))
|
|
49011
|
+
return sections;
|
|
49012
|
+
const heading2 = { kind: "heading", text: title, level: 1 };
|
|
49013
|
+
if (first2 && first2.layout.mode === "classic" && !first2.background?.image && !first2.role) {
|
|
49014
|
+
const row = first2.layout.rows[0];
|
|
49015
|
+
if (row && row.columns.length === 1) {
|
|
49016
|
+
row.columns[0]?.blocks.unshift(heading2);
|
|
49017
|
+
return sections;
|
|
49018
|
+
}
|
|
49019
|
+
first2.layout.rows.unshift({ columns: [{ span: 12, blocks: [heading2] }] });
|
|
49020
|
+
return sections;
|
|
49021
|
+
}
|
|
49022
|
+
return [
|
|
49023
|
+
{
|
|
49024
|
+
theme: "",
|
|
49025
|
+
layout: { mode: "classic", rows: [{ columns: [{ span: 12, blocks: [heading2] }] }] }
|
|
49026
|
+
},
|
|
49027
|
+
...sections
|
|
49028
|
+
];
|
|
49029
|
+
}
|
|
48663
49030
|
|
|
48664
49031
|
// ../import/src/fidelity/component-gen.ts
|
|
48665
49032
|
var norm5 = (s2) => (s2 ?? "").toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
@@ -48667,7 +49034,7 @@ var esc3 = (s2) => s2.replace(/[<>&"]/g, "");
|
|
|
48667
49034
|
var round5 = (n, d = 2) => Math.round(n * 10 ** d) / 10 ** d;
|
|
48668
49035
|
function canGenerate(section) {
|
|
48669
49036
|
const b = section.blueprint;
|
|
48670
|
-
return !!b && b.length >= 2 && b.length <=
|
|
49037
|
+
return !!b && b.filter((i) => i.kind !== "rule" && i.kind !== "panel").length >= 2 && b.length <= 36 && section.height <= 2400;
|
|
48671
49038
|
}
|
|
48672
49039
|
function componentFromBlueprint(input) {
|
|
48673
49040
|
const items = (input.section.blueprint ?? []).filter(
|
|
@@ -48685,7 +49052,9 @@ function componentFromBlueprint(input) {
|
|
|
48685
49052
|
const css = [
|
|
48686
49053
|
":scope { width: min(100%, " + Math.round(width) + "px); margin-inline: auto; }",
|
|
48687
49054
|
".gx { display: grid; grid-template-columns: repeat(24, minmax(0, 1fr)); grid-auto-rows: minmax(8px, auto); column-gap: 0; }",
|
|
48688
|
-
".gx__i { margin: 0; min-width: 0; overflow-wrap: break-word; }",
|
|
49055
|
+
".gx__i { position: relative; z-index: 1; margin: 0; min-width: 0; overflow-wrap: break-word; }",
|
|
49056
|
+
".gx__i p { margin: 0; }",
|
|
49057
|
+
".gx__p { z-index: 0; }",
|
|
48689
49058
|
".gx__img picture, .gx__img img { display: block; width: 100%; height: 100%; object-fit: cover; }"
|
|
48690
49059
|
];
|
|
48691
49060
|
const phone = [];
|
|
@@ -48723,6 +49092,10 @@ function componentFromBlueprint(input) {
|
|
|
48723
49092
|
]
|
|
48724
49093
|
].join("; ");
|
|
48725
49094
|
};
|
|
49095
|
+
const tokenColor = (hex, fallback2) => {
|
|
49096
|
+
const v = colorVar(hex);
|
|
49097
|
+
return v === "inherit" ? fallback2 : v;
|
|
49098
|
+
};
|
|
48726
49099
|
items.forEach((it, k) => {
|
|
48727
49100
|
const n = k + 1;
|
|
48728
49101
|
const cls = `gx__i gx__i${n}`;
|
|
@@ -48731,6 +49104,21 @@ function componentFromBlueprint(input) {
|
|
|
48731
49104
|
const row = Math.floor((it.box.y - top) / 8) + 1;
|
|
48732
49105
|
const rows = Math.max(1, Math.round(it.box.h / 8));
|
|
48733
49106
|
const place = `grid-column: ${col} / span ${span}; grid-row: ${row} / span ${rows}`;
|
|
49107
|
+
if (it.kind === "rule") {
|
|
49108
|
+
parts.push(`<hr class="gx__p gx__r${n}">`);
|
|
49109
|
+
css.push(
|
|
49110
|
+
`.gx__r${n} { ${place.replace(/span \d+$/, "span 1")}; align-self: start; width: 100%; height: 0; margin: 0; border: 0; border-top: ${Math.max(1, Math.min(8, it.box.h))}px solid ${tokenColor(it.color, "var(--os-color-border)")}; }`
|
|
49111
|
+
);
|
|
49112
|
+
return;
|
|
49113
|
+
}
|
|
49114
|
+
if (it.kind === "panel") {
|
|
49115
|
+
parts.push(`<div class="gx__p gx__p${n}" aria-hidden="true"></div>`);
|
|
49116
|
+
css.push(
|
|
49117
|
+
`.gx__p${n} { ${place}; ${it.color ? `background: ${tokenColor(it.color, "var(--os-color-surface)")};` : ""} ${it.border ? `border: ${Math.max(1, Math.min(8, it.border.width))}px solid ${tokenColor(it.border.color, "var(--os-color-border)")};` : ""} border-radius: ${Math.min(48, Math.round(it.radius ?? 0))}px; }`
|
|
49118
|
+
);
|
|
49119
|
+
phone.push(`.gx__p${n} { display: none; }`);
|
|
49120
|
+
return;
|
|
49121
|
+
}
|
|
48734
49122
|
if (it.kind === "image") {
|
|
48735
49123
|
const key3 = `image${n}`;
|
|
48736
49124
|
fields[key3] = { kind: "image", label: `Picture ${n}` };
|
|
@@ -48772,6 +49160,21 @@ function componentFromBlueprint(input) {
|
|
|
48772
49160
|
return;
|
|
48773
49161
|
}
|
|
48774
49162
|
const key2 = `text${n}`;
|
|
49163
|
+
const doc = it.kind === "text" ? richDoc(it.runs, input.origin) : void 0;
|
|
49164
|
+
if (doc) {
|
|
49165
|
+
fields[key2] = {
|
|
49166
|
+
kind: "richText",
|
|
49167
|
+
label: `Text ${n}`,
|
|
49168
|
+
marks: ["bold", "italic", "link"],
|
|
49169
|
+
blocks: ["paragraph"],
|
|
49170
|
+
maxLength: 3e3
|
|
49171
|
+
};
|
|
49172
|
+
values[key2] = doc;
|
|
49173
|
+
parts.push(`<div class="${cls}">{{rich ${key2}}}</div>`);
|
|
49174
|
+
css.push(`.gx__i${n} { ${place}; ${typeCss(it.style, false)}; }`);
|
|
49175
|
+
phone.push(`.gx__i${n} { ${typeCss(it.style, true)}; }`);
|
|
49176
|
+
return;
|
|
49177
|
+
}
|
|
48775
49178
|
const text7 = (it.text ?? "").slice(0, it.kind === "heading" ? 300 : 1500);
|
|
48776
49179
|
fields[key2] = {
|
|
48777
49180
|
kind: "text",
|
|
@@ -48809,15 +49212,62 @@ ${parts.map((p) => ` ${p}`).join("\n")}
|
|
|
48809
49212
|
};
|
|
48810
49213
|
return { def, fields: values };
|
|
48811
49214
|
}
|
|
49215
|
+
function richDoc(runs, origin) {
|
|
49216
|
+
if (!runs?.length) return void 0;
|
|
49217
|
+
const content3 = [];
|
|
49218
|
+
for (const r of runs) {
|
|
49219
|
+
if (r.t === "\n") {
|
|
49220
|
+
content3.push({ type: "hardBreak" });
|
|
49221
|
+
continue;
|
|
49222
|
+
}
|
|
49223
|
+
const text7 = r.t;
|
|
49224
|
+
if (!text7) continue;
|
|
49225
|
+
const marks = [];
|
|
49226
|
+
if (r.b) marks.push({ type: "bold" });
|
|
49227
|
+
if (r.i) marks.push({ type: "italic" });
|
|
49228
|
+
const href = r.href && safeHref(r.href, origin);
|
|
49229
|
+
if (href) marks.push({ type: "link", attrs: { href } });
|
|
49230
|
+
const last2 = content3[content3.length - 1];
|
|
49231
|
+
if (last2?.type === "text" && JSON.stringify(last2.marks ?? []) === JSON.stringify(marks)) {
|
|
49232
|
+
last2.text = `${last2.text}${text7}`;
|
|
49233
|
+
continue;
|
|
49234
|
+
}
|
|
49235
|
+
content3.push({ type: "text", text: text7, ...marks.length ? { marks } : {} });
|
|
49236
|
+
}
|
|
49237
|
+
const first2 = content3[0];
|
|
49238
|
+
if (first2?.type === "text" && first2.text) first2.text = first2.text.replace(/^\s+/, "");
|
|
49239
|
+
const end = content3[content3.length - 1];
|
|
49240
|
+
if (end?.type === "text" && end.text) end.text = end.text.replace(/\s+$/, "");
|
|
49241
|
+
const kept = content3.filter((c) => c.type !== "text" || c.text);
|
|
49242
|
+
if (!kept.length) return void 0;
|
|
49243
|
+
return { type: "doc", content: [{ type: "paragraph", content: kept }] };
|
|
49244
|
+
}
|
|
49245
|
+
function safeHref(href, origin) {
|
|
49246
|
+
if (/^(?:mailto:|tel:)/i.test(href)) return href;
|
|
49247
|
+
try {
|
|
49248
|
+
const u = new URL(href);
|
|
49249
|
+
if (u.protocol !== "https:" && u.protocol !== "http:") return void 0;
|
|
49250
|
+
if (origin && u.origin === origin) return `${u.pathname}${u.hash}` || "/";
|
|
49251
|
+
return u.toString();
|
|
49252
|
+
} catch {
|
|
49253
|
+
return void 0;
|
|
49254
|
+
}
|
|
49255
|
+
}
|
|
48812
49256
|
|
|
48813
49257
|
// ../import/src/fidelity/tune.ts
|
|
48814
49258
|
var TUNE_GROUPS = [
|
|
48815
49259
|
"type",
|
|
48816
49260
|
"fonts",
|
|
48817
49261
|
"rhythm",
|
|
49262
|
+
"rhythm-phone",
|
|
49263
|
+
"widths",
|
|
49264
|
+
"chapters",
|
|
48818
49265
|
"colors",
|
|
49266
|
+
"page-color",
|
|
48819
49267
|
"buttons",
|
|
48820
49268
|
"layout",
|
|
49269
|
+
"headings",
|
|
49270
|
+
"heights",
|
|
48821
49271
|
"backgrounds",
|
|
48822
49272
|
"crops",
|
|
48823
49273
|
"phone-images",
|
|
@@ -48866,21 +49316,29 @@ var pageOf = (doc, path2) => {
|
|
|
48866
49316
|
const slug = path2.replace(/^\/+|\/+$/g, "");
|
|
48867
49317
|
return Object.values(doc.pages).find((p) => p.slug === slug);
|
|
48868
49318
|
};
|
|
48869
|
-
function tuneRound(input, measured, groups = TUNE_GROUPS) {
|
|
49319
|
+
function tuneRound(input, measured, groups = TUNE_GROUPS, opts = {}) {
|
|
48870
49320
|
const doc = structuredClone(input);
|
|
48871
49321
|
const on = (g) => groups.includes(g);
|
|
48872
49322
|
const changes = [];
|
|
48873
49323
|
const choices = doc.styles.choices;
|
|
48874
49324
|
const typography = { ...choices.typography ?? {} };
|
|
48875
49325
|
const scale = { ...typography.scale ?? {} };
|
|
49326
|
+
const forPreset = (preset, list5) => {
|
|
49327
|
+
if (preset !== "button") return list5;
|
|
49328
|
+
const content3 = list5.filter((m) => !m.original.buttons?.chrome);
|
|
49329
|
+
return content3.length ? content3 : list5;
|
|
49330
|
+
};
|
|
48876
49331
|
for (const preset of on("type") ? new Set(Object.values(PRESET_OF)) : []) {
|
|
48877
49332
|
const label = Object.entries(PRESET_OF).find(([, p]) => p === preset)?.[0];
|
|
48878
49333
|
const step = { ...scale[preset] ?? {} };
|
|
48879
49334
|
let touched = false;
|
|
48880
49335
|
for (const phone2 of [false, true]) {
|
|
48881
49336
|
if (phone2 && preset === "nav") continue;
|
|
48882
|
-
const list5 =
|
|
48883
|
-
|
|
49337
|
+
const list5 = forPreset(
|
|
49338
|
+
preset,
|
|
49339
|
+
measured.filter(
|
|
49340
|
+
(m) => m.viewport < 700 === phone2 && m.original.presets[preset] && m.imported.presets[preset]
|
|
49341
|
+
)
|
|
48884
49342
|
);
|
|
48885
49343
|
if (!list5.length) continue;
|
|
48886
49344
|
const sizes = list5.map((m) => m.original.presets[preset]?.size);
|
|
@@ -48897,14 +49355,21 @@ function tuneRound(input, measured, groups = TUNE_GROUPS) {
|
|
|
48897
49355
|
touched = true;
|
|
48898
49356
|
}
|
|
48899
49357
|
if (["eyebrow", "button", "nav", "wordmark"].includes(preset)) {
|
|
48900
|
-
const
|
|
48901
|
-
|
|
48902
|
-
|
|
48903
|
-
const failing = list5.filter(
|
|
48904
|
-
(m) => m.comparison.checks.some((c) => c.what === `${label} letter spacing and case` && !c.ok)
|
|
49358
|
+
const all = forPreset(
|
|
49359
|
+
preset,
|
|
49360
|
+
measured.filter((m) => m.original.presets[preset] && m.imported.presets[preset])
|
|
48905
49361
|
);
|
|
48906
|
-
|
|
48907
|
-
|
|
49362
|
+
const list5 = all.filter((m) => m.viewport >= 700);
|
|
49363
|
+
const fails = (m) => m.comparison.checks.some((c) => c.what === `${label} letter spacing and case` && !c.ok);
|
|
49364
|
+
const failing = list5.filter(fails);
|
|
49365
|
+
const phones = all.filter((m) => m.viewport < 700);
|
|
49366
|
+
const phoneFailing = phones.filter(fails);
|
|
49367
|
+
if (list5.length && (failing.length * 2 >= list5.length || phones.length && phoneFailing.length * 2 >= phones.length)) {
|
|
49368
|
+
const lsDesk = median(list5.map((m) => m.original.presets[preset]?.letterSpacing));
|
|
49369
|
+
const lsPhone = median(
|
|
49370
|
+
phones.map((m) => m.original.presets[preset]?.letterSpacing)
|
|
49371
|
+
);
|
|
49372
|
+
const ls = lsDesk !== void 0 && lsPhone !== void 0 && Math.abs(lsDesk - lsPhone) <= 0.075 ? (lsDesk + lsPhone) / 2 : lsDesk;
|
|
48908
49373
|
const cases = list5.map((m) => m.original.presets[preset]?.case);
|
|
48909
49374
|
if (ls !== void 0 && Math.abs((step.letterSpacing ?? -1) - ls) > 0.01) {
|
|
48910
49375
|
step.letterSpacing = round6(clamp2(ls, -0.2, 1), 3);
|
|
@@ -48942,7 +49407,7 @@ function tuneRound(input, measured, groups = TUNE_GROUPS) {
|
|
|
48942
49407
|
for (const preset of ["h2", "h3"]) {
|
|
48943
49408
|
const fams = origs.map((p) => p[preset]?.family).filter((f2) => !!f2);
|
|
48944
49409
|
const f = fams[0];
|
|
48945
|
-
if (!f ||
|
|
49410
|
+
if (!f || !fams.every((x) => norm6(x) === norm6(f))) continue;
|
|
48946
49411
|
const role = norm6(f) === norm6(heading2) ? void 0 : norm6(f) === norm6(label) ? "label" : norm6(f) === norm6(body) ? "body" : void 0;
|
|
48947
49412
|
if (!role || scale[preset]?.font === role) continue;
|
|
48948
49413
|
const sample2 = origs.map((p) => p[preset]).find(Boolean);
|
|
@@ -48965,6 +49430,29 @@ function tuneRound(input, measured, groups = TUNE_GROUPS) {
|
|
|
48965
49430
|
changes.push(`${role} font: ${match.family} for ${c.original}`);
|
|
48966
49431
|
}
|
|
48967
49432
|
}
|
|
49433
|
+
if (on("fonts")) {
|
|
49434
|
+
const roleFamily = (r) => norm6(
|
|
49435
|
+
typography[r]?.family ?? (r === "label" ? typography.body?.family ?? void 0 : void 0)
|
|
49436
|
+
);
|
|
49437
|
+
for (const preset of ["eyebrow", "button", "nav", "wordmark"]) {
|
|
49438
|
+
const fams = forPreset(
|
|
49439
|
+
preset,
|
|
49440
|
+
measured.filter((m) => m.viewport >= 700 && m.original.presets[preset])
|
|
49441
|
+
).map((m) => m.original.presets[preset]?.family).filter((f2) => !!f2);
|
|
49442
|
+
const f = fams[0];
|
|
49443
|
+
if (!f || !fams.every((x) => norm6(x) === norm6(f))) continue;
|
|
49444
|
+
const want = ["label", "heading", "body"].find(
|
|
49445
|
+
(r) => roleFamily(r) && roleFamily(r) === norm6(f)
|
|
49446
|
+
);
|
|
49447
|
+
if (!want) continue;
|
|
49448
|
+
const cur = scale[preset]?.font ?? "label";
|
|
49449
|
+
const curFamily = roleFamily(cur);
|
|
49450
|
+
if (cur === want || curFamily === norm6(f)) continue;
|
|
49451
|
+
scale[preset] = { ...scale[preset] ?? {}, font: want };
|
|
49452
|
+
changes.push(`${preset}: in the ${want} font (${f}), as on the original`);
|
|
49453
|
+
}
|
|
49454
|
+
if (Object.keys(scale).length) typography.scale = scale;
|
|
49455
|
+
}
|
|
48968
49456
|
choices.typography = typography;
|
|
48969
49457
|
const layout = { ...choices.layout ?? {} };
|
|
48970
49458
|
const desk = measured.filter((m) => m.viewport >= 700);
|
|
@@ -48978,13 +49466,89 @@ function tuneRound(input, measured, groups = TUNE_GROUPS) {
|
|
|
48978
49466
|
layout.contentWidth = Math.round(cw / 8) * 8;
|
|
48979
49467
|
changes.push(`content width ${layout.contentWidth}px`);
|
|
48980
49468
|
}
|
|
49469
|
+
if (on("widths")) {
|
|
49470
|
+
const content3 = layout.contentWidth;
|
|
49471
|
+
for (const m of desk) {
|
|
49472
|
+
const check = m.comparison.checks.find((c) => c.what === "Content width" && !c.ok);
|
|
49473
|
+
const want = m.original.layout.contentWidth;
|
|
49474
|
+
const have = m.imported.layout.contentWidth;
|
|
49475
|
+
const page = pageOf(doc, m.path);
|
|
49476
|
+
if (!check || !want || !have || !page || want >= have) continue;
|
|
49477
|
+
let width;
|
|
49478
|
+
if (content3 && Math.abs(content3 - want) / want <= 0.06) width = "content";
|
|
49479
|
+
else if (want >= 360 && want <= 1600) {
|
|
49480
|
+
layout.narrowWidth ??= Math.round(want / 8) * 8;
|
|
49481
|
+
if (Math.abs(layout.narrowWidth - want) / want <= 0.06) width = "narrow";
|
|
49482
|
+
}
|
|
49483
|
+
if (!width) continue;
|
|
49484
|
+
let n = 0;
|
|
49485
|
+
for (const section of page.sections) {
|
|
49486
|
+
if (section.layout.mode !== "stack" || section.layout.width === "full") continue;
|
|
49487
|
+
if (section.style.background?.type === "image" || section.style.background?.type === "video")
|
|
49488
|
+
continue;
|
|
49489
|
+
const cur = section.layout.width ?? "content";
|
|
49490
|
+
if (cur === width) continue;
|
|
49491
|
+
if (width === "content") delete section.layout.width;
|
|
49492
|
+
else section.layout.width = width;
|
|
49493
|
+
delete section.preset;
|
|
49494
|
+
n++;
|
|
49495
|
+
}
|
|
49496
|
+
if (n) changes.push(`${m.path}: ${n} section(s) at the ${width} width, as on the original`);
|
|
49497
|
+
}
|
|
49498
|
+
}
|
|
49499
|
+
if (on("chapters")) {
|
|
49500
|
+
const bag = (t) => new Set(
|
|
49501
|
+
t.toLowerCase().split(/[^a-z0-9]+/).filter((w) => w.length > 3)
|
|
49502
|
+
);
|
|
49503
|
+
for (const m of desk) {
|
|
49504
|
+
const page = pageOf(doc, m.path);
|
|
49505
|
+
if (!page || m.imported.sections.length !== page.sections.length) continue;
|
|
49506
|
+
const keep = new Set(page.sections.map((_, j) => j));
|
|
49507
|
+
for (const o of m.original.sections) {
|
|
49508
|
+
const mine = bag(`${o.heading?.text ?? ""} ${o.words ?? o.text}`);
|
|
49509
|
+
if (mine.size < 20) continue;
|
|
49510
|
+
const inside = m.imported.sections.map((x, j) => {
|
|
49511
|
+
const theirs = bag(`${x.heading?.text ?? ""} ${x.words ?? x.text}`);
|
|
49512
|
+
let n = 0;
|
|
49513
|
+
for (const w of theirs) if (mine.has(w)) n++;
|
|
49514
|
+
return theirs.size >= 3 && n / theirs.size >= 0.85 ? j : -1;
|
|
49515
|
+
}).filter((j) => j >= 0);
|
|
49516
|
+
let run2 = [];
|
|
49517
|
+
const join21 = () => {
|
|
49518
|
+
if (run2.length >= 2) {
|
|
49519
|
+
const [first2, ...rest] = run2.map((j) => page.sections[j]);
|
|
49520
|
+
for (const r of rest) {
|
|
49521
|
+
first2?.blocks.push(...r.blocks);
|
|
49522
|
+
keep.delete(page.sections.indexOf(r));
|
|
49523
|
+
}
|
|
49524
|
+
changes.push(`${m.path}: ${run2.length} sections joined into one, as on the original`);
|
|
49525
|
+
}
|
|
49526
|
+
run2 = [];
|
|
49527
|
+
};
|
|
49528
|
+
for (const j of inside) {
|
|
49529
|
+
const a = page.sections[j];
|
|
49530
|
+
const prev = run2.length ? page.sections[run2[run2.length - 1]] : void 0;
|
|
49531
|
+
const plain3 = a.layout.mode === "stack" && a.layout.columns.d.length === 1 && !a.anchor && a.style.background?.type !== "image" && a.style.background?.type !== "video";
|
|
49532
|
+
const same = prev && j === run2[run2.length - 1] + 1 && (prev.style.colorTheme ?? "light") === (a.style.colorTheme ?? "light") && prev.layout.mode === "stack" && (prev.layout.width ?? "content") === (a.layout.mode === "stack" ? a.layout.width ?? "content" : "");
|
|
49533
|
+
if (plain3 && (!run2.length || same)) run2.push(j);
|
|
49534
|
+
else {
|
|
49535
|
+
join21();
|
|
49536
|
+
if (plain3) run2.push(j);
|
|
49537
|
+
}
|
|
49538
|
+
}
|
|
49539
|
+
join21();
|
|
49540
|
+
}
|
|
49541
|
+
if (keep.size !== page.sections.length)
|
|
49542
|
+
page.sections = page.sections.filter((_, j) => keep.has(j));
|
|
49543
|
+
}
|
|
49544
|
+
}
|
|
48981
49545
|
const pad3 = (list5) => median(
|
|
48982
49546
|
list5.filter((m) => m.comparison.checks.some((c) => c.what === "Section padding" && !c.ok)).map(
|
|
48983
49547
|
(m) => m.original.layout.sectionPadding && m.imported.layout.sectionPadding ? m.original.layout.sectionPadding / m.imported.layout.sectionPadding : 0
|
|
48984
49548
|
).filter((n) => n > 0)
|
|
48985
49549
|
);
|
|
48986
49550
|
const pd = on("rhythm") ? pad3(desk) : void 0;
|
|
48987
|
-
const pm = on("rhythm") ? pad3(phone) : void 0;
|
|
49551
|
+
const pm = on("rhythm-phone") ? pad3(phone) : void 0;
|
|
48988
49552
|
if (pd) {
|
|
48989
49553
|
const base = layout.sectionPadding ?? median(desk.map((m) => m.imported.layout.sectionPadding ?? 0).filter(Boolean));
|
|
48990
49554
|
if (base) {
|
|
@@ -49010,7 +49574,7 @@ function tuneRound(input, measured, groups = TUNE_GROUPS) {
|
|
|
49010
49574
|
}
|
|
49011
49575
|
}
|
|
49012
49576
|
const inks = /* @__PURE__ */ new Map();
|
|
49013
|
-
for (const m of on("colors") ? desk : []) {
|
|
49577
|
+
for (const m of on("colors") || on("page-color") ? desk : []) {
|
|
49014
49578
|
const page = pageOf(doc, m.path);
|
|
49015
49579
|
if (!page || m.imported.sections.length !== page.sections.length) continue;
|
|
49016
49580
|
for (const s2 of m.comparison.sections) {
|
|
@@ -49020,21 +49584,38 @@ function tuneRound(input, measured, groups = TUNE_GROUPS) {
|
|
|
49020
49584
|
const section = page.sections[s2.importedIndex];
|
|
49021
49585
|
if (!o || !i || !section || o.backgroundImage) continue;
|
|
49022
49586
|
const theme = section.style.colorTheme ?? "light";
|
|
49023
|
-
const e = inks.get(theme) ?? { text: [], heading: [] };
|
|
49024
|
-
if (o.
|
|
49025
|
-
e.
|
|
49026
|
-
if (o.
|
|
49027
|
-
|
|
49587
|
+
const e = inks.get(theme) ?? { text: [], heading: [], background: [] };
|
|
49588
|
+
if (o.background && i.background && !section.style.background)
|
|
49589
|
+
e.background.push({ o: o.background, i: i.background });
|
|
49590
|
+
if (o.textColor && i.textColor) e.text.push({ o: o.textColor, i: i.textColor });
|
|
49591
|
+
if (o.heading?.color && i.heading?.color)
|
|
49592
|
+
e.heading.push({ o: o.heading.color, i: i.heading.color });
|
|
49028
49593
|
inks.set(theme, e);
|
|
49029
49594
|
}
|
|
49030
49595
|
}
|
|
49031
49596
|
for (const [theme, e] of inks)
|
|
49032
|
-
for (const role of [
|
|
49597
|
+
for (const role of [
|
|
49598
|
+
...on("colors") ? ["text", "heading"] : [],
|
|
49599
|
+
...on("page-color") ? ["background"] : []
|
|
49600
|
+
]) {
|
|
49033
49601
|
const list5 = e[role];
|
|
49034
|
-
const
|
|
49035
|
-
|
|
49036
|
-
|
|
49037
|
-
|
|
49602
|
+
const tol = role === "background" ? 16 : 28;
|
|
49603
|
+
const passes = (c) => list5.filter((p) => colorDistance(p.o, c(p)) < tol).length;
|
|
49604
|
+
const now = passes((p) => p.i);
|
|
49605
|
+
let best;
|
|
49606
|
+
let bestN = now;
|
|
49607
|
+
for (const cand of new Set(list5.map((p) => p.o))) {
|
|
49608
|
+
const n = passes(() => cand);
|
|
49609
|
+
if (n > bestN) {
|
|
49610
|
+
best = cand;
|
|
49611
|
+
bestN = n;
|
|
49612
|
+
}
|
|
49613
|
+
}
|
|
49614
|
+
if (!best || list5.length < 2) continue;
|
|
49615
|
+
if (!setRole(doc, theme, role, best)) continue;
|
|
49616
|
+
changes.push(
|
|
49617
|
+
`${role} on ${theme} sections ${best}, as on the original (${bestN} of ${list5.length} sections)`
|
|
49618
|
+
);
|
|
49038
49619
|
}
|
|
49039
49620
|
const fills = /* @__PURE__ */ new Map();
|
|
49040
49621
|
for (const m of on("buttons") ? desk : []) {
|
|
@@ -49063,6 +49644,135 @@ function tuneRound(input, measured, groups = TUNE_GROUPS) {
|
|
|
49063
49644
|
} catch {
|
|
49064
49645
|
themes = void 0;
|
|
49065
49646
|
}
|
|
49647
|
+
if (on("heights")) {
|
|
49648
|
+
const scaleOf = SPACE_SCALES[choices.density] ?? SPACE_SCALES.comfortable;
|
|
49649
|
+
const tokens = Object.entries(scaleOf).sort((a, b) => a[1] - b[1]);
|
|
49650
|
+
const fit = (o, i) => {
|
|
49651
|
+
if (i.contentTop === void 0 || i.contentBottom === void 0) return void 0;
|
|
49652
|
+
const content3 = i.contentBottom - i.contentTop;
|
|
49653
|
+
const room = (o.height - content3) / 2;
|
|
49654
|
+
const token = [...tokens].reverse().find(([, px3]) => px3 <= room)?.[0] ?? "none";
|
|
49655
|
+
return { token, height: Math.round(o.height) };
|
|
49656
|
+
};
|
|
49657
|
+
for (const m of desk) {
|
|
49658
|
+
const page = pageOf(doc, m.path);
|
|
49659
|
+
if (!page || m.imported.sections.length !== page.sections.length) continue;
|
|
49660
|
+
const mp = phone.find((x) => x.path === m.path);
|
|
49661
|
+
for (const s2 of m.comparison.sections) {
|
|
49662
|
+
if (s2.importedIndex === void 0) continue;
|
|
49663
|
+
const h = s2.checks.find((c) => c.what === "Height");
|
|
49664
|
+
const o = m.original.sections[s2.index];
|
|
49665
|
+
const i = m.imported.sections[s2.importedIndex];
|
|
49666
|
+
const section = page.sections[s2.importedIndex];
|
|
49667
|
+
if (!h || h.ok || !o?.backgroundImage || !i || !section) continue;
|
|
49668
|
+
if (section.style.background?.type !== "image" && section.style.background?.type !== "video")
|
|
49669
|
+
continue;
|
|
49670
|
+
const d = fit(o, i);
|
|
49671
|
+
if (!d) continue;
|
|
49672
|
+
const ps = mp?.comparison.sections.find((x) => x.importedIndex === s2.importedIndex);
|
|
49673
|
+
const po = ps ? mp?.original.sections[ps.index] : void 0;
|
|
49674
|
+
const pi = mp?.imported.sections[s2.importedIndex];
|
|
49675
|
+
const p = po?.backgroundImage && pi ? fit(po, pi) : void 0;
|
|
49676
|
+
section.style.padding = { d: d.token, ...p ? { m: p.token } : {} };
|
|
49677
|
+
section.style.minHeight = {
|
|
49678
|
+
d: `${clamp2(d.height, 10, 9999)}px`,
|
|
49679
|
+
...p ? { m: `${clamp2(p.height, 10, 9999)}px` } : {}
|
|
49680
|
+
};
|
|
49681
|
+
changes.push(`${m.path}: section ${s2.index + 1} ${d.height}px tall, as on the original`);
|
|
49682
|
+
}
|
|
49683
|
+
}
|
|
49684
|
+
}
|
|
49685
|
+
if (on("headings")) {
|
|
49686
|
+
const presetSizes = (list5) => {
|
|
49687
|
+
const out = /* @__PURE__ */ new Map();
|
|
49688
|
+
for (const k of ["display", "h1", "h2", "h3", "h4", "lead", "eyebrow"]) {
|
|
49689
|
+
const xs = list5.map((m) => m.imported.presets[k]).filter((t) => !!t);
|
|
49690
|
+
const size2 = median(xs.map((t) => t.size));
|
|
49691
|
+
if (size2 && xs[0]) out.set(k, { size: size2, family: xs[0].family });
|
|
49692
|
+
}
|
|
49693
|
+
return out;
|
|
49694
|
+
};
|
|
49695
|
+
const deskSizes = presetSizes(desk);
|
|
49696
|
+
const phoneSizes = presetSizes(phone);
|
|
49697
|
+
const fits = (a, b) => Math.abs(a - b) <= Math.max(1, a * 0.1);
|
|
49698
|
+
for (const m of desk) {
|
|
49699
|
+
const page = pageOf(doc, m.path);
|
|
49700
|
+
if (!page || m.imported.sections.length !== page.sections.length) continue;
|
|
49701
|
+
const mp = phone.find((x) => x.path === m.path);
|
|
49702
|
+
for (const s2 of m.comparison.sections) {
|
|
49703
|
+
if (s2.importedIndex === void 0) continue;
|
|
49704
|
+
const hc = s2.checks.find((c) => c.what === "Heading");
|
|
49705
|
+
if (!hc || hc.ok) continue;
|
|
49706
|
+
const o = m.original.sections[s2.index]?.heading;
|
|
49707
|
+
const i = m.imported.sections[s2.importedIndex]?.heading;
|
|
49708
|
+
const section = page.sections[s2.importedIndex];
|
|
49709
|
+
if (!o || !i || !section || fits(o.size, i.size)) continue;
|
|
49710
|
+
const po = mp?.original.sections.find((x) => norm6(x.heading?.text) === norm6(o.text))?.heading?.size;
|
|
49711
|
+
const label = o.size < 18 && (o.case === "uppercase" || o.letterSpacing >= 0.05);
|
|
49712
|
+
const pick = [...deskSizes].filter(([k, v]) => {
|
|
49713
|
+
if (!fits(o.size, v.size)) return false;
|
|
49714
|
+
if (k === "eyebrow" !== label) return false;
|
|
49715
|
+
const ph = phoneSizes.get(k);
|
|
49716
|
+
return po === void 0 || !ph || fits(po, ph.size);
|
|
49717
|
+
}).sort(([, a], [, b]) => Math.abs(a.size - o.size) - Math.abs(b.size - o.size))[0]?.[0];
|
|
49718
|
+
if (!pick) continue;
|
|
49719
|
+
const block4 = section.blocks.find(
|
|
49720
|
+
(b) => b.type === "heading" && norm6(String(b.fields.text ?? "")) === norm6(i.text)
|
|
49721
|
+
);
|
|
49722
|
+
const holder = block4 ? void 0 : section.blocks.find(
|
|
49723
|
+
(b) => b.type === "card" && norm6(String(b.fields.heading ?? "")) === norm6(i.text) || b.type === "columns" && Array.isArray(b.fields.items) && b.fields.items.some(
|
|
49724
|
+
(x) => norm6(x.heading) === norm6(i.text)
|
|
49725
|
+
)
|
|
49726
|
+
);
|
|
49727
|
+
if (holder && pick !== "lead") {
|
|
49728
|
+
const style = holder.style ?? {};
|
|
49729
|
+
if (style.typography?.heading === pick) continue;
|
|
49730
|
+
holder.style = {
|
|
49731
|
+
...style,
|
|
49732
|
+
typography: { ...style.typography ?? {}, heading: pick }
|
|
49733
|
+
};
|
|
49734
|
+
changes.push(
|
|
49735
|
+
`${m.path}: "${o.text.slice(0, 40)}" set at the ${pick} size, as on the original`
|
|
49736
|
+
);
|
|
49737
|
+
continue;
|
|
49738
|
+
}
|
|
49739
|
+
if (!block4) {
|
|
49740
|
+
let done = false;
|
|
49741
|
+
for (const b of section.blocks) {
|
|
49742
|
+
if (b.type !== "text" || done) continue;
|
|
49743
|
+
const doc2 = b.fields.content;
|
|
49744
|
+
for (const node2 of doc2?.content ?? []) {
|
|
49745
|
+
const words3 = (node2.content ?? []).map((x) => x.text ?? "").join("");
|
|
49746
|
+
if (node2.type !== "heading" || norm6(words3) !== norm6(i.text)) continue;
|
|
49747
|
+
if (pick === "lead" || node2.attrs?.preset === pick) break;
|
|
49748
|
+
node2.attrs = { ...node2.attrs ?? {}, preset: pick };
|
|
49749
|
+
done = true;
|
|
49750
|
+
break;
|
|
49751
|
+
}
|
|
49752
|
+
}
|
|
49753
|
+
if (done)
|
|
49754
|
+
changes.push(
|
|
49755
|
+
`${m.path}: "${o.text.slice(0, 40)}" set at the ${pick} size, as on the original`
|
|
49756
|
+
);
|
|
49757
|
+
continue;
|
|
49758
|
+
}
|
|
49759
|
+
if (["display", "h1", "h2", "h3", "h4"].includes(pick)) {
|
|
49760
|
+
if (block4.fields.size === pick) continue;
|
|
49761
|
+
block4.fields.size = pick;
|
|
49762
|
+
} else {
|
|
49763
|
+
const style = block4.style ?? {};
|
|
49764
|
+
if (style.typography?.heading === pick) continue;
|
|
49765
|
+
block4.style = {
|
|
49766
|
+
...style,
|
|
49767
|
+
typography: { ...style.typography ?? {}, heading: pick }
|
|
49768
|
+
};
|
|
49769
|
+
}
|
|
49770
|
+
changes.push(
|
|
49771
|
+
`${m.path}: "${o.text.slice(0, 40)}" set at the ${pick} size, as on the original`
|
|
49772
|
+
);
|
|
49773
|
+
}
|
|
49774
|
+
}
|
|
49775
|
+
}
|
|
49066
49776
|
for (const m of on("layout") || on("backgrounds") || on("crops") ? desk : []) {
|
|
49067
49777
|
const page = pageOf(doc, m.path);
|
|
49068
49778
|
if (!page) continue;
|
|
@@ -49156,18 +49866,32 @@ function tuneRound(input, measured, groups = TUNE_GROUPS) {
|
|
|
49156
49866
|
if (u.protocol === "mailto:") return { type: "email", email: u.pathname };
|
|
49157
49867
|
return u.protocol === "https:" ? { type: "url", url: u.toString() } : void 0;
|
|
49158
49868
|
};
|
|
49159
|
-
for (const m of desk) {
|
|
49869
|
+
for (const m of desk.filter((x) => !opts.paths || opts.paths.includes(x.path))) {
|
|
49160
49870
|
const page = pageOf(doc, m.path);
|
|
49161
49871
|
if (!page || m.imported.sections.length !== page.sections.length) continue;
|
|
49162
49872
|
const used = /* @__PURE__ */ new Set();
|
|
49163
49873
|
for (const s2 of m.comparison.sections) {
|
|
49164
|
-
if (s2.importedIndex === void 0 || s2.score >= COMPONENT_THRESHOLD)
|
|
49874
|
+
if (s2.importedIndex === void 0 || s2.score >= (opts.threshold ?? COMPONENT_THRESHOLD))
|
|
49875
|
+
continue;
|
|
49165
49876
|
if (used.has(s2.importedIndex)) continue;
|
|
49166
49877
|
const o = m.original.sections[s2.index];
|
|
49167
49878
|
const old = page.sections[s2.importedIndex];
|
|
49168
49879
|
if (!o || !old || !canGenerate(o)) continue;
|
|
49169
49880
|
if (m.comparison.sections.filter((x) => x.importedIndex === s2.importedIndex).length > 1)
|
|
49170
49881
|
continue;
|
|
49882
|
+
const bag = (t) => new Set(
|
|
49883
|
+
t.toLowerCase().split(/[^a-z0-9]+/).filter((w) => w.length > 3)
|
|
49884
|
+
);
|
|
49885
|
+
const mine = bag(`${o.heading?.text ?? ""} ${o.words ?? o.text}`);
|
|
49886
|
+
const shared = m.imported.sections.some((x, j) => {
|
|
49887
|
+
if (j === s2.importedIndex) return false;
|
|
49888
|
+
const theirs = bag(x.words ?? x.text);
|
|
49889
|
+
if (theirs.size < 4) return false;
|
|
49890
|
+
let n = 0;
|
|
49891
|
+
for (const w of theirs) if (mine.has(w)) n++;
|
|
49892
|
+
return n / theirs.size >= 0.5;
|
|
49893
|
+
});
|
|
49894
|
+
if (shared) continue;
|
|
49171
49895
|
const name = `imported-${(page.slug || "home").replace(/[^a-z0-9]+/g, "-")}-${s2.index + 1}`.replace(/-+/g, "-").slice(0, 40).replace(/-$/, "");
|
|
49172
49896
|
const theme = themes?.[old.style.colorTheme ?? "light"];
|
|
49173
49897
|
const gen = componentFromBlueprint({
|
|
@@ -49181,10 +49905,14 @@ function tuneRound(input, measured, groups = TUNE_GROUPS) {
|
|
|
49181
49905
|
heading: theme.heading,
|
|
49182
49906
|
"text-muted": theme["text-muted"],
|
|
49183
49907
|
accent: theme.accent,
|
|
49184
|
-
link: theme.link
|
|
49908
|
+
link: theme.link,
|
|
49909
|
+
surface: theme.surface,
|
|
49910
|
+
border: theme.border,
|
|
49911
|
+
background: theme.background
|
|
49185
49912
|
}
|
|
49186
49913
|
} : {},
|
|
49187
|
-
link: linkFor2(m.original.url)
|
|
49914
|
+
link: linkFor2(m.original.url),
|
|
49915
|
+
origin: new URL(m.original.url).origin
|
|
49188
49916
|
});
|
|
49189
49917
|
if (!gen) continue;
|
|
49190
49918
|
const check = checkComponent(gen.def);
|
|
@@ -49500,7 +50228,7 @@ async function renderAgentsMd(root, siteTitle) {
|
|
|
49500
50228
|
} catch {
|
|
49501
50229
|
}
|
|
49502
50230
|
}
|
|
49503
|
-
const { generateAgentsMd: generateAgentsMd2 } = await import("./agents-md-
|
|
50231
|
+
const { generateAgentsMd: generateAgentsMd2 } = await import("./agents-md-63S3T7CX.js");
|
|
49504
50232
|
return generateAgentsMd2(title ? { siteTitle: title } : {});
|
|
49505
50233
|
}
|
|
49506
50234
|
async function writeAgentsMd(root, siteTitle) {
|
|
@@ -58573,6 +59301,7 @@ async function runMcp(io, dir, opts = {}) {
|
|
|
58573
59301
|
|
|
58574
59302
|
// src/fidelity.ts
|
|
58575
59303
|
var DESKTOP = 1440;
|
|
59304
|
+
var COMPONENT_RETRY_BELOW = 75;
|
|
58576
59305
|
var PHONE = 390;
|
|
58577
59306
|
async function launch() {
|
|
58578
59307
|
const pw = await loadPlaywright();
|
|
@@ -58840,14 +59569,33 @@ async function runCompare(original, dir, opts = {}, log = () => void 0) {
|
|
|
58840
59569
|
let ran = 0;
|
|
58841
59570
|
let withoutComponents;
|
|
58842
59571
|
let withComponents2;
|
|
58843
|
-
let budget = 2 + rounds *
|
|
59572
|
+
let budget = 2 + rounds * 5;
|
|
59573
|
+
let componentBudget = 2 + rounds * 2;
|
|
58844
59574
|
for (let pass = 0; pass < rounds && budget > 0; pass++) {
|
|
58845
59575
|
let improved = false;
|
|
59576
|
+
const attempts = [];
|
|
58846
59577
|
for (const group of TUNE_GROUPS) {
|
|
58847
|
-
if (
|
|
58848
|
-
|
|
59578
|
+
if (group !== "components") {
|
|
59579
|
+
attempts.push({ group });
|
|
59580
|
+
continue;
|
|
59581
|
+
}
|
|
59582
|
+
const paths = [
|
|
59583
|
+
...new Set(
|
|
59584
|
+
best.measured.filter(
|
|
59585
|
+
(m) => m.viewport >= 700 && m.comparison.sections.some((x) => x.score < COMPONENT_RETRY_BELOW)
|
|
59586
|
+
).map((m) => m.path)
|
|
59587
|
+
)
|
|
59588
|
+
];
|
|
59589
|
+
for (const path2 of paths) attempts.push({ group, paths: [path2] });
|
|
59590
|
+
}
|
|
59591
|
+
for (const { group, paths } of attempts) {
|
|
59592
|
+
if (group === "components" ? componentBudget <= 0 : budget <= 0) continue;
|
|
59593
|
+
const t = tuneRound(bestDoc, best.measured, [group], {
|
|
59594
|
+
...paths ? { paths, threshold: COMPONENT_RETRY_BELOW } : {}
|
|
59595
|
+
});
|
|
58849
59596
|
if (!t.changes.length) continue;
|
|
58850
|
-
|
|
59597
|
+
if (group === "components") componentBudget--;
|
|
59598
|
+
else budget--;
|
|
58851
59599
|
log(
|
|
58852
59600
|
`Tuning ${group}: ${t.changes.slice(0, 4).join("; ")}${t.changes.length > 4 ? "\u2026" : ""}`
|
|
58853
59601
|
);
|
|
@@ -59640,4 +60388,4 @@ export {
|
|
|
59640
60388
|
createProgram,
|
|
59641
60389
|
run
|
|
59642
60390
|
};
|
|
59643
|
-
//# sourceMappingURL=chunk-
|
|
60391
|
+
//# sourceMappingURL=chunk-TDLUJHWB.js.map
|