Files
picpeak/frontend/src/services/fonts.service.ts
T

60 lines
1.8 KiB
TypeScript

import { api } from '../config/api';
export interface FontDefinition {
family: string;
weights: number[];
// CSS generic to use as a fallback when building font-family strings.
// Optional in the type so older backends without this field still work;
// callers must default to 'sans-serif' when undefined.
generic?: 'sans-serif' | 'serif' | 'cursive' | 'monospace';
}
export interface FontsListResponse {
fonts: FontDefinition[];
}
export const fontsService = {
/**
* List all self-hosted font families discovered by the backend scanner.
* Cached aggressively at the React Query layer; the underlying endpoint
* is also TTL-cached on the backend.
*/
async list(): Promise<FontDefinition[]> {
const res = await api.get<FontsListResponse>('/public/fonts');
return res.data.fonts;
}
};
const GENERIC_FAMILIES = new Set([
'sans-serif',
'serif',
'monospace',
'cursive',
'fantasy',
'system-ui',
'ui-sans-serif',
'ui-serif',
'ui-monospace',
'ui-rounded'
]);
/**
* Extract the primary font family name from a CSS font-family string.
*
* "'Jost', sans-serif" → "Jost"
* "Inter, sans-serif" → "Inter"
* "'Playfair Display', serif" → "Playfair Display"
* "system-ui, sans-serif" → null (generic, no @font-face needed)
* undefined / "" / "sans-serif" → null
*/
export function extractFamilyName(cssFontFamily: string | undefined | null): string | null {
if (!cssFontFamily) return null;
const first = cssFontFamily.split(',')[0]?.trim();
if (!first) return null;
// Strip surrounding single or double quotes
const unquoted = first.replace(/^['"]|['"]$/g, '').trim();
if (!unquoted) return null;
if (GENERIC_FAMILIES.has(unquoted.toLowerCase())) return null;
return unquoted;
}