| 1 | import { useMemo } from 'react' |
| 2 | import { ipc } from '@renderer/lib/ipc' |
| 3 | import { useGenerateStore, useSessionDetailUiStore, useToastStore } from '@renderer/store' |
| 4 | import { useT } from '@renderer/i18n' |
| 5 | import { useModelAction } from '@renderer/hooks/useModelAction' |
| 6 | import type { SpeechConfig } from '@shared/speech' |
| 7 | import { normalizePagesForSelection } from '../shared/pageUtils' |
| 8 | |
| 9 | export function useSpeechScriptDrawerController(sessionId: string) { |
| 10 | const t = useT() |
| 11 | const modelAction = useModelAction() |
| 12 | const currentPages = useGenerateStore((state) => state.currentPages) |
| 13 | const selectedPageId = useSessionDetailUiStore((state) => state.selectedPageId) |
| 14 | const open = useSessionDetailUiStore((state) => state.speechScriptDialogOpen) |
| 15 | const isGenerating = useSessionDetailUiStore((state) => state.isGeneratingSpeechScript) |
| 16 | const speechProgress = useSessionDetailUiStore((state) => state.speechProgress) |
| 17 | const speechConfig = useSessionDetailUiStore((state) => state.speechConfig) |
| 18 | const setSpeechConfig = useSessionDetailUiStore((state) => state.setSpeechConfig) |
| 19 | const setOpen = useSessionDetailUiStore((state) => state.setSpeechScriptDialogOpen) |
| 20 | const toastError = useToastStore((state) => state.error) |
| 21 | |
| 22 | const pages = useMemo(() => normalizePagesForSelection(currentPages), [currentPages]) |
| 23 | const selectedPage = useMemo( |
| 24 | () => pages.find((page) => page.id === selectedPageId) ?? pages[0] ?? null, |
| 25 | [pages, selectedPageId] |
| 26 | ) |
| 27 | |
| 28 | const handleGenerate = async (config: SpeechConfig): Promise<void> => { |
| 29 | const detailState = useSessionDetailUiStore.getState() |
| 30 | if (!sessionId || detailState.isGeneratingSpeechScript) return |
| 31 | detailState.setIsGeneratingSpeechScript(true) |
| 32 | detailState.setSpeechProgress(null) |
| 33 | try { |
| 34 | const modelConfigId = await modelAction.ensureModelActive(config.modelConfigId) |
| 35 | if (!modelConfigId) return |
| 36 | const result = await ipc.generateSpeechScript(sessionId, { |
| 37 | ...config, |
| 38 | modelConfigId, |
| 39 | currentPageId: config.scope === 'single' ? selectedPage?.id : undefined |
| 40 | }) |
| 41 | if (!result.success) { |
| 42 | toastError(t('sessionDetail.speechScriptError')) |
| 43 | } |
| 44 | } catch (error) { |
| 45 | toastError(error instanceof Error ? error.message : t('sessionDetail.speechScriptError')) |
| 46 | } finally { |
| 47 | const state = useSessionDetailUiStore.getState() |
| 48 | state.setIsGeneratingSpeechScript(false) |
| 49 | state.setSpeechProgress(null) |
| 50 | } |
| 51 | } |
| 52 | |
| 53 | return { |
| 54 | open, |
| 55 | isGenerating, |
| 56 | speechProgress, |
| 57 | speechConfig, |
| 58 | modelAction, |
| 59 | currentPageNumber: selectedPage?.pageNumber, |
| 60 | currentPageTitle: selectedPage?.title || undefined, |
| 61 | setSpeechConfig, |
| 62 | generate: (config: SpeechConfig) => void handleGenerate(config), |
| 63 | close: () => { |
| 64 | setOpen(false) |
| 65 | useSessionDetailUiStore.getState().setWorkspaceTab('preview') |
| 66 | } |
| 67 | } |
| 68 | } |
| 69 |