feat(web): 前端收敛为「共享消息流 + 按消息 mode 渲染」,并补全 emoji→MD3 图标
架构变更(主人要求,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:
1 parent
265b207699
commit
4aab0f501a
28 files changed
+539
-950
No files matched your search
@@ -0,0 +1,45 @@
|
||||
import { useEffect, useRef } from "react";
|
||||
import { useAppStore } from "../store/useAppStore";
|
||||
import { useModeStore } from "../store/useModeStore";
|
||||
import { useSendChat } from "../lib/useChat";
|
||||
import { MessageBubble } from "../components/chat";
|
||||
import { ChatInput } from "../components/ChatInput";
|
||||
import { EmptyState } from "../components/primitives";
|
||||
import { Icon } from "../components/Icon";
|
||||
|
||||
/**
|
||||
* The single shared page (design/07 §1). All behavior modes live in ONE message
|
||||
* stream; each message carries its own `mode`, so its bubble renders per that
|
||||
* mode — a session can mix chat / code / home / docs / work / vision shapes.
|
||||
* The "input mode" chip above the composer sets the mode of the NEXT message.
|
||||
*/
|
||||
export function ChatShell(): React.ReactNode {
|
||||
const messages = useAppStore((s) => s.messages);
|
||||
const { send, sending } = useSendChat();
|
||||
const bottomRef = useRef<HTMLDivElement>(null);
|
||||
const setMode = useModeStore((s) => s.setMode);
|
||||
|
||||
useEffect(() => {
|
||||
bottomRef.current?.scrollIntoView({ behavior: "smooth", block: "end" });
|
||||
}, [messages]);
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col">
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
<div className="mx-auto flex max-w-2xl flex-col gap-4 p-4">
|
||||
{messages.length === 0 ? (
|
||||
<EmptyState
|
||||
icon={<Icon name="chat" size={24} />}
|
||||
title="开始与昔涟对话"
|
||||
hint="模式只改变消息框的呈现方式,会话上下文与记忆保持不变"
|
||||
/>
|
||||
) : (
|
||||
messages.map((m) => <MessageBubble key={m.id} message={m} />)
|
||||
)}
|
||||
<div ref={bottomRef} />
|
||||
</div>
|
||||
</div>
|
||||
<ChatInput onSend={(t, mode) => void send(t, mode)} sending={sending} onVoice={() => setMode("voice")} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user