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,219 @@
|
||||
import { create } from "zustand";
|
||||
import { persist } from "zustand/middleware";
|
||||
import type {
|
||||
AgentInfo,
|
||||
BackthinkItem,
|
||||
ChatMessage,
|
||||
FlowGraph,
|
||||
ToolUsed,
|
||||
WsStatus,
|
||||
} from "../lib/types";
|
||||
import { now, uid } from "../lib/util";
|
||||
|
||||
interface ChatContext {
|
||||
topic?: string;
|
||||
lastAgentMode?: string;
|
||||
notes?: string[];
|
||||
}
|
||||
|
||||
export interface AppStore {
|
||||
sessionId: string;
|
||||
wsStatus: WsStatus;
|
||||
messages: ChatMessage[];
|
||||
mood: string | null;
|
||||
context: ChatContext;
|
||||
agents: AgentInfo[];
|
||||
backthink: BackthinkItem[];
|
||||
flowGraph: FlowGraph | null;
|
||||
deviceUpdates: string[];
|
||||
|
||||
setWsStatus: (status: WsStatus) => void;
|
||||
setSessionId: (id: string) => void;
|
||||
setContext: (patch: Partial<ChatContext>) => void;
|
||||
setMood: (mood: string | null) => void;
|
||||
|
||||
addUserMessage: (text: string) => void;
|
||||
beginAssistant: () => string;
|
||||
appendChunk: (delta: string) => void;
|
||||
endStream: () => void;
|
||||
setThinking: (text: string) => void;
|
||||
addTool: (tool: ToolUsed) => void;
|
||||
|
||||
setAgents: (agents: AgentInfo[]) => void;
|
||||
upsertAgent: (agent: AgentInfo) => void;
|
||||
addBackthink: (item: BackthinkItem) => void;
|
||||
revealBackthink: (id: string, conclusion: string) => void;
|
||||
markBackthinkPushed: (id: string) => void;
|
||||
setFlowGraph: (graph: FlowGraph) => void;
|
||||
addDeviceUpdate: (text: string) => void;
|
||||
|
||||
taskLogs: Record<string, string>;
|
||||
appendTaskLog: (taskId: string, text: string) => void;
|
||||
clearTaskLog: (taskId: string) => void;
|
||||
|
||||
resetChat: () => void;
|
||||
}
|
||||
|
||||
export const useAppStore = create<AppStore>()(
|
||||
persist(
|
||||
(set, get) => ({
|
||||
sessionId: "web",
|
||||
wsStatus: "idle",
|
||||
messages: [],
|
||||
mood: null,
|
||||
context: {},
|
||||
agents: [
|
||||
{ id: "main", name: "昔涟", status: "idle", role: "owner" },
|
||||
],
|
||||
backthink: [],
|
||||
flowGraph: null,
|
||||
deviceUpdates: [],
|
||||
taskLogs: {},
|
||||
|
||||
setWsStatus: (status) => set({ wsStatus: status }),
|
||||
setSessionId: (id) => set({ sessionId: id }),
|
||||
setContext: (patch) =>
|
||||
set((s) => ({ context: { ...s.context, ...patch } })),
|
||||
setMood: (mood) => set({ mood }),
|
||||
|
||||
addUserMessage: (text) =>
|
||||
set((s) => ({
|
||||
messages: [
|
||||
...s.messages,
|
||||
{
|
||||
id: uid(),
|
||||
role: "user",
|
||||
content: text,
|
||||
createdAt: now(),
|
||||
},
|
||||
],
|
||||
})),
|
||||
|
||||
beginAssistant: () => {
|
||||
const id = uid();
|
||||
set((s) => ({
|
||||
messages: [
|
||||
...s.messages,
|
||||
{
|
||||
id,
|
||||
role: "assistant",
|
||||
content: "",
|
||||
streaming: true,
|
||||
tools: [],
|
||||
createdAt: now(),
|
||||
},
|
||||
],
|
||||
}));
|
||||
return id;
|
||||
},
|
||||
|
||||
appendChunk: (delta) =>
|
||||
set((s) => {
|
||||
const messages = s.messages.slice();
|
||||
const last = messages[messages.length - 1];
|
||||
if (!last || last.role !== "assistant") return s;
|
||||
messages[messages.length - 1] = { ...last, content: last.content + delta };
|
||||
return { messages };
|
||||
}),
|
||||
|
||||
endStream: () =>
|
||||
set((s) => {
|
||||
const messages = s.messages.slice();
|
||||
const last = messages[messages.length - 1];
|
||||
if (!last || last.role !== "assistant") return s;
|
||||
messages[messages.length - 1] = { ...last, streaming: false };
|
||||
return { messages };
|
||||
}),
|
||||
|
||||
setThinking: (text) =>
|
||||
set((s) => {
|
||||
const messages = s.messages.slice();
|
||||
const last = messages[messages.length - 1];
|
||||
if (!last || last.role !== "assistant") return s;
|
||||
messages[messages.length - 1] = { ...last, thinking: text };
|
||||
return { messages };
|
||||
}),
|
||||
|
||||
addTool: (tool) =>
|
||||
set((s) => {
|
||||
const messages = s.messages.slice();
|
||||
const last = messages[messages.length - 1];
|
||||
if (!last || last.role !== "assistant") return s;
|
||||
messages[messages.length - 1] = {
|
||||
...last,
|
||||
tools: [...(last.tools ?? []), tool],
|
||||
};
|
||||
return { messages };
|
||||
}),
|
||||
|
||||
setAgents: (agents) => set({ agents }),
|
||||
upsertAgent: (agent) =>
|
||||
set((s) => {
|
||||
const idx = s.agents.findIndex((a) => a.id === agent.id);
|
||||
if (idx === -1) return { agents: [...s.agents, agent] };
|
||||
const agents = s.agents.slice();
|
||||
agents[idx] = { ...agents[idx], ...agent };
|
||||
return { agents };
|
||||
}),
|
||||
|
||||
addBackthink: (item) =>
|
||||
set((s) => ({ backthink: [item, ...s.backthink] })),
|
||||
revealBackthink: (id, conclusion) =>
|
||||
set((s) => ({
|
||||
backthink: s.backthink.map((b) =>
|
||||
b.id === id ? { ...b, conclusion, shouldNotify: true } : b
|
||||
),
|
||||
})),
|
||||
markBackthinkPushed: (id) =>
|
||||
set((s) => ({
|
||||
backthink: s.backthink.map((b) =>
|
||||
b.id === id ? { ...b, pushed: true } : b
|
||||
),
|
||||
})),
|
||||
|
||||
setFlowGraph: (graph) => set({ flowGraph: graph }),
|
||||
addDeviceUpdate: (text) =>
|
||||
set((s) => ({ deviceUpdates: [text, ...s.deviceUpdates].slice(0, 50) })),
|
||||
|
||||
appendTaskLog: (taskId, text) =>
|
||||
set((s) => ({
|
||||
taskLogs: {
|
||||
...s.taskLogs,
|
||||
[taskId]: (s.taskLogs[taskId] ?? "") + text,
|
||||
},
|
||||
})),
|
||||
clearTaskLog: (taskId) =>
|
||||
set((s) => {
|
||||
if (!(taskId in s.taskLogs)) return s;
|
||||
const taskLogs = { ...s.taskLogs };
|
||||
delete taskLogs[taskId];
|
||||
return { taskLogs };
|
||||
}),
|
||||
|
||||
resetChat: () =>
|
||||
set((s) => ({
|
||||
messages: [],
|
||||
mood: null,
|
||||
flowGraph: null,
|
||||
backthink: s.backthink,
|
||||
})),
|
||||
}),
|
||||
{
|
||||
name: "cyrene.app",
|
||||
version: 1,
|
||||
partialize: (s) => ({
|
||||
sessionId: s.sessionId,
|
||||
messages: s.messages,
|
||||
mood: s.mood,
|
||||
context: s.context,
|
||||
backthink: s.backthink,
|
||||
}),
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
/** Convenience selector for the last streaming assistant message id. */
|
||||
export function lastStreamingMessage(s: AppStore): ChatMessage | undefined {
|
||||
const last = s.messages[s.messages.length - 1];
|
||||
return last?.role === "assistant" ? last : undefined;
|
||||
}
|
||||
Reference in new issue
Block a user