@xingwangzhe/stalux 1.35.2 → 1.35.5
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/LICENSE +4 -0
- package/README.md +6 -0
- package/package.json +4 -3
- package/public/fonts/GUST-FONT-LICENSE.txt +30 -0
- package/public/fonts/Google-Sans-Code-OFL.txt +93 -0
- package/public/fonts/LPPL-1.3c.txt +416 -0
- package/public/fonts/LXGW-WenKai-OFL.txt +94 -0
- package/public/fonts/StaluxMathAux-OFL.txt +93 -0
- package/public/fonts/StaluxMathAux.woff2 +0 -0
- package/public/fonts/latinmodern-math.woff2 +0 -0
- package/src/index.ts +114 -20
- package/src/internal/font-slices.ts +56 -6
- package/src/internal/html-images.ts +23 -2
- package/src/internal/image-dimensions.ts +60 -33
- package/src/internal/page-output-cache.ts +78 -0
- package/src/plugins/feature-flags.ts +5 -17
- package/src/styles/generated-prose.css +1 -1
- package/src/styles/math.css +5 -5
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { findHtmlImages, updateImageTag } from "./html-images";
|
|
3
|
+
import { findHtmlImages, type HtmlImage, imageLoadingUpdates, updateImageTag } from "./html-images";
|
|
4
4
|
|
|
5
5
|
export type ImageDimensions = { width: number; height: number };
|
|
6
6
|
|
|
@@ -72,41 +72,68 @@ export function readImageDimensions(data: Buffer): ImageDimensions | undefined {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
/** Add width/height to same-origin emitted images so their space is reserved before download. */
|
|
75
|
-
export async function addLocalImageDimensions(
|
|
76
|
-
|
|
75
|
+
export async function addLocalImageDimensions(
|
|
76
|
+
html: string,
|
|
77
|
+
outputDir: string,
|
|
78
|
+
options: {
|
|
79
|
+
images?: HtmlImage[];
|
|
80
|
+
cache?: Map<string, Promise<ImageDimensions | undefined>>;
|
|
81
|
+
loadingPolicy?: boolean;
|
|
82
|
+
dependencies?: Set<string>;
|
|
83
|
+
} = {},
|
|
84
|
+
): Promise<string> {
|
|
85
|
+
const dimensionsCache =
|
|
86
|
+
options.cache ?? new Map<string, Promise<ImageDimensions | undefined>>();
|
|
77
87
|
const root = path.resolve(outputDir);
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
88
|
+
const images = options.images ?? findHtmlImages(html);
|
|
89
|
+
const loading = options.loadingPolicy ? imageLoadingUpdates(images) : new Map();
|
|
90
|
+
const replacements: Array<{ start: number; end: number; tag: string }> = [];
|
|
91
|
+
for (const image of images) {
|
|
92
|
+
const updates: Record<string, string> = { ...loading.get(image) };
|
|
82
93
|
const src = image.attributes.get("src");
|
|
83
|
-
if (!
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
94
|
+
if (src && !(image.attributes.has("width") && image.attributes.has("height"))) {
|
|
95
|
+
let filePath: string | undefined;
|
|
96
|
+
try {
|
|
97
|
+
const url = new URL(src, "https://stalux.invalid");
|
|
98
|
+
if (url.origin === "https://stalux.invalid") {
|
|
99
|
+
const candidate = path.resolve(root, `.${decodeURIComponent(url.pathname)}`);
|
|
100
|
+
if (candidate === root || candidate.startsWith(`${root}${path.sep}`))
|
|
101
|
+
filePath = candidate;
|
|
102
|
+
}
|
|
103
|
+
} catch {
|
|
104
|
+
// Invalid image URLs still receive the loading policy.
|
|
105
|
+
}
|
|
106
|
+
if (filePath) {
|
|
107
|
+
options.dependencies?.add(filePath);
|
|
108
|
+
let dimensions = dimensionsCache.get(filePath);
|
|
109
|
+
if (!dimensions) {
|
|
110
|
+
dimensions = readFile(filePath)
|
|
111
|
+
.then(readImageDimensions)
|
|
112
|
+
.catch(() => undefined);
|
|
113
|
+
dimensionsCache.set(filePath, dimensions);
|
|
114
|
+
}
|
|
115
|
+
const size = await dimensions;
|
|
116
|
+
if (size && size.width > 0 && size.height > 0) {
|
|
117
|
+
if (!image.attributes.has("width")) updates.width = String(size.width);
|
|
118
|
+
if (!image.attributes.has("height")) updates.height = String(size.height);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
92
121
|
}
|
|
93
|
-
if (
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
.catch(() => undefined);
|
|
100
|
-
dimensionsCache.set(filePath, dimensions);
|
|
122
|
+
if (Object.keys(updates).length) {
|
|
123
|
+
replacements.push({
|
|
124
|
+
start: image.start,
|
|
125
|
+
end: image.end,
|
|
126
|
+
tag: updateImageTag(image, updates),
|
|
127
|
+
});
|
|
101
128
|
}
|
|
102
|
-
const size = await dimensions;
|
|
103
|
-
if (!size || size.width <= 0 || size.height <= 0) continue;
|
|
104
|
-
|
|
105
|
-
const updates: Record<string, string> = {};
|
|
106
|
-
if (!image.attributes.has("width")) updates.width = String(size.width);
|
|
107
|
-
if (!image.attributes.has("height")) updates.height = String(size.height);
|
|
108
|
-
const updated = updateImageTag(image, updates);
|
|
109
|
-
html = html.slice(0, image.start) + updated + html.slice(image.end);
|
|
110
129
|
}
|
|
111
|
-
return html;
|
|
130
|
+
if (!replacements.length) return html;
|
|
131
|
+
const parts: string[] = [];
|
|
132
|
+
let offset = 0;
|
|
133
|
+
for (const replacement of replacements) {
|
|
134
|
+
parts.push(html.slice(offset, replacement.start), replacement.tag);
|
|
135
|
+
offset = replacement.end;
|
|
136
|
+
}
|
|
137
|
+
parts.push(html.slice(offset));
|
|
138
|
+
return parts.join("");
|
|
112
139
|
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { promises as fs } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
export interface CachedPageOutput {
|
|
6
|
+
html: string;
|
|
7
|
+
font?: { filename: string; sourcePath: string };
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const digest = (value: string | Buffer) => createHash("sha256").update(value).digest("hex");
|
|
11
|
+
|
|
12
|
+
/** Content hashes include dependencies even when their public URL has not changed. */
|
|
13
|
+
export async function createPageOutputCache(
|
|
14
|
+
directory: string,
|
|
15
|
+
dependencies: readonly string[],
|
|
16
|
+
font?: Buffer,
|
|
17
|
+
assets: readonly string[] = [],
|
|
18
|
+
) {
|
|
19
|
+
const hash = createHash("sha256").update("stalux-page-output-v1\0");
|
|
20
|
+
if (font) hash.update(font);
|
|
21
|
+
for (const dependency of [...dependencies].sort()) {
|
|
22
|
+
hash.update(dependency).update("\0");
|
|
23
|
+
hash.update(await fs.readFile(dependency)).update("\0");
|
|
24
|
+
}
|
|
25
|
+
const namespace = path.join(directory, hash.digest("hex"));
|
|
26
|
+
await fs.mkdir(namespace, { recursive: true });
|
|
27
|
+
const assetDigests = new Map<string, string>();
|
|
28
|
+
for (const asset of assets) assetDigests.set(asset, digest(await fs.readFile(asset)));
|
|
29
|
+
const key = (entry: string, html: string) => digest(`${entry}\0${html}`);
|
|
30
|
+
return {
|
|
31
|
+
async get(entry: string, html: string): Promise<CachedPageOutput | undefined> {
|
|
32
|
+
try {
|
|
33
|
+
const record = JSON.parse(
|
|
34
|
+
await fs.readFile(path.join(namespace, `${key(entry, html)}.json`), "utf8"),
|
|
35
|
+
);
|
|
36
|
+
if (typeof record.html !== "string" || digest(record.html) !== record.digest)
|
|
37
|
+
return undefined;
|
|
38
|
+
if (!Array.isArray(record.assets)) return undefined;
|
|
39
|
+
for (const [asset, expected] of record.assets) {
|
|
40
|
+
if ((assetDigests.get(asset) ?? null) !== expected) return undefined;
|
|
41
|
+
}
|
|
42
|
+
if (record.font) {
|
|
43
|
+
if (
|
|
44
|
+
!/^page-[a-f\d]+\.woff2$/u.test(record.font.filename) ||
|
|
45
|
+
typeof record.font.sourcePath !== "string" ||
|
|
46
|
+
!(await fs.stat(record.font.sourcePath)).isFile()
|
|
47
|
+
)
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
return { html: record.html, font: record.font };
|
|
51
|
+
} catch {
|
|
52
|
+
// Missing, truncated, or invalid cache entries are rebuilt from source.
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
async set(
|
|
57
|
+
entry: string,
|
|
58
|
+
html: string,
|
|
59
|
+
output: CachedPageOutput,
|
|
60
|
+
referencedAssets: readonly string[] = [],
|
|
61
|
+
): Promise<void> {
|
|
62
|
+
const target = path.join(namespace, `${key(entry, html)}.json`);
|
|
63
|
+
const temporary = `${target}.${process.pid}.tmp`;
|
|
64
|
+
await fs.writeFile(
|
|
65
|
+
temporary,
|
|
66
|
+
JSON.stringify({
|
|
67
|
+
...output,
|
|
68
|
+
digest: digest(output.html),
|
|
69
|
+
assets: referencedAssets.map((asset) => [
|
|
70
|
+
asset,
|
|
71
|
+
assetDigests.get(asset) ?? null,
|
|
72
|
+
]),
|
|
73
|
+
}),
|
|
74
|
+
);
|
|
75
|
+
await fs.rename(temporary, target);
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
@@ -16,7 +16,7 @@ import type { HastVisitorContext, MdastNode, MdastVisitorContext } from "satteri
|
|
|
16
16
|
* - 数学公式由 satteri-temml 插件直接输出 MathML,无需额外标记
|
|
17
17
|
*/
|
|
18
18
|
import { defineHastPlugin, defineMdastPlugin } from "satteri";
|
|
19
|
-
import { findHtmlImages, updateImageTag } from "../internal/html-images";
|
|
19
|
+
import { findHtmlImages, imageLoadingUpdates, updateImageTag } from "../internal/html-images";
|
|
20
20
|
import { logDetail } from "../utils/diagnostics";
|
|
21
21
|
|
|
22
22
|
declare module "satteri" {
|
|
@@ -247,22 +247,10 @@ export function applyImageLoadingPolicy(
|
|
|
247
247
|
}
|
|
248
248
|
|
|
249
249
|
export function applyHtmlImageLoadingPolicy(html: string): string {
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
const body = image.body as number;
|
|
255
|
-
const first = !firstSeen.has(body);
|
|
256
|
-
firstSeen.add(body);
|
|
257
|
-
const updates: Record<string, string> = {
|
|
258
|
-
decoding: image.attributes.get("decoding") || "async",
|
|
259
|
-
loading: first ? "eager" : "lazy",
|
|
260
|
-
};
|
|
261
|
-
if (first) updates.fetchpriority = "high";
|
|
262
|
-
else if (image.attributes.get("fetchpriority") === "high")
|
|
263
|
-
updates.fetchpriority = "auto";
|
|
264
|
-
return { image, tag: updateImageTag(image, updates) };
|
|
265
|
-
});
|
|
250
|
+
const changes = [...imageLoadingUpdates(findHtmlImages(html))].map(([image, updates]) => ({
|
|
251
|
+
image,
|
|
252
|
+
tag: updateImageTag(image, updates),
|
|
253
|
+
}));
|
|
266
254
|
for (const { image, tag } of changes.reverse())
|
|
267
255
|
html = html.slice(0, image.start) + tag + html.slice(image.end);
|
|
268
256
|
return html;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
@layer theme,base;@layer components{.stalux-prose{font-family:var(--font-body), "Noto Sans SC", "Noto Sans CJK SC", system-ui, -apple-system, sans-serif;font-size:var(--font-size-base);color:var(--white-85p);text-rendering:optimizelegibility;line-height:1.75}.stalux-prose h1,.stalux-prose h2,.stalux-prose h3,.stalux-prose h4,.stalux-prose h5,.stalux-prose h6{color:var(--white-full);margin-top:var(--space-xl);margin-bottom:var(--space-md);font-weight:600;line-height:1.3}.stalux-prose h1{margin-top:2.5rem;margin-bottom:var(--space-lg);border-bottom:3px solid var(--accent-50p);padding-bottom:.6rem;font-size:2.6rem;font-weight:700;line-height:1.25}.stalux-prose h2{border-bottom:2px solid var(--accent-30p);margin-top:2.5rem;padding-bottom:.4rem;font-size:2rem;font-weight:650}.stalux-prose h3{color:var(--white-95p);margin-top:2rem;font-size:1.5rem;line-height:1.4}.stalux-prose h4{color:var(--white-90p);margin-top:1.5rem;margin-bottom:.6rem;font-size:1.25rem;line-height:1.45}.stalux-prose h5{color:var(--white-90p);margin-top:1.5rem;margin-bottom:.5rem;font-size:1.1rem;line-height:1.5}.stalux-prose h6{color:var(--white-80p);margin-top:1.5rem;margin-bottom:.5rem;font-size:1rem;line-height:1.5}.stalux-prose p{margin:0 0 var(--space-lg);line-height:1.75}.stalux-prose strong{color:#ffd966;font-weight:600}.stalux-prose em{color:var(--white-85p);font-style:italic}.stalux-prose strong em,.stalux-prose em strong{color:#ffd966}.stalux-prose del{color:var(--white-60p)}.stalux-prose mark{color:inherit;background-color:#ffd96640;border-radius:2px;padding:.1em .2em}.stalux-prose kbd{font-family:var(--font-code), "JetBrains Mono", "Fira Code", "Consolas", "Courier New", monospace;background-color:var(--white-10p);border:1px solid var(--white-20p);color:var(--white-85p);border-radius:4px;padding:.15em .4em;font-size:.85em}.stalux-prose sup,.stalux-prose sub{font-size:.8em}.stalux-prose a{color:var(--accent-80p);transition:color var(--transition-fast), border-bottom-color var(--transition-fast);overflow-wrap:anywhere;border-bottom:2px solid #0000;text-decoration:none}.stalux-prose a:focus-visible{color:var(--accent-90p);border-bottom-color:var(--accent-60p)}@media (hover:hover) and (pointer:fine){.stalux-prose a:hover{color:var(--accent-90p);border-bottom-color:var(--accent-60p)}}.stalux-prose code{font-family:var(--font-code), "JetBrains Mono", "Fira Code", "Consolas", "Courier New", monospace;color:#ff92d0;background-color:#ff79c626;border:1px solid #ff79c640;border-radius:4px;padding:.2em .4em;font-size:.88em}.stalux-prose pre{background:var(--black-30p);color:var(--white-85p);border-left:4px solid var(--accent-60p);margin:var(--space-xl) 0;font-size:.92em;line-height:1.65;font-family:var(--font-code), "JetBrains Mono", "Fira Code", "Consolas", "Courier New", monospace;box-shadow:var(--shadow-md);border-radius:8px;padding:1.2rem 1.5rem;overflow-x:auto}.stalux-prose pre code{color:inherit;font-size:inherit;background:0 0;border:none;padding:0;font-family:inherit}.expressive-code pre,.expressive-code code{all:revert}.stalux-prose ul,.stalux-prose ol{margin:0 0 var(--space-lg);padding-left:1.75rem}.stalux-prose li{color:var(--white-85p);margin:.4rem 0;line-height:1.75}.stalux-prose ul li{list-style:outside}.stalux-prose ul li ul li{list-style:circle}.stalux-prose ul li ul li ul li{list-style:square}.stalux-prose ol li{list-style:decimal}.stalux-prose ol li ol li{list-style:lower-alpha}.stalux-prose li>ul,.stalux-prose li>ol{margin-top:.3rem;margin-bottom:.3rem}.stalux-prose blockquote{margin:var(--space-xl) 0;border-left:4px solid var(--accent-60p);background:var(--surface-glass);border-top:1px solid var(--white-10p);border-right:1px solid var(--white-10p);border-bottom:1px solid var(--white-10p);color:var(--white-85p);border-radius:12px;padding:1rem 1.5rem;box-shadow:0 4px 16px #0002}.stalux-prose blockquote p{margin:0;line-height:1.7}.stalux-prose blockquote p+p{margin-top:.8rem}.stalux-prose blockquote strong{color:#ffd966}.stalux-prose blockquote code{font-size:.85em}.stalux-prose table{border-collapse:collapse;width:100%;margin:var(--space-xl) 0;box-shadow:var(--shadow-md);border-radius:8px;display:block;overflow:auto hidden}.stalux-prose th,.stalux-prose td{text-align:left;border-bottom:1px solid var(--white-10p);padding:.75rem 1rem;line-height:1.6}.stalux-prose th{white-space:nowrap;background:var(--accent-30p);color:var(--white-full);font-size:.95em;font-weight:600}.stalux-prose td{color:var(--white-85p)}.stalux-prose tr:nth-child(2n) td{background-color:var(--white-05p)}@media (hover:hover) and (pointer:fine){.stalux-prose tr:hover td{background-color:var(--black-10p)}}.stalux-prose table code{font-size:.85em}.stalux-prose img{max-width:100%;height:auto;margin:var(--space-xl) auto;box-shadow:var(--shadow-lg);transition:transform var(--transition-normal), box-shadow var(--transition-normal);border-radius:8px;display:block}@media (hover:hover) and (pointer:fine){.stalux-prose img:hover{box-shadow:var(--shadow-xl);transform:none}}.stalux-prose embed,.stalux-prose video{max-width:100%;height:auto;margin:var(--space-xl) auto;border-radius:8px;display:block}.stalux-prose hr{margin:var(--space-2xl) 0;background:linear-gradient(90deg, transparent, var(--white-15p) 20%, var(--accent-40p) 50%, var(--white-15p) 80%, transparent);border:none;height:1.5px}.stalux-prose details{margin:var(--space-lg) 0;background-color:var(--white-05p);border:1px solid var(--white-10p);transition:border-color var(--transition-fast);border-radius:6px;padding:.8rem 1rem}@media (hover:hover) and (pointer:fine){.stalux-prose details:hover{border-color:var(--white-20p)}}.stalux-prose summary{cursor:pointer;color:var(--white-90p);font-weight:600}.stalux-prose details[open] summary{border-bottom:1px solid var(--white-10p);margin-bottom:.8rem;padding-bottom:.6rem}.stalux-prose figure{text-align:center;margin:var(--space-xl) 0}.stalux-prose figcaption{color:var(--white-70p);margin-top:.5rem;font-size:.9em;font-style:italic}.stalux-prose .footnotes{color:var(--white-75p);border-top:1px solid var(--white-15p);margin-top:var(--space-2xl);padding-top:var(--space-lg);font-size:.88em}.stalux-prose .footnotes ol{margin-left:1rem;padding-left:.5rem}.stalux-prose .footnotes li{margin:.5rem 0;line-height:1.6}.stalux-prose .footnotes a{font-size:.9em}@media (width<=1024px){.stalux-prose{font-size:1.05rem}.stalux-prose h1{font-size:2.2rem}.stalux-prose h2{font-size:1.8rem}.stalux-prose h3{font-size:1.35rem}.stalux-prose h4{font-size:1.15rem}.stalux-prose pre{padding:1rem 1.2rem;font-size:.88em}}@media (width<=768px){.stalux-prose{font-size:1rem;line-height:1.7}.stalux-prose h1{margin-top:1.5rem;font-size:1.8rem}.stalux-prose h2{margin-top:1.5rem;font-size:1.4rem}.stalux-prose h3{margin-top:1.25rem;font-size:1.2rem}.stalux-prose h4{margin-top:1rem;font-size:1.1rem}.stalux-prose h5{font-size:1rem}.stalux-prose h6{font-size:.95rem}.stalux-prose p{margin-bottom:var(--space-md)}.stalux-prose pre{margin:var(--space-lg) 0;border-left-width:3px;padding:.8rem 1rem;font-size:.82em}.stalux-prose table{font-size:.85rem}.stalux-prose th,.stalux-prose td{padding:.5rem .7rem}.stalux-prose blockquote{padding:.8rem 1rem;font-size:.9em}.stalux-prose ul,.stalux-prose ol{padding-left:1.5rem}}.stalux-prose,.stalux-prose>*,.stalux-prose div,.stalux-prose .expressive-code,.stalux-prose .expressive-code .frame{box-sizing:border-box;min-width:0;max-width:100%}.stalux-prose{overflow-wrap:anywhere}.stalux-prose a:after{content:none}.stalux-prose a{text-decoration:underline;text-decoration-color:var(--accent-50p);text-underline-offset:.2em;border-bottom:0;text-decoration-thickness:1px}.stalux-prose a:has(img){text-decoration:none}.stalux-prose pre,.stalux-prose table,.stalux-prose .mermaid,.stalux-prose math{overscroll-behavior-x:contain;max-width:100%;overflow-x:auto}.stalux-prose pre,.stalux-prose pre code{overflow-wrap:normal;word-break:normal}.stalux-prose :is(img,video,iframe,embed,svg,canvas){max-width:100%}.stalux-prose :is(img,video,svg,canvas){height:auto}.stalux-prose iframe{margin:var(--space-xl) auto;border-radius:8px;display:block}.stalux-prose .mermaid svg{max-width:none}.stalux-prose canvas{box-sizing:border-box}.stalux-prose summary{overflow-wrap:anywhere}}@layer utilities;@font-face{font-family:"Temml", serif;src:url(/fonts/Temml.woff2)format("woff2");font-weight:400;font-style:normal}@font-face{font-family:Latin Modern Math;src:url(/fonts/latinmodern-math.woff2)format("woff2");font-display:swap}math{font-size-adjust:none;text-indent:0;text-transform:none;letter-spacing:normal;word-wrap:normal;font-feature-settings:"dtls" off;font-style:normal;font-weight:400;line-height:normal;font-family:"Latin Modern Math", var(--font-body), math;direction:ltr}math *{border-color:currentColor}math.tml-display{width:100%;display:block}.mathscr{font-family:Temml,serif}mfrac>:nth-child(2),msqrt,mover>:first-child{math-shift:compact}.menclose{padding:.5ex 0;display:inline-block;position:relative}.tml-cancelto{background-color:currentColor;padding:.5ex 0;display:inline-block;position:absolute;top:0;left:0;-webkit-mask-image:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><defs><marker id='a' markerHeight='5' markerUnits='strokeWidth' markerWidth='7' orient='auto' refX='7' refY='2.5'><path fill='black' d='m0 0 7 2.5L0 5z'/></marker></defs><line x2='100%25' y1='100%25' stroke='black' stroke-width='.06em' marker-end='url(%23a)' vector-effect='non-scaling-stroke'/></svg>");mask-image:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><defs><marker id='a' markerHeight='5' markerUnits='strokeWidth' markerWidth='7' orient='auto' refX='7' refY='2.5'><path fill='black' d='m0 0 7 2.5L0 5z'/></marker></defs><line x2='100%25' y1='100%25' stroke='black' stroke-width='.06em' marker-end='url(%23a)' vector-effect='non-scaling-stroke'/></svg>");-webkit-mask-position:0 0;mask-position:0 0;-webkit-mask-size:100% 100%;mask-size:100% 100%;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}@-moz-document url-prefix(){math{display:inline}math>mrow{padding:0}mtd,mtable.tml-small mtd{padding-top:0;padding-bottom:0}mtable.tml-jot mtd{padding-top:.2ex;padding-bottom:0}.tml-vec{transform:scale(.75)}.ff-narrow{width:0}.ff-nudge-left{margin-left:-.2em}.ff-squash mtd{height:0;display:block}}@supports not ((-moz-appearance:none)){mo.tml-prime{font-family:Temml,serif}.tml-sml-pad{padding-left:.05em}.tml-med-pad{padding-left:.1em}.tml-lrg-pad{padding-left:.15em}}@supports ((-webkit-backdrop-filter:blur(1px))){.wbk-acc{transform:translateY(.431em)}.wbk-sml{transform:translate(.07em)}.wbk-sml-acc{transform:translate(.07em,.431em)}.wbk-sml-vec{transform:scale(.75)translate(.07em)}.wbk-med{transform:translate(.14em)}.wbk-med-acc{transform:translate(.14em,.431em)}.wbk-med-vec{transform:scale(.75)translate(.14em)}.wbk-lrg{transform:translate(.21em)}.wbk-lrg-acc{transform:translate(.21em,.431em)}.wbk-lrg-vec{transform:scale(.75)translate(.21em)}}@supports (not ((-webkit-backdrop-filter:blur(1px)))) and (not ((-moz-appearance:none))){.chr-sml{transform:translate(.07em)}.chr-sml-vec{transform:scale(.75)translate(.07em)}.chr-med{transform:translate(.14em)}.chr-med-vec{transform:scale(.75)translate(.14em)}.chr-lrg{transform:translate(.21em)}.chr-lrg-vec{transform:scale(.75)translate(.21em)}.tml-shift-left{margin-left:-100%}menclose{padding:.5ex 0;position:relative}.tml-overline{border-top:.065em solid;padding:.1em 0 0}.tml-underline{border-bottom:.065em solid;padding:0 0 .1em}.tml-cancel{background-color:currentColor;width:100%;height:100%;display:inline-block;position:absolute;bottom:0;left:.5px}.upstrike{clip-path:polygon(.05em 100%,0 calc(100% - .05em),calc(100% - .05em) 0,100% .05em)}.downstrike{clip-path:polygon(0 .05em,.05em 0,100% calc(100% - .05em),calc(100% - .05em) 100%)}.sout{clip-path:polygon(0 calc(55% + .0333em),0 calc(55% - .0333em),100% calc(55% - .0333em),100% calc(55% + .0333em))}.tml-xcancel{clip-path:polygon(.05em 0,0 .05em,calc(50% - .05em) 50%,0 calc(100% - .05em),.05em 100%,50% calc(50% + .05em),calc(100% - .05em) 100%,100% calc(100% - .05em),calc(50% + .05em) 50%,100% .05em,calc(100% - .05em) 0%,50% calc(50% - .05em))}.longdiv-top{border-top:.067em solid;padding:.1em .2em .2em .433em}.longdiv-arc{clip-path:inset(0 0 0 .4em);box-sizing:border-box;border:.067em solid;border-radius:70%;width:.7em;position:absolute;top:0;bottom:.1em;left:-.4em;transform:translateY(-.067em)}.menclose{text-align:left;display:inline-block;position:relative}.phasor-bottom{border-bottom:.067em solid;padding:.2em .2em .1em .6em}.phasor-angle{aspect-ratio:.5;clip-path:polygon(.05em 100%,0 calc(100% - .05em),calc(100% - .05em) 0,100% .05em);background-color:currentColor;height:100%;display:inline-block;position:absolute;bottom:-.04em;left:.5px}.tml-fbox{border:1px solid;padding:3pt}.circle-pad{padding:.267em}.textcircle{border:.067em solid;border-radius:50%;position:absolute;inset:0}.actuarial{border-style:solid;border-width:.08em .08em 0 0;margin-right:.03889em;padding:.03889em .03889em 0}.tml-crooked-2{transform:scale(2,1.1)}.tml-crooked-3{transform:scale(3,1.3)}.tml-crooked-4{transform:scale(4,1.4)}.tml-right{text-align:-webkit-right}.tml-left{text-align:-webkit-left}}menclose{-webkit-print-color-adjust:exact;print-color-adjust:exact}.tml-right{text-align:right}.tml-left{text-align:left}.special-fraction{font-family:Times New Roman,Times,STIX TWO,Tinos,serif}math{flex-wrap:wrap;align-items:baseline;display:inline-flex}math>mrow{padding:.5ex 0}mtable.tml-jot mtd{padding-top:.7ex;padding-bottom:.7ex}mtable.tml-small mtd{padding-top:.35ex;padding-bottom:.35ex}.tml-shift-left{margin-left:-200%}.tml-eqn:before{counter-increment:tmlEqnNo;content:"(" counter(tmlEqnNo) ")"}body{counter-reset:tmlEqnNo}
|
|
1
|
+
@layer theme,base;@layer components{.stalux-prose{font-family:var(--font-body), "Noto Sans SC", "Noto Sans CJK SC", system-ui, -apple-system, sans-serif;font-size:var(--font-size-base);color:var(--white-85p);text-rendering:optimizelegibility;line-height:1.75}.stalux-prose h1,.stalux-prose h2,.stalux-prose h3,.stalux-prose h4,.stalux-prose h5,.stalux-prose h6{color:var(--white-full);margin-top:var(--space-xl);margin-bottom:var(--space-md);font-weight:600;line-height:1.3}.stalux-prose h1{margin-top:2.5rem;margin-bottom:var(--space-lg);border-bottom:3px solid var(--accent-50p);padding-bottom:.6rem;font-size:2.6rem;font-weight:700;line-height:1.25}.stalux-prose h2{border-bottom:2px solid var(--accent-30p);margin-top:2.5rem;padding-bottom:.4rem;font-size:2rem;font-weight:650}.stalux-prose h3{color:var(--white-95p);margin-top:2rem;font-size:1.5rem;line-height:1.4}.stalux-prose h4{color:var(--white-90p);margin-top:1.5rem;margin-bottom:.6rem;font-size:1.25rem;line-height:1.45}.stalux-prose h5{color:var(--white-90p);margin-top:1.5rem;margin-bottom:.5rem;font-size:1.1rem;line-height:1.5}.stalux-prose h6{color:var(--white-80p);margin-top:1.5rem;margin-bottom:.5rem;font-size:1rem;line-height:1.5}.stalux-prose p{margin:0 0 var(--space-lg);line-height:1.75}.stalux-prose strong{color:#ffd966;font-weight:600}.stalux-prose em{color:var(--white-85p);font-style:italic}.stalux-prose strong em,.stalux-prose em strong{color:#ffd966}.stalux-prose del{color:var(--white-60p)}.stalux-prose mark{color:inherit;background-color:#ffd96640;border-radius:2px;padding:.1em .2em}.stalux-prose kbd{font-family:var(--font-code), "JetBrains Mono", "Fira Code", "Consolas", "Courier New", monospace;background-color:var(--white-10p);border:1px solid var(--white-20p);color:var(--white-85p);border-radius:4px;padding:.15em .4em;font-size:.85em}.stalux-prose sup,.stalux-prose sub{font-size:.8em}.stalux-prose a{color:var(--accent-80p);transition:color var(--transition-fast), border-bottom-color var(--transition-fast);overflow-wrap:anywhere;border-bottom:2px solid #0000;text-decoration:none}.stalux-prose a:focus-visible{color:var(--accent-90p);border-bottom-color:var(--accent-60p)}@media (hover:hover) and (pointer:fine){.stalux-prose a:hover{color:var(--accent-90p);border-bottom-color:var(--accent-60p)}}.stalux-prose code{font-family:var(--font-code), "JetBrains Mono", "Fira Code", "Consolas", "Courier New", monospace;color:#ff92d0;background-color:#ff79c626;border:1px solid #ff79c640;border-radius:4px;padding:.2em .4em;font-size:.88em}.stalux-prose pre{background:var(--black-30p);color:var(--white-85p);border-left:4px solid var(--accent-60p);margin:var(--space-xl) 0;font-size:.92em;line-height:1.65;font-family:var(--font-code), "JetBrains Mono", "Fira Code", "Consolas", "Courier New", monospace;box-shadow:var(--shadow-md);border-radius:8px;padding:1.2rem 1.5rem;overflow-x:auto}.stalux-prose pre code{color:inherit;font-size:inherit;background:0 0;border:none;padding:0;font-family:inherit}.expressive-code pre,.expressive-code code{all:revert}.stalux-prose ul,.stalux-prose ol{margin:0 0 var(--space-lg);padding-left:1.75rem}.stalux-prose li{color:var(--white-85p);margin:.4rem 0;line-height:1.75}.stalux-prose ul li{list-style:outside}.stalux-prose ul li ul li{list-style:circle}.stalux-prose ul li ul li ul li{list-style:square}.stalux-prose ol li{list-style:decimal}.stalux-prose ol li ol li{list-style:lower-alpha}.stalux-prose li>ul,.stalux-prose li>ol{margin-top:.3rem;margin-bottom:.3rem}.stalux-prose blockquote{margin:var(--space-xl) 0;border-left:4px solid var(--accent-60p);background:var(--surface-glass);border-top:1px solid var(--white-10p);border-right:1px solid var(--white-10p);border-bottom:1px solid var(--white-10p);color:var(--white-85p);border-radius:12px;padding:1rem 1.5rem;box-shadow:0 4px 16px #0002}.stalux-prose blockquote p{margin:0;line-height:1.7}.stalux-prose blockquote p+p{margin-top:.8rem}.stalux-prose blockquote strong{color:#ffd966}.stalux-prose blockquote code{font-size:.85em}.stalux-prose table{border-collapse:collapse;width:100%;margin:var(--space-xl) 0;box-shadow:var(--shadow-md);border-radius:8px;display:block;overflow:auto hidden}.stalux-prose th,.stalux-prose td{text-align:left;border-bottom:1px solid var(--white-10p);padding:.75rem 1rem;line-height:1.6}.stalux-prose th{white-space:nowrap;background:var(--accent-30p);color:var(--white-full);font-size:.95em;font-weight:600}.stalux-prose td{color:var(--white-85p)}.stalux-prose tr:nth-child(2n) td{background-color:var(--white-05p)}@media (hover:hover) and (pointer:fine){.stalux-prose tr:hover td{background-color:var(--black-10p)}}.stalux-prose table code{font-size:.85em}.stalux-prose img{max-width:100%;height:auto;margin:var(--space-xl) auto;box-shadow:var(--shadow-lg);transition:transform var(--transition-normal), box-shadow var(--transition-normal);border-radius:8px;display:block}@media (hover:hover) and (pointer:fine){.stalux-prose img:hover{box-shadow:var(--shadow-xl);transform:none}}.stalux-prose embed,.stalux-prose video{max-width:100%;height:auto;margin:var(--space-xl) auto;border-radius:8px;display:block}.stalux-prose hr{margin:var(--space-2xl) 0;background:linear-gradient(90deg, transparent, var(--white-15p) 20%, var(--accent-40p) 50%, var(--white-15p) 80%, transparent);border:none;height:1.5px}.stalux-prose details{margin:var(--space-lg) 0;background-color:var(--white-05p);border:1px solid var(--white-10p);transition:border-color var(--transition-fast);border-radius:6px;padding:.8rem 1rem}@media (hover:hover) and (pointer:fine){.stalux-prose details:hover{border-color:var(--white-20p)}}.stalux-prose summary{cursor:pointer;color:var(--white-90p);font-weight:600}.stalux-prose details[open] summary{border-bottom:1px solid var(--white-10p);margin-bottom:.8rem;padding-bottom:.6rem}.stalux-prose figure{text-align:center;margin:var(--space-xl) 0}.stalux-prose figcaption{color:var(--white-70p);margin-top:.5rem;font-size:.9em;font-style:italic}.stalux-prose .footnotes{color:var(--white-75p);border-top:1px solid var(--white-15p);margin-top:var(--space-2xl);padding-top:var(--space-lg);font-size:.88em}.stalux-prose .footnotes ol{margin-left:1rem;padding-left:.5rem}.stalux-prose .footnotes li{margin:.5rem 0;line-height:1.6}.stalux-prose .footnotes a{font-size:.9em}@media (width<=1024px){.stalux-prose{font-size:1.05rem}.stalux-prose h1{font-size:2.2rem}.stalux-prose h2{font-size:1.8rem}.stalux-prose h3{font-size:1.35rem}.stalux-prose h4{font-size:1.15rem}.stalux-prose pre{padding:1rem 1.2rem;font-size:.88em}}@media (width<=768px){.stalux-prose{font-size:1rem;line-height:1.7}.stalux-prose h1{margin-top:1.5rem;font-size:1.8rem}.stalux-prose h2{margin-top:1.5rem;font-size:1.4rem}.stalux-prose h3{margin-top:1.25rem;font-size:1.2rem}.stalux-prose h4{margin-top:1rem;font-size:1.1rem}.stalux-prose h5{font-size:1rem}.stalux-prose h6{font-size:.95rem}.stalux-prose p{margin-bottom:var(--space-md)}.stalux-prose pre{margin:var(--space-lg) 0;border-left-width:3px;padding:.8rem 1rem;font-size:.82em}.stalux-prose table{font-size:.85rem}.stalux-prose th,.stalux-prose td{padding:.5rem .7rem}.stalux-prose blockquote{padding:.8rem 1rem;font-size:.9em}.stalux-prose ul,.stalux-prose ol{padding-left:1.5rem}}.stalux-prose,.stalux-prose>*,.stalux-prose div,.stalux-prose .expressive-code,.stalux-prose .expressive-code .frame{box-sizing:border-box;min-width:0;max-width:100%}.stalux-prose{overflow-wrap:anywhere}.stalux-prose a:after{content:none}.stalux-prose a{text-decoration:underline;text-decoration-color:var(--accent-50p);text-underline-offset:.2em;border-bottom:0;text-decoration-thickness:1px}.stalux-prose a:has(img){text-decoration:none}.stalux-prose pre,.stalux-prose table,.stalux-prose .mermaid,.stalux-prose math{overscroll-behavior-x:contain;max-width:100%;overflow-x:auto}.stalux-prose pre,.stalux-prose pre code{overflow-wrap:normal;word-break:normal}.stalux-prose :is(img,video,iframe,embed,svg,canvas){max-width:100%}.stalux-prose :is(img,video,svg,canvas){height:auto}.stalux-prose iframe{margin:var(--space-xl) auto;border-radius:8px;display:block}.stalux-prose .mermaid svg{max-width:none}.stalux-prose canvas{box-sizing:border-box}.stalux-prose summary{overflow-wrap:anywhere}}@layer utilities;@font-face{font-family:Stalux Math Aux;src:url(/fonts/StaluxMathAux.woff2)format("woff2");font-weight:400;font-style:normal}@font-face{font-family:Latin Modern Math;src:url(/fonts/latinmodern-math.woff2)format("woff2");font-display:swap}math{font-size-adjust:none;text-indent:0;text-transform:none;letter-spacing:normal;word-wrap:normal;font-feature-settings:"dtls" off;font-style:normal;font-weight:400;line-height:normal;font-family:"Latin Modern Math", var(--font-body), math;direction:ltr}math *{border-color:currentColor}math.tml-display{width:100%;display:block}.mathscr{font-family:Stalux Math Aux,serif}mfrac>:nth-child(2),msqrt,mover>:first-child{math-shift:compact}.menclose{padding:.5ex 0;display:inline-block;position:relative}.tml-cancelto{background-color:currentColor;padding:.5ex 0;display:inline-block;position:absolute;top:0;left:0;-webkit-mask-image:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><defs><marker id='a' markerHeight='5' markerUnits='strokeWidth' markerWidth='7' orient='auto' refX='7' refY='2.5'><path fill='black' d='m0 0 7 2.5L0 5z'/></marker></defs><line x2='100%25' y1='100%25' stroke='black' stroke-width='.06em' marker-end='url(%23a)' vector-effect='non-scaling-stroke'/></svg>");mask-image:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><defs><marker id='a' markerHeight='5' markerUnits='strokeWidth' markerWidth='7' orient='auto' refX='7' refY='2.5'><path fill='black' d='m0 0 7 2.5L0 5z'/></marker></defs><line x2='100%25' y1='100%25' stroke='black' stroke-width='.06em' marker-end='url(%23a)' vector-effect='non-scaling-stroke'/></svg>");-webkit-mask-position:0 0;mask-position:0 0;-webkit-mask-size:100% 100%;mask-size:100% 100%;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}@-moz-document url-prefix(){math{display:inline}math>mrow{padding:0}mtd,mtable.tml-small mtd{padding-top:0;padding-bottom:0}mtable.tml-jot mtd{padding-top:.2ex;padding-bottom:0}.tml-vec{transform:scale(.75)}.ff-narrow{width:0}.ff-nudge-left{margin-left:-.2em}.ff-squash mtd{height:0;display:block}}@supports not ((-moz-appearance:none)){mo.tml-prime{font-family:Stalux Math Aux,serif}.tml-sml-pad{padding-left:.05em}.tml-med-pad{padding-left:.1em}.tml-lrg-pad{padding-left:.15em}}@supports ((-webkit-backdrop-filter:blur(1px))){.wbk-acc{transform:translateY(.431em)}.wbk-sml{transform:translate(.07em)}.wbk-sml-acc{transform:translate(.07em,.431em)}.wbk-sml-vec{transform:scale(.75)translate(.07em)}.wbk-med{transform:translate(.14em)}.wbk-med-acc{transform:translate(.14em,.431em)}.wbk-med-vec{transform:scale(.75)translate(.14em)}.wbk-lrg{transform:translate(.21em)}.wbk-lrg-acc{transform:translate(.21em,.431em)}.wbk-lrg-vec{transform:scale(.75)translate(.21em)}}@supports (not ((-webkit-backdrop-filter:blur(1px)))) and (not ((-moz-appearance:none))){.chr-sml{transform:translate(.07em)}.chr-sml-vec{transform:scale(.75)translate(.07em)}.chr-med{transform:translate(.14em)}.chr-med-vec{transform:scale(.75)translate(.14em)}.chr-lrg{transform:translate(.21em)}.chr-lrg-vec{transform:scale(.75)translate(.21em)}.tml-shift-left{margin-left:-100%}menclose{padding:.5ex 0;position:relative}.tml-overline{border-top:.065em solid;padding:.1em 0 0}.tml-underline{border-bottom:.065em solid;padding:0 0 .1em}.tml-cancel{background-color:currentColor;width:100%;height:100%;display:inline-block;position:absolute;bottom:0;left:.5px}.upstrike{clip-path:polygon(.05em 100%,0 calc(100% - .05em),calc(100% - .05em) 0,100% .05em)}.downstrike{clip-path:polygon(0 .05em,.05em 0,100% calc(100% - .05em),calc(100% - .05em) 100%)}.sout{clip-path:polygon(0 calc(55% + .0333em),0 calc(55% - .0333em),100% calc(55% - .0333em),100% calc(55% + .0333em))}.tml-xcancel{clip-path:polygon(.05em 0,0 .05em,calc(50% - .05em) 50%,0 calc(100% - .05em),.05em 100%,50% calc(50% + .05em),calc(100% - .05em) 100%,100% calc(100% - .05em),calc(50% + .05em) 50%,100% .05em,calc(100% - .05em) 0%,50% calc(50% - .05em))}.longdiv-top{border-top:.067em solid;padding:.1em .2em .2em .433em}.longdiv-arc{clip-path:inset(0 0 0 .4em);box-sizing:border-box;border:.067em solid;border-radius:70%;width:.7em;position:absolute;top:0;bottom:.1em;left:-.4em;transform:translateY(-.067em)}.menclose{text-align:left;display:inline-block;position:relative}.phasor-bottom{border-bottom:.067em solid;padding:.2em .2em .1em .6em}.phasor-angle{aspect-ratio:.5;clip-path:polygon(.05em 100%,0 calc(100% - .05em),calc(100% - .05em) 0,100% .05em);background-color:currentColor;height:100%;display:inline-block;position:absolute;bottom:-.04em;left:.5px}.tml-fbox{border:1px solid;padding:3pt}.circle-pad{padding:.267em}.textcircle{border:.067em solid;border-radius:50%;position:absolute;inset:0}.actuarial{border-style:solid;border-width:.08em .08em 0 0;margin-right:.03889em;padding:.03889em .03889em 0}.tml-crooked-2{transform:scale(2,1.1)}.tml-crooked-3{transform:scale(3,1.3)}.tml-crooked-4{transform:scale(4,1.4)}.tml-right{text-align:-webkit-right}.tml-left{text-align:-webkit-left}}menclose{-webkit-print-color-adjust:exact;print-color-adjust:exact}.tml-right{text-align:right}.tml-left{text-align:left}.special-fraction{font-family:Times New Roman,Times,STIX TWO,Tinos,serif}math{flex-wrap:wrap;align-items:baseline;display:inline-flex}math>mrow{padding:.5ex 0}mtable.tml-jot mtd{padding-top:.7ex;padding-bottom:.7ex}mtable.tml-small mtd{padding-top:.35ex;padding-bottom:.35ex}.tml-shift-left{margin-left:-200%}.tml-eqn:before{counter-increment:tmlEqnNo;content:"(" counter(tmlEqnNo) ")"}body{counter-reset:tmlEqnNo}
|
|
2
2
|
/*$vite$:1*/
|
package/src/styles/math.css
CHANGED
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
* 仅作用于 <math> 元素,不影响全局字体。
|
|
4
4
|
*
|
|
5
5
|
* Latin Modern Math 基于 GUST Font License (LPPL equivalent)
|
|
6
|
-
*
|
|
6
|
+
* StaluxMathAux.woff2 从 OFL KaTeX 字体独立重建,用于 \mathscr{} 和 Chromium prime 对齐
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
/* ===== @font-face 声明 ===== */
|
|
10
10
|
|
|
11
11
|
@font-face {
|
|
12
|
-
font-family: "
|
|
13
|
-
src: url("/fonts/
|
|
12
|
+
font-family: "Stalux Math Aux";
|
|
13
|
+
src: url("/fonts/StaluxMathAux.woff2") format("woff2");
|
|
14
14
|
font-weight: normal;
|
|
15
15
|
font-style: normal;
|
|
16
16
|
}
|
|
@@ -50,7 +50,7 @@ math.tml-display {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
*.mathscr {
|
|
53
|
-
font-family: "
|
|
53
|
+
font-family: "Stalux Math Aux", serif;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
/* Cramped superscripts in WebKit */
|
|
@@ -127,7 +127,7 @@ mover > :first-child {
|
|
|
127
127
|
@supports (not (-moz-appearance: none)) {
|
|
128
128
|
/* prime vertical alignment */
|
|
129
129
|
mo.tml-prime {
|
|
130
|
-
font-family:
|
|
130
|
+
font-family: "Stalux Math Aux", serif;
|
|
131
131
|
}
|
|
132
132
|
/* Italic correction on superscripts */
|
|
133
133
|
.tml-sml-pad {
|