@ox-content/vite-plugin 3.0.0-alpha.8 → 3.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +8898 -2585
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2050 -221
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +2050 -221
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +8839 -2531
- package/dist/index.mjs.map +1 -1
- package/dist/markdown-tables.cjs +70 -0
- package/dist/markdown-tables.cjs.map +1 -0
- package/dist/markdown-tables.d.cts +22 -0
- package/dist/markdown-tables.d.cts.map +1 -0
- package/dist/markdown-tables.d.mts +22 -0
- package/dist/markdown-tables.d.mts.map +1 -0
- package/dist/markdown-tables.mjs +66 -0
- package/dist/markdown-tables.mjs.map +1 -0
- package/dist/napi.cjs +44 -0
- package/dist/napi.cjs.map +1 -0
- package/dist/napi.mjs +34 -0
- package/dist/napi.mjs.map +1 -0
- package/dist/reader-chrome-client.cjs +103 -0
- package/dist/reader-chrome-client.d.cts +2 -0
- package/dist/reader-chrome-client.d.mts +2 -0
- package/dist/reader-chrome-client.mjs +102 -0
- package/dist/reader-chrome.cjs +109 -0
- package/dist/reader-chrome.cjs.map +1 -0
- package/dist/reader-chrome.d.cts +103 -0
- package/dist/reader-chrome.d.mts +103 -0
- package/dist/reader-chrome.mjs +98 -0
- package/dist/reader-chrome.mjs.map +1 -0
- package/dist/styles/all.css +15 -0
- package/dist/styles/citations.css +62 -0
- package/dist/styles/core.css +2261 -0
- package/dist/styles/github.css +262 -0
- package/dist/styles/graphviz.css +34 -0
- package/dist/styles/magic-links.css +33 -0
- package/dist/styles/markdown-tables.css +16 -0
- package/dist/styles/mermaid.css +151 -0
- package/dist/styles/not-by-ai.css +43 -0
- package/dist/styles/ogp.css +170 -0
- package/dist/styles/reader-chrome.css +163 -0
- package/dist/styles/social.css +678 -0
- package/dist/styles/tabs.css +210 -0
- package/dist/styles/theme-transition.css +37 -0
- package/dist/styles/twitter-full.css +598 -0
- package/dist/styles/youtube.css +87 -0
- package/dist/theme-tokens.cjs +95 -0
- package/dist/theme-tokens.cjs.map +1 -0
- package/dist/theme-tokens.d.cts +72 -0
- package/dist/theme-tokens.d.cts.map +1 -0
- package/dist/theme-tokens.d.mts +72 -0
- package/dist/theme-tokens.d.mts.map +1 -0
- package/dist/theme-tokens.mjs +91 -0
- package/dist/theme-tokens.mjs.map +1 -0
- package/dist/theme-transition-client.cjs +161 -0
- package/dist/theme-transition-client.d.cts +21 -0
- package/dist/theme-transition-client.d.mts +21 -0
- package/dist/theme-transition-client.mjs +160 -0
- package/dist/twitter-client.cjs +84 -0
- package/dist/twitter-client.d.cts +11 -0
- package/dist/twitter-client.d.mts +11 -0
- package/dist/twitter-client.mjs +83 -0
- package/dist/vitepress.cjs +614 -77
- package/dist/vitepress.cjs.map +1 -1
- package/dist/vitepress.mjs +588 -65
- package/dist/vitepress.mjs.map +1 -1
- package/package.json +89 -4
- package/dist/api.cjs +0 -6563
- package/dist/api.cjs.map +0 -1
- package/dist/api.mjs +0 -6456
- package/dist/api.mjs.map +0 -1
- package/dist/interop.cjs +0 -31
- package/dist/interop.cjs.map +0 -1
- package/dist/interop.mjs +0 -26
- package/dist/interop.mjs.map +0 -1
- package/dist/tabs.cjs +0 -98
- package/dist/tabs.cjs.map +0 -1
- package/dist/tabs.mjs +0 -99
- package/dist/tabs.mjs.map +0 -1
package/dist/vitepress.cjs
CHANGED
|
@@ -37,35 +37,590 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
37
37
|
enumerable: true
|
|
38
38
|
}) : target, mod));
|
|
39
39
|
var __toCommonJS = (mod) => __hasOwnProp.call(mod, "module.exports") ? mod["module.exports"] : __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
40
|
-
//#
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
function
|
|
59
|
-
if (
|
|
60
|
-
if (
|
|
40
|
+
//#endregion
|
|
41
|
+
const require_napi = require("./napi.cjs");
|
|
42
|
+
const require_theme_tokens = require("./theme-tokens.cjs");
|
|
43
|
+
let node_module = require("node:module");
|
|
44
|
+
let node_fs = require("node:fs");
|
|
45
|
+
let node_path = require("node:path");
|
|
46
|
+
let node_crypto = require("node:crypto");
|
|
47
|
+
let node_fs_promises = require("node:fs/promises");
|
|
48
|
+
let glob = require("glob");
|
|
49
|
+
//#region src/theme-fonts-acquire.ts
|
|
50
|
+
/**
|
|
51
|
+
* Resolve self-hosted faces from a local file / `@fontsource` directory or
|
|
52
|
+
* Google Fonts. Downloads are cached; tests inject `fetch` so CI never hits
|
|
53
|
+
* the network.
|
|
54
|
+
*/
|
|
55
|
+
const GOOGLE_CSS = "https://fonts.googleapis.com/css2";
|
|
56
|
+
const GOOGLE_UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36";
|
|
57
|
+
const ALLOWED_HOSTS = /* @__PURE__ */ new Set(["fonts.googleapis.com", "fonts.gstatic.com"]);
|
|
58
|
+
function fontMime(fileName) {
|
|
59
|
+
if (fileName.endsWith(".woff")) return "font/woff";
|
|
60
|
+
if (fileName.endsWith(".ttf")) return "font/ttf";
|
|
61
|
+
if (fileName.endsWith(".otf")) return "font/otf";
|
|
62
|
+
return "font/woff2";
|
|
63
|
+
}
|
|
64
|
+
function renderFontFaceCss(faces) {
|
|
65
|
+
return faces.map((face) => {
|
|
66
|
+
const range = face.unicodeRange ? `\n unicode-range: ${face.unicodeRange};` : "";
|
|
67
|
+
const fileName = face.fileName;
|
|
68
|
+
const format = fileName.endsWith(".woff") ? "woff" : fileName.endsWith(".ttf") ? "truetype" : fileName.endsWith(".otf") ? "opentype" : "woff2";
|
|
69
|
+
return `@font-face {
|
|
70
|
+
font-family: ${/^[a-zA-Z_-][\w-]*$/.test(face.family) ? face.family : `"${face.family.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`};
|
|
71
|
+
font-style: ${face.style};
|
|
72
|
+
font-weight: ${face.weight};
|
|
73
|
+
font-display: ${face.display};
|
|
74
|
+
src: url(./${fileName}) format("${format}");${range}
|
|
75
|
+
}`;
|
|
76
|
+
}).join("\n\n");
|
|
77
|
+
}
|
|
78
|
+
function resolveFontCacheDir(root, cacheDir) {
|
|
79
|
+
return cacheDir ?? (0, node_path.join)(root, "node_modules", ".cache", "ox-content", "fonts");
|
|
80
|
+
}
|
|
81
|
+
async function acquireSelfHostedFaces(faces, options) {
|
|
82
|
+
const cacheDir = resolveFontCacheDir(options.root, options.cacheDir);
|
|
83
|
+
const acquired = [];
|
|
84
|
+
for (const face of faces) acquired.push(face.provider === "local" ? await acquireLocalFace(face, options.root) : await acquireGoogleFace(face, cacheDir, options.fetch ?? fetch));
|
|
85
|
+
return acquired;
|
|
86
|
+
}
|
|
87
|
+
async function acquireLocalFace(face, root) {
|
|
88
|
+
if (!face.path) throw new Error(`Theme font "${face.family}" uses provider "local" but has no path.`);
|
|
89
|
+
if (face.path.includes("\0")) throw new Error(`Theme font "${face.family}" path must not contain NUL.`);
|
|
90
|
+
const resolved = resolveLocalPath(root, face.path);
|
|
91
|
+
const info = await (0, node_fs_promises.stat)(resolved).catch(() => void 0);
|
|
92
|
+
if (!info) throw new Error(`Theme font "${face.family}" was not found at ${resolved}.`);
|
|
93
|
+
const file = info.isDirectory() ? await findDirectoryFont(resolved, face) : resolved;
|
|
94
|
+
return {
|
|
95
|
+
...face,
|
|
96
|
+
bytes: await (0, node_fs_promises.readFile)(file)
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function resolveLocalPath(root, spec) {
|
|
100
|
+
if ((0, node_path.isAbsolute)(spec)) return spec;
|
|
101
|
+
if (spec.startsWith("@") || !spec.startsWith(".")) return (0, node_path.resolve)(root, "node_modules", spec);
|
|
102
|
+
return (0, node_path.resolve)(root, spec);
|
|
103
|
+
}
|
|
104
|
+
async function findDirectoryFont(dir, face) {
|
|
105
|
+
const filesDir = (0, node_fs.existsSync)((0, node_path.join)(dir, "files")) ? (0, node_path.join)(dir, "files") : dir;
|
|
106
|
+
const names = (await (0, node_fs_promises.readdir)(filesDir)).filter((name) => /\.(woff2|woff|ttf|otf)$/i.test(name));
|
|
107
|
+
const weight = String(face.weight);
|
|
108
|
+
const wantItalic = face.style === "italic";
|
|
109
|
+
const match = names.find((name) => {
|
|
110
|
+
const lower = name.toLowerCase();
|
|
111
|
+
const hasWeight = lower.includes(weight);
|
|
112
|
+
const italic = lower.includes("italic");
|
|
113
|
+
const subset = face.subset === "all" || lower.includes(face.subset.toLowerCase());
|
|
114
|
+
return hasWeight && subset && italic === wantItalic;
|
|
115
|
+
});
|
|
116
|
+
const fallback = names[0];
|
|
117
|
+
const chosen = match ?? (names.length === 1 ? fallback : void 0);
|
|
118
|
+
if (!chosen) throw new Error(`Theme font "${face.family}" has no ${face.weight} ${face.style} ${face.subset} file in ${filesDir}.`);
|
|
119
|
+
return (0, node_path.join)(filesDir, chosen);
|
|
120
|
+
}
|
|
121
|
+
async function acquireGoogleFace(face, cacheDir, fetchFn) {
|
|
122
|
+
const parsed = parseGoogleCss(await cachedText(googleCssUrl(face), cacheDir, fetchFn, ".css")).find((entry) => entry.weight === face.weight && entry.style === face.style && (entry.subset === face.subset || !entry.subset));
|
|
123
|
+
if (!parsed) throw new Error(`Google Fonts CSS for "${face.family}" has no ${face.weight} ${face.style} ${face.subset} face.`);
|
|
124
|
+
const bytes = await cachedBytes(parsed.url, cacheDir, fetchFn, ".woff2");
|
|
125
|
+
return {
|
|
126
|
+
...face,
|
|
127
|
+
bytes,
|
|
128
|
+
unicodeRange: face.unicodeRange ?? parsed.unicodeRange
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function googleCssUrl(face) {
|
|
132
|
+
const italic = face.style === "italic";
|
|
133
|
+
const axis = italic ? "ital,wght" : "wght";
|
|
134
|
+
const spec = italic ? `1,${face.weight}` : `${face.weight}`;
|
|
135
|
+
const family = `${face.family.replace(/ /g, "+")}:${axis}@${spec}`;
|
|
136
|
+
return `${GOOGLE_CSS}?family=${family}&display=${encodeURIComponent(face.display)}`;
|
|
137
|
+
}
|
|
138
|
+
function parseGoogleCss(css) {
|
|
139
|
+
const faces = [];
|
|
140
|
+
const blocks = css.matchAll(/\/\*\s*([a-z0-9-]+)\s*\*\/\s*@font-face\s*\{([^}]+)\}/gi);
|
|
141
|
+
for (const match of blocks) {
|
|
142
|
+
const parsed = parseGoogleBlock(match[2] ?? "", match[1]?.toLowerCase() ?? "");
|
|
143
|
+
if (parsed) faces.push(parsed);
|
|
144
|
+
}
|
|
145
|
+
if (faces.length === 0) for (const match of css.matchAll(/@font-face\s*\{([^}]+)\}/gi)) {
|
|
146
|
+
const parsed = parseGoogleBlock(match[1] ?? "", "");
|
|
147
|
+
if (parsed) faces.push(parsed);
|
|
148
|
+
}
|
|
149
|
+
return faces;
|
|
150
|
+
}
|
|
151
|
+
function parseGoogleBlock(body, subset) {
|
|
152
|
+
const url = body.match(/url\((['"]?)(https?:\/\/[^'")]+)\1\)/)?.[2];
|
|
153
|
+
if (!url || !isAllowedFontUrl(url)) return;
|
|
154
|
+
return {
|
|
155
|
+
subset,
|
|
156
|
+
weight: Number(body.match(/font-weight:\s*(\d+)/i)?.[1] ?? 400),
|
|
157
|
+
style: /font-style:\s*italic/i.test(body) ? "italic" : "normal",
|
|
158
|
+
url,
|
|
159
|
+
unicodeRange: body.match(/unicode-range:\s*([^;]+)/i)?.[1]?.trim()
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
function isAllowedFontUrl(url) {
|
|
61
163
|
try {
|
|
62
|
-
|
|
63
|
-
return
|
|
164
|
+
const parsed = new URL(url);
|
|
165
|
+
return parsed.protocol === "https:" && ALLOWED_HOSTS.has(parsed.hostname);
|
|
64
166
|
} catch {
|
|
65
|
-
|
|
66
|
-
throw new Error("[ox-content] @ox-content/napi is required. Please ensure the NAPI module is built.");
|
|
167
|
+
return false;
|
|
67
168
|
}
|
|
68
169
|
}
|
|
170
|
+
async function cachedText(url, cacheDir, fetchFn, ext) {
|
|
171
|
+
const bytes = await cachedBytes(url, cacheDir, fetchFn, ext);
|
|
172
|
+
return new TextDecoder().decode(bytes);
|
|
173
|
+
}
|
|
174
|
+
async function cachedBytes(url, cacheDir, fetchFn, ext) {
|
|
175
|
+
if (!isAllowedFontUrl(url)) throw new Error(`Refusing to download font from ${url}.`);
|
|
176
|
+
await (0, node_fs_promises.mkdir)(cacheDir, { recursive: true });
|
|
177
|
+
const dest = (0, node_path.join)(cacheDir, `${(0, node_crypto.createHash)("sha256").update(url).digest("hex").slice(0, 16)}${ext}`);
|
|
178
|
+
if ((0, node_fs.existsSync)(dest)) return (0, node_fs_promises.readFile)(dest);
|
|
179
|
+
const response = await fetchFn(url, { headers: { "User-Agent": GOOGLE_UA } });
|
|
180
|
+
if (!response.ok) throw new Error(`Failed to download ${url}: ${response.status}`);
|
|
181
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
182
|
+
await (0, node_fs_promises.writeFile)(dest, bytes);
|
|
183
|
+
return bytes;
|
|
184
|
+
}
|
|
185
|
+
//#endregion
|
|
186
|
+
//#region src/theme-fonts.ts
|
|
187
|
+
/**
|
|
188
|
+
* Opt-in web-font objects for `theme.fonts`, plus SSG self-host emission.
|
|
189
|
+
*
|
|
190
|
+
* NAPI still receives flattened CSS stacks (`JsThemeFonts`). File acquisition
|
|
191
|
+
* and `@font-face` generation stay in TypeScript so other PRs can keep landing
|
|
192
|
+
* NAPI theme-type changes independently.
|
|
193
|
+
*/
|
|
194
|
+
const FONT_ASSET_DIR = "__ox_fonts__";
|
|
195
|
+
const FONT_CSS_NAME = "fonts.css";
|
|
196
|
+
const NAMED_FONT_PATTERN = /^[a-z][a-z0-9-]*$/;
|
|
197
|
+
const GENERIC_FOR = {
|
|
198
|
+
sans: "sans-serif",
|
|
199
|
+
mono: "monospace",
|
|
200
|
+
named: "sans-serif"
|
|
201
|
+
};
|
|
202
|
+
function isThemeWebFont(value) {
|
|
203
|
+
return typeof value === "object" && value !== null && typeof value.family === "string";
|
|
204
|
+
}
|
|
205
|
+
/** CSS `font-family` identifier; quotes names that are not a single ident. */
|
|
206
|
+
function cssFamilyName(family) {
|
|
207
|
+
const trimmed = family.trim();
|
|
208
|
+
if (trimmed.startsWith("\"") && trimmed.endsWith("\"") || trimmed.startsWith("'") && trimmed.endsWith("'")) return trimmed;
|
|
209
|
+
if (/^[a-zA-Z_-][\w-]*$/.test(trimmed)) return trimmed;
|
|
210
|
+
return `"${trimmed.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`;
|
|
211
|
+
}
|
|
212
|
+
function flattenThemeFont(value, generic) {
|
|
213
|
+
if (value === void 0) return;
|
|
214
|
+
if (typeof value === "string") return value;
|
|
215
|
+
const fallbacks = value.fallbacks?.length ? value.fallbacks.join(", ") : generic;
|
|
216
|
+
return `${cssFamilyName(value.family)}, ${fallbacks}`;
|
|
217
|
+
}
|
|
218
|
+
/** Flatten object fonts so `JsThemeFonts` stays `{ sans?: string; mono?: string }`. */
|
|
219
|
+
function flattenThemeFonts(fonts) {
|
|
220
|
+
const sans = flattenThemeFont(fonts.sans, GENERIC_FOR.sans);
|
|
221
|
+
const mono = flattenThemeFont(fonts.mono, GENERIC_FOR.mono);
|
|
222
|
+
if (!sans && !mono) return;
|
|
223
|
+
return {
|
|
224
|
+
sans,
|
|
225
|
+
mono
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
function namedFontToken(name) {
|
|
229
|
+
if (!NAMED_FONT_PATTERN.test(name)) throw new Error(`Invalid theme font name: ${JSON.stringify(name)}. Named fonts are lowercase kebab-case (e.g. "code").`);
|
|
230
|
+
return name;
|
|
231
|
+
}
|
|
232
|
+
/** Extra `--octc-font-*` variables for `fonts.named`. Roles stay in Rust theme CSS. */
|
|
233
|
+
function namedFontVarsCss(fonts) {
|
|
234
|
+
const entries = Object.entries(fonts.named ?? {});
|
|
235
|
+
if (entries.length === 0) return "";
|
|
236
|
+
return `:root {\n${entries.map(([name, value]) => {
|
|
237
|
+
const stack = flattenThemeFont(value, GENERIC_FOR.named);
|
|
238
|
+
return ` --octc-font-${namedFontToken(name)}: ${stack};`;
|
|
239
|
+
}).join("\n")}\n}`;
|
|
240
|
+
}
|
|
241
|
+
function normalizeBasePath(base) {
|
|
242
|
+
if (!base || base === "/") return "/";
|
|
243
|
+
return base.endsWith("/") ? base : `${base}/`;
|
|
244
|
+
}
|
|
245
|
+
function plannedFontFileName(family, weight, style, subset, extension) {
|
|
246
|
+
const ext = extension.startsWith(".") ? extension : `.${extension}`;
|
|
247
|
+
return `${slugify(family)}-${weight}-${style}-${slugify(subset)}${ext}`;
|
|
248
|
+
}
|
|
249
|
+
function plannedFontExtension(font) {
|
|
250
|
+
if (font.provider === "local" && font.path && /\.\w+$/.test(font.path) && !font.path.endsWith("/")) return font.path.match(/(\.\w+)$/)?.[1] ?? ".woff2";
|
|
251
|
+
return ".woff2";
|
|
252
|
+
}
|
|
253
|
+
function planSelfHostedFaces(fonts) {
|
|
254
|
+
const faces = [];
|
|
255
|
+
for (const value of themeFontValues(fonts)) {
|
|
256
|
+
if (!isThemeWebFont(value) || !value.selfHost) continue;
|
|
257
|
+
const font = normalizeWebFont(value);
|
|
258
|
+
const extension = plannedFontExtension(font);
|
|
259
|
+
for (const weight of font.weights) for (const style of font.styles) for (const subset of font.subsets) faces.push({
|
|
260
|
+
family: font.family,
|
|
261
|
+
weight,
|
|
262
|
+
style,
|
|
263
|
+
subset,
|
|
264
|
+
display: font.display,
|
|
265
|
+
preload: shouldPreload(font.preload, weight),
|
|
266
|
+
provider: font.provider,
|
|
267
|
+
path: font.path,
|
|
268
|
+
fileName: plannedFontFileName(font.family, weight, style, subset, extension),
|
|
269
|
+
unicodeRange: font.unicodeRange
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
const unique = /* @__PURE__ */ new Map();
|
|
273
|
+
for (const face of faces) {
|
|
274
|
+
const existing = unique.get(face.fileName);
|
|
275
|
+
if (existing) existing.preload ||= face.preload;
|
|
276
|
+
else unique.set(face.fileName, face);
|
|
277
|
+
}
|
|
278
|
+
return [...unique.values()];
|
|
279
|
+
}
|
|
280
|
+
function themeFontHeadHtml(fonts, base) {
|
|
281
|
+
const faces = planSelfHostedFaces(fonts);
|
|
282
|
+
if (faces.length === 0) return "";
|
|
283
|
+
const root = normalizeBasePath(base);
|
|
284
|
+
const tags = [`<link rel="stylesheet" href="${root}${FONT_ASSET_DIR}/${FONT_CSS_NAME}">`];
|
|
285
|
+
for (const face of faces) {
|
|
286
|
+
if (!face.preload) continue;
|
|
287
|
+
tags.push(`<link rel="preload" href="${root}${FONT_ASSET_DIR}/${face.fileName}" as="font" type="${fontMime(face.fileName)}" crossorigin>`);
|
|
288
|
+
}
|
|
289
|
+
return tags.join("\n");
|
|
290
|
+
}
|
|
291
|
+
function withSelfHostedFontHead(embed, fonts, base) {
|
|
292
|
+
const extra = themeFontHeadHtml(fonts, base);
|
|
293
|
+
const keys = Object.keys(embed);
|
|
294
|
+
if (!extra && keys.length === 0) return;
|
|
295
|
+
if (!extra) return embed;
|
|
296
|
+
return {
|
|
297
|
+
...embed,
|
|
298
|
+
head: embed.head ? `${extra}\n${embed.head}` : extra
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
/** Copy self-hosted faces into `outDir` and write `@font-face` CSS. */
|
|
302
|
+
async function writeSelfHostedThemeFonts(options) {
|
|
303
|
+
const faces = planSelfHostedFaces(options.fonts);
|
|
304
|
+
if (faces.length === 0) return [];
|
|
305
|
+
const acquired = await acquireSelfHostedFaces(faces, options);
|
|
306
|
+
const destDir = (0, node_path.join)(options.outDir, FONT_ASSET_DIR);
|
|
307
|
+
await (0, node_fs_promises.mkdir)(destDir, { recursive: true });
|
|
308
|
+
const written = [];
|
|
309
|
+
for (const face of acquired) {
|
|
310
|
+
const dest = (0, node_path.join)(destDir, face.fileName);
|
|
311
|
+
await (0, node_fs_promises.writeFile)(dest, face.bytes);
|
|
312
|
+
written.push(dest);
|
|
313
|
+
}
|
|
314
|
+
const cssPath = (0, node_path.join)(destDir, FONT_CSS_NAME);
|
|
315
|
+
await (0, node_fs_promises.writeFile)(cssPath, renderFontFaceCss(acquired), "utf8");
|
|
316
|
+
written.push(cssPath);
|
|
317
|
+
return written;
|
|
318
|
+
}
|
|
319
|
+
function themeFontValues(fonts) {
|
|
320
|
+
return [
|
|
321
|
+
fonts.sans,
|
|
322
|
+
fonts.mono,
|
|
323
|
+
...Object.values(fonts.named ?? {})
|
|
324
|
+
].filter((value) => value !== void 0);
|
|
325
|
+
}
|
|
326
|
+
function normalizeWebFont(font) {
|
|
327
|
+
const provider = font.provider ?? (font.path ? "local" : "google");
|
|
328
|
+
if (provider === "local" && !font.path) throw new Error(`Theme font "${font.family}" uses provider "local" but has no path.`);
|
|
329
|
+
return {
|
|
330
|
+
...font,
|
|
331
|
+
family: font.family.trim(),
|
|
332
|
+
provider,
|
|
333
|
+
weights: font.weights?.length ? font.weights : [400],
|
|
334
|
+
styles: font.styles?.length ? font.styles : ["normal"],
|
|
335
|
+
subsets: font.subsets?.length ? font.subsets : ["latin"],
|
|
336
|
+
display: font.display ?? "swap"
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
function shouldPreload(preload, weight) {
|
|
340
|
+
if (preload === true) return true;
|
|
341
|
+
return Array.isArray(preload) && preload.includes(weight);
|
|
342
|
+
}
|
|
343
|
+
function slugify(value) {
|
|
344
|
+
return value.trim().toLowerCase().replace(/['"]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "font";
|
|
345
|
+
}
|
|
346
|
+
//#endregion
|
|
347
|
+
//#region src/icons-css.ts
|
|
348
|
+
/**
|
|
349
|
+
* Resolve Iconify JSON collections and emit CSS-mask rules.
|
|
350
|
+
*
|
|
351
|
+
* Collections come from installed `@iconify-json/*` or `@iconify/json`.
|
|
352
|
+
* Tests supply fixture JSON under the project `root` — no network.
|
|
353
|
+
*/
|
|
354
|
+
function iconCssSelector(prefix, name) {
|
|
355
|
+
return `.icon-\\[${prefix}--${name}\\]`;
|
|
356
|
+
}
|
|
357
|
+
function resolveIconCollectionPath(prefix, root) {
|
|
358
|
+
const files = [(0, node_path.join)(root, "node_modules", "@iconify-json", prefix, "icons.json"), (0, node_path.join)(root, "node_modules", "@iconify", "json", "json", `${prefix}.json`)];
|
|
359
|
+
for (const file of files) if ((0, node_fs.existsSync)(file)) return file;
|
|
360
|
+
return resolveViaNode(prefix, root);
|
|
361
|
+
}
|
|
362
|
+
function resolveViaNode(prefix, root) {
|
|
363
|
+
try {
|
|
364
|
+
return (0, node_module.createRequire)((0, node_path.join)(root, "package.json")).resolve(`@iconify-json/${prefix}/icons.json`);
|
|
365
|
+
} catch {
|
|
366
|
+
try {
|
|
367
|
+
return (0, node_module.createRequire)((0, node_path.join)(root, "package.json")).resolve(`@iconify/json/json/${prefix}.json`);
|
|
368
|
+
} catch {
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
async function loadIconCollection(prefix, root) {
|
|
374
|
+
const path = resolveIconCollectionPath(prefix, root);
|
|
375
|
+
if (!path) return;
|
|
376
|
+
const raw = await (0, node_fs_promises.readFile)(path, "utf8");
|
|
377
|
+
return JSON.parse(raw);
|
|
378
|
+
}
|
|
379
|
+
function lookupIcon(collection, name) {
|
|
380
|
+
const fallback = collection.width ?? 16;
|
|
381
|
+
const fallbackH = collection.height ?? fallback;
|
|
382
|
+
const direct = collection.icons[name];
|
|
383
|
+
if (direct) return {
|
|
384
|
+
body: direct.body,
|
|
385
|
+
width: direct.width ?? fallback,
|
|
386
|
+
height: direct.height ?? fallbackH
|
|
387
|
+
};
|
|
388
|
+
const alias = collection.aliases?.[name];
|
|
389
|
+
if (!alias) return;
|
|
390
|
+
const parent = collection.icons[alias.parent];
|
|
391
|
+
if (!parent) return;
|
|
392
|
+
return {
|
|
393
|
+
body: parent.body,
|
|
394
|
+
width: alias.width ?? parent.width ?? fallback,
|
|
395
|
+
height: alias.height ?? parent.height ?? fallbackH
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
function isMulticolorIcon(body) {
|
|
399
|
+
return /(?:fill|stroke)=["'](?!currentColor|none)[^"']+["']/i.test(body);
|
|
400
|
+
}
|
|
401
|
+
function renderIconsCss(icons) {
|
|
402
|
+
return `/* ox-content self-hosted Iconify icons */\n${icons.map(renderOneIconCss).join("\n")}\n`;
|
|
403
|
+
}
|
|
404
|
+
function renderOneIconCss(icon) {
|
|
405
|
+
const selector = iconCssSelector(icon.prefix, icon.name);
|
|
406
|
+
const url = svgToDataUrl(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${icon.width} ${icon.height}">${maskBody(icon)}</svg>`);
|
|
407
|
+
if (icon.multicolor) return `${selector}{display:inline-block;width:1em;height:1em;background-color:transparent;background-image:${url};background-repeat:no-repeat;background-size:100% 100%}`;
|
|
408
|
+
return `${selector}{display:inline-block;width:1em;height:1em;background-color:currentColor;-webkit-mask-image:${url};mask-image:${url};-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%}`;
|
|
409
|
+
}
|
|
410
|
+
function maskBody(icon) {
|
|
411
|
+
return icon.multicolor ? icon.body : icon.body.replace(/currentColor/g, "black");
|
|
412
|
+
}
|
|
413
|
+
function svgToDataUrl(svg) {
|
|
414
|
+
return `url("data:image/svg+xml,${svg.replace(/"/g, "'").replace(/%/g, "%25").replace(/#/g, "%23").replace(/</g, "%3C").replace(/>/g, "%3E").replace(/\s+/g, " ")}")`;
|
|
415
|
+
}
|
|
416
|
+
//#endregion
|
|
417
|
+
//#region src/icons.ts
|
|
418
|
+
/**
|
|
419
|
+
* Opt-in self-hosted Iconify CSS for used and safelisted icons.
|
|
420
|
+
*
|
|
421
|
+
* Collection lookup stays on disk (`@iconify-json/*` / `@iconify/json`).
|
|
422
|
+
* Theme embed injection composes with self-hosted font `<link>` tags.
|
|
423
|
+
*/
|
|
424
|
+
const ICON_ASSET_DIR = "__ox_icons__";
|
|
425
|
+
const ICON_CSS_NAME = "icons.css";
|
|
426
|
+
const URL_SCHEMES = /* @__PURE__ */ new Set([
|
|
427
|
+
"http",
|
|
428
|
+
"https",
|
|
429
|
+
"data",
|
|
430
|
+
"mailto",
|
|
431
|
+
"file",
|
|
432
|
+
"javascript",
|
|
433
|
+
"vscode",
|
|
434
|
+
"tel",
|
|
435
|
+
"blob"
|
|
436
|
+
]);
|
|
437
|
+
const COLON_ICON = /(?<![A-Za-z0-9_-])([a-z][a-z0-9-]*):([a-z0-9][a-z0-9-]*)/gi;
|
|
438
|
+
const CLASS_ICON = /icon-\[([a-z][a-z0-9-]*)--([a-z0-9][a-z0-9-]*)\]/gi;
|
|
439
|
+
const ICON_FIELD = /(?:^|[\s,{])icon\s*:\s*["']([^"']+)["']/g;
|
|
440
|
+
function resolveIconsOptions(value) {
|
|
441
|
+
if (!value) return {
|
|
442
|
+
enabled: false,
|
|
443
|
+
mode: "css-mask",
|
|
444
|
+
syntax: "unocss",
|
|
445
|
+
include: [],
|
|
446
|
+
safelist: []
|
|
447
|
+
};
|
|
448
|
+
if (value === true) return {
|
|
449
|
+
enabled: true,
|
|
450
|
+
mode: "css-mask",
|
|
451
|
+
syntax: "unocss",
|
|
452
|
+
include: [],
|
|
453
|
+
safelist: []
|
|
454
|
+
};
|
|
455
|
+
return {
|
|
456
|
+
enabled: true,
|
|
457
|
+
mode: value.mode ?? "css-mask",
|
|
458
|
+
syntax: value.syntax ?? "unocss",
|
|
459
|
+
include: value.include ?? [],
|
|
460
|
+
safelist: value.safelist ?? []
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
function parseIconName(value) {
|
|
464
|
+
const trimmed = value.trim();
|
|
465
|
+
const classMatch = /^icon-\[(.+)\]$/.exec(trimmed);
|
|
466
|
+
if (classMatch?.[1]) {
|
|
467
|
+
const inner = classMatch[1];
|
|
468
|
+
const sep = inner.indexOf("--");
|
|
469
|
+
if (sep <= 0) return;
|
|
470
|
+
return tokenPair(inner.slice(0, sep), inner.slice(sep + 2));
|
|
471
|
+
}
|
|
472
|
+
const sep = trimmed.indexOf(":");
|
|
473
|
+
if (sep <= 0) return;
|
|
474
|
+
return tokenPair(trimmed.slice(0, sep), trimmed.slice(sep + 1));
|
|
475
|
+
}
|
|
476
|
+
function normalizeIconName(value) {
|
|
477
|
+
const parsed = parseIconName(value);
|
|
478
|
+
return parsed ? `${parsed.prefix}:${parsed.name}` : value;
|
|
479
|
+
}
|
|
480
|
+
function tokenPair(prefix, name) {
|
|
481
|
+
if (!/^[a-z][a-z0-9-]*$/i.test(prefix) || !/^[a-z0-9][a-z0-9-]*$/i.test(name)) return;
|
|
482
|
+
if (URL_SCHEMES.has(prefix.toLowerCase())) return;
|
|
483
|
+
return {
|
|
484
|
+
prefix,
|
|
485
|
+
name
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
function collectIconNamesFromText(text, into = /* @__PURE__ */ new Set()) {
|
|
489
|
+
COLON_ICON.lastIndex = 0;
|
|
490
|
+
for (const match of text.matchAll(COLON_ICON)) addParsed(into, match[1], match[2]);
|
|
491
|
+
CLASS_ICON.lastIndex = 0;
|
|
492
|
+
for (const match of text.matchAll(CLASS_ICON)) addParsed(into, match[1], match[2]);
|
|
493
|
+
return into;
|
|
494
|
+
}
|
|
495
|
+
function collectIconFieldNames(text, into = /* @__PURE__ */ new Set()) {
|
|
496
|
+
ICON_FIELD.lastIndex = 0;
|
|
497
|
+
for (const match of text.matchAll(ICON_FIELD)) {
|
|
498
|
+
const parsed = match[1] ? parseIconName(match[1]) : void 0;
|
|
499
|
+
if (parsed) into.add(`${parsed.prefix}:${parsed.name}`);
|
|
500
|
+
}
|
|
501
|
+
return into;
|
|
502
|
+
}
|
|
503
|
+
function collectThemeIconNames(socialLinks) {
|
|
504
|
+
if (!Array.isArray(socialLinks)) return [];
|
|
505
|
+
const names = [];
|
|
506
|
+
for (const link of socialLinks) {
|
|
507
|
+
if (!link || typeof link !== "object") continue;
|
|
508
|
+
const icon = link.icon;
|
|
509
|
+
if (typeof icon === "string" && parseIconName(icon)) names.push(normalizeIconName(icon));
|
|
510
|
+
}
|
|
511
|
+
return names;
|
|
512
|
+
}
|
|
513
|
+
function addParsed(into, prefix, name) {
|
|
514
|
+
if (!prefix || !name) return;
|
|
515
|
+
const parsed = tokenPair(prefix, name);
|
|
516
|
+
if (parsed) into.add(`${parsed.prefix}:${parsed.name}`);
|
|
517
|
+
}
|
|
518
|
+
function iconStylesheetHref(base) {
|
|
519
|
+
return `${normalizeBasePath(base)}${ICON_ASSET_DIR}/${ICON_CSS_NAME}`;
|
|
520
|
+
}
|
|
521
|
+
function iconStylesheetLink(base) {
|
|
522
|
+
return `<link rel="stylesheet" href="${iconStylesheetHref(base)}">`;
|
|
523
|
+
}
|
|
524
|
+
function withSelfHostedIconHead(embed, enabled, base) {
|
|
525
|
+
if (!enabled) return embed;
|
|
526
|
+
const extra = iconStylesheetLink(base);
|
|
527
|
+
if (!embed) return { head: extra };
|
|
528
|
+
return {
|
|
529
|
+
...embed,
|
|
530
|
+
head: embed.head ? `${extra}\n${embed.head}` : extra
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
/** Copy resolved icon CSS into `outDir`. Missing collections or names become errors. */
|
|
534
|
+
async function writeSelfHostedIcons(input) {
|
|
535
|
+
if (!input.options.enabled) return {
|
|
536
|
+
files: [],
|
|
537
|
+
errors: [],
|
|
538
|
+
names: []
|
|
539
|
+
};
|
|
540
|
+
const names = await collectResolvedIconNames(input);
|
|
541
|
+
const { icons, errors } = await resolveIconBodies(names, input.root);
|
|
542
|
+
const destDir = (0, node_path.join)(input.outDir, ICON_ASSET_DIR);
|
|
543
|
+
await (0, node_fs_promises.mkdir)(destDir, { recursive: true });
|
|
544
|
+
const cssPath = (0, node_path.join)(destDir, ICON_CSS_NAME);
|
|
545
|
+
await (0, node_fs_promises.writeFile)(cssPath, renderIconsCss(icons), "utf8");
|
|
546
|
+
return {
|
|
547
|
+
files: [cssPath],
|
|
548
|
+
errors,
|
|
549
|
+
names
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
async function collectResolvedIconNames(input) {
|
|
553
|
+
const names = /* @__PURE__ */ new Set();
|
|
554
|
+
for (const item of input.options.safelist) addName(names, item);
|
|
555
|
+
for (const item of collectThemeIconNames(input.socialLinks)) names.add(item);
|
|
556
|
+
const { names: includeNames, globs } = partitionInclude(input.options.include);
|
|
557
|
+
for (const item of includeNames) names.add(item);
|
|
558
|
+
for (const pattern of globs) {
|
|
559
|
+
const files = await (0, glob.glob)(pattern, {
|
|
560
|
+
cwd: input.root,
|
|
561
|
+
nodir: true,
|
|
562
|
+
absolute: true,
|
|
563
|
+
ignore: ["**/node_modules/**"]
|
|
564
|
+
});
|
|
565
|
+
for (const file of files) collectIconNamesFromText(await (0, node_fs_promises.readFile)(file, "utf8"), names);
|
|
566
|
+
}
|
|
567
|
+
if (input.srcDir) {
|
|
568
|
+
const files = await (0, glob.glob)("**/*.{md,mdx,markdown}", {
|
|
569
|
+
cwd: input.srcDir,
|
|
570
|
+
nodir: true,
|
|
571
|
+
absolute: true,
|
|
572
|
+
ignore: ["**/node_modules/**"]
|
|
573
|
+
});
|
|
574
|
+
for (const file of files) collectIconFieldNames(await (0, node_fs_promises.readFile)(file, "utf8"), names);
|
|
575
|
+
}
|
|
576
|
+
return [...names].sort();
|
|
577
|
+
}
|
|
578
|
+
function partitionInclude(include) {
|
|
579
|
+
const names = [];
|
|
580
|
+
const globs = [];
|
|
581
|
+
for (const entry of include) if (parseIconName(entry)) names.push(normalizeIconName(entry));
|
|
582
|
+
else globs.push(entry);
|
|
583
|
+
return {
|
|
584
|
+
names,
|
|
585
|
+
globs
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
function addName(into, value) {
|
|
589
|
+
const parsed = parseIconName(value);
|
|
590
|
+
if (parsed) into.add(`${parsed.prefix}:${parsed.name}`);
|
|
591
|
+
}
|
|
592
|
+
async function resolveIconBodies(names, root) {
|
|
593
|
+
const icons = [];
|
|
594
|
+
const errors = [];
|
|
595
|
+
const collections = /* @__PURE__ */ new Map();
|
|
596
|
+
for (const id of names) {
|
|
597
|
+
const parsed = parseIconName(id);
|
|
598
|
+
if (!parsed) continue;
|
|
599
|
+
if (!collections.has(parsed.prefix)) collections.set(parsed.prefix, await loadIconCollection(parsed.prefix, root));
|
|
600
|
+
const collection = collections.get(parsed.prefix);
|
|
601
|
+
if (!collection) {
|
|
602
|
+
errors.push(`[ox-content] icons: missing Iconify collection "${parsed.prefix}". Install @iconify-json/${parsed.prefix} or @iconify/json.`);
|
|
603
|
+
continue;
|
|
604
|
+
}
|
|
605
|
+
const found = lookupIcon(collection, parsed.name);
|
|
606
|
+
if (!found) {
|
|
607
|
+
errors.push(`[ox-content] icons: missing icon "${parsed.prefix}:${parsed.name}" in collection "${parsed.prefix}".`);
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
icons.push({
|
|
611
|
+
prefix: parsed.prefix,
|
|
612
|
+
name: parsed.name,
|
|
613
|
+
body: found.body,
|
|
614
|
+
width: found.width,
|
|
615
|
+
height: found.height,
|
|
616
|
+
multicolor: isMulticolorIcon(found.body)
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
return {
|
|
620
|
+
icons,
|
|
621
|
+
errors
|
|
622
|
+
};
|
|
623
|
+
}
|
|
69
624
|
//#endregion
|
|
70
625
|
//#region src/header-chrome.ts
|
|
71
626
|
/** `false` or omitted stays off. `true` or `{}` enables default flag reading. */
|
|
@@ -117,36 +672,6 @@ function resolveHeaderNavItems(items, locale, defaultLocale) {
|
|
|
117
672
|
}));
|
|
118
673
|
}
|
|
119
674
|
//#endregion
|
|
120
|
-
//#region src/theme-tokens.ts
|
|
121
|
-
const TOKEN_PREFIX = "--octc-";
|
|
122
|
-
const TOKEN_NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
|
|
123
|
-
/**
|
|
124
|
-
* Renders light and dark token records as the three selectors the SSG runtime
|
|
125
|
-
* switches between: an explicit `[data-theme="dark"]` opt-in, the OS
|
|
126
|
-
* `prefers-color-scheme` fallback, and the `:root` base.
|
|
127
|
-
*
|
|
128
|
-
* Emitted after the typed color variables and before the theme's own `css`, so
|
|
129
|
-
* a token can override a typed color and raw `css` can override a token.
|
|
130
|
-
*/
|
|
131
|
-
function tokensToCss(light, dark) {
|
|
132
|
-
const lightBody = declarations(light, " ");
|
|
133
|
-
const darkBody = declarations(dark, " ");
|
|
134
|
-
const blocks = [];
|
|
135
|
-
if (lightBody) blocks.push(`:root {\n${lightBody}\n}`);
|
|
136
|
-
if (darkBody) {
|
|
137
|
-
blocks.push(`[data-theme="dark"] {\n${darkBody}\n}`);
|
|
138
|
-
blocks.push(`@media (prefers-color-scheme: dark) {\n :root:not([data-theme="light"]) {\n${declarations(dark, " ")}\n }\n}`);
|
|
139
|
-
}
|
|
140
|
-
return blocks.join("\n");
|
|
141
|
-
}
|
|
142
|
-
function declarations(tokens, indent) {
|
|
143
|
-
return Object.entries(tokens).filter(([, value]) => value !== void 0 && value !== "").map(([name, value]) => `${indent}${TOKEN_PREFIX}${assertTokenName(name)}: ${value};`).join("\n");
|
|
144
|
-
}
|
|
145
|
-
function assertTokenName(name) {
|
|
146
|
-
if (!TOKEN_NAME_PATTERN.test(name)) throw new Error(`Invalid theme token name: ${JSON.stringify(name)}. Token names are lowercase kebab-case without the "${TOKEN_PREFIX}" prefix (e.g. "surface-glass").`);
|
|
147
|
-
return name;
|
|
148
|
-
}
|
|
149
|
-
//#endregion
|
|
150
675
|
//#region src/theme.ts
|
|
151
676
|
/**
|
|
152
677
|
* Default theme configuration.
|
|
@@ -155,6 +680,7 @@ function assertTokenName(name) {
|
|
|
155
680
|
const defaultTheme = {
|
|
156
681
|
name: "default",
|
|
157
682
|
viewTransitions: true,
|
|
683
|
+
toggleTransition: false,
|
|
158
684
|
aside: false,
|
|
159
685
|
breadcrumbs: false,
|
|
160
686
|
headingPermalink: "hover",
|
|
@@ -296,6 +822,7 @@ function resolveTheme(config) {
|
|
|
296
822
|
return {
|
|
297
823
|
name: merged.name ?? "custom",
|
|
298
824
|
viewTransitions: merged.viewTransitions ?? defaultTheme.viewTransitions ?? true,
|
|
825
|
+
toggleTransition: merged.toggleTransition === "circle" ? "circle" : false,
|
|
299
826
|
aside: merged.aside ?? defaultTheme.aside ?? false,
|
|
300
827
|
breadcrumbs: resolveThemeFlag(merged.breadcrumbs),
|
|
301
828
|
headingPermalink: merged.headingPermalink === "always" ? "always" : "hover",
|
|
@@ -351,10 +878,11 @@ function withDerivedCodeBackgroundTop(theme) {
|
|
|
351
878
|
/**
|
|
352
879
|
* Converts resolved theme to the format expected by Rust NAPI.
|
|
353
880
|
*/
|
|
354
|
-
function themeToNapi(theme, locale) {
|
|
881
|
+
function themeToNapi(theme, locale, base, iconsEnabled = false) {
|
|
355
882
|
const socialLinks = socialLinksToNapi(theme.socialLinks);
|
|
356
883
|
return {
|
|
357
884
|
viewTransitions: theme.viewTransitions,
|
|
885
|
+
toggleTransition: theme.toggleTransition === "circle" ? "circle" : void 0,
|
|
358
886
|
aside: theme.aside,
|
|
359
887
|
breadcrumbs: theme.breadcrumbs,
|
|
360
888
|
headingPermalink: theme.headingPermalink,
|
|
@@ -382,10 +910,7 @@ function themeToNapi(theme, locale) {
|
|
|
382
910
|
codeBackgroundTop: theme.darkColors.codeBackgroundTop,
|
|
383
911
|
codeText: theme.darkColors.codeText
|
|
384
912
|
} : void 0,
|
|
385
|
-
fonts: theme.fonts
|
|
386
|
-
sans: theme.fonts.sans,
|
|
387
|
-
mono: theme.fonts.mono
|
|
388
|
-
} : void 0,
|
|
913
|
+
fonts: flattenThemeFonts(theme.fonts),
|
|
389
914
|
entryPage: theme.entryPage.mode ? { mode: theme.entryPage.mode } : void 0,
|
|
390
915
|
layout: theme.layout.sidebarWidth ? {
|
|
391
916
|
sidebarWidth: theme.layout.sidebarWidth,
|
|
@@ -407,7 +932,7 @@ function themeToNapi(theme, locale) {
|
|
|
407
932
|
copyright: theme.footer.copyright
|
|
408
933
|
} : void 0,
|
|
409
934
|
socialLinks,
|
|
410
|
-
embed:
|
|
935
|
+
embed: withSelfHostedIconHead(withSelfHostedFontHead(theme.embed, theme.fonts, base), iconsEnabled, base),
|
|
411
936
|
css: themeCss(theme) || void 0,
|
|
412
937
|
js: theme.js || void 0
|
|
413
938
|
};
|
|
@@ -417,9 +942,9 @@ function themeToNapi(theme, locale) {
|
|
|
417
942
|
* land after the typed color variables the Rust renderer emits.
|
|
418
943
|
*/
|
|
419
944
|
function themeCss(theme) {
|
|
420
|
-
const
|
|
421
|
-
if (!
|
|
422
|
-
return theme.css ? `${
|
|
945
|
+
const prefix = [require_theme_tokens.renderThemeTokenCss(theme), namedFontVarsCss(theme.fonts)].filter(Boolean).join("\n");
|
|
946
|
+
if (!prefix) return theme.css;
|
|
947
|
+
return theme.css ? `${prefix}\n${theme.css}` : prefix;
|
|
423
948
|
}
|
|
424
949
|
function socialLinksToNapi(links) {
|
|
425
950
|
if (Array.isArray(links)) {
|
|
@@ -694,7 +1219,7 @@ function formatObjectKey(key) {
|
|
|
694
1219
|
* Normalizes VitePress-specific frontmatter into ox-content's entry-page shape.
|
|
695
1220
|
*/
|
|
696
1221
|
function normalizeVitePressFrontmatter(frontmatter) {
|
|
697
|
-
return importNapiModuleSync().normalizeVitePressFrontmatter(frontmatter);
|
|
1222
|
+
return require_napi.importNapiModuleSync().normalizeVitePressFrontmatter(frontmatter);
|
|
698
1223
|
}
|
|
699
1224
|
//#endregion
|
|
700
1225
|
Object.defineProperty(exports, "__esmMin", {
|
|
@@ -757,18 +1282,6 @@ Object.defineProperty(exports, "generateVitePressMigrationConfig", {
|
|
|
757
1282
|
return generateVitePressMigrationConfig;
|
|
758
1283
|
}
|
|
759
1284
|
});
|
|
760
|
-
Object.defineProperty(exports, "importNapiModule", {
|
|
761
|
-
enumerable: true,
|
|
762
|
-
get: function() {
|
|
763
|
-
return importNapiModule;
|
|
764
|
-
}
|
|
765
|
-
});
|
|
766
|
-
Object.defineProperty(exports, "importNapiModuleSync", {
|
|
767
|
-
enumerable: true,
|
|
768
|
-
get: function() {
|
|
769
|
-
return importNapiModuleSync;
|
|
770
|
-
}
|
|
771
|
-
});
|
|
772
1285
|
Object.defineProperty(exports, "mergeThemes", {
|
|
773
1286
|
enumerable: true,
|
|
774
1287
|
get: function() {
|
|
@@ -793,6 +1306,12 @@ Object.defineProperty(exports, "resolveHeaderNavItems", {
|
|
|
793
1306
|
return resolveHeaderNavItems;
|
|
794
1307
|
}
|
|
795
1308
|
});
|
|
1309
|
+
Object.defineProperty(exports, "resolveIconsOptions", {
|
|
1310
|
+
enumerable: true,
|
|
1311
|
+
get: function() {
|
|
1312
|
+
return resolveIconsOptions;
|
|
1313
|
+
}
|
|
1314
|
+
});
|
|
796
1315
|
Object.defineProperty(exports, "resolveLocaleLabel", {
|
|
797
1316
|
enumerable: true,
|
|
798
1317
|
get: function() {
|
|
@@ -817,5 +1336,23 @@ Object.defineProperty(exports, "themeToNapi", {
|
|
|
817
1336
|
return themeToNapi;
|
|
818
1337
|
}
|
|
819
1338
|
});
|
|
1339
|
+
Object.defineProperty(exports, "withSelfHostedIconHead", {
|
|
1340
|
+
enumerable: true,
|
|
1341
|
+
get: function() {
|
|
1342
|
+
return withSelfHostedIconHead;
|
|
1343
|
+
}
|
|
1344
|
+
});
|
|
1345
|
+
Object.defineProperty(exports, "writeSelfHostedIcons", {
|
|
1346
|
+
enumerable: true,
|
|
1347
|
+
get: function() {
|
|
1348
|
+
return writeSelfHostedIcons;
|
|
1349
|
+
}
|
|
1350
|
+
});
|
|
1351
|
+
Object.defineProperty(exports, "writeSelfHostedThemeFonts", {
|
|
1352
|
+
enumerable: true,
|
|
1353
|
+
get: function() {
|
|
1354
|
+
return writeSelfHostedThemeFonts;
|
|
1355
|
+
}
|
|
1356
|
+
});
|
|
820
1357
|
|
|
821
1358
|
//# sourceMappingURL=vitepress.cjs.map
|