sentio.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. 'use client';
  2. import { create } from 'zustand';
  3. import { persist } from 'zustand/middleware';
  4. import { AppConfig, ResourceModel, ChatMessage, CHAT_MODE, APP_TYPE, IFER_TYPE, RESOURCE_TYPE } from '@/lib/protocol';
  5. import * as CONSTANTS from '@/lib/constants';
  6. import { getSrcPath } from "../path";
  7. // 默认背景:usky
  8. const defaultBackground: ResourceModel = {
  9. resource_id: 'DYNAMIC_usky.mp4',
  10. type: RESOURCE_TYPE.BACKGROUND,
  11. name: 'usky',
  12. link: getSrcPath(`${CONSTANTS.SENTIO_BACKGROUND_DYNAMIC_PATH}/usky.mp4`)
  13. };
  14. // ==================== 聊天记录 ==================
  15. // 最多保留的消息数量(1组对话:1个Human + 1个AI = 2条消息)
  16. const MAX_CHAT_RECORDS = 2;
  17. interface SentioChatRecordState {
  18. chatRecord: ChatMessage[],
  19. addChatRecord: (message: ChatMessage) => void,
  20. getLastRecord: () => ChatMessage | undefined,
  21. updateLastRecord: (message: ChatMessage) => void,
  22. deleteLastRecord: () => void,
  23. clearChatRecord: () => void
  24. }
  25. export const useChatRecordStore = create<SentioChatRecordState>()(
  26. persist(
  27. (set) => ({
  28. chatRecord: [] as ChatMessage[],
  29. addChatRecord: (message: ChatMessage) => set((state) => {
  30. // 添加新消息
  31. const newChatRecord = [...state.chatRecord, message];
  32. // 如果超过最大数量,删除最旧的消息
  33. if (newChatRecord.length > MAX_CHAT_RECORDS) {
  34. return { chatRecord: newChatRecord.slice(newChatRecord.length - MAX_CHAT_RECORDS) };
  35. }
  36. return { chatRecord: newChatRecord };
  37. }),
  38. getLastRecord: () => {
  39. const chatRecord: ChatMessage[] = useChatRecordStore.getState().chatRecord;
  40. return chatRecord.length > 0 ? chatRecord[chatRecord.length - 1] : undefined;
  41. },
  42. updateLastRecord: (message: ChatMessage) => set((state) => ({ chatRecord: [...state.chatRecord.slice(0, -1), message] })),
  43. deleteLastRecord: () => set((state) => ({ chatRecord: [...state.chatRecord.slice(0, -1)] })),
  44. clearChatRecord: () => set((state) => ({ chatRecord: [] })),
  45. }),
  46. {
  47. name: 'sentio-chat-record-storage'
  48. }
  49. )
  50. )
  51. // ==================== 基础设置 ==================
  52. interface SentioBasicState {
  53. sound: boolean,
  54. lipFactor: number,
  55. showThink: boolean
  56. setSound: (sound: boolean) => void
  57. setShowThink: (showThink: boolean) => void
  58. setLipFactor: (weight: number) => void
  59. }
  60. export const useSentioBasicStore = create<SentioBasicState>()(
  61. persist(
  62. (set) => ({
  63. sound: true,
  64. showThink: true,
  65. lipFactor: CONSTANTS.SENTIO_LIPFACTOR_DEFAULT,
  66. setSound: (sound: boolean) => set((state) => ({ sound: sound })),
  67. setShowThink: (showThink: boolean) => set((state) => ({ showThink: showThink })),
  68. setLipFactor: (weight: number) => set((state) => ({ lipFactor: weight }))
  69. }),
  70. {
  71. name: 'sentio-basic-storage'
  72. }
  73. )
  74. )
  75. // ==================== ASR 相关设置 ==================
  76. interface SentioAsrState {
  77. enable: boolean,
  78. engine: string,
  79. infer_type: IFER_TYPE,
  80. settings: { [key: string]: any },
  81. setEnable: (enable: boolean) => void,
  82. setInferType: (infer_type: IFER_TYPE) => void,
  83. setEngine: (engine: string) => void,
  84. setSettings: (settings: { [key: string]: any }) => void,
  85. }
  86. export const useSentioAsrStore = create<SentioAsrState>()(
  87. persist(
  88. (set) => ({
  89. enable: true,
  90. engine: "default",
  91. infer_type: IFER_TYPE.NORMAL,
  92. settings: {},
  93. setEnable: (enable: boolean) => set((state) => ({ enable: enable })),
  94. setInferType: (infer_type: IFER_TYPE) => set((state) => ({ infer_type: infer_type })),
  95. setEngine: (by: string) => set((state) => ({ engine: by })),
  96. setSettings: (by: { [key: string]: any }) => set((state) => ({ settings: by })),
  97. }),
  98. {
  99. name: 'sentio-asr-storage',
  100. }
  101. )
  102. )
  103. // ==================== TTS 相关设置 ==================
  104. interface SentioTtsState {
  105. enable: boolean,
  106. engine: string,
  107. infer_type: IFER_TYPE,
  108. settings: { [key: string]: any },
  109. setEnable: (enable: boolean) => void,
  110. setInferType: (infer_type: IFER_TYPE) => void,
  111. setEngine: (engine: string) => void,
  112. setSettings: (settings: { [key: string]: any }) => void
  113. }
  114. export const useSentioTtsStore = create<SentioTtsState>()(
  115. persist(
  116. (set) => ({
  117. enable: true,
  118. engine: "default",
  119. infer_type: IFER_TYPE.NORMAL,
  120. settings: {},
  121. setEnable: (enable: boolean) => set((state) => ({ enable: enable })),
  122. setInferType: (infer_type: IFER_TYPE) => set((state) => ({ infer_type: infer_type })),
  123. setEngine: (by: string) => set((state) => ({ engine: by })),
  124. setSettings: (by: { [key: string]: any }) => set((state) => ({ settings: by }))
  125. }),
  126. {
  127. name: 'sentio-tts-storage',
  128. }
  129. )
  130. )
  131. // ==================== Agent 相关设置 ==================
  132. interface SentioAgentState {
  133. enable: boolean,
  134. engine: string,
  135. infer_type: IFER_TYPE,
  136. settings: { [key: string]: any },
  137. setEnable: (enable: boolean) => void,
  138. setInferType: (infer_type: IFER_TYPE) => void,
  139. setEngine: (engine: string) => void,
  140. setSettings: (settings: { [key: string]: any }) => void
  141. }
  142. export const useSentioAgentStore = create<SentioAgentState>()(
  143. persist(
  144. (set) => ({
  145. enable: true,
  146. engine: "default",
  147. infer_type: IFER_TYPE.NORMAL,
  148. settings: {},
  149. // setEnable: (enable: boolean) => set((state) => ({ enable: enable })),
  150. setEnable: (enable: boolean) => set((state) => ({})),
  151. setInferType: (infer_type: IFER_TYPE) => set((state) => ({ infer_type: infer_type })),
  152. setEngine: (by: string) => set((state) => ({ engine: by })),
  153. setSettings: (by: { [key: string]: any }) => set((state) => ({ settings: by }))
  154. }),
  155. {
  156. name: 'sentio-agent-storage',
  157. }
  158. )
  159. )
  160. // ==================== 背景选择 ==================
  161. interface SentioBackgroundState {
  162. background: ResourceModel | null,
  163. setBackground: (background: ResourceModel | null) => void
  164. }
  165. export const useSentioBackgroundStore = create<SentioBackgroundState>()(
  166. persist(
  167. (set) => ({
  168. background: defaultBackground,
  169. setBackground: (by: ResourceModel | null) => set((state) => ({ background: by })),
  170. }),
  171. {
  172. name: 'sentio-background-storage',
  173. }
  174. )
  175. )
  176. // ==================== 人物选择 ==================
  177. interface SentioCharacterState {
  178. character: ResourceModel | null,
  179. setCharacter: (character: ResourceModel | null) => void
  180. }
  181. export const useSentioCharacterStore = create<SentioCharacterState>()(
  182. persist(
  183. (set) => ({
  184. character: null as ResourceModel | null,
  185. setCharacter: (by: ResourceModel | null) => set((state) => ({ character: by })),
  186. }),
  187. {
  188. name: 'sentio-character-storage',
  189. }
  190. )
  191. )
  192. // ==================== 聊天模式 ==================
  193. interface SentioChatModeState {
  194. chatMode: CHAT_MODE,
  195. setChatMode: (chatMode: CHAT_MODE) => void
  196. }
  197. export const useSentioChatModeStore = create<SentioChatModeState>()(
  198. persist(
  199. (set) => ({
  200. chatMode: CONSTANTS.SENTIO_CHATMODE_DEFULT,
  201. setChatMode: (by: CHAT_MODE) => set((state) => ({ chatMode: by })),
  202. }),
  203. {
  204. name: 'sentio-chat-mode-storage',
  205. }
  206. )
  207. )
  208. // ==================== 主题 ==================
  209. interface SentioThemeState {
  210. theme: APP_TYPE,
  211. setTheme: (theme: APP_TYPE) => void
  212. }
  213. export const useSentioThemeStore = create<SentioThemeState>()(
  214. persist(
  215. (set) => ({
  216. theme: CONSTANTS.SENTIO_THENE_DEFAULT,
  217. // setTheme: (by: APP_TYPE) => set((state) => ({ theme: by })),
  218. setTheme: (by: APP_TYPE) => set((state) => ({ theme: by })),
  219. }),
  220. {
  221. name: 'sentio-theme-storage',
  222. }
  223. )
  224. )
  225. // ==================== live2d ==================
  226. interface SentioLive2DState {
  227. ready: boolean,
  228. isRunning: boolean,
  229. showControlButton: boolean,
  230. isAutoStarted: boolean, // 标记是否是自动启动(人脸检测触发)
  231. setReady: (enable: boolean) => void,
  232. setIsRunning: (isRunning: boolean) => void,
  233. setShowControlButton: (show: boolean) => void,
  234. setIsAutoStarted: (isAutoStarted: boolean) => void,
  235. toggleRunning: () => void
  236. }
  237. export const useSentioLive2DStore = create<SentioLive2DState>()(
  238. (set) => ({
  239. ready: false,
  240. isRunning: false, // 默认不运行,等待按钮启动
  241. showControlButton: true, // 默认显示控制按钮
  242. isAutoStarted: false, // 默认不是自动启动
  243. setReady: (ready: boolean) => set((state) => ({ ready: ready })),
  244. setIsRunning: (isRunning: boolean) => set((state) => ({ isRunning: isRunning })),
  245. setShowControlButton: (show: boolean) => set((state) => ({ showControlButton: show })),
  246. setIsAutoStarted: (isAutoStarted: boolean) => set((state) => ({ isAutoStarted: isAutoStarted })),
  247. toggleRunning: () => set((state) => ({
  248. isRunning: !state.isRunning,
  249. showControlButton: false, // 点击后隐藏按钮
  250. isAutoStarted: false // 手动启动,标记为非自动启动
  251. })),
  252. })
  253. )
  254. // ==================== 展示模式 ==================
  255. interface SentioPresentationState {
  256. isPresentationMode: boolean,
  257. presentationBackground: ResourceModel | null,
  258. setPresentationMode: (isPresentation: boolean) => void,
  259. setPresentationBackground: (background: ResourceModel | null) => void,
  260. exitPresentationMode: () => void
  261. }
  262. export const useSentioPresentationStore = create<SentioPresentationState>()(
  263. (set) => ({
  264. isPresentationMode: false,
  265. presentationBackground: null,
  266. setPresentationMode: (isPresentation: boolean) => set((state) => ({ isPresentationMode: isPresentation })),
  267. setPresentationBackground: (background: ResourceModel | null) => set((state) => ({ presentationBackground: background })),
  268. exitPresentationMode: () => set((state) => ({
  269. isPresentationMode: false,
  270. presentationBackground: null
  271. })),
  272. })
  273. )