import { Scale, MessageSquareText, FileSearch, FilePenLine, FilePlus, ScanText, ShieldAlert, ClipboardCheck, ShieldCheck, BrainCircuit, FileCheck, ClipboardList, BarChart3, Building2, Bot, Cpu, Layers, Star, BookOpen, FileText, Mic, Image, Database, Search, BarChart2, PieChart, TrendingUp, Globe, Mail, Phone, Users, Map, Calendar, Clock, AlertCircle, CheckCircle, XCircle, Info, Settings, Home, FolderOpen, Download, Upload, Share2, Edit, Trash, Plus, Minus, } from "lucide-react"; import type { LucideIcon } from "lucide-react"; const ICON_MAP: Record = { scale: Scale, "message-square-text": MessageSquareText, "file-search": FileSearch, "file-pen-line": FilePenLine, "file-plus": FilePlus, "scan-text": ScanText, "shield-alert": ShieldAlert, "clipboard-check": ClipboardCheck, "shield-check": ShieldCheck, "brain-circuit": BrainCircuit, "file-check": FileCheck, "clipboard-list": ClipboardList, "bar-chart-3": BarChart3, "bar-chart-2": BarChart2, "building-2": Building2, bot: Bot, cpu: Cpu, layers: Layers, star: Star, "book-open": BookOpen, "file-text": FileText, mic: Mic, image: Image, database: Database, search: Search, "pie-chart": PieChart, "trending-up": TrendingUp, globe: Globe, mail: Mail, phone: Phone, users: Users, map: Map, calendar: Calendar, clock: Clock, "alert-circle": AlertCircle, "check-circle": CheckCircle, "x-circle": XCircle, info: Info, settings: Settings, home: Home, "folder-open": FolderOpen, download: Download, upload: Upload, "share-2": Share2, edit: Edit, trash: Trash, plus: Plus, minus: Minus, }; /** 检测字符串是否包含 emoji(非 ASCII)*/ function isEmoji(str: string): boolean { return /[^\x00-\x7F]/.test(str); } interface AppIconProps { iconUrl?: string; size?: number; className?: string; } /** 统一渲染 app 图标:emoji 直接显示,Lucide 图标名转组件,未知名用 Bot 兜底 */ export function AppIcon({ iconUrl, size = 20, className = "" }: AppIconProps) { if (!iconUrl) { return ; } if (isEmoji(iconUrl)) { return ( {iconUrl} ); } const Icon = ICON_MAP[iconUrl.toLowerCase()] ?? Bot; return ; }