feat(web): 前端收敛为「共享消息流 + 按消息 mode 渲染」,并补全 emoji→MD3 图标
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

架构变更(主人要求,design/07 §1/§2 已改定稿):把「9 个行为模式 = 9 个独立页面」改为
「一个共享页面 + 每条消息绑 mode,消息框按对应 mode 渲染」。
- ChatMessage 增 mode?(types.ts);useAppStore addUserMessage/beginAssistant 带 mode;
  useSendChat.send(text, mode) / chatStream body 带 mode(通知后端按 mode 回复)。
- MessageBubble 按 message.mode 分发到 components/messages/ 各形态渲染器
  (chat 气泡 / code 等宽代码块 / home 设备卡 / docs 文档卡 / work 任务卡 / vision 视觉卡)。
- ChatInput 顶部加「行为模式」chip 切换下一条消息的 mode(默认 chat);输入框上方显示当前模式。
- ModeRouter 收敛为只渲染 ChatShell;删除 9 个旧独立 mode 页面;Nav/TopBar 的 mode 改为
  「输入模式」语义;settings 归管理面板(design/10)。

同时补全上一轮 emoji→MD3 图标(约 23 文件):新增 Icon.tsx(MD3 SVG、IconName、零依赖、
离线优先),全部界面 emoji 替换为 <Icon name=... />;正文说明箭头保留。

npm run build 绿(tsc 0 错,79→71 模块,JS 284KB/gzip 86KB)。
This commit is contained in:
Aska committed 2026-09-06 13:32:44 +08:00
1 parent 265b207699
commit 4aab0f501a
28 files changed
+539 -950

No files matched your search

+11 -10
View File
@@ -11,12 +11,13 @@ import { useAppStore } from "../../store/useAppStore";
import { navigateAdmin, type AdminSectionId } from "../router";
import { CardGrid, StatCard } from "../components";
import { Surface, Tag } from "../../components/primitives";
import { Icon, type IconName } from "../../components/Icon";
import { useAutoReload } from "../hooks";
interface DashCard {
id: AdminSectionId;
label: string;
icon: string;
icon: IconName;
value: number | string;
sub?: string;
tone: "ok" | "warn" | "accent" | "neutral" | "err";
@@ -52,16 +53,16 @@ export function DashboardModule(): React.ReactNode {
const runningAgents = agents.filter((a) => a.status === "running").length;
const cards: DashCard[] = [
{ id: "personas", label: "人格", icon: "🧬", value: nPersonas, sub: "启停/默认/路由", tone: "accent" },
{ id: "sessions", label: "活跃会话", icon: "💬", value: nSessions, sub: "渠道 × actor", tone: "ok" },
{ id: "memories", label: "记忆", icon: "🧠", value: nMemories, sub: "三类作用域隔离", tone: "accent" },
{ id: "plugins", label: "插件与能力", icon: "🔌", value: nPlugins, sub: "含 MCP/渠道", tone: "neutral" },
{ id: "channels", label: "渠道", icon: "📡", value: `${nChannels - offlineChannels}/${nChannels}`,
{ id: "personas", label: "人格", icon: "dna", value: nPersonas, sub: "启停/默认/路由", tone: "accent" },
{ id: "sessions", label: "活跃会话", icon: "chat", value: nSessions, sub: "渠道 × actor", tone: "ok" },
{ id: "memories", label: "记忆", icon: "memory", value: nMemories, sub: "三类作用域隔离", tone: "accent" },
{ id: "plugins", label: "插件与能力", icon: "plugin", value: nPlugins, sub: "含 MCP/渠道", tone: "neutral" },
{ id: "channels", label: "渠道", icon: "channel", value: `${nChannels - offlineChannels}/${nChannels}`,
sub: offlineChannels > 0 ? `${offlineChannels} 离线` : "全部在线", tone: offlineChannels > 0 ? "warn" : "ok" },
{ id: "iot", label: "IoT 设备", icon: "📲", value: `${onlineDevices}/${nDevices}`,
{ id: "iot", label: "IoT 设备", icon: "device", value: `${onlineDevices}/${nDevices}`,
sub: onlineDevices === nDevices && nDevices > 0 ? "全部在线" : "设备状态", tone: nDevices > 0 && onlineDevices === nDevices ? "ok" : "neutral" },
{ id: "agents", label: "Agent 运行", icon: "🧩", value: runningAgents, sub: `${agents.length} 个已登记`, tone: runningAgents > 0 ? "accent" : "neutral" },
{ id: "backthink", label: "后台思考活跃度", icon: "🌙", value: backthink.length, sub: "实时 (WS)", tone: backthink.length > 0 ? "warn" : "neutral" },
{ id: "agents", label: "Agent 运行", icon: "agent", value: runningAgents, sub: `${agents.length} 个已登记`, tone: runningAgents > 0 ? "accent" : "neutral" },
{ id: "backthink", label: "后台思考活跃度", icon: "night", value: backthink.length, sub: "实时 (WS)", tone: backthink.length > 0 ? "warn" : "neutral" },
];
return (
@@ -82,7 +83,7 @@ export function DashboardModule(): React.ReactNode {
<CardGrid>
{cards.map((c) => (
<button key={c.id} onClick={() => navigateAdmin(c.id)} className="text-left" title="点击下钻">
<StatCard icon={c.icon} label={c.label} value={c.value} sub={c.sub} tone={c.tone} />
<StatCard icon={<Icon name={c.icon} size={18} />} label={c.label} value={c.value} sub={c.sub} tone={c.tone} />
</button>
))}
</CardGrid>