feat: add selectable interface and code fonts (10 for each)
Adds separate UI and code font choices in settings Applies font changes immediately Lazy-loads selected remote fonts
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
import { CODE_FONT_OPTION_MAP, UI_FONT_OPTION_MAP, type FontFaceSource, type MonoFontOption, type UiFontOption } from '@/lib/fontOptions';
|
||||
|
||||
const loadedFaces = new Set<string>();
|
||||
const pendingFaces = new Map<string, Promise<void>>();
|
||||
|
||||
const buildFontUrl = (source: FontFaceSource, weight: number) => {
|
||||
const packageName = encodeURIComponent(source.packageName).replace('%40', '@').replace('%2F', '/');
|
||||
return `https://cdn.jsdelivr.net/npm/${packageName}/files/${source.filePrefix}-latin-${weight}-normal.woff2`;
|
||||
};
|
||||
|
||||
const loadFace = (source: FontFaceSource, weight: number) => {
|
||||
const key = `${source.family}:${weight}`;
|
||||
if (loadedFaces.has(key)) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const pending = pendingFaces.get(key);
|
||||
if (pending) {
|
||||
return pending;
|
||||
}
|
||||
|
||||
if (typeof document === 'undefined' || typeof FontFace === 'undefined' || !document.fonts) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const face = new FontFace(source.family, `url(${buildFontUrl(source, weight)}) format('woff2')`, {
|
||||
style: 'normal',
|
||||
weight: String(weight),
|
||||
display: 'swap',
|
||||
});
|
||||
|
||||
document.fonts.add(face);
|
||||
const promise = face.load()
|
||||
.then(() => {
|
||||
loadedFaces.add(key);
|
||||
})
|
||||
.catch((error) => {
|
||||
document.fonts.delete(face);
|
||||
console.warn(`Failed to load font: ${source.family} ${weight}`, error);
|
||||
})
|
||||
.finally(() => {
|
||||
pendingFaces.delete(key);
|
||||
});
|
||||
|
||||
pendingFaces.set(key, promise);
|
||||
return promise;
|
||||
};
|
||||
|
||||
const loadSource = (source: FontFaceSource | undefined) => {
|
||||
if (!source) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return Promise.all(source.weights.map((weight) => loadFace(source, weight))).then(() => undefined);
|
||||
};
|
||||
|
||||
export const loadUiFont = (font: UiFontOption) => loadSource(UI_FONT_OPTION_MAP[font]?.source);
|
||||
|
||||
export const loadMonoFont = (font: MonoFontOption) => loadSource(CODE_FONT_OPTION_MAP[font]?.source);
|
||||
Reference in New Issue
Block a user