@webto-id/variant-check 0.1.6 → 0.1.8
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/cli.js +285 -14
- package/dist/index.js +177 -14
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -917,6 +917,95 @@ function escAttr(s) {
|
|
|
917
917
|
}
|
|
918
918
|
__name(escAttr, "escAttr");
|
|
919
919
|
|
|
920
|
+
// ../variant-compiler/src/effects.ts
|
|
921
|
+
var WV_EFFECTS = [
|
|
922
|
+
"reveal-up",
|
|
923
|
+
"reveal-down",
|
|
924
|
+
"reveal-left",
|
|
925
|
+
"reveal-right",
|
|
926
|
+
"fade",
|
|
927
|
+
"stagger",
|
|
928
|
+
"counter",
|
|
929
|
+
"parallax-soft",
|
|
930
|
+
"zoom-hover"
|
|
931
|
+
];
|
|
932
|
+
var WV_DELAY_MAX = 10;
|
|
933
|
+
var WV_DURATION_MIN = 100;
|
|
934
|
+
var WV_DURATION_MAX = 5e3;
|
|
935
|
+
var WV_STRENGTHS = ["soft", "medium"];
|
|
936
|
+
var WV_EFFECT_CSS = `@media (prefers-reduced-motion: no-preference){
|
|
937
|
+
.wv-js [data-wv-effect^="reveal-"],.wv-js [data-wv-effect="fade"],.wv-js [data-wv-effect="stagger"]>*{opacity:0;transition:opacity .6s cubic-bezier(.22,.61,.36,1),transform .6s cubic-bezier(.22,.61,.36,1)}
|
|
938
|
+
.wv-js [data-wv-effect="reveal-up"]{transform:translateY(22px)}
|
|
939
|
+
.wv-js [data-wv-effect="reveal-down"]{transform:translateY(-22px)}
|
|
940
|
+
.wv-js [data-wv-effect="reveal-left"]{transform:translateX(-22px)}
|
|
941
|
+
.wv-js [data-wv-effect="reveal-right"]{transform:translateX(22px)}
|
|
942
|
+
.wv-js [data-wv-effect="stagger"]>*{transform:translateY(16px)}
|
|
943
|
+
.wv-js [data-wv-effect].wv-in,.wv-js [data-wv-effect="stagger"]>.wv-in{opacity:1;transform:none}
|
|
944
|
+
[data-wv-effect="zoom-hover"] img{transition:transform .5s ease}
|
|
945
|
+
[data-wv-effect="zoom-hover"]:hover img{transform:scale(1.03)}
|
|
946
|
+
[data-wv-effect="parallax-soft"]{will-change:transform}
|
|
947
|
+
}`;
|
|
948
|
+
var WV_EFFECT_JS = `(function(){
|
|
949
|
+
if(!('IntersectionObserver' in window))return;
|
|
950
|
+
var els=document.querySelectorAll('[data-wv-effect]');
|
|
951
|
+
if(!els.length)return;
|
|
952
|
+
if(window.matchMedia('(prefers-reduced-motion: reduce)').matches)return;
|
|
953
|
+
document.documentElement.classList.add('wv-js');
|
|
954
|
+
function num(el,attr,def){var v=parseInt(el.getAttribute(attr)||'',10);return isFinite(v)?v:def;}
|
|
955
|
+
function timing(el){
|
|
956
|
+
var d=num(el,'data-wv-delay',0);if(d>0)el.style.transitionDelay=(d*100)+'ms';
|
|
957
|
+
var ms=num(el,'data-wv-duration',0);if(ms>=100)el.style.transitionDuration=ms+'ms';
|
|
958
|
+
}
|
|
959
|
+
function counter(el){
|
|
960
|
+
var txt=el.textContent||'';var m=txt.match(/-?\\d[\\d.,]*/);if(!m)return;
|
|
961
|
+
var target=parseFloat(m[0].replace(/[.,](?=\\d{3}(\\D|$))/g,''));if(!isFinite(target))return;
|
|
962
|
+
var pre=txt.slice(0,m.index),post=txt.slice(m.index+m[0].length);
|
|
963
|
+
var dur=Math.min(Math.max(num(el,'data-wv-duration',1200),100),5000),t0=null;
|
|
964
|
+
function tick(t){
|
|
965
|
+
if(t0===null)t0=t;
|
|
966
|
+
var p=Math.min((t-t0)/dur,1),eased=1-Math.pow(1-p,3);
|
|
967
|
+
el.textContent=pre+Math.round(target*eased)+post;
|
|
968
|
+
if(p<1)requestAnimationFrame(tick);else el.textContent=txt;
|
|
969
|
+
}
|
|
970
|
+
requestAnimationFrame(tick);
|
|
971
|
+
}
|
|
972
|
+
var para=[];
|
|
973
|
+
function onScroll(){
|
|
974
|
+
for(var i=0;i<para.length;i++){
|
|
975
|
+
var el=para[i],r=el.getBoundingClientRect();
|
|
976
|
+
var vh=window.innerHeight||1;
|
|
977
|
+
var f=el.getAttribute('data-wv-strength')==='medium'?0.1:0.06;
|
|
978
|
+
var y=((r.top+r.height/2)-vh/2)*-f;
|
|
979
|
+
if(y>48)y=48;if(y<-48)y=-48;
|
|
980
|
+
el.style.transform='translateY('+y.toFixed(1)+'px)';
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
var ticking=false;
|
|
984
|
+
function onScrollRaf(){if(ticking)return;ticking=true;requestAnimationFrame(function(){ticking=false;onScroll();});}
|
|
985
|
+
var io=new IntersectionObserver(function(entries){
|
|
986
|
+
for(var i=0;i<entries.length;i++){
|
|
987
|
+
var en=entries[i];if(!en.isIntersecting)continue;
|
|
988
|
+
var el=en.target;io.unobserve(el);
|
|
989
|
+
var fx=el.getAttribute('data-wv-effect');
|
|
990
|
+
if(fx==='counter'){counter(el);continue;}
|
|
991
|
+
if(fx==='stagger'){
|
|
992
|
+
var step=Math.max(num(el,'data-wv-delay',1),1)*100,kids=el.children;
|
|
993
|
+
for(var k=0;k<kids.length;k++){kids[k].style.transitionDelay=(k*step)+'ms';kids[k].classList.add('wv-in');}
|
|
994
|
+
continue;
|
|
995
|
+
}
|
|
996
|
+
el.classList.add('wv-in');
|
|
997
|
+
}
|
|
998
|
+
},{threshold:0.15,rootMargin:'0px 0px -5% 0px'});
|
|
999
|
+
for(var i=0;i<els.length;i++){
|
|
1000
|
+
var el=els[i],fx=el.getAttribute('data-wv-effect');
|
|
1001
|
+
if(fx==='zoom-hover')continue;
|
|
1002
|
+
if(fx==='parallax-soft'){para.push(el);continue;}
|
|
1003
|
+
timing(el);
|
|
1004
|
+
io.observe(el);
|
|
1005
|
+
}
|
|
1006
|
+
if(para.length){window.addEventListener('scroll',onScrollRaf,{passive:true});window.addEventListener('resize',onScrollRaf,{passive:true});onScroll();}
|
|
1007
|
+
})();`;
|
|
1008
|
+
|
|
920
1009
|
// ../variant-compiler/src/lint/html.ts
|
|
921
1010
|
var ALLOWED_TAGS = /* @__PURE__ */ new Set([
|
|
922
1011
|
"a",
|
|
@@ -1169,12 +1258,39 @@ function lintNodes(nodes, ctx, ariaHidden = false) {
|
|
|
1169
1258
|
}
|
|
1170
1259
|
}
|
|
1171
1260
|
if ("id" in n.attrs) push(ctx, "warning", "id-attr", `<${tag} id> \u2014 the editor's click walker stops at ids; prefer a data-* attribute`);
|
|
1261
|
+
lintEffectAttrs(n.attrs, tag, ctx);
|
|
1172
1262
|
if (tag === "svg") ctx.inSvg++;
|
|
1173
1263
|
lintNodes(n.ch, ctx, hidden);
|
|
1174
1264
|
if (tag === "svg") ctx.inSvg--;
|
|
1175
1265
|
}
|
|
1176
1266
|
}
|
|
1177
1267
|
__name(lintNodes, "lintNodes");
|
|
1268
|
+
function lintEffectAttrs(attrs, tag, ctx) {
|
|
1269
|
+
const fx = "data-wv-effect" in attrs ? staticValue(attrs["data-wv-effect"]) : void 0;
|
|
1270
|
+
if ("data-wv-effect" in attrs) {
|
|
1271
|
+
if (fx !== null && fx !== void 0 && !WV_EFFECTS.includes(fx)) {
|
|
1272
|
+
push(ctx, "error", "effect-unknown", `data-wv-effect="${fx}" is not a platform effect \u2014 one of: ${WV_EFFECTS.join(", ")}`);
|
|
1273
|
+
}
|
|
1274
|
+
if ("data-edit-field" in attrs || "data-edit-image" in attrs) {
|
|
1275
|
+
push(ctx, "warning", "effect-on-editable", `data-wv-effect on an editable element (<${tag}>) can interfere with inline editing \u2014 prefer the effect on a wrapper`);
|
|
1276
|
+
}
|
|
1277
|
+
} else if ("data-wv-delay" in attrs || "data-wv-duration" in attrs || "data-wv-strength" in attrs) {
|
|
1278
|
+
push(ctx, "error", "effect-param", `data-wv-delay/duration/strength on <${tag}> without data-wv-effect does nothing`);
|
|
1279
|
+
}
|
|
1280
|
+
const delay = staticValue(attrs["data-wv-delay"]);
|
|
1281
|
+
if (delay !== null && delay !== void 0 && !(/^\d+$/.test(delay) && Number(delay) <= WV_DELAY_MAX)) {
|
|
1282
|
+
push(ctx, "error", "effect-param", `data-wv-delay="${delay}" \u2014 whole steps of 100ms, 0..${WV_DELAY_MAX}`);
|
|
1283
|
+
}
|
|
1284
|
+
const dur = staticValue(attrs["data-wv-duration"]);
|
|
1285
|
+
if (dur !== null && dur !== void 0 && !(/^\d+$/.test(dur) && Number(dur) >= WV_DURATION_MIN && Number(dur) <= WV_DURATION_MAX)) {
|
|
1286
|
+
push(ctx, "error", "effect-param", `data-wv-duration="${dur}" \u2014 milliseconds, ${WV_DURATION_MIN}..${WV_DURATION_MAX}`);
|
|
1287
|
+
}
|
|
1288
|
+
const strength = staticValue(attrs["data-wv-strength"]);
|
|
1289
|
+
if (strength !== null && strength !== void 0 && !WV_STRENGTHS.includes(strength)) {
|
|
1290
|
+
push(ctx, "error", "effect-param", `data-wv-strength="${strength}" \u2014 one of: ${WV_STRENGTHS.join(", ")}`);
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
__name(lintEffectAttrs, "lintEffectAttrs");
|
|
1178
1294
|
function lintAttr(name, v, tag, ctx) {
|
|
1179
1295
|
const lower = name.toLowerCase();
|
|
1180
1296
|
if (/^on[a-z]/.test(lower)) {
|
|
@@ -1198,7 +1314,7 @@ function lintAttr(name, v, tag, ctx) {
|
|
|
1198
1314
|
const s = staticValue(v);
|
|
1199
1315
|
if (s !== null) {
|
|
1200
1316
|
if (/url\s*\(|expression\s*\(|@import|behavior\s*:/i.test(s)) push(ctx, "error", "style-url", `inline style on <${tag}> must not use url()/expression()`);
|
|
1201
|
-
if (/position\s*:\s*fixed/i.test(s)
|
|
1317
|
+
if (/position\s*:\s*fixed/i.test(s)) push(ctx, "error", "fixed", `position: fixed is not allowed \u2014 the platform makes the section wrapper sticky (navbar) instead; keep the root in normal flow`);
|
|
1202
1318
|
if (/#[0-9a-f]{3,8}\b/i.test(s) || /\b(rgb|hsl)a?\(/i.test(s)) push(ctx, "warning", "hardcoded-color", `inline style on <${tag}> hardcodes a color \u2014 use var(--color-*)`);
|
|
1203
1319
|
if (/border-radius\s*:\s*\d/i.test(s)) push(ctx, "warning", "hardcoded-radius", `inline style on <${tag}> hardcodes border-radius \u2014 use var(--radius)`);
|
|
1204
1320
|
if (/font-family\s*:(?![^;]*var\(--font-)/i.test(s)) push(ctx, "warning", "hardcoded-font", `inline style on <${tag}> hardcodes font-family \u2014 use var(--font-heading|--font-body)`);
|
|
@@ -1225,7 +1341,7 @@ __name(lintHtml, "lintHtml");
|
|
|
1225
1341
|
|
|
1226
1342
|
// ../variant-compiler/src/lint/css.ts
|
|
1227
1343
|
var ALLOWED_URL_HOSTS = ["images.unsplash.com", "images.pexels.com"];
|
|
1228
|
-
function lintCss(css,
|
|
1344
|
+
function lintCss(css, _sectionType, attrs = {}) {
|
|
1229
1345
|
const out = [];
|
|
1230
1346
|
const err = /* @__PURE__ */ __name((code, message) => out.push({ level: "error", code, message }), "err");
|
|
1231
1347
|
const warn = /* @__PURE__ */ __name((code, message) => out.push({ level: "warning", code, message }), "warn");
|
|
@@ -1238,7 +1354,7 @@ function lintCss(css, sectionType2, attrs = {}) {
|
|
|
1238
1354
|
if (/@font-face\b/i.test(stripped)) err("css-font-face", "@font-face is not allowed \u2014 fonts come from the site theme (var(--font-heading|--font-body))");
|
|
1239
1355
|
if (/expression\s*\(/i.test(stripped)) err("css-expression", "expression() is not allowed");
|
|
1240
1356
|
if (/behavior\s*:|-moz-binding\s*:/i.test(stripped)) err("css-behavior", "behavior/-moz-binding are not allowed");
|
|
1241
|
-
if (/\bposition\s*:\s*fixed\b/i.test(stripped)
|
|
1357
|
+
if (/\bposition\s*:\s*fixed\b/i.test(stripped)) err("fixed", "position: fixed is not allowed \u2014 the platform makes the section wrapper sticky (navbar) instead; keep the root in normal flow");
|
|
1242
1358
|
for (const m of stripped.matchAll(/url\s*\(\s*(['"]?)([^'")]+)\1\s*\)/gi)) {
|
|
1243
1359
|
const u = m[2].trim();
|
|
1244
1360
|
if (/^data:image\/svg\+xml/i.test(u)) continue;
|
|
@@ -1739,6 +1855,19 @@ async function compileTailwind(candidates) {
|
|
|
1739
1855
|
__name(compileTailwind, "compileTailwind");
|
|
1740
1856
|
|
|
1741
1857
|
// ../variant-compiler/src/schema.ts
|
|
1858
|
+
var CHROME_CONTEXT_PROPS = /* @__PURE__ */ new Set([
|
|
1859
|
+
"pages",
|
|
1860
|
+
"currentSlug",
|
|
1861
|
+
"linkPrefix",
|
|
1862
|
+
"colorMode",
|
|
1863
|
+
"footerPages",
|
|
1864
|
+
"imageCredits"
|
|
1865
|
+
]);
|
|
1866
|
+
var CHROME_SECTION_TYPES = /* @__PURE__ */ new Set(["navbar", "banner", "footer"]);
|
|
1867
|
+
var NEVER_HIDDEN = {
|
|
1868
|
+
products: ["source"],
|
|
1869
|
+
blog: ["source"]
|
|
1870
|
+
};
|
|
1742
1871
|
function typeOf(s) {
|
|
1743
1872
|
if (!s) return "unknown";
|
|
1744
1873
|
const t = s.type;
|
|
@@ -1751,13 +1880,16 @@ function typeOf(s) {
|
|
|
1751
1880
|
return "unknown";
|
|
1752
1881
|
}
|
|
1753
1882
|
__name(typeOf, "typeOf");
|
|
1754
|
-
function deriveSchema(fields, props, base2) {
|
|
1883
|
+
function deriveSchema(fields, props, base2, sectionType2) {
|
|
1755
1884
|
const lint = [];
|
|
1885
|
+
const isChrome = sectionType2 !== void 0 && CHROME_SECTION_TYPES.has(sectionType2);
|
|
1886
|
+
const isContextProp = /* @__PURE__ */ __name((key) => isChrome && CHROME_CONTEXT_PROPS.has(key), "isContextProp");
|
|
1756
1887
|
const readKeys = new Set(props.map((p) => p.key));
|
|
1757
1888
|
const declared = new Map(fields.map((f) => [f.key, f]));
|
|
1758
1889
|
const extProps = {};
|
|
1759
1890
|
const usedFields = [];
|
|
1760
1891
|
for (const f of fields) {
|
|
1892
|
+
if (isContextProp(f.key)) continue;
|
|
1761
1893
|
if (!f.optional) lint.push({ level: "warning", code: "props-optional", message: `prop '${f.key}' should be optional (\`${f.key}?:\`) \u2014 content may omit it` });
|
|
1762
1894
|
const baseField = base2?.properties[f.key];
|
|
1763
1895
|
if (baseField) {
|
|
@@ -1770,11 +1902,13 @@ function deriveSchema(fields, props, base2) {
|
|
|
1770
1902
|
extProps[f.key] = { ...f.schema, ...f.description ? { description: f.description } : {} };
|
|
1771
1903
|
}
|
|
1772
1904
|
for (const k of readKeys) {
|
|
1905
|
+
if (isContextProp(k)) continue;
|
|
1773
1906
|
if (!declared.has(k)) lint.push({ level: "warning", code: "props-undeclared", message: `'${k}' is read from Astro.props but not declared in interface Props` });
|
|
1774
1907
|
else if (!base2?.properties[k] && !(k in extProps)) extProps[k] = { type: "string" };
|
|
1775
1908
|
if (base2?.properties[k] && !usedFields.includes(k)) usedFields.push(k);
|
|
1776
1909
|
}
|
|
1777
|
-
const
|
|
1910
|
+
const keepVisible = new Set(sectionType2 !== void 0 ? NEVER_HIDDEN[sectionType2] ?? [] : []);
|
|
1911
|
+
const hiddenFields = base2 ? Object.keys(base2.properties).filter((k) => !usedFields.includes(k) && !keepVisible.has(k)) : [];
|
|
1778
1912
|
const extension = { type: "object", properties: extProps, additionalProperties: true };
|
|
1779
1913
|
return { extension, hiddenFields, usedFields, lint };
|
|
1780
1914
|
}
|
|
@@ -1904,7 +2038,7 @@ async function compile2(source2, opts) {
|
|
|
1904
2038
|
lint.push({ level: "error", code: "css-parse", message: `could not scope CSS: ${e.message}` });
|
|
1905
2039
|
}
|
|
1906
2040
|
if (css.length > CSS_MAX_BYTES) lint.push({ level: "error", code: "css-size", message: `compiled CSS exceeds ${CSS_MAX_BYTES / 1024} KB` });
|
|
1907
|
-
const sch = deriveSchema(front.fields, front.props, opts.base ?? null);
|
|
2041
|
+
const sch = deriveSchema(front.fields, front.props, opts.base ?? null, opts.sectionType);
|
|
1908
2042
|
lint.push(...sch.lint);
|
|
1909
2043
|
if (opts.strict) {
|
|
1910
2044
|
for (const m of lint) if (m.level === "warning" && STRICT_CODES.has(m.code)) m.level = "error";
|
|
@@ -2470,7 +2604,12 @@ function renderVariant(ir, content, ctx, opts = {}) {
|
|
|
2470
2604
|
}
|
|
2471
2605
|
for (const c of ir.consts) scope[c.name] = evalExpr(c.e, scope, budget);
|
|
2472
2606
|
const rctx = { ...ctx, marks: { addImageButton: false } };
|
|
2473
|
-
const
|
|
2607
|
+
const isDbMode = (ir.sectionType === "products" || ir.sectionType === "blog") && content.source === "database";
|
|
2608
|
+
let rendered = renderNodes(ir.root, scope, rctx, budget);
|
|
2609
|
+
if (isDbMode) {
|
|
2610
|
+
rendered = rendered.replace(/<div class="webto-add-row[^"]*">\s*<button[^>]*data-edit-add-image="(?:products|posts)\.[^"]*"[\s\S]*?<\/button>\s*<\/div>/g, "").replace(/<button[^>]*data-edit-add-image="(?:products|posts)\.[^"]*"[\s\S]*?<\/button>/g, "").replace(/\s(?:data-edit-(?:field|image|add-image))="(?:products|posts)\.[^"]*"/g, "");
|
|
2611
|
+
}
|
|
2612
|
+
const html = withAddImageFallback(rendered, content, rctx);
|
|
2474
2613
|
const css = ir.css ? layerize(ir.css.replace(/__WV_ROOT__/g, `[data-wv-inst="${uid}"]`)) : "";
|
|
2475
2614
|
const script = ir.script ? wrapScript(ir.script, uid) : "";
|
|
2476
2615
|
return { html, css, script, uid };
|
|
@@ -2519,25 +2658,49 @@ __name(wrapScript, "wrapScript");
|
|
|
2519
2658
|
|
|
2520
2659
|
// ../variant-compiler/src/preview.ts
|
|
2521
2660
|
var PREVIEW_THEMES = {
|
|
2522
|
-
light: { background: "#ffffff", foreground: "#0f172a", card: "#f8fafc", muted: "#f1f5f9", mutedFg: "#64748b", border: "#e2e8f0", primary: "#3b82f6", primaryFg: "#ffffff", secondary: "#6366f1", accent: "#
|
|
2523
|
-
dark: { background: "#0b1220", foreground: "#e5e7eb", card: "#111a2e", muted: "#16213a", mutedFg: "#9aa4b8", border: "#243049", primary: "#60a5fa", primaryFg: "#0b1220", secondary: "#a78bfa", accent: "#
|
|
2524
|
-
warm: { background: "#fbf6ef", foreground: "#2b1d12", card: "#f4ebdf", muted: "#efe3d2", mutedFg: "#7a6652", border: "#e2d3bf", primary: "#b5562d", primaryFg: "#ffffff", secondary: "#7c4a2d", accent: "#
|
|
2661
|
+
light: { background: "#ffffff", foreground: "#0f172a", card: "#f8fafc", muted: "#f1f5f9", mutedFg: "#64748b", border: "#e2e8f0", primary: "#3b82f6", primaryFg: "#ffffff", secondary: "#6366f1", secondaryFg: "#ffffff", accent: "#e8eefb", accentFg: "#1e293b", heading: "Inter, sans-serif", body: "Inter, sans-serif", radius: "8px" },
|
|
2662
|
+
dark: { background: "#0b1220", foreground: "#e5e7eb", card: "#111a2e", muted: "#16213a", mutedFg: "#9aa4b8", border: "#243049", primary: "#60a5fa", primaryFg: "#0b1220", secondary: "#a78bfa", secondaryFg: "#0b1220", accent: "#1d2a47", accentFg: "#e5e7eb", heading: "Inter, sans-serif", body: "Inter, sans-serif", radius: "12px" },
|
|
2663
|
+
warm: { background: "#fbf6ef", foreground: "#2b1d12", card: "#f4ebdf", muted: "#efe3d2", mutedFg: "#7a6652", border: "#e2d3bf", primary: "#b5562d", primaryFg: "#ffffff", secondary: "#7c4a2d", secondaryFg: "#ffffff", accent: "#f1dfc0", accentFg: "#2b1d12", heading: "Georgia, serif", body: "Inter, sans-serif", radius: "2px" }
|
|
2525
2664
|
};
|
|
2526
2665
|
function isPreviewThemeKey(v) {
|
|
2527
2666
|
return typeof v === "string" && v in PREVIEW_THEMES;
|
|
2528
2667
|
}
|
|
2529
2668
|
__name(isPreviewThemeKey, "isPreviewThemeKey");
|
|
2530
2669
|
function buildPreviewDoc(ir, content, themeKey, opts = {}) {
|
|
2531
|
-
const
|
|
2670
|
+
const base2 = PREVIEW_THEMES[themeKey];
|
|
2671
|
+
const o = opts.themeOverride ?? {};
|
|
2672
|
+
const th = {
|
|
2673
|
+
background: o.background ?? base2.background,
|
|
2674
|
+
foreground: o.foreground ?? base2.foreground,
|
|
2675
|
+
card: o.card ?? base2.card,
|
|
2676
|
+
muted: o.muted ?? base2.muted,
|
|
2677
|
+
mutedFg: o.mutedFg ?? base2.mutedFg,
|
|
2678
|
+
border: o.border ?? base2.border,
|
|
2679
|
+
primary: o.primary ?? base2.primary,
|
|
2680
|
+
primaryFg: o.primaryFg ?? base2.primaryFg,
|
|
2681
|
+
secondary: o.secondary ?? base2.secondary,
|
|
2682
|
+
secondaryFg: o.secondaryFg ?? base2.secondaryFg,
|
|
2683
|
+
accent: o.accent ?? base2.accent,
|
|
2684
|
+
accentFg: o.accentFg ?? base2.accentFg,
|
|
2685
|
+
heading: o.heading ?? base2.heading,
|
|
2686
|
+
body: o.body ?? base2.body,
|
|
2687
|
+
radius: o.radius ?? base2.radius
|
|
2688
|
+
};
|
|
2689
|
+
const headingSize = o.headingSize ?? "48px";
|
|
2690
|
+
const bodySize = o.bodySize ?? "16px";
|
|
2691
|
+
const gradientFrom = o.gradientFrom ?? th.primary;
|
|
2692
|
+
const gradientTo = o.gradientTo ?? th.secondary;
|
|
2693
|
+
const isDark = o.dark ?? themeKey === "dark";
|
|
2694
|
+
const fontLinks = (o.fontLinks ?? []).filter((h) => /^https:\/\/fonts\.googleapis\.com\//.test(h)).map((h) => `<link rel="stylesheet" href="${h.replace(/"/g, """)}">`).join("");
|
|
2532
2695
|
const out = renderVariant(ir, content, { t: /* @__PURE__ */ __name((k) => k, "t"), editChrome: opts.editChrome ?? false }, { uid: "wv-preview" });
|
|
2533
|
-
const vars = `--background:${th.background};--foreground:${th.foreground};--card:${th.card};--card-foreground:${th.foreground};--primary:${th.primary};--primary-foreground:${th.primaryFg};--secondary:${th.secondary};--secondary-foreground
|
|
2696
|
+
const vars = `--background:${th.background};--foreground:${th.foreground};--card:${th.card};--card-foreground:${th.foreground};--primary:${th.primary};--primary-foreground:${th.primaryFg};--secondary:${th.secondary};--secondary-foreground:${th.secondaryFg};--muted:${th.muted};--muted-foreground:${th.mutedFg};--accent:${th.accent};--accent-foreground:${th.accentFg};--border:${th.border};--input:${th.border};--ring:${th.primary};--radius:${th.radius};--font-heading:${th.heading};--font-body:${th.body};--gradient-from:${gradientFrom};--gradient-to:${gradientTo};--spacing:.25rem;--site-max-width:72rem;--font-heading-size:${headingSize};--font-body-size:${bodySize};`;
|
|
2534
2697
|
const tokens = `--color-background:var(--background);--color-foreground:var(--foreground);--color-card:var(--card);--color-card-foreground:var(--card-foreground);--color-primary:var(--primary);--color-primary-foreground:var(--primary-foreground);--color-secondary:var(--secondary);--color-secondary-foreground:var(--secondary-foreground);--color-muted:var(--muted);--color-muted-foreground:var(--muted-foreground);--color-accent:var(--accent);--color-accent-foreground:var(--accent-foreground);--color-border:var(--border);--color-input:var(--input);--color-ring:var(--ring);--radius-sm:calc(var(--radius) - 4px);--radius-md:calc(var(--radius) - 2px);--radius-lg:var(--radius);--radius-xl:calc(var(--radius) + 4px);`;
|
|
2535
|
-
return `<!doctype html><html data-theme="${
|
|
2698
|
+
return `<!doctype html><html data-theme="${isDark ? "dark" : "light"}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="robots" content="noindex">${fontLinks}
|
|
2536
2699
|
<style>@layer wvfbase{*,::before,::after{box-sizing:border-box;border:0 solid var(--color-border)}html{font-size:var(--font-body-size,16px);-webkit-text-size-adjust:100%}body{margin:0;font-family:var(--font-body);color:var(--foreground);background:var(--background);line-height:1.5}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}h1,h2,h3,h4,p{margin:0}img,video,svg{display:block;max-width:100%;height:auto}a{color:inherit;text-decoration:inherit}button{font:inherit;color:inherit;background:none}ul,ol{margin:0;padding:0;list-style:none}}
|
|
2537
2700
|
:root{${vars}${tokens}}
|
|
2538
2701
|
h1{font-size:var(--font-heading-size,48px)}h2{font-size:calc(var(--font-heading-size,48px) * 0.75)}h3{font-size:calc(var(--font-heading-size,48px) * 0.6)}h4{font-size:calc(var(--font-heading-size,48px) * 0.5)}
|
|
2539
2702
|
.edit-url-pill{display:none!important}</style>
|
|
2540
|
-
<style>${out.css}</style></head><body><div data-wv-root data-wv-inst="${out.uid}">${out.html}</div>${out.script ? `<script>${out.script}</script>` : ""}</body></html>`;
|
|
2703
|
+
<style>${out.css}</style></head><body><div data-wv-root data-wv-inst="${out.uid}">${out.html}</div>${out.script ? `<script>${out.script}</script>` : ""}<style>${WV_EFFECT_CSS}</style><script>${WV_EFFECT_JS}</script></body></html>`;
|
|
2541
2704
|
}
|
|
2542
2705
|
__name(buildPreviewDoc, "buildPreviewDoc");
|
|
2543
2706
|
|
|
@@ -3729,6 +3892,114 @@ var base_schemas_default = {
|
|
|
3729
3892
|
type: "string"
|
|
3730
3893
|
}
|
|
3731
3894
|
}
|
|
3895
|
+
},
|
|
3896
|
+
navbar: {
|
|
3897
|
+
properties: {
|
|
3898
|
+
siteName: {
|
|
3899
|
+
description: "Nama yang ditampilkan di navigasi",
|
|
3900
|
+
type: "string"
|
|
3901
|
+
},
|
|
3902
|
+
siteNameSize: {
|
|
3903
|
+
default: 18,
|
|
3904
|
+
description: "Ukuran font nama (px)",
|
|
3905
|
+
type: "number",
|
|
3906
|
+
minimum: 12,
|
|
3907
|
+
maximum: 48
|
|
3908
|
+
},
|
|
3909
|
+
logoUrl: {
|
|
3910
|
+
description: "URL logo (image or vectorized SVG)",
|
|
3911
|
+
type: "string"
|
|
3912
|
+
},
|
|
3913
|
+
logoUrlDark: {
|
|
3914
|
+
description: "Optional logo shown when the site is in dark mode \u2014 falls back to the main logo",
|
|
3915
|
+
type: "string"
|
|
3916
|
+
},
|
|
3917
|
+
showSiteNameWithLogo: {
|
|
3918
|
+
description: "Also show the site name text when a logo image is set",
|
|
3919
|
+
type: "boolean"
|
|
3920
|
+
},
|
|
3921
|
+
ctaText: {
|
|
3922
|
+
description: "Teks tombol CTA",
|
|
3923
|
+
type: "string"
|
|
3924
|
+
},
|
|
3925
|
+
ctaUrl: {
|
|
3926
|
+
description: "URL tombol CTA",
|
|
3927
|
+
type: "string"
|
|
3928
|
+
},
|
|
3929
|
+
links: {
|
|
3930
|
+
description: "Custom navbar links (shown alongside page links)",
|
|
3931
|
+
type: "array",
|
|
3932
|
+
items: {
|
|
3933
|
+
type: "object",
|
|
3934
|
+
properties: {
|
|
3935
|
+
label: {
|
|
3936
|
+
type: "string",
|
|
3937
|
+
description: "Link text"
|
|
3938
|
+
},
|
|
3939
|
+
url: {
|
|
3940
|
+
type: "string",
|
|
3941
|
+
description: "Link URL (e.g. /#pricing, /about, https://...)"
|
|
3942
|
+
}
|
|
3943
|
+
},
|
|
3944
|
+
required: [
|
|
3945
|
+
"label",
|
|
3946
|
+
"url"
|
|
3947
|
+
],
|
|
3948
|
+
additionalProperties: false
|
|
3949
|
+
}
|
|
3950
|
+
}
|
|
3951
|
+
}
|
|
3952
|
+
},
|
|
3953
|
+
banner: {
|
|
3954
|
+
properties: {
|
|
3955
|
+
text: {
|
|
3956
|
+
type: "string",
|
|
3957
|
+
description: "Banner announcement text"
|
|
3958
|
+
},
|
|
3959
|
+
ctaText: {
|
|
3960
|
+
description: "Call-to-action button text",
|
|
3961
|
+
type: "string"
|
|
3962
|
+
},
|
|
3963
|
+
ctaUrl: {
|
|
3964
|
+
description: "Call-to-action button URL",
|
|
3965
|
+
type: "string"
|
|
3966
|
+
},
|
|
3967
|
+
icon: {
|
|
3968
|
+
description: "Icon emoji or name",
|
|
3969
|
+
type: "string"
|
|
3970
|
+
}
|
|
3971
|
+
}
|
|
3972
|
+
},
|
|
3973
|
+
footer: {
|
|
3974
|
+
properties: {
|
|
3975
|
+
text: {
|
|
3976
|
+
default: "\xA9 2025 All rights reserved.",
|
|
3977
|
+
description: "Footer copyright text",
|
|
3978
|
+
type: "string"
|
|
3979
|
+
},
|
|
3980
|
+
links: {
|
|
3981
|
+
description: "Footer navigation links",
|
|
3982
|
+
type: "array",
|
|
3983
|
+
items: {
|
|
3984
|
+
type: "object",
|
|
3985
|
+
properties: {
|
|
3986
|
+
label: {
|
|
3987
|
+
type: "string",
|
|
3988
|
+
description: "Link text"
|
|
3989
|
+
},
|
|
3990
|
+
url: {
|
|
3991
|
+
type: "string",
|
|
3992
|
+
description: "Link URL"
|
|
3993
|
+
}
|
|
3994
|
+
},
|
|
3995
|
+
required: [
|
|
3996
|
+
"label",
|
|
3997
|
+
"url"
|
|
3998
|
+
],
|
|
3999
|
+
additionalProperties: false
|
|
4000
|
+
}
|
|
4001
|
+
}
|
|
4002
|
+
}
|
|
3732
4003
|
}
|
|
3733
4004
|
};
|
|
3734
4005
|
|
package/dist/index.js
CHANGED
|
@@ -914,6 +914,95 @@ function escAttr(s) {
|
|
|
914
914
|
}
|
|
915
915
|
__name(escAttr, "escAttr");
|
|
916
916
|
|
|
917
|
+
// ../variant-compiler/src/effects.ts
|
|
918
|
+
var WV_EFFECTS = [
|
|
919
|
+
"reveal-up",
|
|
920
|
+
"reveal-down",
|
|
921
|
+
"reveal-left",
|
|
922
|
+
"reveal-right",
|
|
923
|
+
"fade",
|
|
924
|
+
"stagger",
|
|
925
|
+
"counter",
|
|
926
|
+
"parallax-soft",
|
|
927
|
+
"zoom-hover"
|
|
928
|
+
];
|
|
929
|
+
var WV_DELAY_MAX = 10;
|
|
930
|
+
var WV_DURATION_MIN = 100;
|
|
931
|
+
var WV_DURATION_MAX = 5e3;
|
|
932
|
+
var WV_STRENGTHS = ["soft", "medium"];
|
|
933
|
+
var WV_EFFECT_CSS = `@media (prefers-reduced-motion: no-preference){
|
|
934
|
+
.wv-js [data-wv-effect^="reveal-"],.wv-js [data-wv-effect="fade"],.wv-js [data-wv-effect="stagger"]>*{opacity:0;transition:opacity .6s cubic-bezier(.22,.61,.36,1),transform .6s cubic-bezier(.22,.61,.36,1)}
|
|
935
|
+
.wv-js [data-wv-effect="reveal-up"]{transform:translateY(22px)}
|
|
936
|
+
.wv-js [data-wv-effect="reveal-down"]{transform:translateY(-22px)}
|
|
937
|
+
.wv-js [data-wv-effect="reveal-left"]{transform:translateX(-22px)}
|
|
938
|
+
.wv-js [data-wv-effect="reveal-right"]{transform:translateX(22px)}
|
|
939
|
+
.wv-js [data-wv-effect="stagger"]>*{transform:translateY(16px)}
|
|
940
|
+
.wv-js [data-wv-effect].wv-in,.wv-js [data-wv-effect="stagger"]>.wv-in{opacity:1;transform:none}
|
|
941
|
+
[data-wv-effect="zoom-hover"] img{transition:transform .5s ease}
|
|
942
|
+
[data-wv-effect="zoom-hover"]:hover img{transform:scale(1.03)}
|
|
943
|
+
[data-wv-effect="parallax-soft"]{will-change:transform}
|
|
944
|
+
}`;
|
|
945
|
+
var WV_EFFECT_JS = `(function(){
|
|
946
|
+
if(!('IntersectionObserver' in window))return;
|
|
947
|
+
var els=document.querySelectorAll('[data-wv-effect]');
|
|
948
|
+
if(!els.length)return;
|
|
949
|
+
if(window.matchMedia('(prefers-reduced-motion: reduce)').matches)return;
|
|
950
|
+
document.documentElement.classList.add('wv-js');
|
|
951
|
+
function num(el,attr,def){var v=parseInt(el.getAttribute(attr)||'',10);return isFinite(v)?v:def;}
|
|
952
|
+
function timing(el){
|
|
953
|
+
var d=num(el,'data-wv-delay',0);if(d>0)el.style.transitionDelay=(d*100)+'ms';
|
|
954
|
+
var ms=num(el,'data-wv-duration',0);if(ms>=100)el.style.transitionDuration=ms+'ms';
|
|
955
|
+
}
|
|
956
|
+
function counter(el){
|
|
957
|
+
var txt=el.textContent||'';var m=txt.match(/-?\\d[\\d.,]*/);if(!m)return;
|
|
958
|
+
var target=parseFloat(m[0].replace(/[.,](?=\\d{3}(\\D|$))/g,''));if(!isFinite(target))return;
|
|
959
|
+
var pre=txt.slice(0,m.index),post=txt.slice(m.index+m[0].length);
|
|
960
|
+
var dur=Math.min(Math.max(num(el,'data-wv-duration',1200),100),5000),t0=null;
|
|
961
|
+
function tick(t){
|
|
962
|
+
if(t0===null)t0=t;
|
|
963
|
+
var p=Math.min((t-t0)/dur,1),eased=1-Math.pow(1-p,3);
|
|
964
|
+
el.textContent=pre+Math.round(target*eased)+post;
|
|
965
|
+
if(p<1)requestAnimationFrame(tick);else el.textContent=txt;
|
|
966
|
+
}
|
|
967
|
+
requestAnimationFrame(tick);
|
|
968
|
+
}
|
|
969
|
+
var para=[];
|
|
970
|
+
function onScroll(){
|
|
971
|
+
for(var i=0;i<para.length;i++){
|
|
972
|
+
var el=para[i],r=el.getBoundingClientRect();
|
|
973
|
+
var vh=window.innerHeight||1;
|
|
974
|
+
var f=el.getAttribute('data-wv-strength')==='medium'?0.1:0.06;
|
|
975
|
+
var y=((r.top+r.height/2)-vh/2)*-f;
|
|
976
|
+
if(y>48)y=48;if(y<-48)y=-48;
|
|
977
|
+
el.style.transform='translateY('+y.toFixed(1)+'px)';
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
var ticking=false;
|
|
981
|
+
function onScrollRaf(){if(ticking)return;ticking=true;requestAnimationFrame(function(){ticking=false;onScroll();});}
|
|
982
|
+
var io=new IntersectionObserver(function(entries){
|
|
983
|
+
for(var i=0;i<entries.length;i++){
|
|
984
|
+
var en=entries[i];if(!en.isIntersecting)continue;
|
|
985
|
+
var el=en.target;io.unobserve(el);
|
|
986
|
+
var fx=el.getAttribute('data-wv-effect');
|
|
987
|
+
if(fx==='counter'){counter(el);continue;}
|
|
988
|
+
if(fx==='stagger'){
|
|
989
|
+
var step=Math.max(num(el,'data-wv-delay',1),1)*100,kids=el.children;
|
|
990
|
+
for(var k=0;k<kids.length;k++){kids[k].style.transitionDelay=(k*step)+'ms';kids[k].classList.add('wv-in');}
|
|
991
|
+
continue;
|
|
992
|
+
}
|
|
993
|
+
el.classList.add('wv-in');
|
|
994
|
+
}
|
|
995
|
+
},{threshold:0.15,rootMargin:'0px 0px -5% 0px'});
|
|
996
|
+
for(var i=0;i<els.length;i++){
|
|
997
|
+
var el=els[i],fx=el.getAttribute('data-wv-effect');
|
|
998
|
+
if(fx==='zoom-hover')continue;
|
|
999
|
+
if(fx==='parallax-soft'){para.push(el);continue;}
|
|
1000
|
+
timing(el);
|
|
1001
|
+
io.observe(el);
|
|
1002
|
+
}
|
|
1003
|
+
if(para.length){window.addEventListener('scroll',onScrollRaf,{passive:true});window.addEventListener('resize',onScrollRaf,{passive:true});onScroll();}
|
|
1004
|
+
})();`;
|
|
1005
|
+
|
|
917
1006
|
// ../variant-compiler/src/lint/html.ts
|
|
918
1007
|
var ALLOWED_TAGS = /* @__PURE__ */ new Set([
|
|
919
1008
|
"a",
|
|
@@ -1166,12 +1255,39 @@ function lintNodes(nodes, ctx, ariaHidden = false) {
|
|
|
1166
1255
|
}
|
|
1167
1256
|
}
|
|
1168
1257
|
if ("id" in n.attrs) push(ctx, "warning", "id-attr", `<${tag} id> \u2014 the editor's click walker stops at ids; prefer a data-* attribute`);
|
|
1258
|
+
lintEffectAttrs(n.attrs, tag, ctx);
|
|
1169
1259
|
if (tag === "svg") ctx.inSvg++;
|
|
1170
1260
|
lintNodes(n.ch, ctx, hidden);
|
|
1171
1261
|
if (tag === "svg") ctx.inSvg--;
|
|
1172
1262
|
}
|
|
1173
1263
|
}
|
|
1174
1264
|
__name(lintNodes, "lintNodes");
|
|
1265
|
+
function lintEffectAttrs(attrs, tag, ctx) {
|
|
1266
|
+
const fx = "data-wv-effect" in attrs ? staticValue(attrs["data-wv-effect"]) : void 0;
|
|
1267
|
+
if ("data-wv-effect" in attrs) {
|
|
1268
|
+
if (fx !== null && fx !== void 0 && !WV_EFFECTS.includes(fx)) {
|
|
1269
|
+
push(ctx, "error", "effect-unknown", `data-wv-effect="${fx}" is not a platform effect \u2014 one of: ${WV_EFFECTS.join(", ")}`);
|
|
1270
|
+
}
|
|
1271
|
+
if ("data-edit-field" in attrs || "data-edit-image" in attrs) {
|
|
1272
|
+
push(ctx, "warning", "effect-on-editable", `data-wv-effect on an editable element (<${tag}>) can interfere with inline editing \u2014 prefer the effect on a wrapper`);
|
|
1273
|
+
}
|
|
1274
|
+
} else if ("data-wv-delay" in attrs || "data-wv-duration" in attrs || "data-wv-strength" in attrs) {
|
|
1275
|
+
push(ctx, "error", "effect-param", `data-wv-delay/duration/strength on <${tag}> without data-wv-effect does nothing`);
|
|
1276
|
+
}
|
|
1277
|
+
const delay = staticValue(attrs["data-wv-delay"]);
|
|
1278
|
+
if (delay !== null && delay !== void 0 && !(/^\d+$/.test(delay) && Number(delay) <= WV_DELAY_MAX)) {
|
|
1279
|
+
push(ctx, "error", "effect-param", `data-wv-delay="${delay}" \u2014 whole steps of 100ms, 0..${WV_DELAY_MAX}`);
|
|
1280
|
+
}
|
|
1281
|
+
const dur = staticValue(attrs["data-wv-duration"]);
|
|
1282
|
+
if (dur !== null && dur !== void 0 && !(/^\d+$/.test(dur) && Number(dur) >= WV_DURATION_MIN && Number(dur) <= WV_DURATION_MAX)) {
|
|
1283
|
+
push(ctx, "error", "effect-param", `data-wv-duration="${dur}" \u2014 milliseconds, ${WV_DURATION_MIN}..${WV_DURATION_MAX}`);
|
|
1284
|
+
}
|
|
1285
|
+
const strength = staticValue(attrs["data-wv-strength"]);
|
|
1286
|
+
if (strength !== null && strength !== void 0 && !WV_STRENGTHS.includes(strength)) {
|
|
1287
|
+
push(ctx, "error", "effect-param", `data-wv-strength="${strength}" \u2014 one of: ${WV_STRENGTHS.join(", ")}`);
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
__name(lintEffectAttrs, "lintEffectAttrs");
|
|
1175
1291
|
function lintAttr(name, v, tag, ctx) {
|
|
1176
1292
|
const lower = name.toLowerCase();
|
|
1177
1293
|
if (/^on[a-z]/.test(lower)) {
|
|
@@ -1195,7 +1311,7 @@ function lintAttr(name, v, tag, ctx) {
|
|
|
1195
1311
|
const s = staticValue(v);
|
|
1196
1312
|
if (s !== null) {
|
|
1197
1313
|
if (/url\s*\(|expression\s*\(|@import|behavior\s*:/i.test(s)) push(ctx, "error", "style-url", `inline style on <${tag}> must not use url()/expression()`);
|
|
1198
|
-
if (/position\s*:\s*fixed/i.test(s)
|
|
1314
|
+
if (/position\s*:\s*fixed/i.test(s)) push(ctx, "error", "fixed", `position: fixed is not allowed \u2014 the platform makes the section wrapper sticky (navbar) instead; keep the root in normal flow`);
|
|
1199
1315
|
if (/#[0-9a-f]{3,8}\b/i.test(s) || /\b(rgb|hsl)a?\(/i.test(s)) push(ctx, "warning", "hardcoded-color", `inline style on <${tag}> hardcodes a color \u2014 use var(--color-*)`);
|
|
1200
1316
|
if (/border-radius\s*:\s*\d/i.test(s)) push(ctx, "warning", "hardcoded-radius", `inline style on <${tag}> hardcodes border-radius \u2014 use var(--radius)`);
|
|
1201
1317
|
if (/font-family\s*:(?![^;]*var\(--font-)/i.test(s)) push(ctx, "warning", "hardcoded-font", `inline style on <${tag}> hardcodes font-family \u2014 use var(--font-heading|--font-body)`);
|
|
@@ -1222,7 +1338,7 @@ __name(lintHtml, "lintHtml");
|
|
|
1222
1338
|
|
|
1223
1339
|
// ../variant-compiler/src/lint/css.ts
|
|
1224
1340
|
var ALLOWED_URL_HOSTS = ["images.unsplash.com", "images.pexels.com"];
|
|
1225
|
-
function lintCss(css,
|
|
1341
|
+
function lintCss(css, _sectionType, attrs = {}) {
|
|
1226
1342
|
const out = [];
|
|
1227
1343
|
const err = /* @__PURE__ */ __name((code, message) => out.push({ level: "error", code, message }), "err");
|
|
1228
1344
|
const warn = /* @__PURE__ */ __name((code, message) => out.push({ level: "warning", code, message }), "warn");
|
|
@@ -1235,7 +1351,7 @@ function lintCss(css, sectionType, attrs = {}) {
|
|
|
1235
1351
|
if (/@font-face\b/i.test(stripped)) err("css-font-face", "@font-face is not allowed \u2014 fonts come from the site theme (var(--font-heading|--font-body))");
|
|
1236
1352
|
if (/expression\s*\(/i.test(stripped)) err("css-expression", "expression() is not allowed");
|
|
1237
1353
|
if (/behavior\s*:|-moz-binding\s*:/i.test(stripped)) err("css-behavior", "behavior/-moz-binding are not allowed");
|
|
1238
|
-
if (/\bposition\s*:\s*fixed\b/i.test(stripped)
|
|
1354
|
+
if (/\bposition\s*:\s*fixed\b/i.test(stripped)) err("fixed", "position: fixed is not allowed \u2014 the platform makes the section wrapper sticky (navbar) instead; keep the root in normal flow");
|
|
1239
1355
|
for (const m of stripped.matchAll(/url\s*\(\s*(['"]?)([^'")]+)\1\s*\)/gi)) {
|
|
1240
1356
|
const u = m[2].trim();
|
|
1241
1357
|
if (/^data:image\/svg\+xml/i.test(u)) continue;
|
|
@@ -1736,6 +1852,19 @@ async function compileTailwind(candidates) {
|
|
|
1736
1852
|
__name(compileTailwind, "compileTailwind");
|
|
1737
1853
|
|
|
1738
1854
|
// ../variant-compiler/src/schema.ts
|
|
1855
|
+
var CHROME_CONTEXT_PROPS = /* @__PURE__ */ new Set([
|
|
1856
|
+
"pages",
|
|
1857
|
+
"currentSlug",
|
|
1858
|
+
"linkPrefix",
|
|
1859
|
+
"colorMode",
|
|
1860
|
+
"footerPages",
|
|
1861
|
+
"imageCredits"
|
|
1862
|
+
]);
|
|
1863
|
+
var CHROME_SECTION_TYPES = /* @__PURE__ */ new Set(["navbar", "banner", "footer"]);
|
|
1864
|
+
var NEVER_HIDDEN = {
|
|
1865
|
+
products: ["source"],
|
|
1866
|
+
blog: ["source"]
|
|
1867
|
+
};
|
|
1739
1868
|
function typeOf(s) {
|
|
1740
1869
|
if (!s) return "unknown";
|
|
1741
1870
|
const t = s.type;
|
|
@@ -1748,13 +1877,16 @@ function typeOf(s) {
|
|
|
1748
1877
|
return "unknown";
|
|
1749
1878
|
}
|
|
1750
1879
|
__name(typeOf, "typeOf");
|
|
1751
|
-
function deriveSchema(fields, props, base) {
|
|
1880
|
+
function deriveSchema(fields, props, base, sectionType) {
|
|
1752
1881
|
const lint = [];
|
|
1882
|
+
const isChrome = sectionType !== void 0 && CHROME_SECTION_TYPES.has(sectionType);
|
|
1883
|
+
const isContextProp = /* @__PURE__ */ __name((key) => isChrome && CHROME_CONTEXT_PROPS.has(key), "isContextProp");
|
|
1753
1884
|
const readKeys = new Set(props.map((p) => p.key));
|
|
1754
1885
|
const declared = new Map(fields.map((f) => [f.key, f]));
|
|
1755
1886
|
const extProps = {};
|
|
1756
1887
|
const usedFields = [];
|
|
1757
1888
|
for (const f of fields) {
|
|
1889
|
+
if (isContextProp(f.key)) continue;
|
|
1758
1890
|
if (!f.optional) lint.push({ level: "warning", code: "props-optional", message: `prop '${f.key}' should be optional (\`${f.key}?:\`) \u2014 content may omit it` });
|
|
1759
1891
|
const baseField = base?.properties[f.key];
|
|
1760
1892
|
if (baseField) {
|
|
@@ -1767,11 +1899,13 @@ function deriveSchema(fields, props, base) {
|
|
|
1767
1899
|
extProps[f.key] = { ...f.schema, ...f.description ? { description: f.description } : {} };
|
|
1768
1900
|
}
|
|
1769
1901
|
for (const k of readKeys) {
|
|
1902
|
+
if (isContextProp(k)) continue;
|
|
1770
1903
|
if (!declared.has(k)) lint.push({ level: "warning", code: "props-undeclared", message: `'${k}' is read from Astro.props but not declared in interface Props` });
|
|
1771
1904
|
else if (!base?.properties[k] && !(k in extProps)) extProps[k] = { type: "string" };
|
|
1772
1905
|
if (base?.properties[k] && !usedFields.includes(k)) usedFields.push(k);
|
|
1773
1906
|
}
|
|
1774
|
-
const
|
|
1907
|
+
const keepVisible = new Set(sectionType !== void 0 ? NEVER_HIDDEN[sectionType] ?? [] : []);
|
|
1908
|
+
const hiddenFields = base ? Object.keys(base.properties).filter((k) => !usedFields.includes(k) && !keepVisible.has(k)) : [];
|
|
1775
1909
|
const extension = { type: "object", properties: extProps, additionalProperties: true };
|
|
1776
1910
|
return { extension, hiddenFields, usedFields, lint };
|
|
1777
1911
|
}
|
|
@@ -1901,7 +2035,7 @@ async function compile2(source, opts) {
|
|
|
1901
2035
|
lint.push({ level: "error", code: "css-parse", message: `could not scope CSS: ${e.message}` });
|
|
1902
2036
|
}
|
|
1903
2037
|
if (css.length > CSS_MAX_BYTES) lint.push({ level: "error", code: "css-size", message: `compiled CSS exceeds ${CSS_MAX_BYTES / 1024} KB` });
|
|
1904
|
-
const sch = deriveSchema(front.fields, front.props, opts.base ?? null);
|
|
2038
|
+
const sch = deriveSchema(front.fields, front.props, opts.base ?? null, opts.sectionType);
|
|
1905
2039
|
lint.push(...sch.lint);
|
|
1906
2040
|
if (opts.strict) {
|
|
1907
2041
|
for (const m of lint) if (m.level === "warning" && STRICT_CODES.has(m.code)) m.level = "error";
|
|
@@ -2467,7 +2601,12 @@ function renderVariant(ir, content, ctx, opts = {}) {
|
|
|
2467
2601
|
}
|
|
2468
2602
|
for (const c of ir.consts) scope[c.name] = evalExpr(c.e, scope, budget);
|
|
2469
2603
|
const rctx = { ...ctx, marks: { addImageButton: false } };
|
|
2470
|
-
const
|
|
2604
|
+
const isDbMode = (ir.sectionType === "products" || ir.sectionType === "blog") && content.source === "database";
|
|
2605
|
+
let rendered = renderNodes(ir.root, scope, rctx, budget);
|
|
2606
|
+
if (isDbMode) {
|
|
2607
|
+
rendered = rendered.replace(/<div class="webto-add-row[^"]*">\s*<button[^>]*data-edit-add-image="(?:products|posts)\.[^"]*"[\s\S]*?<\/button>\s*<\/div>/g, "").replace(/<button[^>]*data-edit-add-image="(?:products|posts)\.[^"]*"[\s\S]*?<\/button>/g, "").replace(/\s(?:data-edit-(?:field|image|add-image))="(?:products|posts)\.[^"]*"/g, "");
|
|
2608
|
+
}
|
|
2609
|
+
const html = withAddImageFallback(rendered, content, rctx);
|
|
2471
2610
|
const css = ir.css ? layerize(ir.css.replace(/__WV_ROOT__/g, `[data-wv-inst="${uid}"]`)) : "";
|
|
2472
2611
|
const script = ir.script ? wrapScript(ir.script, uid) : "";
|
|
2473
2612
|
return { html, css, script, uid };
|
|
@@ -2516,25 +2655,49 @@ __name(wrapScript, "wrapScript");
|
|
|
2516
2655
|
|
|
2517
2656
|
// ../variant-compiler/src/preview.ts
|
|
2518
2657
|
var PREVIEW_THEMES = {
|
|
2519
|
-
light: { background: "#ffffff", foreground: "#0f172a", card: "#f8fafc", muted: "#f1f5f9", mutedFg: "#64748b", border: "#e2e8f0", primary: "#3b82f6", primaryFg: "#ffffff", secondary: "#6366f1", accent: "#
|
|
2520
|
-
dark: { background: "#0b1220", foreground: "#e5e7eb", card: "#111a2e", muted: "#16213a", mutedFg: "#9aa4b8", border: "#243049", primary: "#60a5fa", primaryFg: "#0b1220", secondary: "#a78bfa", accent: "#
|
|
2521
|
-
warm: { background: "#fbf6ef", foreground: "#2b1d12", card: "#f4ebdf", muted: "#efe3d2", mutedFg: "#7a6652", border: "#e2d3bf", primary: "#b5562d", primaryFg: "#ffffff", secondary: "#7c4a2d", accent: "#
|
|
2658
|
+
light: { background: "#ffffff", foreground: "#0f172a", card: "#f8fafc", muted: "#f1f5f9", mutedFg: "#64748b", border: "#e2e8f0", primary: "#3b82f6", primaryFg: "#ffffff", secondary: "#6366f1", secondaryFg: "#ffffff", accent: "#e8eefb", accentFg: "#1e293b", heading: "Inter, sans-serif", body: "Inter, sans-serif", radius: "8px" },
|
|
2659
|
+
dark: { background: "#0b1220", foreground: "#e5e7eb", card: "#111a2e", muted: "#16213a", mutedFg: "#9aa4b8", border: "#243049", primary: "#60a5fa", primaryFg: "#0b1220", secondary: "#a78bfa", secondaryFg: "#0b1220", accent: "#1d2a47", accentFg: "#e5e7eb", heading: "Inter, sans-serif", body: "Inter, sans-serif", radius: "12px" },
|
|
2660
|
+
warm: { background: "#fbf6ef", foreground: "#2b1d12", card: "#f4ebdf", muted: "#efe3d2", mutedFg: "#7a6652", border: "#e2d3bf", primary: "#b5562d", primaryFg: "#ffffff", secondary: "#7c4a2d", secondaryFg: "#ffffff", accent: "#f1dfc0", accentFg: "#2b1d12", heading: "Georgia, serif", body: "Inter, sans-serif", radius: "2px" }
|
|
2522
2661
|
};
|
|
2523
2662
|
function isPreviewThemeKey(v) {
|
|
2524
2663
|
return typeof v === "string" && v in PREVIEW_THEMES;
|
|
2525
2664
|
}
|
|
2526
2665
|
__name(isPreviewThemeKey, "isPreviewThemeKey");
|
|
2527
2666
|
function buildPreviewDoc(ir, content, themeKey, opts = {}) {
|
|
2528
|
-
const
|
|
2667
|
+
const base = PREVIEW_THEMES[themeKey];
|
|
2668
|
+
const o = opts.themeOverride ?? {};
|
|
2669
|
+
const th = {
|
|
2670
|
+
background: o.background ?? base.background,
|
|
2671
|
+
foreground: o.foreground ?? base.foreground,
|
|
2672
|
+
card: o.card ?? base.card,
|
|
2673
|
+
muted: o.muted ?? base.muted,
|
|
2674
|
+
mutedFg: o.mutedFg ?? base.mutedFg,
|
|
2675
|
+
border: o.border ?? base.border,
|
|
2676
|
+
primary: o.primary ?? base.primary,
|
|
2677
|
+
primaryFg: o.primaryFg ?? base.primaryFg,
|
|
2678
|
+
secondary: o.secondary ?? base.secondary,
|
|
2679
|
+
secondaryFg: o.secondaryFg ?? base.secondaryFg,
|
|
2680
|
+
accent: o.accent ?? base.accent,
|
|
2681
|
+
accentFg: o.accentFg ?? base.accentFg,
|
|
2682
|
+
heading: o.heading ?? base.heading,
|
|
2683
|
+
body: o.body ?? base.body,
|
|
2684
|
+
radius: o.radius ?? base.radius
|
|
2685
|
+
};
|
|
2686
|
+
const headingSize = o.headingSize ?? "48px";
|
|
2687
|
+
const bodySize = o.bodySize ?? "16px";
|
|
2688
|
+
const gradientFrom = o.gradientFrom ?? th.primary;
|
|
2689
|
+
const gradientTo = o.gradientTo ?? th.secondary;
|
|
2690
|
+
const isDark = o.dark ?? themeKey === "dark";
|
|
2691
|
+
const fontLinks = (o.fontLinks ?? []).filter((h) => /^https:\/\/fonts\.googleapis\.com\//.test(h)).map((h) => `<link rel="stylesheet" href="${h.replace(/"/g, """)}">`).join("");
|
|
2529
2692
|
const out = renderVariant(ir, content, { t: /* @__PURE__ */ __name((k) => k, "t"), editChrome: opts.editChrome ?? false }, { uid: "wv-preview" });
|
|
2530
|
-
const vars = `--background:${th.background};--foreground:${th.foreground};--card:${th.card};--card-foreground:${th.foreground};--primary:${th.primary};--primary-foreground:${th.primaryFg};--secondary:${th.secondary};--secondary-foreground
|
|
2693
|
+
const vars = `--background:${th.background};--foreground:${th.foreground};--card:${th.card};--card-foreground:${th.foreground};--primary:${th.primary};--primary-foreground:${th.primaryFg};--secondary:${th.secondary};--secondary-foreground:${th.secondaryFg};--muted:${th.muted};--muted-foreground:${th.mutedFg};--accent:${th.accent};--accent-foreground:${th.accentFg};--border:${th.border};--input:${th.border};--ring:${th.primary};--radius:${th.radius};--font-heading:${th.heading};--font-body:${th.body};--gradient-from:${gradientFrom};--gradient-to:${gradientTo};--spacing:.25rem;--site-max-width:72rem;--font-heading-size:${headingSize};--font-body-size:${bodySize};`;
|
|
2531
2694
|
const tokens = `--color-background:var(--background);--color-foreground:var(--foreground);--color-card:var(--card);--color-card-foreground:var(--card-foreground);--color-primary:var(--primary);--color-primary-foreground:var(--primary-foreground);--color-secondary:var(--secondary);--color-secondary-foreground:var(--secondary-foreground);--color-muted:var(--muted);--color-muted-foreground:var(--muted-foreground);--color-accent:var(--accent);--color-accent-foreground:var(--accent-foreground);--color-border:var(--border);--color-input:var(--input);--color-ring:var(--ring);--radius-sm:calc(var(--radius) - 4px);--radius-md:calc(var(--radius) - 2px);--radius-lg:var(--radius);--radius-xl:calc(var(--radius) + 4px);`;
|
|
2532
|
-
return `<!doctype html><html data-theme="${
|
|
2695
|
+
return `<!doctype html><html data-theme="${isDark ? "dark" : "light"}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="robots" content="noindex">${fontLinks}
|
|
2533
2696
|
<style>@layer wvfbase{*,::before,::after{box-sizing:border-box;border:0 solid var(--color-border)}html{font-size:var(--font-body-size,16px);-webkit-text-size-adjust:100%}body{margin:0;font-family:var(--font-body);color:var(--foreground);background:var(--background);line-height:1.5}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}h1,h2,h3,h4,p{margin:0}img,video,svg{display:block;max-width:100%;height:auto}a{color:inherit;text-decoration:inherit}button{font:inherit;color:inherit;background:none}ul,ol{margin:0;padding:0;list-style:none}}
|
|
2534
2697
|
:root{${vars}${tokens}}
|
|
2535
2698
|
h1{font-size:var(--font-heading-size,48px)}h2{font-size:calc(var(--font-heading-size,48px) * 0.75)}h3{font-size:calc(var(--font-heading-size,48px) * 0.6)}h4{font-size:calc(var(--font-heading-size,48px) * 0.5)}
|
|
2536
2699
|
.edit-url-pill{display:none!important}</style>
|
|
2537
|
-
<style>${out.css}</style></head><body><div data-wv-root data-wv-inst="${out.uid}">${out.html}</div>${out.script ? `<script>${out.script}</script>` : ""}</body></html>`;
|
|
2700
|
+
<style>${out.css}</style></head><body><div data-wv-root data-wv-inst="${out.uid}">${out.html}</div>${out.script ? `<script>${out.script}</script>` : ""}<style>${WV_EFFECT_CSS}</style><script>${WV_EFFECT_JS}</script></body></html>`;
|
|
2538
2701
|
}
|
|
2539
2702
|
__name(buildPreviewDoc, "buildPreviewDoc");
|
|
2540
2703
|
export {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webto-id/variant-check",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "Lint, compile and preview a Webto Variant Format (.astro subset) section variant with the exact compiler the webto.id marketplace uses.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|