mirror of
https://github.com/2234839/web-font.git
synced 2026-06-26 00:08:12 +08:00
feat: 前端格式选择器组件,服务端支持能力透传
- 新增 FontSelector.tsx 独立组件,字体和输出格式并排选择 - /api/config 新增 supportedOutTypes 字段(LLRT 仅 ttf) - 前端加载配置后自动适配可用格式 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
820fc71166
commit
e2cc7c1144
@ -199,11 +199,14 @@ async function handleListFonts(req: Request, res: Response) {
|
||||
|
||||
/** GET /api/config — 返回公开配置 */
|
||||
async function handleGetConfig(req: Request, res: Response) {
|
||||
const isLlrt = release_name === "llrt";
|
||||
return {
|
||||
req,
|
||||
res: jsonResponse({
|
||||
enableTempUpload,
|
||||
adminUploadEnabled: !!adminApiKey,
|
||||
/** LLRT 不支持 wasm,无法输出 woff2 */
|
||||
supportedOutTypes: isLlrt ? ["ttf"] : ["woff2", "ttf"],
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "webfont",
|
||||
"private": true,
|
||||
"version": "1.3.1",
|
||||
"version": "1.3.2",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "pnpx tsx scripts/dev-all.ts",
|
||||
|
||||
38
src/App.tsx
38
src/App.tsx
@ -1,6 +1,7 @@
|
||||
import { createMemo, createSignal, onMount, Show, For } from "solid-js";
|
||||
import { fetchFonts, fetchConfig, type FontInfo, type ServerConfig } from "./api";
|
||||
import UploadSection from "./UploadSection";
|
||||
import { SelectorRow } from "./FontSelector";
|
||||
|
||||
const s = {
|
||||
wrap: {
|
||||
@ -92,9 +93,11 @@ function App() {
|
||||
const [text, set_text] = createSignal("天地无极,乾坤借法");
|
||||
const [fonts, set_fonts] = createSignal<FontInfo[]>([]);
|
||||
const [selectedFont, set_selectedFont] = createSignal("");
|
||||
const [outType, set_outType] = createSignal<"woff2" | "ttf">("woff2");
|
||||
const [serverConfig, set_serverConfig] = createSignal<ServerConfig>({
|
||||
enableTempUpload: false,
|
||||
adminUploadEnabled: false,
|
||||
supportedOutTypes: ["woff2", "ttf"],
|
||||
});
|
||||
|
||||
const SLOGAN = "如清风似闪电,超级快的字体子集化裁剪";
|
||||
@ -103,6 +106,10 @@ function App() {
|
||||
const [fontList, config] = await Promise.all([fetchFonts().catch(() => []), fetchConfig().catch(() => ({ enableTempUpload: false, adminUploadEnabled: false }))]);
|
||||
set_fonts(fontList);
|
||||
set_serverConfig(config);
|
||||
/** 服务端不支持当前 outType 时自动回退 */
|
||||
if (!config.supportedOutTypes?.includes(outType())) {
|
||||
set_outType(config.supportedOutTypes?.[0] || "ttf");
|
||||
}
|
||||
if (fontList.length > 0) {
|
||||
/** 标语随机使用一个字体展示 */
|
||||
const randomFont = fontList[Math.floor(Math.random() * fontList.length)];
|
||||
@ -123,10 +130,11 @@ function App() {
|
||||
|
||||
const cssStyle = createMemo(() => {
|
||||
const font = selectedFont();
|
||||
const ot = outType();
|
||||
if (!font) return "";
|
||||
return `@font-face {
|
||||
font-family: "CustomFont";
|
||||
src: url("${location.origin}/api?font=${font}&text=${encodeURIComponent(text())}&outType=woff2") format("woff2");
|
||||
src: url("${location.origin}/api?font=${font}&text=${encodeURIComponent(text())}&outType=${ot}") format("${ot}");
|
||||
}
|
||||
.custom-font {
|
||||
color: red;
|
||||
@ -189,21 +197,14 @@ function App() {
|
||||
<p id="slogan" style={s.desc}>{SLOGAN}</p>
|
||||
|
||||
<section style={s.section}>
|
||||
<label style={s.label}>选择字体</label>
|
||||
<select
|
||||
style={s.select}
|
||||
value={selectedFont()}
|
||||
onChange={(e) => onFontChange(e.target.value)}
|
||||
>
|
||||
<option value="">-- 请选择 --</option>
|
||||
<For each={fonts()}>
|
||||
{(f) => (
|
||||
<option value={f.name}>
|
||||
{f.name} ({f.dir})
|
||||
</option>
|
||||
)}
|
||||
</For>
|
||||
</select>
|
||||
<SelectorRow
|
||||
fonts={fonts()}
|
||||
selectedFont={selectedFont()}
|
||||
onFontChange={onFontChange}
|
||||
supportedOutTypes={serverConfig().supportedOutTypes || ["woff2", "ttf"]}
|
||||
outType={outType()}
|
||||
onOutTypeChange={set_outType}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section style={s.section}>
|
||||
@ -228,9 +229,10 @@ function App() {
|
||||
<button
|
||||
style={{ ...s.btn, padding: "3px 12px", "font-size": "12px" }}
|
||||
onClick={() => {
|
||||
const ot = outType();
|
||||
const a = document.createElement("a");
|
||||
a.href = `/api?font=${selectedFont()}&text=${encodeURIComponent(text())}&outType=woff2`;
|
||||
a.download = selectedFont().replace(/\.[^.]+$/, "") + "_subset.woff2";
|
||||
a.href = `/api?font=${selectedFont()}&text=${encodeURIComponent(text())}&outType=${ot}`;
|
||||
a.download = selectedFont().replace(/\.[^.]+$/, "") + `_subset.${ot}`;
|
||||
a.click();
|
||||
}}
|
||||
>
|
||||
|
||||
118
src/FontSelector.tsx
Normal file
118
src/FontSelector.tsx
Normal file
@ -0,0 +1,118 @@
|
||||
import { For } from "solid-js";
|
||||
|
||||
interface FontSelectorProps {
|
||||
fonts: Array<{ name: string; dir: string }>;
|
||||
selectedFont: string;
|
||||
onFontChange: (font: string) => void;
|
||||
}
|
||||
|
||||
interface OutTypeSelectorProps {
|
||||
supportedOutTypes: ("woff2" | "ttf")[];
|
||||
outType: "woff2" | "ttf";
|
||||
onOutTypeChange: (type: "woff2" | "ttf") => void;
|
||||
}
|
||||
|
||||
const outTypeLabels: Record<string, string> = {
|
||||
woff2: "WOFF2 体积更小",
|
||||
ttf: "TTF 速度更快",
|
||||
};
|
||||
|
||||
const outTypeDescs: Record<string, string> = {
|
||||
woff2: "约压缩 50%,适合生产",
|
||||
ttf: "无编码开销,适合开发",
|
||||
};
|
||||
|
||||
const s = {
|
||||
wrap: {
|
||||
display: "flex",
|
||||
gap: "12px",
|
||||
} as const,
|
||||
col: {
|
||||
flex: 1,
|
||||
} as const,
|
||||
label: {
|
||||
display: "block",
|
||||
"font-size": "13px",
|
||||
color: "#555",
|
||||
"margin-bottom": "6px",
|
||||
} as const,
|
||||
select: {
|
||||
width: "100%",
|
||||
padding: "8px 12px",
|
||||
"font-size": "14px",
|
||||
border: "1px solid #d9d9d9",
|
||||
"border-radius": "6px",
|
||||
outline: "none",
|
||||
"box-sizing": "border-box",
|
||||
cursor: "pointer",
|
||||
appearance: "none",
|
||||
"background-image": "url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2 4l4 4 4-4' fill='none' stroke='%23999' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E\")",
|
||||
"background-repeat": "no-repeat",
|
||||
"background-position": "right 10px center",
|
||||
"padding-right": "28px",
|
||||
} as const,
|
||||
desc: {
|
||||
"font-size": "11px",
|
||||
color: "#bbb",
|
||||
"margin-top": "4px",
|
||||
} as const,
|
||||
};
|
||||
|
||||
export function FontSelector(props: FontSelectorProps) {
|
||||
return (
|
||||
<div style={s.col}>
|
||||
<label style={s.label}>选择字体</label>
|
||||
<select
|
||||
style={s.select}
|
||||
value={props.selectedFont}
|
||||
onChange={(e) => props.onFontChange(e.target.value)}
|
||||
>
|
||||
<option value="">-- 请选择 --</option>
|
||||
<For each={props.fonts}>
|
||||
{(f) => (
|
||||
<option value={f.name}>
|
||||
{f.name}
|
||||
</option>
|
||||
)}
|
||||
</For>
|
||||
</select>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function OutTypeSelector(props: OutTypeSelectorProps) {
|
||||
return (
|
||||
<div style={{ width: "160px" }}>
|
||||
<label style={s.label}>输出格式</label>
|
||||
<select
|
||||
style={s.select}
|
||||
value={props.outType}
|
||||
onChange={(e) => props.onOutTypeChange(e.target.value as "woff2" | "ttf")}
|
||||
>
|
||||
<For each={props.supportedOutTypes}>
|
||||
{(t) => (
|
||||
<option value={t}>{outTypeLabels[t]}</option>
|
||||
)}
|
||||
</For>
|
||||
</select>
|
||||
<p style={s.desc}>{outTypeDescs[props.outType]}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function SelectorRow(props: FontSelectorProps & OutTypeSelectorProps) {
|
||||
return (
|
||||
<div style={s.wrap}>
|
||||
<FontSelector
|
||||
fonts={props.fonts}
|
||||
selectedFont={props.selectedFont}
|
||||
onFontChange={props.onFontChange}
|
||||
/>
|
||||
<OutTypeSelector
|
||||
supportedOutTypes={props.supportedOutTypes}
|
||||
outType={props.outType}
|
||||
onOutTypeChange={props.onOutTypeChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -6,6 +6,7 @@ export interface FontInfo {
|
||||
export interface ServerConfig {
|
||||
enableTempUpload: boolean;
|
||||
adminUploadEnabled: boolean;
|
||||
supportedOutTypes: ("woff2" | "ttf")[];
|
||||
}
|
||||
|
||||
export interface UploadResult {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user