feat: Add multi-source font and style manager

Rename md-emfont to md-font with support for Google, emfont, and local
sources. Introduce article-style-manager.ts for centralized lifecycle
management of article-level styles and external stylesheet link
injection with reference counting. Refactor md-bg to use the new
manager, removing direct createEffect on article styles.
This commit is contained in:
hyper
2026-06-25 20:26:34 +08:00
parent 771fe6a112
commit 1a4ae417fe
5 changed files with 264 additions and 77 deletions
+63
View File
@@ -0,0 +1,63 @@
import { customElement, noShadowDOM } from "solid-element";
import { onCleanup } from "solid-js";
import { registerStyle } from "./utils/article-style-manager";
export interface FontProps {
/** Font source: "google" (Google Fonts), "emfont" (emtech.cc),
* or "local" (already loaded, just set fontFamily) */
source?: "google" | "emfont" | "local";
/** Font weight (for google and emfont sources) */
weight?: string;
}
function buildLinkHref(
font: string,
source: FontProps["source"],
weight: string,
): string | null {
if (source === "google") {
const family = font.replace(/\s+/g, "+");
return `https://fonts.googleapis.com/css2?family=${family}:wght@${weight}&display=swap`;
}
if (source === "emfont") {
return `https://font.emtech.cc/css/${font}?weight=${weight}`;
}
return null;
}
customElement(
"md-font",
{ source: "local", weight: "400" },
(props, { element }) => {
noShadowDOM();
const font = element?.textContent?.trim() || "";
// Hide original text content
if (element) {
element.textContent = "";
}
const articleEl = element?.closest("article") as HTMLElement;
if (!articleEl) return null;
const source = (props.source as FontProps["source"]) || "google";
const weight = props.weight || "400";
const href = buildLinkHref(font, source, weight);
const links = href
? [{ href, dataset: { mdFontSource: source, mdFontName: font } }]
: [];
const handle = registerStyle({
key: "fontFamily",
article: articleEl,
styles: { fontFamily: `"${font}", sans-serif` },
links,
});
onCleanup(() => handle.dispose());
return null;
},
);