'use client'; import { create } from 'zustand'; import { persist } from 'zustand/middleware'; import { AppConfig, ResourceModel, ChatMessage, CHAT_MODE, APP_TYPE, IFER_TYPE, RESOURCE_TYPE } from '@/lib/protocol'; import * as CONSTANTS from '@/lib/constants'; import { getSrcPath } from "../path"; // 默认背景:usky const defaultBackground: ResourceModel = { resource_id: 'DYNAMIC_usky.mp4', type: RESOURCE_TYPE.BACKGROUND, name: 'usky', link: getSrcPath(`${CONSTANTS.SENTIO_BACKGROUND_DYNAMIC_PATH}/usky.mp4`) }; // ==================== 聊天记录 ================== // 最多保留的消息数量(1组对话:1个Human + 1个AI = 2条消息) const MAX_CHAT_RECORDS = 2; interface SentioChatRecordState { chatRecord: ChatMessage[], addChatRecord: (message: ChatMessage) => void, getLastRecord: () => ChatMessage | undefined, updateLastRecord: (message: ChatMessage) => void, deleteLastRecord: () => void, clearChatRecord: () => void } export const useChatRecordStore = create()( persist( (set) => ({ chatRecord: [] as ChatMessage[], addChatRecord: (message: ChatMessage) => set((state) => { // 添加新消息 const newChatRecord = [...state.chatRecord, message]; // 如果超过最大数量,删除最旧的消息 if (newChatRecord.length > MAX_CHAT_RECORDS) { return { chatRecord: newChatRecord.slice(newChatRecord.length - MAX_CHAT_RECORDS) }; } return { chatRecord: newChatRecord }; }), getLastRecord: () => { const chatRecord: ChatMessage[] = useChatRecordStore.getState().chatRecord; return chatRecord.length > 0 ? chatRecord[chatRecord.length - 1] : undefined; }, updateLastRecord: (message: ChatMessage) => set((state) => ({ chatRecord: [...state.chatRecord.slice(0, -1), message] })), deleteLastRecord: () => set((state) => ({ chatRecord: [...state.chatRecord.slice(0, -1)] })), clearChatRecord: () => set((state) => ({ chatRecord: [] })), }), { name: 'sentio-chat-record-storage' } ) ) // ==================== 基础设置 ================== interface SentioBasicState { sound: boolean, lipFactor: number, showThink: boolean setSound: (sound: boolean) => void setShowThink: (showThink: boolean) => void setLipFactor: (weight: number) => void } export const useSentioBasicStore = create()( persist( (set) => ({ sound: true, showThink: true, lipFactor: CONSTANTS.SENTIO_LIPFACTOR_DEFAULT, setSound: (sound: boolean) => set((state) => ({ sound: sound })), setShowThink: (showThink: boolean) => set((state) => ({ showThink: showThink })), setLipFactor: (weight: number) => set((state) => ({ lipFactor: weight })) }), { name: 'sentio-basic-storage' } ) ) // ==================== ASR 相关设置 ================== interface SentioAsrState { enable: boolean, engine: string, infer_type: IFER_TYPE, settings: { [key: string]: any }, setEnable: (enable: boolean) => void, setInferType: (infer_type: IFER_TYPE) => void, setEngine: (engine: string) => void, setSettings: (settings: { [key: string]: any }) => void, } export const useSentioAsrStore = create()( persist( (set) => ({ enable: true, engine: "default", infer_type: IFER_TYPE.NORMAL, settings: {}, setEnable: (enable: boolean) => set((state) => ({ enable: enable })), setInferType: (infer_type: IFER_TYPE) => set((state) => ({ infer_type: infer_type })), setEngine: (by: string) => set((state) => ({ engine: by })), setSettings: (by: { [key: string]: any }) => set((state) => ({ settings: by })), }), { name: 'sentio-asr-storage', } ) ) // ==================== TTS 相关设置 ================== interface SentioTtsState { enable: boolean, engine: string, infer_type: IFER_TYPE, settings: { [key: string]: any }, setEnable: (enable: boolean) => void, setInferType: (infer_type: IFER_TYPE) => void, setEngine: (engine: string) => void, setSettings: (settings: { [key: string]: any }) => void } export const useSentioTtsStore = create()( persist( (set) => ({ enable: true, engine: "default", infer_type: IFER_TYPE.NORMAL, settings: {}, setEnable: (enable: boolean) => set((state) => ({ enable: enable })), setInferType: (infer_type: IFER_TYPE) => set((state) => ({ infer_type: infer_type })), setEngine: (by: string) => set((state) => ({ engine: by })), setSettings: (by: { [key: string]: any }) => set((state) => ({ settings: by })) }), { name: 'sentio-tts-storage', } ) ) // ==================== Agent 相关设置 ================== interface SentioAgentState { enable: boolean, engine: string, infer_type: IFER_TYPE, settings: { [key: string]: any }, setEnable: (enable: boolean) => void, setInferType: (infer_type: IFER_TYPE) => void, setEngine: (engine: string) => void, setSettings: (settings: { [key: string]: any }) => void } export const useSentioAgentStore = create()( persist( (set) => ({ enable: true, engine: "default", infer_type: IFER_TYPE.NORMAL, settings: {}, // setEnable: (enable: boolean) => set((state) => ({ enable: enable })), setEnable: (enable: boolean) => set((state) => ({})), setInferType: (infer_type: IFER_TYPE) => set((state) => ({ infer_type: infer_type })), setEngine: (by: string) => set((state) => ({ engine: by })), setSettings: (by: { [key: string]: any }) => set((state) => ({ settings: by })) }), { name: 'sentio-agent-storage', } ) ) // ==================== 背景选择 ================== interface SentioBackgroundState { background: ResourceModel | null, setBackground: (background: ResourceModel | null) => void } export const useSentioBackgroundStore = create()( persist( (set) => ({ background: defaultBackground, setBackground: (by: ResourceModel | null) => set((state) => ({ background: by })), }), { name: 'sentio-background-storage', } ) ) // ==================== 人物选择 ================== interface SentioCharacterState { character: ResourceModel | null, setCharacter: (character: ResourceModel | null) => void } export const useSentioCharacterStore = create()( persist( (set) => ({ character: null as ResourceModel | null, setCharacter: (by: ResourceModel | null) => set((state) => ({ character: by })), }), { name: 'sentio-character-storage', } ) ) // ==================== 聊天模式 ================== interface SentioChatModeState { chatMode: CHAT_MODE, setChatMode: (chatMode: CHAT_MODE) => void } export const useSentioChatModeStore = create()( persist( (set) => ({ chatMode: CONSTANTS.SENTIO_CHATMODE_DEFULT, setChatMode: (by: CHAT_MODE) => set((state) => ({ chatMode: by })), }), { name: 'sentio-chat-mode-storage', } ) ) // ==================== 主题 ================== interface SentioThemeState { theme: APP_TYPE, setTheme: (theme: APP_TYPE) => void } export const useSentioThemeStore = create()( persist( (set) => ({ theme: CONSTANTS.SENTIO_THENE_DEFAULT, // setTheme: (by: APP_TYPE) => set((state) => ({ theme: by })), setTheme: (by: APP_TYPE) => set((state) => ({ theme: by })), }), { name: 'sentio-theme-storage', } ) ) // ==================== live2d ================== interface SentioLive2DState { ready: boolean, isRunning: boolean, showControlButton: boolean, isAutoStarted: boolean, // 标记是否是自动启动(人脸检测触发) setReady: (enable: boolean) => void, setIsRunning: (isRunning: boolean) => void, setShowControlButton: (show: boolean) => void, setIsAutoStarted: (isAutoStarted: boolean) => void, toggleRunning: () => void } export const useSentioLive2DStore = create()( (set) => ({ ready: false, isRunning: false, // 默认不运行,等待按钮启动 showControlButton: true, // 默认显示控制按钮 isAutoStarted: false, // 默认不是自动启动 setReady: (ready: boolean) => set((state) => ({ ready: ready })), setIsRunning: (isRunning: boolean) => set((state) => ({ isRunning: isRunning })), setShowControlButton: (show: boolean) => set((state) => ({ showControlButton: show })), setIsAutoStarted: (isAutoStarted: boolean) => set((state) => ({ isAutoStarted: isAutoStarted })), toggleRunning: () => set((state) => ({ isRunning: !state.isRunning, showControlButton: false, // 点击后隐藏按钮 isAutoStarted: false // 手动启动,标记为非自动启动 })), }) ) // ==================== 展示模式 ================== interface SentioPresentationState { isPresentationMode: boolean, presentationBackground: ResourceModel | null, setPresentationMode: (isPresentation: boolean) => void, setPresentationBackground: (background: ResourceModel | null) => void, exitPresentationMode: () => void } export const useSentioPresentationStore = create()( (set) => ({ isPresentationMode: false, presentationBackground: null, setPresentationMode: (isPresentation: boolean) => set((state) => ({ isPresentationMode: isPresentation })), setPresentationBackground: (background: ResourceModel | null) => set((state) => ({ presentationBackground: background })), exitPresentationMode: () => set((state) => ({ isPresentationMode: false, presentationBackground: null })), }) )