import { resolveImagerBase } from "@/lib/imager"; /** Build a Habbo avatar-imager URL for a figure string. */ export function avatarImageUrl( base: string, look: string, opts: { size?: "s" | "m" | "l"; headOnly?: boolean; direction?: number; headDirection?: number } = {}, ): string { const resolved = resolveImagerBase(base); const params = new URLSearchParams({ figure: look, img_format: "png" }); if (opts.size) params.set("size", opts.size); if (opts.headOnly) params.set("headonly", "1"); if (opts.direction !== undefined) params.set("direction", String(opts.direction)); if (opts.headDirection !== undefined) params.set("head_direction", String(opts.headDirection)); const sep = resolved.includes("?") ? "&" : "?"; return `${resolved}${sep}${params.toString()}`; } /** URL-safe slug from a title (lowercase, ascii, hyphenated). */ export function slugify(input: string): string { // NFKD splits accented letters into base + combining mark; the combining // marks (and any other non-alphanumerics) are then collapsed to hyphens. const slug = input .toLowerCase() .normalize("NFKD") .replace(/[^a-z0-9]+/g, "-") .replace(/^-+|-+$/g, "") .slice(0, 255); return slug || "article"; } /** Trim text to `max` chars on a word boundary, adding an ellipsis. */ export function excerpt(text: string, max = 160): string { if (text.length <= max) return text; const cut = text.slice(0, max); const lastSpace = cut.lastIndexOf(" "); return `${(lastSpace > 0 ? cut.slice(0, lastSpace) : cut).trimEnd()}…`; }