mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-06-16 13:34:04 +08:00
feat: support URL-based icons for large SVGs and raster images
Add dual rendering mode to the icon system: small optimized SVGs continue to be inlined via dangerouslySetInnerHTML, while large SVGs and raster images (png/jpg/webp/etc) use Vite URL imports rendered as <img> tags. Added dds.svg (1.4MB) as the first URL-based icon. Updated generate-icon-index.js to support multi-format icons with a manual URL_ICONS control list. Updated ProviderIcon to handle both inline SVG and URL-based rendering paths.
This commit is contained in:
+128
-23
@@ -1,10 +1,30 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
const ICONS_DIR = path.join(__dirname, '../src/icons/extracted');
|
||||
const INDEX_FILE = path.join(ICONS_DIR, 'index.ts');
|
||||
const METADATA_FILE = path.join(ICONS_DIR, 'metadata.ts');
|
||||
|
||||
// Supported image extensions
|
||||
const SUPPORTED_EXTENSIONS = ['.svg', '.png', '.jpg', '.jpeg', '.webp', '.gif', '.ico'];
|
||||
|
||||
// ── Manual render mode control ──────────────────────────────────────
|
||||
// SVG icons listed here will be imported as URLs and rendered via <img>.
|
||||
// All other SVGs will be inlined as strings and rendered via dangerouslySetInnerHTML.
|
||||
// Raster images (png/jpg/…) are always URL-based regardless of this list.
|
||||
//
|
||||
// Add an icon name here when:
|
||||
// - The SVG file is too large to inline (e.g. > 100 KB)
|
||||
// - The SVG doesn't render correctly when inlined in HTML
|
||||
const URL_ICONS = new Set([
|
||||
'dds',
|
||||
]);
|
||||
// ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
// Known metadata from previous configuration
|
||||
const KNOWN_METADATA = {
|
||||
openai: { name: 'openai', displayName: 'OpenAI', category: 'ai-provider', keywords: ['gpt', 'chatgpt'], defaultColor: '#00A67E' },
|
||||
@@ -43,48 +63,133 @@ const KNOWN_METADATA = {
|
||||
link: { name: 'link', displayName: 'Link', category: 'other', keywords: ['url', 'hyperlink'], defaultColor: '#6B7280' },
|
||||
};
|
||||
|
||||
// Get all SVG files
|
||||
const files = fs.readdirSync(ICONS_DIR).filter(file => file.endsWith('.svg'));
|
||||
// Sanitize a filename into a valid JS identifier for import variable names
|
||||
function toImportVar(name) {
|
||||
return '_' + name.replace(/[^a-zA-Z0-9_]/g, '_');
|
||||
}
|
||||
|
||||
console.log(`Found ${files.length} SVG files.`);
|
||||
// Strip XML declarations and DOCTYPE from SVG content for safe HTML embedding
|
||||
function cleanSvgForInline(svg) {
|
||||
return svg
|
||||
.replace(/<\?xml[^?]*\?>\s*/g, '')
|
||||
.replace(/<!DOCTYPE[^>]*>\s*/g, '')
|
||||
.trim();
|
||||
}
|
||||
|
||||
// Generate index.ts
|
||||
const indexContent = `// Auto-generated icon index
|
||||
// Do not edit manually
|
||||
// Get all supported image files
|
||||
const files = fs.readdirSync(ICONS_DIR).filter(file =>
|
||||
SUPPORTED_EXTENSIONS.includes(path.extname(file).toLowerCase())
|
||||
);
|
||||
|
||||
export const icons: Record<string, string> = {
|
||||
${files.map(file => {
|
||||
const name = path.basename(file, '.svg');
|
||||
const svg = fs.readFileSync(path.join(ICONS_DIR, file), 'utf-8');
|
||||
console.log(`Found ${files.length} icon files.`);
|
||||
|
||||
// Classify files
|
||||
const inlineFiles = []; // SVGs to inline as strings (dangerouslySetInnerHTML)
|
||||
const urlFiles = []; // SVGs/raster to import as URLs (<img>)
|
||||
const seenNames = new Map();
|
||||
|
||||
for (const file of files) {
|
||||
const ext = path.extname(file).toLowerCase();
|
||||
const name = path.basename(file, path.extname(file)).toLowerCase();
|
||||
|
||||
// Duplicate name detection: prefer SVG over raster
|
||||
if (seenNames.has(name)) {
|
||||
const existing = seenNames.get(name);
|
||||
const existingExt = path.extname(existing).toLowerCase();
|
||||
if (ext === '.svg' && existingExt !== '.svg') {
|
||||
console.warn(`Warning: duplicate icon name "${name}" — ${file} (SVG) replaces ${existing}`);
|
||||
inlineFiles.splice(inlineFiles.indexOf(existing), 1);
|
||||
urlFiles.splice(urlFiles.indexOf(existing), 1);
|
||||
} else {
|
||||
console.warn(`Warning: duplicate icon name "${name}" — skipping ${file}, keeping ${existing}`);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
seenNames.set(name, file);
|
||||
|
||||
if (ext === '.svg' && !URL_ICONS.has(name)) {
|
||||
inlineFiles.push(file);
|
||||
} else {
|
||||
urlFiles.push(file);
|
||||
const reason = ext !== '.svg' ? 'raster' : 'listed in URL_ICONS';
|
||||
console.log(` URL import (${reason}): ${file}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(` Inline SVGs: ${inlineFiles.length}, URL-based: ${urlFiles.length}`);
|
||||
|
||||
// ── Generate index.ts ──
|
||||
|
||||
const urlImports = urlFiles.map(file => {
|
||||
const ext = path.extname(file).toLowerCase();
|
||||
const name = path.basename(file, path.extname(file)).toLowerCase();
|
||||
const varName = toImportVar(name);
|
||||
const importSuffix = ext === '.svg' ? '?url' : '';
|
||||
return `import ${varName} from './${file}${importSuffix}';`;
|
||||
}).join('\n');
|
||||
|
||||
const inlineEntries = inlineFiles.map(file => {
|
||||
const name = path.basename(file, '.svg').toLowerCase();
|
||||
const raw = fs.readFileSync(path.join(ICONS_DIR, file), 'utf-8');
|
||||
const svg = cleanSvgForInline(raw);
|
||||
const escaped = svg.replace(/`/g, '\\`').replace(/\$/g, '\\$');
|
||||
return ` '${name}': \`${escaped}\`,`;
|
||||
}).join('\n')}
|
||||
}).join('\n');
|
||||
|
||||
const urlEntries = urlFiles.map(file => {
|
||||
const name = path.basename(file, path.extname(file)).toLowerCase();
|
||||
const varName = toImportVar(name);
|
||||
return ` '${name}': ${varName},`;
|
||||
}).join('\n');
|
||||
|
||||
const indexContent = `// Auto-generated icon index
|
||||
// Do not edit manually
|
||||
${urlImports ? '\n' + urlImports + '\n' : ''}
|
||||
export const icons: Record<string, string> = {
|
||||
${inlineEntries}
|
||||
};
|
||||
|
||||
export const iconList = Object.keys(icons);
|
||||
export const iconUrls: Record<string, string> = {
|
||||
${urlEntries}
|
||||
};
|
||||
|
||||
export const iconList = [...Object.keys(icons), ...Object.keys(iconUrls)].sort();
|
||||
|
||||
export function getIcon(name: string): string {
|
||||
return icons[name.toLowerCase()] || '';
|
||||
}
|
||||
|
||||
export function hasIcon(name: string): boolean {
|
||||
return name.toLowerCase() in icons;
|
||||
export function getIconUrl(name: string): string {
|
||||
return iconUrls[name.toLowerCase()] || '';
|
||||
}
|
||||
|
||||
export function hasIcon(name: string): boolean {
|
||||
const key = name.toLowerCase();
|
||||
return key in icons || key in iconUrls;
|
||||
}
|
||||
|
||||
export function isUrlIcon(name: string): boolean {
|
||||
return name.toLowerCase() in iconUrls;
|
||||
}
|
||||
|
||||
export { getIconMetadata } from './metadata';
|
||||
`;
|
||||
|
||||
fs.writeFileSync(INDEX_FILE, indexContent);
|
||||
console.log(`Generated ${INDEX_FILE}`);
|
||||
console.log(`Generated ${INDEX_FILE} (inline: ${inlineFiles.length}, url: ${urlFiles.length})`);
|
||||
|
||||
// Generate metadata.ts
|
||||
const metadataEntries = files.map(file => {
|
||||
const name = path.basename(file, '.svg').toLowerCase();
|
||||
// ── Generate metadata.ts ──
|
||||
|
||||
const allFiles = [...inlineFiles, ...urlFiles];
|
||||
const metadataEntries = allFiles.map(file => {
|
||||
const ext = path.extname(file);
|
||||
const name = path.basename(file, ext).toLowerCase();
|
||||
const known = KNOWN_METADATA[name];
|
||||
|
||||
|
||||
if (known) {
|
||||
return ` ${name}: ${JSON.stringify(known)},`;
|
||||
}
|
||||
|
||||
// Default metadata for unknown icons
|
||||
|
||||
return ` '${name}': { name: '${name}', displayName: '${name}', category: 'other', keywords: [], defaultColor: 'currentColor' },`;
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import React, { useMemo } from "react";
|
||||
import { getIcon, hasIcon, getIconMetadata } from "@/icons/extracted";
|
||||
import {
|
||||
getIcon,
|
||||
hasIcon,
|
||||
getIconMetadata,
|
||||
getIconUrl,
|
||||
isUrlIcon,
|
||||
} from "@/icons/extracted";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
interface ProviderIconProps {
|
||||
@@ -19,21 +25,28 @@ export const ProviderIcon: React.FC<ProviderIconProps> = ({
|
||||
className,
|
||||
showFallback = true,
|
||||
}) => {
|
||||
// 获取图标 SVG
|
||||
// 获取内联 SVG 字符串
|
||||
const iconSvg = useMemo(() => {
|
||||
if (icon && hasIcon(icon)) {
|
||||
if (icon && !isUrlIcon(icon) && hasIcon(icon)) {
|
||||
return getIcon(icon);
|
||||
}
|
||||
return "";
|
||||
}, [icon]);
|
||||
|
||||
// 获取图标 URL(URL_ICONS 列表中的 SVG / 光栅图片)
|
||||
const iconUrl = useMemo(() => {
|
||||
if (icon && isUrlIcon(icon)) {
|
||||
return getIconUrl(icon);
|
||||
}
|
||||
return "";
|
||||
}, [icon]);
|
||||
|
||||
// 计算尺寸样式
|
||||
const sizeStyle = useMemo(() => {
|
||||
const sizeValue = typeof size === "number" ? `${size}px` : size;
|
||||
return {
|
||||
width: sizeValue,
|
||||
height: sizeValue,
|
||||
// 内嵌 SVG 使用 1em 作为尺寸基准,这里同步 fontSize 让图标实际跟随 size 放大
|
||||
fontSize: sizeValue,
|
||||
lineHeight: 1,
|
||||
};
|
||||
@@ -41,14 +54,11 @@ export const ProviderIcon: React.FC<ProviderIconProps> = ({
|
||||
|
||||
// 获取有效颜色:优先使用传入的有效 color,否则从元数据获取 defaultColor
|
||||
const effectiveColor = useMemo(() => {
|
||||
// 只有当 color 是有效的非空字符串时才使用
|
||||
if (color && typeof color === "string" && color.trim() !== "") {
|
||||
return color;
|
||||
}
|
||||
// 否则从元数据获取 defaultColor
|
||||
if (icon) {
|
||||
const metadata = getIconMetadata(icon);
|
||||
// 只有当 defaultColor 不是 currentColor 时才使用
|
||||
if (metadata?.defaultColor && metadata.defaultColor !== "currentColor") {
|
||||
return metadata.defaultColor;
|
||||
}
|
||||
@@ -56,7 +66,7 @@ export const ProviderIcon: React.FC<ProviderIconProps> = ({
|
||||
return undefined;
|
||||
}, [color, icon]);
|
||||
|
||||
// 如果有图标,显示图标
|
||||
// 内联 SVG 渲染(支持 CSS currentColor 着色)
|
||||
if (iconSvg) {
|
||||
return (
|
||||
<span
|
||||
@@ -70,6 +80,22 @@ export const ProviderIcon: React.FC<ProviderIconProps> = ({
|
||||
);
|
||||
}
|
||||
|
||||
// URL-based 图标(大型 SVG / 光栅图片):以 <img> 渲染
|
||||
if (iconUrl) {
|
||||
return (
|
||||
<img
|
||||
src={iconUrl}
|
||||
alt={name}
|
||||
className={cn(
|
||||
"inline-flex items-center justify-center flex-shrink-0 object-contain",
|
||||
className,
|
||||
)}
|
||||
style={{ width: sizeStyle.width, height: sizeStyle.height }}
|
||||
loading="lazy"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Fallback:显示首字母
|
||||
if (showFallback) {
|
||||
const initials = name
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Auto-generated icon index
|
||||
// Do not edit manually
|
||||
|
||||
import _dds from "./dds.svg?url";
|
||||
|
||||
export const icons: Record<string, string> = {
|
||||
aicodemirror: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" style="flex:none;line-height:1" viewBox="0 0 1017.97 1056.47"><title>AICodeMirror</title><path fill="#E4906E" fill-rule="nonzero" d="M944.92 1014.53c-17.29,-9.23 -33.98,-19.28 -50.08,-30.16 -5.39,-3.65 -14.99,-11.7 -28.81,-24.16 -6.06,-5.47 -14.07,-13.51 -24.03,-24.13 -15.15,-16.17 -29.61,-29.9 -41.69,-40.4 -3.98,-3.46 -14.2,-11.02 -30.68,-22.69 -6.24,-4.42 -12.88,-12.15 -18.63,-19.09 -23.98,-29.04 -49.53,-58.44 -76.66,-88.19 -11.93,-13.1 -25.64,-26.11 -35.61,-36.5 -28.72,-29.92 -51.92,-51.96 -78.23,-79.66 -11.24,-11.83 -20.52,-21.3 -27.85,-28.41 -0.56,-0.53 -1.3,-0.84 -2.08,-0.84 -0.51,0 -1.02,0.14 -1.47,0.39 -9.76,5.54 -17.53,14.33 -24.91,23.31 -10.71,13.01 -21.86,26.65 -33.44,40.91 -4.37,5.38 -7.9,9.46 -10.56,12.24 -5.43,5.67 -9.83,10.88 -15.08,15.39 -9.57,8.23 -19.57,16.01 -29.98,23.31 -14.73,10.32 -29.07,20.29 -43.04,29.9 -5.22,3.61 -13.68,9.81 -20.14,15.41 -25.71,22.32 -53.59,46.12 -83.65,71.41 -9.46,7.95 -19.65,16.88 -34.02,29.22 -25.66,22.03 -52.94,40.06 -81.67,55.65 -7.71,4.19 -14.15,8.2 -19.32,12.01 -19.3,14.23 -33.84,25.65 -43.62,34.28 -25.99,22.91 -43.04,37.82 -51.16,44.73 -9.19,7.8 -18.6,17.05 -28.42,25.54 -2.71,2.35 -5.7,3.03 -8.96,2.01 -0.78,-0.24 -1.25,-1.02 -1.1,-1.82 0.36,-2 1.19,-4.1 2.47,-6.32 6.86,-11.81 14.46,-23.09 19.95,-36.03 3.48,-8.23 7.87,-16.52 13.18,-24.89 2.03,-3.19 4.73,-8.77 8.11,-16.74 2.98,-7.02 7.34,-15.05 13.07,-24.12 5.79,-9.14 16,-23.36 30.63,-42.67 7.66,-10.11 19.49,-23.6 35.49,-40.47 4.9,-5.16 12.21,-11.87 21.92,-20.14 12.12,-10.31 23.53,-21.19 34.23,-32.65 11.73,-12.54 16.99,-22.33 27.39,-40.8 2.37,-4.19 6.49,-9.43 12.37,-15.71 8.27,-8.82 17,-17.23 26.21,-25.23 30.11,-26.18 55.17,-47.43 75.17,-63.76 8.66,-7.08 26.42,-21.39 39.65,-30.77 17.11,-12.13 28.62,-20.44 34.53,-24.91 4.5,-3.4 8.93,-6.6 13.3,-10.56 26.03,-23.54 51.66,-45.71 77.28,-70.7 0.42,-0.41 0.51,-1.06 0.21,-1.58 -6.8,-11.78 -12.84,-21.8 -18.11,-30.06 -10.22,-15.99 -22.07,-29.65 -35.57,-40.99 -7.56,-6.36 -18.41,-13.85 -28.65,-20.43 -15.08,-9.66 -30.62,-21.97 -46.63,-36.9 -35.08,-32.73 -67.65,-71.22 -85.32,-115.42 -5.53,-13.85 -10.8,-29.31 -15.8,-46.37 -5.89,-20.13 -12.37,-35.63 -23.22,-51.27 -8.93,-12.9 -15.77,-21.94 -19.58,-35.93 -1.27,-4.67 -2.93,-12.75 -4.99,-24.23 -2.07,-11.54 -6.54,-22.62 -13.41,-33.25 -7.54,-11.68 -13.66,-21.04 -18.33,-28.08 -3.68,-5.53 -7.02,-12.39 -9.63,-18.53 -3.9,-9.18 -8.14,-15.7 -13.6,-23.37 -3.94,-5.53 -5.07,-12.75 0,-18.32 4.14,-4.57 17.49,-3.02 21.56,-1.13 3.86,1.81 8.1,5.13 12.71,9.94 16.16,16.88 26.41,27.77 30.74,32.66 4.69,5.31 11.21,13.79 16.69,19.94 20.19,22.63 36.17,39.74 47.36,59.71 10.46,18.66 16.41,30.42 29.84,44.67 9.32,9.92 17.94,19.33 25.85,28.23 9.01,10.15 19.25,22.95 30.72,38.39 7.54,10.17 13.89,20.11 19.05,29.84 6.39,12.05 10.8,30.19 15.13,41.41 4.88,12.67 12.52,23.25 22.92,31.75 0.58,0.47 6.79,5.44 18.62,14.89 13.54,10.82 23.74,23.47 30.61,37.96 4.55,9.58 7.82,16.16 9.8,19.74 6.62,11.85 14.64,22.05 24.07,30.59 8.99,8.14 17.47,13.2 31.06,22.64 4.28,2.96 6.68,5.98 10.65,2.54 7.08,-6.11 13.73,-10.71 17.96,-14.53 6.12,-5.54 11.71,-11.84 16.79,-18.92 3.5,-4.88 8.77,-10.16 15.19,-14.42 22.77,-15.02 38.17,-31.11 63.32,-55.15 22.13,-21.17 46.22,-47.56 69.25,-66.8 32.17,-26.89 54.99,-45.9 68.46,-57.01 17.15,-14.15 35.82,-30.97 56.02,-50.46 16.06,-15.5 29.25,-27.72 39.57,-36.66 9.78,-8.47 17.55,-14.8 23.31,-18.98 8.52,-6.2 18.55,-10.61 30.56,-15.03 12.34,-4.55 23.44,-11.06 35.67,-17.61 9.07,-4.85 19.76,-9.89 30.05,-8.84 0.5,0.06 0.97,0.32 1.3,0.72 0.85,1.07 1.01,2.48 0.48,4.23 -2.1,6.99 -5.15,13.55 -9.66,20.87 -6.42,10.42 -11.51,19.46 -15.29,27.11 -6.09,12.35 -9.66,19.49 -10.69,21.43 -7.78,14.65 -17.56,27.97 -29.34,39.97 -4.8,4.89 -12.93,12.92 -24.37,24.07 -14.23,13.87 -25.02,30.77 -37.12,50.78 -15.21,25.13 -29.56,48.47 -43.06,70.01 -5.21,8.29 -13.68,15.13 -21.58,21.47 -25.71,20.7 -46.75,41.48 -70.98,64.67 -1.97,1.88 -4.98,4.47 -9.03,7.76 -22.62,18.36 -45.88,35.93 -69.78,52.74 -5.96,4.2 -13.77,11.24 -23.42,21.11 -17.12,17.5 -25.93,26.56 -26.42,27.19 -1.22,1.54 -1.08,3.09 0.41,4.67 14.11,14.81 34.25,37.65 60.42,68.51 11.89,14.01 24.87,27.08 36.03,39.46 8.75,9.7 16.81,22.11 31.82,42.59 2.69,3.68 13.53,16.07 32.5,37.17 5.17,5.76 11.64,14.47 19.4,26.12 16.37,24.6 36.28,56.2 59.73,94.79 4.2,6.92 7.74,12.33 10.62,16.21 5.41,7.29 10.37,13.74 14.92,20.97 6.26,9.94 11.3,19.92 15.11,29.92 4.29,11.27 7.73,19.49 10.32,24.69 7.21,14.5 14.81,28.41 22.8,41.73 3.44,5.75 6.78,13.03 6.11,20.05 -0.07,0.76 -0.71,1.34 -1.48,1.34 -0.25,0 -0.49,-0.06 -0.71,-0.18l0 0.01z"/></svg>`,
|
||||
aicoding: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" style="flex:none;line-height:1" viewBox="0 0 470 470"><title>AICoding</title><path fill="#a78bfa" d="M 33 73 L 137 13 L 263 83 L 159 143 Z"/><path fill="#a78bfa" opacity="0.92" d="M 33 73 L 33 213 L 159 283 L 159 143 Z"/><path fill="#fff" d="M 207 247 L 311 187 L 431 257 L 327 317 Z"/><path fill="#a78bfa" opacity="0.92" d="M 207 247 L 207 387 L 327 457 L 327 317 Z"/><path fill="#fdba74" d="M 327 317 L 431 257 L 431 397 L 327 457 Z"/><path fill="none" stroke="#2b1b4b" stroke-width="22" stroke-linecap="round" stroke-linejoin="round" d="M 33 73 L 137 13 L 263 83 L 159 143 L 33 73"/><path fill="none" stroke="#2b1b4b" stroke-width="22" stroke-linecap="round" stroke-linejoin="round" d="M 33 73 L 33 213 L 159 283 L 159 143"/><path fill="none" stroke="#2b1b4b" stroke-width="22" stroke-linecap="round" stroke-linejoin="round" d="M 159 143 L 263 83 L 263 223"/><path fill="none" stroke="#2b1b4b" stroke-width="22" stroke-linecap="round" stroke-linejoin="round" d="M 207 247 L 311 187 L 431 257 L 327 317 L 207 247"/><path fill="none" stroke="#2b1b4b" stroke-width="22" stroke-linecap="round" stroke-linejoin="round" d="M 207 247 L 207 387 L 327 457 L 327 317"/><path fill="none" stroke="#2b1b4b" stroke-width="22" stroke-linecap="round" stroke-linejoin="round" d="M 327 317 L 431 257 L 431 397 L 327 457"/><path fill="none" stroke="#2b1b4b" stroke-width="22" stroke-linecap="round" stroke-linejoin="round" d="M 245 163 L 365 163"/><path fill="none" stroke="#2b1b4b" stroke-width="22" stroke-linecap="round" stroke-linejoin="round" d="M 175 197 L 335 117"/><path fill="none" stroke="#2b1b4b" stroke-width="22" stroke-linecap="round" stroke-linejoin="round" d="M 365 163 L 365 241"/></svg>`,
|
||||
@@ -73,14 +75,30 @@ export const icons: Record<string, string> = {
|
||||
bailian: `<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>BaiLian</title><path d="M6.336 8.919v6.162l5.335-3.083L6.337 8.92z" fill-opacity=".4"></path><path d="M21.394 5.288s-.006-.006-.01-.006L17.01 2.754 6.336 8.92l5.335 3.082 9.701-5.6.016-.01a.635.635 0 00.006-1.1v-.003z" fill-opacity=".8"></path><path d="M21.71 12.465a.62.62 0 00-.316.085s-.006 0-.009.003l-4.375 2.528 5.05 2.915h.006a2.06 2.06 0 00.28-1.04v-3.855a.637.637 0 00-.636-.636z"></path><path d="M22.06 17.996l-5.05-2.915L6.34 21.242l4.27 2.465s.016.006.022.012a2.102 2.102 0 002.093 0c.006-.003.016-.006.022-.012l8.538-4.93c.003 0 .006-.003.01-.006.321-.183.589-.45.775-.772h-.006l-.004-.003z" fill-opacity=".8"></path><path d="M11.672 11.998l-5.336 3.083-1.444.832-3.605 2.083H1.28c.173.303.416.555.709.738l.078.044.016.01.02.012 4.232 2.442 10.671-6.161-5.335-3.082z"></path><path d="M12.74.29c-.1-.06-.208-.107-.315-.148-.02-.006-.038-.016-.057-.022a2.121 2.121 0 00-.7-.12c-.233 0-.457.038-.668.11l-.031.01a2.196 2.196 0 00-.372.17L2.068 5.222s-.003 0-.006.003c-.324.183-.592.451-.781.773h.006l5.049 2.918L17.01 2.758 12.74.29z" fill-opacity=".6"></path><path d="M1.287 6.001H1.28A2.06 2.06 0 001 7.041v9.915c0 .378.1.735.28 1.043h.007l5.049-2.918V8.919l-5.05-2.918z" fill-opacity=".3"></path></svg>`,
|
||||
};
|
||||
|
||||
export const iconList = Object.keys(icons);
|
||||
export const iconUrls: Record<string, string> = {
|
||||
dds: _dds,
|
||||
};
|
||||
|
||||
export const iconList = [
|
||||
...Object.keys(icons),
|
||||
...Object.keys(iconUrls),
|
||||
].sort();
|
||||
|
||||
export function getIcon(name: string): string {
|
||||
return icons[name.toLowerCase()] || "";
|
||||
}
|
||||
|
||||
export function getIconUrl(name: string): string {
|
||||
return iconUrls[name.toLowerCase()] || "";
|
||||
}
|
||||
|
||||
export function hasIcon(name: string): boolean {
|
||||
return name.toLowerCase() in icons;
|
||||
const key = name.toLowerCase();
|
||||
return key in icons || key in iconUrls;
|
||||
}
|
||||
|
||||
export function isUrlIcon(name: string): boolean {
|
||||
return name.toLowerCase() in iconUrls;
|
||||
}
|
||||
|
||||
export { getIconMetadata } from "./metadata";
|
||||
|
||||
@@ -107,6 +107,13 @@ export const iconMetadata: Record<string, IconMetadata> = {
|
||||
keywords: ["cubence", "api", "relay"],
|
||||
defaultColor: "#4B5563",
|
||||
},
|
||||
dds: {
|
||||
name: "dds",
|
||||
displayName: "DDS",
|
||||
category: "other",
|
||||
keywords: [],
|
||||
defaultColor: "currentColor",
|
||||
},
|
||||
deepseek: {
|
||||
name: "deepseek",
|
||||
displayName: "DeepSeek",
|
||||
|
||||
Reference in New Issue
Block a user