feat(web): 聊天背景渲染 + 思维链/工具卡富化
- persona.ts: 新增 personaChatBackground(p, orientation),只取本角色对应方向的图 - ChatShell.tsx: 按横/竖屏渲染角色聊天背景(衬底+scrim),null/无图回退纯 Miuix 面 - chat.tsx: 思维链块加图标 chip+spinner+收起预览;工具卡加状态色 chip+ok/fail Tag - 红线: 绝不 fallback 昔涟/其它角色;TemplateCard detail 折叠判定等行为不变
This commit is contained in:
1 parent
7455d09d05
commit
4ad1e465da
3 files changed
+139
-49
No files matched your search
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useMemo, useRef } from "react";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useAppStore } from "../store/useAppStore";
|
||||
import { useModeStore } from "../store/useModeStore";
|
||||
import { useSendChat } from "../lib/useChat";
|
||||
@@ -8,6 +8,7 @@ import { ChatInput } from "../components/ChatInput";
|
||||
import { EmptyState } from "../components/primitives";
|
||||
import { Icon } from "../components/Icon";
|
||||
import { SessionStatusHint } from "../components/status";
|
||||
import { personaChatBackground, type Orientation } from "../lib/persona";
|
||||
|
||||
/**
|
||||
* The single shared page (design/07 §1). All behavior modes live in ONE message
|
||||
@@ -17,9 +18,27 @@ import { SessionStatusHint } from "../components/status";
|
||||
*/
|
||||
export function ChatShell(): React.ReactNode {
|
||||
const messages = useAppStore((s) => s.messages);
|
||||
const currentPersona = useAppStore((s) => s.currentPersona);
|
||||
const { send, sending } = useSendChat();
|
||||
const bottomRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Chat wallpaper follows the current orientation (landscape when wide, else
|
||||
// portrait) and re-evaluates on window resize.
|
||||
const [orientation, setOrientation] = useState<Orientation>(() =>
|
||||
window.innerWidth > window.innerHeight ? "landscape" : "portrait"
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const onResize = () =>
|
||||
setOrientation(window.innerWidth > window.innerHeight ? "landscape" : "portrait");
|
||||
window.addEventListener("resize", onResize);
|
||||
return () => window.removeEventListener("resize", onResize);
|
||||
}, []);
|
||||
|
||||
// The persona's own wallpaper for the current orientation; undefined keeps the
|
||||
// plain Miuix surface (never another role's image).
|
||||
const chatBackground = personaChatBackground(currentPersona, orientation);
|
||||
|
||||
// The latest assistant message is the one being streamed right now; only it
|
||||
// carries the live 唤醒路径 node sequence (WakePath), so the capacity state
|
||||
// reflects the current turn rather than a collapsed historical message.
|
||||
@@ -36,29 +55,42 @@ export function ChatShell(): React.ReactNode {
|
||||
|
||||
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">
|
||||
<SessionStatusHint />
|
||||
{messages.length === 0 ? (
|
||||
<EmptyState
|
||||
icon={<Icon name="chat" size={24} />}
|
||||
title="开始与昔涟对话"
|
||||
hint="模式只改变消息框的呈现方式,会话上下文与记忆保持不变"
|
||||
<div className="relative min-h-0 flex-1">
|
||||
{chatBackground ? (
|
||||
<div className="absolute inset-0 overflow-hidden" aria-hidden>
|
||||
<img
|
||||
src={chatBackground}
|
||||
alt=""
|
||||
draggable={false}
|
||||
className="h-full w-full object-cover"
|
||||
/>
|
||||
) : (
|
||||
messages.map((m) => (
|
||||
<SplitChatBubble
|
||||
key={m.id}
|
||||
message={m}
|
||||
active={
|
||||
m.id === lastAssistantId &&
|
||||
m.role === "assistant" &&
|
||||
(m.streaming ?? false)
|
||||
}
|
||||
<div className="absolute inset-0 bg-[var(--mu-surface-2)]/40" />
|
||||
</div>
|
||||
) : null}
|
||||
<div className="relative h-full overflow-y-auto">
|
||||
<div className="mx-auto flex max-w-2xl flex-col gap-4 p-4">
|
||||
<SessionStatusHint />
|
||||
{messages.length === 0 ? (
|
||||
<EmptyState
|
||||
icon={<Icon name="chat" size={24} />}
|
||||
title="开始与昔涟对话"
|
||||
hint="模式只改变消息框的呈现方式,会话上下文与记忆保持不变"
|
||||
/>
|
||||
))
|
||||
)}
|
||||
<div ref={bottomRef} />
|
||||
) : (
|
||||
messages.map((m) => (
|
||||
<SplitChatBubble
|
||||
key={m.id}
|
||||
message={m}
|
||||
active={
|
||||
m.id === lastAssistantId &&
|
||||
m.role === "assistant" &&
|
||||
(m.streaming ?? false)
|
||||
}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
<div ref={bottomRef} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ChatInput
|
||||
|
||||
Reference in new issue
Block a user