From 7bf0984698e43b9aa2dd879f65951318ed410390 Mon Sep 17 00:00:00 2001 From: Maze <167211895+mazeincoding@users.noreply.github.com> Date: Sat, 31 Jan 2026 00:20:04 +0100 Subject: codebase overhaul (#697) --- apps/web/src/lib/iconify-api.ts | 367 ++++++++++++++++++++-------------------- 1 file changed, 183 insertions(+), 184 deletions(-) (limited to 'apps/web/src/lib/iconify-api.ts') diff --git a/apps/web/src/lib/iconify-api.ts b/apps/web/src/lib/iconify-api.ts index 9d1bcb97..e4afe111 100644 --- a/apps/web/src/lib/iconify-api.ts +++ b/apps/web/src/lib/iconify-api.ts @@ -1,242 +1,241 @@ - export const ICONIFY_HOSTS = [ - "https://api.iconify.design", - "https://api.simplesvg.com", - "https://api.unisvg.com", + "https://api.iconify.design", + "https://api.simplesvg.com", + "https://api.unisvg.com", ]; let currentHost = ICONIFY_HOSTS[0]; async function fetchWithFallback(path: string): Promise { - for (const host of ICONIFY_HOSTS) { - try { - const response = await fetch(`${host}${path}`, { - signal: AbortSignal.timeout(2000), - }); - if (response.ok) { - currentHost = host; - return response; - } - } catch (error) { - console.warn(`Failed to fetch from ${host}:`, error); - } - } - throw new Error("All API hosts failed"); + for (const host of ICONIFY_HOSTS) { + try { + const response = await fetch(`${host}${path}`, { + signal: AbortSignal.timeout(2000), + }); + if (response.ok) { + currentHost = host; + return response; + } + } catch (error) { + console.warn(`Failed to fetch from ${host}:`, error); + } + } + throw new Error("All API hosts failed"); } export interface IconSet { - prefix: string; - name: string; - total: number; - author?: { - name: string; - url?: string; - }; - license?: { - title: string; - spdx?: string; - url?: string; - }; - samples?: string[]; - category?: string; - palette?: boolean; + prefix: string; + name: string; + total: number; + author?: { + name: string; + url?: string; + }; + license?: { + title: string; + spdx?: string; + url?: string; + }; + samples?: string[]; + category?: string; + palette?: boolean; } export interface IconSearchResult { - icons: string[]; - total: number; - limit: number; - start: number; - collections: Record; + icons: string[]; + total: number; + limit: number; + start: number; + collections: Record; } export interface CollectionInfo { - prefix: string; - total: number; - title?: string; - uncategorized?: string[]; - categories?: Record; - hidden?: string[]; - aliases?: Record; + prefix: string; + total: number; + title?: string; + uncategorized?: string[]; + categories?: Record; + hidden?: string[]; + aliases?: Record; } export async function getCollections( - category?: string + category?: string, ): Promise> { - try { - const response = await fetchWithFallback("/collections?pretty=1"); - const data = (await response.json()) as Record; - - if (category) { - const filtered = Object.fromEntries( - Object.entries(data).filter( - ([_key, info]) => info.category === category - ) - ) as Record; - return filtered; - } - - return data; - } catch (error) { - console.error("Failed to fetch collections:", error); - return {}; - } + try { + const response = await fetchWithFallback("/collections?pretty=1"); + const data = (await response.json()) as Record; + + if (category) { + const filtered = Object.fromEntries( + Object.entries(data).filter( + ([_key, info]) => info.category === category, + ), + ) as Record; + return filtered; + } + + return data; + } catch (error) { + console.error("Failed to fetch collections:", error); + return {}; + } } export async function getCollection( - prefix: string + prefix: string, ): Promise { - try { - const response = await fetchWithFallback( - `/collection?prefix=${prefix}&pretty=1` - ); - return await response.json(); - } catch (error) { - console.error(`Failed to fetch collection ${prefix}:`, error); - return null; - } + try { + const response = await fetchWithFallback( + `/collection?prefix=${prefix}&pretty=1`, + ); + return await response.json(); + } catch (error) { + console.error(`Failed to fetch collection ${prefix}:`, error); + return null; + } } export async function searchIcons( - query: string, - limit: number = 64, - prefixes?: string[], - category?: string + query: string, + limit: number = 64, + prefixes?: string[], + category?: string, ): Promise { - const params = new URLSearchParams({ - query, - limit: limit.toString(), - pretty: "1", - }); - - if (prefixes?.length) { - params.append("prefixes", prefixes.join(",")); - } - - if (category) { - params.append("category", category); - } - - try { - const response = await fetchWithFallback(`/search?${params}`); - return await response.json(); - } catch (error) { - console.error("Failed to search icons:", error); - return { - icons: [], - total: 0, - limit, - start: 0, - collections: {}, - }; - } + const params = new URLSearchParams({ + query, + limit: limit.toString(), + pretty: "1", + }); + + if (prefixes?.length) { + params.append("prefixes", prefixes.join(",")); + } + + if (category) { + params.append("category", category); + } + + try { + const response = await fetchWithFallback(`/search?${params}`); + return await response.json(); + } catch (error) { + console.error("Failed to search icons:", error); + return { + icons: [], + total: 0, + limit, + start: 0, + collections: {}, + }; + } } export function buildIconSvgUrl( - host: string, - iconName: string, - params?: { - color?: string; - width?: number; - height?: number; - flip?: "horizontal" | "vertical" | "horizontal,vertical"; - rotate?: number | string; - } + host: string, + iconName: string, + params?: { + color?: string; + width?: number; + height?: number; + flip?: "horizontal" | "vertical" | "horizontal,vertical"; + rotate?: number | string; + }, ): string { - const [prefix, name] = iconName.includes(":") - ? iconName.split(":") - : ["", iconName]; + const [prefix, name] = iconName.includes(":") + ? iconName.split(":") + : ["", iconName]; - if (!prefix || !name) { - throw new Error('Invalid icon name format. Expected "prefix:name"'); - } + if (!prefix || !name) { + throw new Error('Invalid icon name format. Expected "prefix:name"'); + } - const urlParams = new URLSearchParams(); + const urlParams = new URLSearchParams(); - if (params?.color) { - urlParams.append("color", params.color.replace("#", "%23")); - } + if (params?.color) { + urlParams.append("color", params.color.replace("#", "%23")); + } - if (params?.width) { - urlParams.append("width", params.width.toString()); - } + if (params?.width) { + urlParams.append("width", params.width.toString()); + } - if (params?.height) { - urlParams.append("height", params.height.toString()); - } + if (params?.height) { + urlParams.append("height", params.height.toString()); + } - if (params?.flip) { - urlParams.append("flip", params.flip); - } + if (params?.flip) { + urlParams.append("flip", params.flip); + } - if (params?.rotate) { - urlParams.append("rotate", params.rotate.toString()); - } + if (params?.rotate) { + urlParams.append("rotate", params.rotate.toString()); + } - const queryString = urlParams.toString(); - return `${host}/${prefix}/${name}.svg${queryString ? "?" + queryString : ""}`; + const queryString = urlParams.toString(); + return `${host}/${prefix}/${name}.svg${queryString ? `?${queryString}` : ""}`; } export function getIconSvgUrl( - iconName: string, - params?: Parameters[2] + iconName: string, + params?: Parameters[2], ): string { - return buildIconSvgUrl(currentHost, iconName, params); + return buildIconSvgUrl(currentHost, iconName, params); } export async function downloadSvgAsText( - iconName: string, - params?: Parameters[1] + iconName: string, + params?: Parameters[1], ): Promise { - const url = getIconSvgUrl(iconName, params); - const response = await fetch(url); - if (!response.ok) { - throw new Error(`Failed to download SVG: ${response.statusText}`); - } - return await response.text(); + const url = getIconSvgUrl(iconName, params); + const response = await fetch(url); + if (!response.ok) { + throw new Error(`Failed to download SVG: ${response.statusText}`); + } + return await response.text(); } export function svgToFile(svgText: string, fileName: string): File { - const blob = new Blob([svgText], { type: "image/svg+xml" }); - return new File([blob], fileName, { type: "image/svg+xml" }); + const blob = new Blob([svgText], { type: "image/svg+xml" }); + return new File([blob], fileName, { type: "image/svg+xml" }); } export const POPULAR_COLLECTIONS = { - general: [ - { prefix: "mdi", name: "Material Design Icons" }, - { prefix: "ic", name: "Google Material Icons" }, - { prefix: "ph", name: "Phosphor" }, - { prefix: "heroicons", name: "Heroicons" }, - { prefix: "lucide", name: "Lucide" }, - { prefix: "tabler", name: "Tabler Icons" }, - { prefix: "fe", name: "Feather Icons" }, - { prefix: "bi", name: "Bootstrap Icons" }, - ], - brands: [ - { prefix: "simple-icons", name: "Simple Icons" }, - { prefix: "logos", name: "SVG Logos" }, - { prefix: "skill-icons", name: "Skill Icons" }, - { prefix: "devicon", name: "Devicon" }, - { prefix: "fa-brands", name: "Font Awesome Brands" }, - ], - emoji: [ - { prefix: "noto", name: "Noto Emoji" }, - { prefix: "twemoji", name: "Twemoji" }, - { prefix: "fluent-emoji", name: "Fluent Emoji" }, - { prefix: "fluent-emoji-flat", name: "Fluent Emoji Flat" }, - { prefix: "emojione", name: "EmojiOne" }, - { prefix: "openmoji", name: "OpenMoji" }, - ], + general: [ + { prefix: "mdi", name: "Material Design Icons" }, + { prefix: "ic", name: "Google Material Icons" }, + { prefix: "ph", name: "Phosphor" }, + { prefix: "heroicons", name: "Heroicons" }, + { prefix: "lucide", name: "Lucide" }, + { prefix: "tabler", name: "Tabler Icons" }, + { prefix: "fe", name: "Feather Icons" }, + { prefix: "bi", name: "Bootstrap Icons" }, + ], + brands: [ + { prefix: "simple-icons", name: "Simple Icons" }, + { prefix: "logos", name: "SVG Logos" }, + { prefix: "skill-icons", name: "Skill Icons" }, + { prefix: "devicon", name: "Devicon" }, + { prefix: "fa-brands", name: "Font Awesome Brands" }, + ], + emoji: [ + { prefix: "noto", name: "Noto Emoji" }, + { prefix: "twemoji", name: "Twemoji" }, + { prefix: "fluent-emoji", name: "Fluent Emoji" }, + { prefix: "fluent-emoji-flat", name: "Fluent Emoji Flat" }, + { prefix: "emojione", name: "EmojiOne" }, + { prefix: "openmoji", name: "OpenMoji" }, + ], }; export function getCategoriesFromCollections( - collections: Record + collections: Record, ): string[] { - const categories = new Set(); - Object.values(collections).forEach((collection) => { - if (collection.category) { - categories.add(collection.category); - } - }); - return Array.from(categories).sort(); + const categories = new Set(); + Object.values(collections).forEach((collection) => { + if (collection.category) { + categories.add(collection.category); + } + }); + return Array.from(categories).sort(); } -- cgit v1.3.1