chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import {fileURLToPath} from "url";
|
||||
|
||||
import * as tar from "tar";
|
||||
|
||||
import {VALID_SKILLS} from "../src/lib/skills-constants.mjs";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
// Paths
|
||||
const repoRoot = path.join(__dirname, "../..");
|
||||
const skillsSourceDir = path.join(repoRoot, "..", "skills");
|
||||
const skillsOutputDir = path.join(__dirname, "../public/skills");
|
||||
|
||||
/**
|
||||
* Build tar.gz file for a skill
|
||||
*/
|
||||
async function buildSkillTarball(skillName) {
|
||||
const skillDir = path.join(skillsSourceDir, skillName);
|
||||
const outputFile = path.join(skillsOutputDir, `${skillName}.tar.gz`);
|
||||
|
||||
if (!fs.existsSync(skillDir)) {
|
||||
throw new Error(`Skill directory not found: ${skillDir}`);
|
||||
}
|
||||
|
||||
// Ensure output directory exists
|
||||
fs.mkdirSync(skillsOutputDir, {recursive: true});
|
||||
|
||||
// Create tar.gz file
|
||||
await tar.create(
|
||||
{
|
||||
cwd: skillDir,
|
||||
file: outputFile,
|
||||
gzip: true,
|
||||
},
|
||||
["."],
|
||||
);
|
||||
|
||||
const stats = fs.statSync(outputFile);
|
||||
|
||||
console.log(`✓ Built ${skillName}.tar.gz (${(stats.size / 1024).toFixed(1)}KB)`);
|
||||
}
|
||||
|
||||
// Build all skills
|
||||
async function buildAllSkills() {
|
||||
console.log("Building skill tarballs...");
|
||||
console.log(`Source: ${skillsSourceDir}`);
|
||||
console.log(`Output: ${skillsOutputDir}`);
|
||||
console.log("");
|
||||
|
||||
if (!fs.existsSync(skillsSourceDir)) {
|
||||
throw new Error(`Skills directory not found: ${skillsSourceDir}`);
|
||||
}
|
||||
|
||||
for (const skillName of VALID_SKILLS) {
|
||||
try {
|
||||
await buildSkillTarball(skillName);
|
||||
} catch (error) {
|
||||
console.error(`✗ Failed to build ${skillName}:`, error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
console.log("");
|
||||
console.log("✓ All skill tarballs built successfully");
|
||||
}
|
||||
|
||||
buildAllSkills().catch((error) => {
|
||||
console.error("Error building skills:", error);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -0,0 +1,494 @@
|
||||
/**
|
||||
* Build script: generates docs theme-presets.css from the frontend's preset definitions.
|
||||
*
|
||||
* Usage: node apps/docs/scripts/build-theme-presets.mjs
|
||||
* Output: apps/docs/src/styles/theme-presets.css
|
||||
*/
|
||||
|
||||
import {writeFileSync} from "fs";
|
||||
import {dirname, resolve} from "path";
|
||||
import {fileURLToPath} from "url";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const OUTPUT = resolve(__dirname, "../src/styles/theme-presets.css");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Color math
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function hexToOklch(hex) {
|
||||
const r = parseInt(hex.slice(1, 3), 16) / 255;
|
||||
const g = parseInt(hex.slice(3, 5), 16) / 255;
|
||||
const b = parseInt(hex.slice(5, 7), 16) / 255;
|
||||
|
||||
const toLinear = (v) => (v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4));
|
||||
|
||||
const lr = toLinear(r),
|
||||
lg = toLinear(g),
|
||||
lb = toLinear(b);
|
||||
|
||||
const x = 0.4124564 * lr + 0.3575761 * lg + 0.1804375 * lb;
|
||||
const y = 0.2126729 * lr + 0.7151522 * lg + 0.072175 * lb;
|
||||
const z = 0.0193339 * lr + 0.119192 * lg + 0.9503041 * lb;
|
||||
|
||||
const l_ = 0.8189330101 * x + 0.3618667424 * y - 0.1288597137 * z;
|
||||
const m_ = 0.0329845436 * x + 0.9293118715 * y + 0.0361456387 * z;
|
||||
const s_ = 0.0482003018 * x + 0.2643662691 * y + 0.633851707 * z;
|
||||
|
||||
const l3 = Math.cbrt(l_),
|
||||
m3 = Math.cbrt(m_),
|
||||
s3 = Math.cbrt(s_);
|
||||
|
||||
const okL = 0.2104542553 * l3 + 0.793617785 * m3 - 0.0040720468 * s3;
|
||||
const okA = 1.9779984951 * l3 - 2.428592205 * m3 + 0.4505937099 * s3;
|
||||
const okB = 0.0259040371 * l3 + 0.7827717662 * m3 - 0.808675766 * s3;
|
||||
|
||||
const c = Math.sqrt(okA * okA + okB * okB);
|
||||
let h = (Math.atan2(okB, okA) * 180) / Math.PI;
|
||||
|
||||
if (h < 0) h += 360;
|
||||
|
||||
return {c, h, l: Math.max(0, Math.min(1, okL))};
|
||||
}
|
||||
|
||||
function fmt(color) {
|
||||
return `oklch(${(color.l * 100).toFixed(2)}% ${color.c.toFixed(4)} ${color.h.toFixed(2)})`;
|
||||
}
|
||||
|
||||
function adjustHue(color, newHue) {
|
||||
return {...color, h: newHue};
|
||||
}
|
||||
|
||||
function adjustChroma(color, newChroma) {
|
||||
return {...color, c: Math.max(0, newChroma)};
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Default baseline neutral values (from generate-css-variables.ts)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const DEFAULTS = {
|
||||
dark: {
|
||||
background: {c: 0.005, h: 285.823, l: 0.12},
|
||||
border: {c: 0.006, h: 286.033, l: 0.28},
|
||||
default: {c: 0.006, h: 286.033, l: 0.274},
|
||||
foreground: {c: 0, h: 0, l: 0.9911},
|
||||
muted: {c: 0.015, h: 286.067, l: 0.705},
|
||||
overlay: {c: 0.0059, h: 285.89, l: 0.2103},
|
||||
scrollbar: {c: 0.015, h: 286.067, l: 0.705},
|
||||
segment: {c: 0.01, h: 285.93, l: 0.3964},
|
||||
separator: {c: 0.006, h: 286.033, l: 0.25},
|
||||
surface: {c: 0.0059, h: 285.89, l: 0.2103},
|
||||
surfaceSecondary: {c: 0.0037, h: 286.14, l: 0.257},
|
||||
surfaceTertiary: {c: 0.0024, h: 247.91, l: 0.2721},
|
||||
},
|
||||
light: {
|
||||
background: {c: 0, h: 0, l: 0.9702},
|
||||
border: {c: 0.004, h: 286.32, l: 0.9},
|
||||
default: {c: 0.001, h: 286.375, l: 0.94},
|
||||
foreground: {c: 0.0059, h: 285.89, l: 0.2103},
|
||||
muted: {c: 0.0138, h: 285.94, l: 0.5517},
|
||||
overlay: {c: 0, h: 0, l: 1},
|
||||
scrollbar: {c: 0.006, h: 286.286, l: 0.871},
|
||||
segment: {c: 0, h: 0, l: 1},
|
||||
separator: {c: 0.004, h: 286.32, l: 0.92},
|
||||
surface: {c: 0, h: 0, l: 1},
|
||||
surfaceSecondary: {c: 0.0013, h: 286.37, l: 0.9524},
|
||||
surfaceTertiary: {c: 0.0013, h: 286.37, l: 0.9373},
|
||||
},
|
||||
};
|
||||
|
||||
const SEMANTIC_HUE_BLEND = 0.12;
|
||||
const DEFAULT_ACCENT_HUE = 253.83;
|
||||
|
||||
function semanticHue(semanticDefault, baseHue) {
|
||||
let h = semanticDefault + (baseHue - DEFAULT_ACCENT_HUE) * SEMANTIC_HUE_BLEND;
|
||||
|
||||
while (h < 0) h += 360;
|
||||
while (h >= 360) h -= 360;
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Variable generators
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function accentForeground(l, c, h) {
|
||||
if (l > 0.65) {
|
||||
return `oklch(15% ${Math.min(c * 0.2, 0.03).toFixed(4)} ${h.toFixed(2)})`;
|
||||
}
|
||||
|
||||
return "oklch(99.11% 0 0)";
|
||||
}
|
||||
|
||||
function genAccent(l, c, h, fgOverride) {
|
||||
const accent = fmt({c, h, l});
|
||||
const fg = fgOverride ?? accentForeground(l, c, h);
|
||||
|
||||
return {
|
||||
"--accent": accent,
|
||||
"--accent-foreground": fg,
|
||||
"--focus": accent,
|
||||
};
|
||||
}
|
||||
|
||||
function genNeutrals(hue, grayChroma, mode) {
|
||||
const defs = DEFAULTS[mode];
|
||||
const f = (color, chromaMul) => fmt(adjustChroma(adjustHue(color, hue), grayChroma * chromaMul));
|
||||
|
||||
const bg = f(defs.background, 1);
|
||||
const fg = f(defs.foreground, 1);
|
||||
const muted = f(defs.muted, 2);
|
||||
const surface = f(defs.surface, mode === "dark" ? 2 : 0.5);
|
||||
const surfaceSec = f(defs.surfaceSecondary, 1.5);
|
||||
const surfaceTer = f(defs.surfaceTertiary, 1.5);
|
||||
const defaultBg = f(defs.default, 1);
|
||||
const border = f(defs.border, 1);
|
||||
const separator = f(defs.separator, 1);
|
||||
const overlay = f(defs.overlay, mode === "dark" ? 2 : 0.3);
|
||||
const scrollbar = f(defs.scrollbar, 1);
|
||||
const segment = f(defs.segment, 1);
|
||||
const fieldBg = surface;
|
||||
const fieldFg = fg;
|
||||
|
||||
const defaultFg = mode === "dark" ? "oklch(99.11% 0 0)" : fmt({c: 0.0059, h: hue, l: 0.2103});
|
||||
|
||||
return {
|
||||
"--background": bg,
|
||||
"--border": border,
|
||||
"--default": defaultBg,
|
||||
"--default-foreground": defaultFg,
|
||||
"--field-background": fieldBg,
|
||||
"--field-border": border,
|
||||
"--field-foreground": fieldFg,
|
||||
"--field-placeholder": muted,
|
||||
"--foreground": fg,
|
||||
"--link": fg,
|
||||
"--muted": muted,
|
||||
"--overlay": overlay,
|
||||
"--overlay-foreground": fg,
|
||||
"--scrollbar": scrollbar,
|
||||
"--segment": segment,
|
||||
"--segment-foreground": fg,
|
||||
"--separator": separator,
|
||||
"--surface": surface,
|
||||
"--surface-foreground": fg,
|
||||
"--surface-secondary": surfaceSec,
|
||||
"--surface-secondary-foreground": fg,
|
||||
"--surface-tertiary": surfaceTer,
|
||||
"--surface-tertiary-foreground": fg,
|
||||
};
|
||||
}
|
||||
|
||||
function genSemanticFromHex(name, hex, baseHue, grayChroma) {
|
||||
const oklch = hexToOklch(hex);
|
||||
const h = semanticHue(oklch.h, baseHue);
|
||||
const boost = 1 + grayChroma * 2;
|
||||
const c = Math.min(oklch.c * boost, 0.35);
|
||||
const color = fmt({c, h, l: oklch.l});
|
||||
const fg = oklch.l > 0.67 ? fmt({c: 0.0059, h, l: 0.2103}) : "oklch(99.11% 0 0)";
|
||||
|
||||
return {
|
||||
[`--${name}`]: color,
|
||||
[`--${name}-foreground`]: fg,
|
||||
};
|
||||
}
|
||||
|
||||
function genSemanticFromOklch(name, oklchStr, fgOverride) {
|
||||
const lMatch = oklchStr.match(/oklch\(([\d.]+)/);
|
||||
const l = lMatch?.[1] ? parseFloat(lMatch[1]) : 0.5;
|
||||
let autoFg;
|
||||
|
||||
if (l > 0.65) {
|
||||
const hMatch = oklchStr.match(/oklch\([\d.]+\s+[\d.]+\s+([\d.]+)/);
|
||||
const h = hMatch?.[1] ? parseFloat(hMatch[1]) : 0;
|
||||
const cMatch = oklchStr.match(/oklch\([\d.]+\s+([\d.]+)/);
|
||||
const c = cMatch?.[1] ? parseFloat(cMatch[1]) : 0;
|
||||
|
||||
autoFg = `oklch(15% ${Math.min(c * 0.3, 0.05).toFixed(4)} ${h.toFixed(2)})`;
|
||||
} else {
|
||||
autoFg = "oklch(99.11% 0 0)";
|
||||
}
|
||||
const fg = fgOverride ?? autoFg;
|
||||
|
||||
return {
|
||||
[`--${name}`]: oklchStr,
|
||||
[`--${name}-foreground`]: fg,
|
||||
};
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Radius map
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const RADIUS_MAP = {
|
||||
"extra-large": "1rem",
|
||||
"extra-small": "0.125rem",
|
||||
large: "0.75rem",
|
||||
medium: "0.5rem",
|
||||
none: "0",
|
||||
small: "0.25rem",
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Preset definitions
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const DEFAULT_SEMANTIC = {
|
||||
dangerHex: "#FF383C",
|
||||
successHex: "#17C964",
|
||||
warningHex: "#F5A524",
|
||||
};
|
||||
|
||||
const PRESETS = [
|
||||
{
|
||||
accentHex: "#7DD3FC",
|
||||
base: 0.015,
|
||||
formRadius: "large",
|
||||
id: "sky",
|
||||
radius: "medium",
|
||||
},
|
||||
{
|
||||
accentHex: "#C084FC",
|
||||
base: 0.015,
|
||||
formRadius: "large",
|
||||
id: "lavender",
|
||||
radius: "medium",
|
||||
},
|
||||
{
|
||||
accentHex: "#86EFAC",
|
||||
base: 0.015,
|
||||
formRadius: "large",
|
||||
id: "mint",
|
||||
radius: "medium",
|
||||
},
|
||||
{
|
||||
accentHex: "#E50914",
|
||||
base: 0.008,
|
||||
formRadius: "extra-small",
|
||||
id: "netflix",
|
||||
radius: "extra-small",
|
||||
semanticOverrides: {
|
||||
dark: {
|
||||
danger: {color: "oklch(0.4964 0.1994 28.56)"},
|
||||
success: {color: "oklch(0.7677 0.1899 148.1)"},
|
||||
warning: {color: "oklch(0.8239 0.153 74.6)"},
|
||||
},
|
||||
light: {
|
||||
danger: {color: "oklch(0.4823 0.1938 27.64)"},
|
||||
success: {color: "oklch(0.5148 0.1337 146.82)"},
|
||||
warning: {color: "oklch(0.561 0.116571 78.9352)"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
accentHex: "#000000",
|
||||
base: 0.005,
|
||||
darkAccent: {c: 0, h: 0, l: 0.9848},
|
||||
darkAccentHex: "#FBFBFB",
|
||||
formRadius: "small",
|
||||
id: "uber",
|
||||
radius: "small",
|
||||
semanticOverrides: {
|
||||
dark: {
|
||||
danger: {color: "oklch(0.7044 0.1872 23.19)"},
|
||||
success: {color: "oklch(0.6514 0.1321 156.22)"},
|
||||
warning: {color: "oklch(0.8803 0.1348 86.06)"},
|
||||
},
|
||||
light: {
|
||||
danger: {color: "oklch(0.573 0.2249 21.97)"},
|
||||
success: {color: "oklch(0.6277 0.1604 153.06)"},
|
||||
warning: {color: "oklch(0.8446 0.1525 80.6)"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
accentHex: "#1ED760",
|
||||
base: 0.002,
|
||||
formRadius: "extra-small",
|
||||
id: "spotify",
|
||||
radius: "medium",
|
||||
semanticOverrides: {
|
||||
dark: {
|
||||
danger: {color: "oklch(0.5931 0.2338 25.42)"},
|
||||
success: {color: "oklch(0.7697 0.2124 148.67)"},
|
||||
warning: {color: "oklch(0.7921 0.1626 67.42)"},
|
||||
},
|
||||
light: {
|
||||
danger: {color: "oklch(0.5509 0.2166 25.29)"},
|
||||
success: {color: "oklch(0.6072 0.1647 149.02)"},
|
||||
warning: {color: "oklch(0.6972 0.1687 54.22)"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
accentHex: "#4F44E0",
|
||||
base: 0.002,
|
||||
formRadius: "extra-small",
|
||||
id: "coinbase",
|
||||
radius: "medium",
|
||||
semanticOverrides: {
|
||||
dark: {
|
||||
danger: {color: "oklch(0.6545 0.2145 22.31)"},
|
||||
success: {color: "oklch(0.7574 0.180554 156.931)"},
|
||||
warning: {color: "oklch(0.8095 0.1119 61.69)"},
|
||||
},
|
||||
light: {
|
||||
danger: {color: "oklch(0.5507 0.2062 24)"},
|
||||
success: {color: "oklch(0.5438 0.1268 157.17)"},
|
||||
warning: {color: "oklch(0.8095 0.1119 61.69)"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
accentHex: "#FF5A5F",
|
||||
base: 0,
|
||||
formRadius: "large",
|
||||
id: "airbnb",
|
||||
radius: "medium",
|
||||
semanticOverrides: {
|
||||
dark: {
|
||||
accentForeground: "oklch(0.9911 0 0)",
|
||||
danger: {color: "oklch(0.5392 0.1816 33.72)", foreground: "oklch(0.9911 0 0)"},
|
||||
success: {color: "oklch(0.652 0.114864 185.0749)", foreground: "oklch(0.9911 0 0)"},
|
||||
warning: {color: "oklch(0.8197 0.170602 78.4658)"},
|
||||
},
|
||||
light: {
|
||||
accentForeground: "oklch(0.9911 0 0)",
|
||||
danger: {color: "oklch(0.5392 0.1816 33.72)", foreground: "oklch(0.9911 0 0)"},
|
||||
success: {color: "oklch(0.5573 0.0947 199.48)", foreground: "oklch(0.9911 0 0)"},
|
||||
warning: {color: "oklch(0.6904 0.1972 38.75)", foreground: "oklch(0.9911 0 0)"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
accentHex: "#5865F2",
|
||||
base: 0.01,
|
||||
formRadius: "large",
|
||||
id: "discord",
|
||||
radius: "small",
|
||||
semanticOverrides: {
|
||||
dark: {
|
||||
danger: {color: "oklch(0.6318 0.2075 24.57)"},
|
||||
success: {color: "oklch(0.8548 0.1967 150.16)"},
|
||||
warning: {color: "oklch(0.9218 0.1571 99.87)"},
|
||||
},
|
||||
light: {
|
||||
danger: {color: "oklch(0.5884 0.1993 24.39)"},
|
||||
success: {color: "oklch(0.532 0.1238 151.57)"},
|
||||
warning: {color: "oklch(0.9218 0.1571 99.87)"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
accentHex: "#FF6600",
|
||||
base: 0.01,
|
||||
formRadius: "extra-large",
|
||||
id: "rabbit",
|
||||
radius: "medium",
|
||||
semanticOverrides: {
|
||||
dark: {
|
||||
danger: {color: "oklch(0.6291 0.2565 29.09)"},
|
||||
success: {color: "oklch(0.7113 0.2043 140.81)"},
|
||||
},
|
||||
light: {
|
||||
danger: {color: "oklch(0.6291 0.2565 29.09)"},
|
||||
success: {color: "oklch(0.7113 0.2043 140.81)"},
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Generate CSS for a single preset
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function generatePresetCss(preset) {
|
||||
const lightAccent = hexToOklch(preset.accentHex);
|
||||
const darkAccent = preset.darkAccentHex ? hexToOklch(preset.darkAccentHex) : lightAccent;
|
||||
const hue = lightAccent.h;
|
||||
const grayChroma = preset.base;
|
||||
const so = preset.semanticOverrides;
|
||||
|
||||
function modeVars(mode) {
|
||||
const accent = mode === "dark" ? darkAccent : lightAccent;
|
||||
const modeAccent = preset[`${mode}Accent`] ?? accent;
|
||||
const accentVars = genAccent(
|
||||
modeAccent.l,
|
||||
modeAccent.c,
|
||||
modeAccent.h,
|
||||
so?.[mode]?.accentForeground,
|
||||
);
|
||||
const neutrals = genNeutrals(hue, grayChroma, mode);
|
||||
|
||||
let semantics = {};
|
||||
|
||||
for (const name of ["success", "warning", "danger"]) {
|
||||
if (so?.[mode]?.[name]) {
|
||||
semantics = {
|
||||
...semantics,
|
||||
...genSemanticFromOklch(name, so[mode][name].color, so[mode][name].foreground),
|
||||
};
|
||||
} else {
|
||||
semantics = {
|
||||
...semantics,
|
||||
...genSemanticFromHex(name, DEFAULT_SEMANTIC[`${name}Hex`], hue, grayChroma),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const radius = {};
|
||||
|
||||
if (preset.radius !== "medium") {
|
||||
radius["--radius"] = RADIUS_MAP[preset.radius];
|
||||
}
|
||||
if (preset.formRadius !== "large") {
|
||||
radius["--field-radius"] = RADIUS_MAP[preset.formRadius];
|
||||
}
|
||||
|
||||
const charts = {
|
||||
"--chart-1": "oklch(from var(--accent) calc(l - 0.24) c h)",
|
||||
"--chart-2": "oklch(from var(--accent) calc(l - 0.12) c h)",
|
||||
"--chart-3": "var(--accent)",
|
||||
"--chart-4": "oklch(from var(--accent) calc(l + 0.12) c h)",
|
||||
"--chart-5": "oklch(from var(--accent) calc(l + 0.24) c h)",
|
||||
};
|
||||
|
||||
return {...neutrals, ...accentVars, ...semantics, ...radius, ...charts};
|
||||
}
|
||||
|
||||
const light = modeVars("light");
|
||||
const dark = modeVars("dark");
|
||||
|
||||
const renderVars = (vars) =>
|
||||
Object.entries(vars)
|
||||
.map(([k, v]) => ` ${k}: ${v};`)
|
||||
.join("\n");
|
||||
|
||||
return `/* ${preset.id.charAt(0).toUpperCase() + preset.id.slice(1)} theme */
|
||||
[data-design-theme="${preset.id}"]:not(.dark):not([data-theme="dark"]) {
|
||||
${renderVars(light)}
|
||||
}
|
||||
|
||||
[data-design-theme="${preset.id}"].dark,
|
||||
[data-design-theme="${preset.id}"][data-theme="dark"] {
|
||||
color-scheme: dark;
|
||||
${renderVars(dark)}
|
||||
}`;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Main
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const banner = `/* ============================================================================
|
||||
Auto-generated theme presets for docs.
|
||||
Run: node apps/docs/scripts/build-theme-presets.mjs
|
||||
============================================================================ */\n`;
|
||||
|
||||
const body = PRESETS.map(generatePresetCss).join("\n\n");
|
||||
const css = `${banner}@layer base {\n${body}\n}\n`;
|
||||
|
||||
writeFileSync(OUTPUT, css);
|
||||
console.log(`✓ Generated ${PRESETS.length} theme presets → ${OUTPUT}`);
|
||||
Reference in New Issue
Block a user