feat(frontend): M9 模态 UI(mode 系统+共享 store+WS 客户端 + chat(思维链/工具回显/情绪徽标)/code/Agent 监控/后台思考页 + Miuix 设计 token + PWA);tsc+vite build 通过
This commit is contained in:
1 parent
b3ffd51736
commit
4c48e94659
40 files changed
+5321
No files matched your search
@@ -0,0 +1,87 @@
|
||||
import { useModeStore } from "../store/useModeStore";
|
||||
import { sendUp } from "../lib/ws";
|
||||
import { MODES, type Mode } from "../lib/types";
|
||||
|
||||
const modeMeta: Record<Mode, { label: string; icon: string }> = {
|
||||
chat: { label: "对话", icon: "💬" },
|
||||
code: { label: "代码", icon: "🖥" },
|
||||
home: { label: "家居", icon: "🏠" },
|
||||
work: { label: "工作", icon: "🗂" },
|
||||
docs: { label: "知识库", icon: "📚" },
|
||||
voice: { label: "语音", icon: "🎙" },
|
||||
vision: { label: "视觉", icon: "👁" },
|
||||
agents: { label: "子代理", icon: "🧩" },
|
||||
settings: { label: "设置", icon: "⚙" },
|
||||
};
|
||||
|
||||
function useSwitchMode(): (m: Mode) => void {
|
||||
const setMode = useModeStore((s) => s.setMode);
|
||||
const setSelfMode = useModeStore((s) => s.setSelfMode);
|
||||
return (m: Mode) => {
|
||||
setMode(m);
|
||||
setSelfMode(null);
|
||||
sendUp({ type: "mode_switch", data: { mode: m } });
|
||||
};
|
||||
}
|
||||
|
||||
export function Sidebar(): React.ReactNode {
|
||||
const mode = useModeStore((s) => s.mode);
|
||||
const selfMode = useModeStore((s) => s.selfMode);
|
||||
const switchMode = useSwitchMode();
|
||||
|
||||
return (
|
||||
<nav className="glass hidden h-full w-[var(--mu-sidebar-w)] shrink-0 flex-col gap-1 border-r border-[var(--mu-glass-border)] p-3 md:flex">
|
||||
<div className="px-2 py-2 text-xs font-semibold uppercase tracking-wider text-[var(--mu-text-lo)]">
|
||||
行为模式
|
||||
</div>
|
||||
{MODES.map((m) => {
|
||||
const meta = modeMeta[m];
|
||||
const active = m === mode;
|
||||
return (
|
||||
<button
|
||||
key={m}
|
||||
onClick={() => switchMode(m)}
|
||||
className={`flex items-center gap-2.5 rounded-[var(--mu-radius)] px-3 py-2 text-sm transition-all duration-200 ease-[var(--mu-spring)] active:scale-[0.98] ${
|
||||
active
|
||||
? "bg-[var(--mu-accent)]/15 font-medium text-[var(--mu-accent-hi)]"
|
||||
: "text-[var(--mu-text-mid)] hover:bg-[var(--mu-surface-4)]"
|
||||
}`}
|
||||
>
|
||||
<span className="text-base">{meta.icon}</span>
|
||||
<span>{meta.label}</span>
|
||||
{m === selfMode ? (
|
||||
<span className="ml-auto rounded-full bg-[var(--mu-warn)]/15 px-1.5 py-0.5 text-[10px] text-[var(--mu-warn)]">
|
||||
自切
|
||||
</span>
|
||||
) : null}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
||||
export function BottomNav(): React.ReactNode {
|
||||
const mode = useModeStore((s) => s.mode);
|
||||
const switchMode = useSwitchMode();
|
||||
return (
|
||||
<nav className="glass fixed inset-x-0 bottom-0 z-30 flex h-[var(--mu-bottomnav-h)] items-stretch border-t border-[var(--mu-glass-border)] md:hidden">
|
||||
{MODES.slice(0, 5).map((m) => {
|
||||
const meta = modeMeta[m];
|
||||
const active = m === mode;
|
||||
return (
|
||||
<button
|
||||
key={m}
|
||||
onClick={() => switchMode(m)}
|
||||
className={`flex flex-1 flex-col items-center justify-center gap-0.5 text-[11px] transition-colors duration-200 ${
|
||||
active ? "text-[var(--mu-accent-hi)]" : "text-[var(--mu-text-lo)]"
|
||||
}`}
|
||||
>
|
||||
<span className="text-base">{meta.icon}</span>
|
||||
<span>{meta.label}</span>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user