Files
Cyrene_v2/frontend/web/src/components/Icon.tsx
T
Aska cc3eecacac
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
feat(web): 移动端适配——顶栏防换行 + 抽屉式侧边栏
- TopBar: 标题区 min-w-0+truncate 防换行;移动端(sm 下)隐藏心情/WS文字tag、管理/主题按钮shrink-0;新增汉堡按钮(md:hidden)
- Nav: 抽共享 NavContent;桌面 Sidebar(dd:flex) 保持;移动端新增 MobileNav 抽屉(遮罩+弹簧滑入 md:hidden)
- App: 加 menuOpen state,渲染 MobileNav + TopBar 传 onMenu
- Icon: 新增 menu 图标;index.css: 加 mu-drawer-in 滑入动画
- npm run build 绿(77模块);后端未动
2026-09-07 03:13:24 +08:00

162 lines
11 KiB
TypeScript

/**
* MD3-style vector icon set (Material Symbols Rounded, 24 viewBox, currentColor).
* Zero runtime dependency — pure inline SVG paths, tree-shakeable & offline-first.
*
* Usage:
* import { Icon, type IconName } from "./Icon";
* <Icon name="chat" />
* <Icon name="settings" size={20} className="text-[var(--mu-accent)]" />
*/
export type IconName =
| "chart" // 📊 dashboard (bar_chart_rounded)
| "dna" // 🧬 personas (biotech_rounded)
| "chat" // 💬 sessions/chat (chat_bubble_rounded)
| "memory" // 🧠 memories (psychology_rounded)
| "docs" // 📚 rag/docs (menu_book_rounded)
| "plugin" // 🔌 plugins (extension_rounded)
| "channel" // 📡 channels (sensors_rounded)
| "device" // 📲 iot (devices_rounded)
| "robot" // 🤖 llm (smart_toy_rounded)
| "clock" // ⏰ automations (access_time_rounded)
| "agent" // 🧩 agents (hub_rounded)
| "night" // 🌙 backthink (dark_mode_rounded)
| "trend" // 📈 observability (monitoring_rounded)
| "users" // 👥 users (group_rounded)
| "settings" // ⚙ system/settings (settings_rounded)
| "light" // 💡 light (lightbulb_rounded)
| "ac" // ❄️ ac (ac_unit_rounded)
| "mic" // 🎙 voice (mic_rounded)
| "micOff" // 🔇 mute (mic_off_rounded)
| "sun" // ☀ light mode (light_mode_rounded)
| "moon" // ☾ dark mode (dark_mode_rounded)
| "lock" // 🔒 login (lock_rounded)
| "block" // 🚫 forbidden (block_rounded)
| "check" // ✓ done (check_rounded)
| "close" // ✗ error (close_rounded)
| "gear" // ⚙ loading/tool (settings_rounded)
| "file" // 📄 doc (description_rounded)
| "package" // 📦 env/package (inventory_rounded)
| "folder" // 🗂 work/folder (folder_rounded)
| "shield" // 🔐/🛡 security (shield_rounded)
| "storage" // 🗃 storage (storage_rounded)
| "refresh" // 🔄 reload (refresh_rounded)
| "vision" // 👁 vision (visibility_rounded)
| "home" // 🏠 home (home_rounded)
| "arrow" // → direction (arrow_forward_rounded)
| "terminal" // 🖥 code (terminal_rounded)
| "image" // 🖼 attachment/vision (image_rounded)
| "phone" // 📞 voice call / hang up (call_rounded)
| "menu" // ☰ drawer/open nav (menu_rounded);
const ICON_PATHS: Record<IconName, string> = {
chart:
"M4 20V10h3v10H4Zm6.5 0V4h3v16h-3ZM17 20v-7h3v7h-3Z",
dna:
"M12 3c2.1 0 3.9.7 5 1.7-1.1 1-2.9 1.7-5 1.7s-3.9-.7-5-1.7c1.1-1 2.9-1.7 5-1.7Zm0 5c2.4 0 4.5.9 6 2.3l-1.4 1.4A5.9 5.9 0 0 0 12 10c-1.8 0-3.5.8-4.6 2.1L6 10.7A7.7 7.7 0 0 1 12 8Zm0 5c2.4 0 4.5.9 6 2.3l-1.4 1.4A5.9 5.9 0 0 0 12 15c-1.8 0-3.5.8-4.6 2.1l-1.5-1.4A7.5 7.5 0 0 1 12 13Zm0 5c.9 0 1.7.2 2.4.5L15 20.5c-.9.3-2 .5-3 .5s-2.1-.2-3-.5l.6-2.5c.7-.3 1.5-.5 2.4-.5Z",
chat:
"M12 3C6.5 3 2 6.9 2 11.7c0 2.7 1.4 5.1 3.6 6.7-.1.8-.5 2.2-1.8 3.4-.2.2 0 .6.3.5 1.6-.1 3.6-.8 4.7-1.6.9.2 1.9.3 2.9.3 5.5 0 10-3.9 10-8.7S17.5 3 12 3Z",
memory:
"M13 4a2 2 0 0 1 2 2h1a3 3 0 0 1-3 3h-1v1.1c3.1.4 5.5 2.9 6 6H16a2 2 0 0 1-4 0h-.5a2 2 0 0 1-4 0H5.9c.3-2.9 2.4-5.3 5.1-5.9V9H7a3 3 0 0 1-3-3h1a2 2 0 0 1 4 0 3 3 0 0 1 4 0 4.9 4.9 0 0 1 0-2ZM13 18a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z",
docs:
"M12 4C8.7 2.9 5.5 2.9 3 3.5V19c2.5-.6 5.7-.6 9 .5 3.3-1.1 6.5-1.1 9-.5V3.5C18.5 2.9 15.3 2.9 12 4Zm-1 12.5c-1.6-.4-3.1-.5-4-.5v-1c.9 0 2.4.1 4 .5v1Zm0-3c-1.6-.4-3.1-.5-4-.5v-1c.9 0 2.4.1 4 .5v1Zm0-3c-1.6-.4-3.1-.5-4-.5V9c.9 0 2.4.1 4 .5v1Zm1-3c1.6-.4 3.1-.5 4-.5v1c-.9 0-2.4.1-4 .5v-1Zm0 3c1.6-.4 3.1-.5 4-.5v1c-.9 0-2.4.1-4 .5v-1Z",
plugin:
"M12 3c0 1.1.9 2 2 2s2-.9 2-2h4v4c0 1.1-.9 2-2 2s-2-.9-2-2H8v5h3a2 2 0 0 1-2 2s0 0 0 0H5c-1.1 0-2-.9-2-2v-4H1c0-1.1.9-2 2-2h2V3h4c1.1 0 2 .9 2 2s-.9 2-2 2H7C6.4 6 6 5.5 6 5h2c1.1 0 2-.9 2-2h3V3Z",
channel:
"M12 9.5a2.5 2.5 0 0 1 2.5 2.5c0 1.4-1.1 2.5-2.5 2.5S9.5 13.4 9.5 12 10.6 9.5 12 9.5Zm0-6c3.9 0 7.4 1.6 9.9 4.1l-1.4 1.4A12.6 12.6 0 0 0 12 5.5c-2.7 0-5.2.9-7.2 2.3L3.4 6.5A13.4 13.4 0 0 1 12 3.5Zm0 6a2.5 2.5 0 0 1 0 5v3a3.5 3.5 0 0 0 0-7v-1Z",
device:
"M4 7a5 5 0 0 1 5 5c0 1.7-.9 3.2-2.2 4.1L5.6 18H5a3 3 0 0 1-3-3v-4a3 3 0 0 1 3-3h-1Zm6 0h6a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3h-6a3 3 0 0 1-3-3v-6a3 3 0 0 1 3-3Z",
robot:
"M12 3a1.5 1.5 0 0 1 1.5 1.5c0 .5-.2.9-.5 1.2 2.4.6 4.5 2.3 5.5 4.5H20a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h1.5c1-2.2 3.1-3.9 5.5-4.5-.3-.3-.5-.7-.5-1.2A1.5 1.5 0 0 1 12 3ZM8.5 12a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Zm7 0a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Z",
clock:
"M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2Zm0 2c4.4 0 8 3.6 8 8s-3.6 8-8 8-8-3.6-8-8 3.6-8 8-8Zm1 4v5.2l3.5 2-1 1.7L11 14V8h2Z",
agent:
"M12 3c1.7 0 3.2 1.3 3.2 3 0 .7-.2 1.3-.6 1.8l3.1 1.8c.4-.2.9-.3 1.4-.3a3 3 0 0 1 3 3c0 1.7-1.3 3-3 3-.5 0-1-.1-1.4-.4l-3 1.8c.3.4.5 1 .5 1.5a3.2 3.2 0 0 1-3.2 3.1c-1.7 0-3.2-1.3-3.2-3 0-.8.3-1.5.8-2L7.2 15c-.3.2-.7.3-1.1.3a3 3 0 0 1-3-3c0-1.7 1.3-3 3-3 .4 0 .8.1 1.1.3l3-1.8c-.5-.5-.8-1.2-.8-2C9.4 4.3 10.3 3 12 3Z",
night:
"M20.2 14.2A8.5 8.5 0 0 1 9.8 3.8a8.5 8.5 0 1 0 10.4 10.4Z",
trend:
"M3 21v-2h18v2H3Zm2-4v-5h2v5H5Zm4 0V8h2v9H9Zm4 0v-3h2v3h-2Zm4 0V6h2v11h-2Z",
users:
"M9 11a3 3 0 1 0 0-6 3 3 0 0 0 0 6Zm6 0a3 3 0 1 0 0-6 3 3 0 0 0 0 6Zm-6 2c2.3 0 7 1.1 7 3.5V19H2v-2.5C2 14.1 6.7 13 9 13Zm6 0c.6 0 1.4.1 2.2.3 2 .6 3.8 1.6 3.8 3.2V19h-4v-2.5c0-1.1-.7-2-1.8-2.7.4-.1.8-.2 1.3-.2l-.5.4Z",
settings:
"M12 9.5A2.5 2.5 0 1 1 9.5 12 2.5 2.5 0 0 1 12 9.5Zm7.4 7.4a7.5 7.5 0 0 0 0-2.1l2.1-1.6-2-3.5-2.5 1a7.6 7.6 0 0 0-1.8-1.1L14 6.5h-4l-.4 2.7c-.7.3-1.3.6-1.8 1.1l-2.5-1-2 3.5 2.1 1.6a7.5 7.5 0 0 0 0 2.1l-2.1 1.6 2 3.5 2.5-1c.5.4 1.1.8 1.8 1.1l.4 2.7h4l.4-2.7c.7-.3 1.3-.6 1.8-1.1l2.5 1 2-3.5-2.1-1.6Z",
light:
"M12 3c-2.8 0-5 2.2-5 5 0 1.5.7 2.8 1.7 3.7.1.1.3.6.3 1.3h6c0-.7.2-1.2.3-1.3a4.9 4.9 0 0 0 1.7-3.7c0-2.8-2.2-5-5-5Zm-2 16a2 2 0 0 0 4 0h-4Z",
ac:
"M12 4l-1.5 2H15l-1.5-2L12 4Zm0 5.5L10.5 12H15l-1.5-2.5H12Zm0 5.5L10.5 14H15l-1.5-2H12v3ZM9 6h6l1.5 2H7.5L9 6Zm3 4.5h.5L12 9h.5l1.5 2.5h-4l1.5-2.5H12Zm0 5.5h.5L12 14h.5l1.5 2h-4l1.5-2H12v2.5Z",
mic:
"M12 14a3 3 0 0 0 3-3V6a3 3 0 0 0-6 0v5a3 3 0 0 0 3 3Zm0-1a2 2 0 0 1-2-2V6a2 2 0 0 1 4 0v5a2 2 0 0 1-2 2ZM6 11a1 1 0 0 1 2 0c0 2.2 1.8 4 4 4s4-1.8 4-4a1 1 0 0 1 2 0c0 3.2-2.5 5.8-5.5 5.9V20h2a1 1 0 0 1 0 2H9a1 1 0 0 1 0-2h2v-3.1C7.9 16.8 6 14.2 6 11Z",
micOff:
"M12 14a2 2 0 0 0 2-2V6a2 2 0 0 0-4 0v6c0 .4.1.7.3 1l1.7-1.7V3H12Zm0-9a1 1 0 0 1 1 1v5l1.9-1.9A3 3 0 0 0 13 6c0-.6-.4-1-1-1Zm5 6a5 5 0 0 1-2.6 4.4L13 14a3 3 0 0 1-3-3H8.5c0 .7.1 1.3.4 1.9L7 14.8A5.2 5.2 0 0 1 5.6 11H6a1 1 0 0 1 2 0c0 .4.1.8.2 1.2l1.3-1.3A1 1 0 0 1 9.5 11H11V9.6L7.5 6.1A5 5 0 0 1 13 6l2.1 2.1A5 5 0 0 1 17 11Zm-2.9 5.4 1.4 1.4A6.9 6.9 0 0 1 13 16.9V20h2a1 1 0 0 1 0 2H9a1 1 0 0 1 0-2h2v-3.1A6.9 6.9 0 0 1 5 11l1-1 .1 1.4-1.9 1.9a1 1 0 0 0 1.4 1.4l1.8-1.8a6.9 6.9 0 0 0 3.9 2.5l4.3-4.3 1.4 1.4-4.9 4.9c.5.1.9.1 1.4.1Z",
sun:
"M12 17a5 5 0 1 0 0-10 5 5 0 0 0 0 10Zm0-2a3 3 0 1 1 0-6 3 3 0 0 1 0 6Zm0-15h2v3h-2V0Zm0 19h2v3h-2v-3Zm-9-8v-2H0v2h3Zm19 0v-2h3v2h-3ZM12 2.5 12.7 1 19 3l-.5 1.7L12 4.5V2.5ZM11.3 22.5 12.7 21l6.3 2.4-1.4 1.4L12 24.3v-1.8ZM2.5 12 1 12.7 3 19l1.7-.5L4.5 12H2.5ZM21.5 11.3 23 12.7 21 19l-1.7-1.4L20 12h1.5v-.7Z",
moon:
"M20.2 14.2A8.5 8.5 0 0 1 9.8 3.8a8.5 8.5 0 1 0 10.4 10.4Z",
lock:
"M12 2a4 4 0 0 0-4 4v3H7a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-8a2 2 0 0 0-2-2h-1V6a4 4 0 0 0-4-4Zm-2 4a2 2 0 0 1 4 0v3h-4V6Zm2 8a2 2 0 0 1 1 3.7V19a1 1 0 0 1-2 0v-1.3A2 2 0 0 1 12 14Z",
block:
"M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2Zm0 2c4.4 0 8 3.6 8 8 0 1.9-.7 3.7-1.8 5.1L6.9 5.8A7.9 7.9 0 0 1 12 4Zm-6.2 2.9 11.3 11.3A7.9 7.9 0 0 1 12 20c-4.4 0-8-3.6-8-8 0-1.9.7-3.7 1.8-5.1Z",
check:
"M9.5 16.2 5.3 12l-1.4 1.4 5.6 5.7 11.6-11.6L19.7 6 9.5 16.2Z",
close:
"M18.3 5.7 12 12l-6.3-6.3-1.4 1.4L10.6 12l-6.3 6.3 1.4 1.4L12 13.4l6.3 6.3 1.4-1.4L13.4 12l6.3-6.3-1.4-1.4Z",
gear:
"M12 9.5A2.5 2.5 0 1 1 9.5 12 2.5 2.5 0 0 1 12 9.5Zm7.4 7.4a7.5 7.5 0 0 0 0-2.1l2.1-1.6-2-3.5-2.5 1a7.6 7.6 0 0 0-1.8-1.1L14 6.5h-4l-.4 2.7c-.7.3-1.3.6-1.8 1.1l-2.5-1-2 3.5 2.1 1.6a7.5 7.5 0 0 0 0 2.1l-2.1 1.6 2 3.5 2.5-1c.5.4 1.1.8 1.8 1.1l.4 2.7h4l.4-2.7c.7-.3 1.3-.6 1.8-1.1l2.5 1 2-3.5-2.1-1.6Z",
file:
"M6 2a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8l-6-6H6Zm6 2.5V9h4.5L12 4.5ZM8 12h8v1.5H8V12Zm0 3.5h8v1.5H8v-1.5Z",
package:
"M12 3 3 8l9 5 9-5-9-5Zm-8 7v7l8 4.5V18l-8-4.5v-3.5Zm16 0L12 14.5v6.5l8-4.5v-7l-8-4.5 8-4.5Z",
folder:
"M10 4H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-8l-2-2Z",
shield:
"M12 2 4 5v6c0 5.6 3.4 9.7 8 10 4.6-.3 8-4.4 8-10V5l-8-3Zm0 2.2 6 2.3v4.5c0 4.4-2.6 7.9-6 8.2-3.4-.3-6-3.8-6-8.2V6.5l6-2.3ZM8.5 10l1.4-1.4L11 9.7l2.6-2.6L15 8.5 12.4 11l2.6 2.6-1.4 1.4-2.6-2.6-2.6 2.6-1.4-1.4L9.7 11 8.5 10Z",
storage:
"M4 5h16v4H4V5Zm0 5h16v4H4v-4Zm0 5h16v4H4v-4ZM2 7H1v2h2V7Zm0 5H1v2h2v-2Zm0 5H1v2h2v-2Zm2-10v2h14V5H4Zm0 4v2h12v-2H4Zm0 4v2h16v-2H4Z",
refresh:
"M17.6 6.4A8 8 0 1 0 20 12h-2a6 6 0 1 1-1.8-4.3L13 11h7V4l-2.4 2.4Z",
vision:
"M12 5C6.5 5 2.5 9.5 2 12c.5 2.5 4.5 7 10 7s9.5-4.5 10-7c-.5-2.5-4.5-7-10-7Zm0 2.5A4.5 4.5 0 1 1 7.5 12 4.5 4.5 0 0 1 12 7.5Zm0 2A2.5 2.5 0 1 0 14.5 12 2.5 2.5 0 0 0 12 9.5Z",
home:
"M12 3 2 11.5 3.3 13 5 11.6V21h5v-6h4v6h5v-9.4l1.7 1.4L22 11.5 12 3Z",
arrow:
"M5 11h10.2l-3.6-3.6L13 6l6 6-6 6-1.4-1.4 3.6-3.6H5v-2Z",
terminal:
"M4 3a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2H4Zm2.5 4 3 3.5-3 3.5L4.6 12l2.4-2.5-1-1 1-1.5Zm5.5 7h5v1.5h-5V14Z",
image:
"M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2ZM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5Z",
phone:
"M6.6 10.8a15.1 15.1 0 0 0 6.6 6.6l2.2-2.2c.3-.3.7-.4 1-.2 1.1.4 2.3.6 3.6.6.6 0 1 .4 1 1V20c0 .6-.4 1-1 1C10.6 21 3 13.4 3 4c0-.6.4-1 1-1h3.5c.6 0 1 .4 1 1 0 1.3.2 2.5.6 3.6.1.3 0 .7-.2 1l-2.3 2.2Z",
menu:
"M4 6h16v2H4V6Zm0 5h16v2H4v-2Zm0 5h16v2H4v-2Z",
};
export interface IconProps {
name: IconName;
size?: number;
title?: string;
className?: string;
strokeWidth?: number;
}
export function Icon({
name,
size = 18,
title,
className,
}: IconProps): React.ReactNode {
return (
<svg
width={size}
height={size}
viewBox="0 0 24 24"
fill="currentColor"
aria-hidden={title ? undefined : true}
className={className}
role={title ? "img" : undefined}
>
{title ? <title>{title}</title> : null}
<path d={ICON_PATHS[name]} />
</svg>
);
}