import { useState, useCallback, useMemo } from "react"; import { ScriptSlice } from "../domain/valueObject"; import { ScriptEditUseCase } from "../usecase/ScriptEditUseCase"; /** * 剧本服务Hook接口 * 定义剧本服务Hook的所有状态和操作方法 */ export interface UseScriptService { // 响应式状态 /** 当前剧本文本 */ scriptText: string; /** 剧本片段列表 */ scriptSlices: ScriptSlice[]; /** 当前聚焦的剧本片段ID */ focusedSliceId: string; /** 当前聚焦的剧本片段 */ focusedSlice: ScriptSlice | null; /** 当前聚焦的剧本片段文本 */ scriptSliceText: string; /** 用户提示词(可编辑) */ userPrompt: string; /** 加载状态 */ loading: boolean; /** 错误信息 */ error: string | null; // 操作方法 /** 获取剧本数据(用户提示词) */ fetchScriptData: (prompt: string) => Promise; /** 设置当前聚焦的剧本片段 */ setFocusedSlice: (sliceId: string) => void; /** 清除聚焦状态 */ clearFocusedSlice: () => void; /** 快速更新当前聚焦的剧本片段文本(无防抖) */ updateScriptSliceText: (text: string, metaData?: any) => void; /** 更新用户提示词 */ updateUserPrompt: (prompt: string) => void; /** 重置剧本内容到初始状态 */ resetScript: () => void; /** AI生成剧本 */ generateScript: (prompt: string) => Promise; /** 应用剧本 */ applyScript: () => Promise; /** 更新聚焦剧本片段 */ UpdateFocusedSlice: (text: string) => void; } /** * 剧本服务Hook * 提供剧本相关的所有状态管理和操作方法 * 包括剧本数据获取、片段管理、聚焦状态、防抖更新等功能 */ export const useScriptService = (): UseScriptService => { // 响应式状态 const [scriptText, setScriptText] = useState(""); const [scriptSlices, setScriptSlices] = useState([]); const [focusedSliceId, setFocusedSliceId] = useState(""); const [scriptSliceText, setScriptSliceText] = useState(""); const [userPrompt, setUserPrompt] = useState(""); const [initialScriptText, setInitialScriptText] = useState(""); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); // UseCase实例 const [scriptEditUseCase, setScriptEditUseCase] = useState(null); // 防抖定时器 const [debounceTimer, setDebounceTimer] = useState(null); const DEBOUNCE_DELAY = 300; /** * 当前聚焦的剧本片段 */ const focusedSlice = useMemo(() => { return scriptSlices.find(slice => slice.id === focusedSliceId) || null; }, [scriptSlices, focusedSliceId]); /** * 获取剧本数据(用户提示词) * @param prompt 用户提示词 */ const fetchScriptData = useCallback(async (prompt: string): Promise => { try { setLoading(true); setError(null); // 清空当前状态 setScriptText(""); setScriptSlices([]); setFocusedSliceId(""); setScriptSliceText(""); // 更新用户提示词状态 setUserPrompt(prompt); // 保存初始提示词(只在第一次获取时保存) if (!initialScriptText) { setInitialScriptText(prompt); } // 创建新的剧本编辑用例 const newScriptEditUseCase = new ScriptEditUseCase(''); setScriptEditUseCase(newScriptEditUseCase); // 调用AI生成剧本 await newScriptEditUseCase.generateScript(prompt); // 获取生成的剧本文本 const generatedScriptText = newScriptEditUseCase.toString(); setScriptText(generatedScriptText); // 获取剧本片段列表 const slices = newScriptEditUseCase.getScriptSlices(); setScriptSlices(slices); } catch (error) { console.error('获取剧本数据失败:', error); setError(error instanceof Error ? error.message : '获取剧本数据失败'); throw error; } finally { setLoading(false); } }, [initialScriptText]); /** * 设置当前聚焦的剧本片段 * @param sliceId 剧本片段ID */ const setFocusedSlice = useCallback((sliceId: string): void => { setFocusedSliceId(sliceId); // 同步输入框文本为当前聚焦片段的文本 const focusedSlice = scriptSlices.find(slice => slice.id === sliceId); if (focusedSlice) { setScriptSliceText(focusedSlice.text); } else { setScriptSliceText(""); } }, [scriptSlices]); /** * 清除聚焦状态 */ const clearFocusedSlice = useCallback((): void => { setFocusedSliceId(""); setScriptSliceText(""); }, []); /** * 快速更新输入框文本(无防抖) * @param text 新的文本内容 * @param metaData 新的元数据 */ const updateScriptSliceText = useCallback((text: string, metaData?: any): void => { setScriptSliceText(text); // 自动触发防抖更新值对象 // 清除之前的定时器 if (debounceTimer) { clearTimeout(debounceTimer); } // 设置新的防抖定时器 const timer = setTimeout(() => { UpdateFocusedSlice(text, metaData); }, DEBOUNCE_DELAY); setDebounceTimer(timer); }, [debounceTimer]); /** * 执行更新聚焦剧本片段 * @param text 新的文本内容 * @param metaData 新的元数据 */ const UpdateFocusedSlice = useCallback((text: string, metaData?: any): void => { if (!focusedSliceId || !scriptEditUseCase) { return; } const success = scriptEditUseCase.updateScriptSlice( focusedSliceId, text, metaData ); if (success) { // 更新本地片段列表 const slices = scriptEditUseCase.getScriptSlices(); setScriptSlices(slices); } }, [focusedSliceId, scriptEditUseCase]); /** * 更新用户提示词 * @param prompt 新的用户提示词 */ const updateUserPrompt = useCallback((prompt: string): void => { setUserPrompt(prompt); }, []); /** * 重置剧本内容到初始状态 */ const resetScript = useCallback((): void => { if (initialScriptText) { // 重新调用AI生成剧本(fetchScriptData会自动清空状态) fetchScriptData(initialScriptText); } }, [initialScriptText, fetchScriptData]); /** * AI生成剧本 * @param prompt 剧本提示词 */ const generateScript = useCallback(async (prompt: string): Promise => { try { setLoading(true); setError(null); if (!scriptEditUseCase) { throw new Error("剧本编辑用例未初始化"); } await scriptEditUseCase.generateScript(prompt); // 更新片段列表(这里需要根据实际的流式数据处理逻辑来调整) const slices = scriptEditUseCase.getScriptSlices(); setScriptSlices(slices); } catch (error) { console.error("AI生成剧本失败:", error); setError(error instanceof Error ? error.message : "AI生成剧本失败"); throw error; } finally { setLoading(false); } }, [scriptEditUseCase]); /** * 应用剧本 */ const applyScript = useCallback(async (): Promise => { try { setLoading(true); setError(null); if (!scriptEditUseCase) { throw new Error("剧本编辑用例未初始化"); } await scriptEditUseCase.applyScript(); } catch (error) { console.error("应用剧本失败:", error); setError(error instanceof Error ? error.message : "应用剧本失败"); throw error; } finally { setLoading(false); } }, [scriptEditUseCase]); return { // 响应式状态 scriptText, scriptSlices, focusedSliceId, focusedSlice, scriptSliceText, userPrompt, loading, error, // 操作方法 fetchScriptData, setFocusedSlice, clearFocusedSlice, updateScriptSliceText, updateUserPrompt, resetScript, generateScript, applyScript, UpdateFocusedSlice }; };