| 1 | import { create } from 'zustand' |
| 2 | import type { UploadedAsset } from '@shared/generation.js' |
| 3 | import type { SpeechConfig } from '@shared/speech' |
| 4 | import type { |
| 5 | ImageGenerationMessage, |
| 6 | InteractionMode, |
| 7 | SessionDetailAiPanelMode, |
| 8 | SessionDetailChatType, |
| 9 | SessionWorkspaceTab |
| 10 | } from '@renderer/types/session-detail' |
| 11 | |
| 12 | export type { |
| 13 | ImageGenerationMessage, |
| 14 | InteractionMode, |
| 15 | SessionDetailAiPanelMode, |
| 16 | SessionDetailChatType, |
| 17 | SessionWorkspaceTab |
| 18 | } from '@renderer/types/session-detail' |
| 19 | |
| 20 | export interface EditorGuideState { |
| 21 | vertical: number[] |
| 22 | horizontal: number[] |
| 23 | } |
| 24 | |
| 25 | interface HtmlEditorUiStore { |
| 26 | input: string |
| 27 | aiPanelMode: SessionDetailAiPanelMode |
| 28 | chatType: SessionDetailChatType |
| 29 | imagePrompt: string |
| 30 | imageMessages: ImageGenerationMessage[] |
| 31 | imageMessageCache: Record<string, ImageGenerationMessage[]> |
| 32 | loadedImageMessageKeys: Record<string, boolean> |
| 33 | selectedImageModelConfigId: string |
| 34 | imageSize: string |
| 35 | imageCount: number |
| 36 | isGeneratingImage: boolean |
| 37 | imageProgress: { label?: string; progress: number } | null |
| 38 | selectedPageId: string | null |
| 39 | consoleOpen: boolean |
| 40 | previewKey: number |
| 41 | isExportingPdf: boolean |
| 42 | isExportingPng: boolean |
| 43 | isExportingLongImage: boolean |
| 44 | isExportingPptx: boolean |
| 45 | isExportingVideo: boolean |
| 46 | isExportingSlidePack: boolean |
| 47 | isExportingSessionZip: boolean |
| 48 | interactionMode: InteractionMode |
| 49 | workspaceTab: SessionWorkspaceTab |
| 50 | editorSnapEnabled: boolean |
| 51 | editorGridVisible: boolean |
| 52 | editorGridSize: number |
| 53 | editorGuidesByPage: Record<string, EditorGuideState> |
| 54 | thumbnailVersions: Record<string, number> |
| 55 | selectedSelector: string | null |
| 56 | editSelectedSelector: string | null |
| 57 | selectorLabel: string |
| 58 | elementTag: string |
| 59 | elementText: string |
| 60 | pendingAssets: UploadedAsset[] |
| 61 | assetDragActive: boolean |
| 62 | isUploadingAssets: boolean |
| 63 | addPageDialogOpen: boolean |
| 64 | blankPageDialogOpen: boolean |
| 65 | mergeSessionPagesDialogOpen: boolean |
| 66 | mergeTemplatePagesDialogOpen: boolean |
| 67 | blankPageSourceId: string |
| 68 | historyDialogOpen: boolean |
| 69 | pageTitleEditPageId: string | null |
| 70 | pageTitleEditDraft: string |
| 71 | deleteConfirmPageId: string | null |
| 72 | isAddingPage: boolean |
| 73 | isRetryingSinglePage: boolean |
| 74 | isManagingPages: boolean |
| 75 | sidebarCollapsed: boolean |
| 76 | assetPickerOpen: boolean |
| 77 | assetPickerType: 'image' | 'video' |
| 78 | isGeneratingSpeechScript: boolean |
| 79 | speechProgress: { current: number; total: number } | null |
| 80 | speechScriptDialogOpen: boolean |
| 81 | speechConfig: SpeechConfig |
| 82 | |
| 83 | setInput: (input: string) => void |
| 84 | setAiPanelMode: (mode: SessionDetailAiPanelMode) => void |
| 85 | setChatType: (chatType: SessionDetailChatType) => void |
| 86 | setImagePrompt: (input: string) => void |
| 87 | setImageMessages: (messages: ImageGenerationMessage[]) => void |
| 88 | cacheImageMessages: (key: string, messages: ImageGenerationMessage[]) => void |
| 89 | setLoadedImageMessages: (key: string, messages: ImageGenerationMessage[]) => void |
| 90 | addImageMessage: (message: ImageGenerationMessage) => void |
| 91 | addCachedImageMessage: (key: string, message: ImageGenerationMessage) => void |
| 92 | setSelectedImageModelConfigId: (id: string) => void |
| 93 | setImageSize: (size: string) => void |
| 94 | setImageCount: (count: number) => void |
| 95 | setIsGeneratingImage: (generating: boolean) => void |
| 96 | setImageProgress: (progress: { label?: string; progress: number } | null) => void |
| 97 | setSelectedPageId: (pageId: string | null) => void |
| 98 | setConsoleOpen: (open: boolean | ((open: boolean) => boolean)) => void |
| 99 | bumpPreviewKey: () => void |
| 100 | setIsExportingPdf: (isExporting: boolean) => void |
| 101 | setIsExportingPng: (isExporting: boolean) => void |
| 102 | setIsExportingLongImage: (isExporting: boolean) => void |
| 103 | setIsExportingPptx: (isExporting: boolean) => void |
| 104 | setIsExportingVideo: (isExporting: boolean) => void |
| 105 | setIsExportingSlidePack: (isExporting: boolean) => void |
| 106 | setIsExportingSessionZip: (isExporting: boolean) => void |
| 107 | setInteractionMode: (mode: InteractionMode) => void |
| 108 | setWorkspaceTab: (tab: SessionWorkspaceTab) => void |
| 109 | setEditorSnapEnabled: (enabled: boolean) => void |
| 110 | setEditorGridVisible: (visible: boolean) => void |
| 111 | setEditorGridSize: (size: number) => void |
| 112 | addEditorGuide: (pageId: string, axis: keyof EditorGuideState, position: number) => void |
| 113 | moveEditorGuide: ( |
| 114 | pageId: string, |
| 115 | axis: keyof EditorGuideState, |
| 116 | index: number, |
| 117 | position: number |
| 118 | ) => void |
| 119 | removeEditorGuide: (pageId: string, axis: keyof EditorGuideState, index: number) => void |
| 120 | setSelectedElement: ( |
| 121 | selector: string, |
| 122 | label: string, |
| 123 | elementTag?: string, |
| 124 | elementText?: string |
| 125 | ) => void |
| 126 | setEditSelectedElement: (selector: string | null) => void |
| 127 | clearEditSelectedElement: () => void |
| 128 | clearSelectedElement: () => void |
| 129 | addPendingAssets: (assets: UploadedAsset[]) => void |
| 130 | removePendingAsset: (assetId: string) => void |
| 131 | clearPendingAssets: () => void |
| 132 | setAssetDragActive: (active: boolean) => void |
| 133 | setIsUploadingAssets: (isUploading: boolean) => void |
| 134 | bumpThumbnailVersion: (pageId: string) => void |
| 135 | setAddPageDialogOpen: (open: boolean) => void |
| 136 | setBlankPageDialogOpen: (open: boolean) => void |
| 137 | setMergeSessionPagesDialogOpen: (open: boolean) => void |
| 138 | setMergeTemplatePagesDialogOpen: (open: boolean) => void |
| 139 | setBlankPageSourceId: (pageId: string) => void |
| 140 | openBlankPageDialog: (sourcePageId: string) => void |
| 141 | setHistoryDialogOpen: (open: boolean) => void |
| 142 | openPageTitleEdit: (pageId: string, title: string) => void |
| 143 | setPageTitleEditDraft: (title: string) => void |
| 144 | closePageTitleEdit: () => void |
| 145 | setDeleteConfirmPageId: (pageId: string | null) => void |
| 146 | setIsAddingPage: (adding: boolean) => void |
| 147 | setIsRetryingSinglePage: (retrying: boolean) => void |
| 148 | setIsManagingPages: (managing: boolean) => void |
| 149 | toggleSidebarCollapsed: () => void |
| 150 | setAssetPickerOpen: (open: boolean, type?: 'image' | 'video') => void |
| 151 | setIsGeneratingSpeechScript: (v: boolean) => void |
| 152 | setSpeechProgress: (progress: { current: number; total: number } | null) => void |
| 153 | setSpeechScriptDialogOpen: (v: boolean) => void |
| 154 | setSpeechConfig: (config: SpeechConfig) => void |
| 155 | finishAddPage: (selectedPageId?: string | null) => void |
| 156 | resetForPageChange: () => void |
| 157 | resetEditingPageState: () => void |
| 158 | resetForSessionChange: () => void |
| 159 | } |
| 160 | |
| 161 | export const useHtmlEditorUiStore = create<HtmlEditorUiStore>((set) => ({ |
| 162 | input: '', |
| 163 | aiPanelMode: 'chat', |
| 164 | chatType: 'page', |
| 165 | imagePrompt: '', |
| 166 | imageMessages: [], |
| 167 | imageMessageCache: {}, |
| 168 | loadedImageMessageKeys: {}, |
| 169 | selectedImageModelConfigId: '', |
| 170 | imageSize: '16:9', |
| 171 | imageCount: 1, |
| 172 | isGeneratingImage: false, |
| 173 | imageProgress: null, |
| 174 | selectedPageId: null, |
| 175 | consoleOpen: true, |
| 176 | previewKey: 0, |
| 177 | isExportingPdf: false, |
| 178 | isExportingPng: false, |
| 179 | isExportingLongImage: false, |
| 180 | isExportingPptx: false, |
| 181 | isExportingVideo: false, |
| 182 | isExportingSlidePack: false, |
| 183 | isExportingSessionZip: false, |
| 184 | interactionMode: 'preview' as InteractionMode, |
| 185 | workspaceTab: 'preview' as SessionWorkspaceTab, |
| 186 | editorSnapEnabled: true, |
| 187 | editorGridVisible: false, |
| 188 | editorGridSize: 20, |
| 189 | editorGuidesByPage: {}, |
| 190 | thumbnailVersions: {}, |
| 191 | selectedSelector: null, |
| 192 | editSelectedSelector: null, |
| 193 | selectorLabel: '', |
| 194 | elementTag: '', |
| 195 | elementText: '', |
| 196 | pendingAssets: [], |
| 197 | assetDragActive: false, |
| 198 | isUploadingAssets: false, |
| 199 | addPageDialogOpen: false, |
| 200 | blankPageDialogOpen: false, |
| 201 | mergeSessionPagesDialogOpen: false, |
| 202 | mergeTemplatePagesDialogOpen: false, |
| 203 | blankPageSourceId: '', |
| 204 | historyDialogOpen: false, |
| 205 | pageTitleEditPageId: null, |
| 206 | pageTitleEditDraft: '', |
| 207 | deleteConfirmPageId: null, |
| 208 | isAddingPage: false, |
| 209 | isRetryingSinglePage: false, |
| 210 | isManagingPages: false, |
| 211 | sidebarCollapsed: false, |
| 212 | assetPickerOpen: false, |
| 213 | assetPickerType: 'image' as const, |
| 214 | isGeneratingSpeechScript: false, |
| 215 | speechProgress: null, |
| 216 | speechScriptDialogOpen: false, |
| 217 | speechConfig: { |
| 218 | scope: 'all' as const, |
| 219 | length: 'medium' as const, |
| 220 | style: 'conversational' as const |
| 221 | }, |
| 222 | |
| 223 | setInput: (input) => set({ input }), |
| 224 | setAiPanelMode: (aiPanelMode) => set({ aiPanelMode }), |
| 225 | setChatType: (chatType) => set({ chatType }), |
| 226 | setImagePrompt: (imagePrompt) => set({ imagePrompt }), |
| 227 | setImageMessages: (imageMessages) => set({ imageMessages }), |
| 228 | cacheImageMessages: (key, messages) => |
| 229 | set((state) => ({ |
| 230 | imageMessageCache: { |
| 231 | ...state.imageMessageCache, |
| 232 | [key]: messages |
| 233 | } |
| 234 | })), |
| 235 | setLoadedImageMessages: (key, messages) => |
| 236 | set((state) => ({ |
| 237 | imageMessageCache: { |
| 238 | ...state.imageMessageCache, |
| 239 | [key]: messages |
| 240 | }, |
| 241 | loadedImageMessageKeys: { |
| 242 | ...state.loadedImageMessageKeys, |
| 243 | [key]: true |
| 244 | } |
| 245 | })), |
| 246 | addImageMessage: (message) => |
| 247 | set((state) => ({ |
| 248 | imageMessages: [...state.imageMessages, message].slice(-48) |
| 249 | })), |
| 250 | addCachedImageMessage: (key, message) => |
| 251 | set((state) => { |
| 252 | const cached = state.imageMessageCache[key] || [] |
| 253 | return { |
| 254 | imageMessageCache: { |
| 255 | ...state.imageMessageCache, |
| 256 | [key]: [...cached, message].slice(-48) |
| 257 | } |
| 258 | } |
| 259 | }), |
| 260 | setSelectedImageModelConfigId: (selectedImageModelConfigId) => |
| 261 | set({ selectedImageModelConfigId }), |
| 262 | setImageSize: (imageSize) => set({ imageSize }), |
| 263 | setImageCount: (imageCount) => set({ imageCount: Math.max(1, Math.min(4, imageCount)) }), |
| 264 | setIsGeneratingImage: (isGeneratingImage) => set({ isGeneratingImage }), |
| 265 | setImageProgress: (imageProgress) => set({ imageProgress }), |
| 266 | setSelectedPageId: (selectedPageId) => set({ selectedPageId }), |
| 267 | setConsoleOpen: (open) => |
| 268 | set((state) => ({ |
| 269 | consoleOpen: typeof open === 'function' ? open(state.consoleOpen) : open |
| 270 | })), |
| 271 | bumpPreviewKey: () => set((state) => ({ previewKey: state.previewKey + 1 })), |
| 272 | setIsExportingPdf: (isExportingPdf) => set({ isExportingPdf }), |
| 273 | setIsExportingPng: (isExportingPng) => set({ isExportingPng }), |
| 274 | setIsExportingLongImage: (isExportingLongImage) => set({ isExportingLongImage }), |
| 275 | setIsExportingPptx: (isExportingPptx) => set({ isExportingPptx }), |
| 276 | setIsExportingVideo: (isExportingVideo) => set({ isExportingVideo }), |
| 277 | setIsExportingSlidePack: (isExportingSlidePack) => set({ isExportingSlidePack }), |
| 278 | setIsExportingSessionZip: (isExportingSessionZip) => set({ isExportingSessionZip }), |
| 279 | setInteractionMode: (interactionMode) => set({ interactionMode }), |
| 280 | setWorkspaceTab: (workspaceTab) => set({ workspaceTab }), |
| 281 | setEditorSnapEnabled: (editorSnapEnabled) => set({ editorSnapEnabled }), |
| 282 | setEditorGridVisible: (editorGridVisible) => set({ editorGridVisible }), |
| 283 | setEditorGridSize: (editorGridSize) => |
| 284 | set({ editorGridSize: Math.max(4, Math.min(200, Math.round(editorGridSize))) }), |
| 285 | addEditorGuide: (pageId, axis, position) => |
| 286 | set((state) => { |
| 287 | const current = state.editorGuidesByPage[pageId] || { vertical: [], horizontal: [] } |
| 288 | return { |
| 289 | editorGuidesByPage: { |
| 290 | ...state.editorGuidesByPage, |
| 291 | [pageId]: { |
| 292 | ...current, |
| 293 | [axis]: [...current[axis], Number(position.toFixed(1))] |
| 294 | } |
| 295 | } |
| 296 | } |
| 297 | }), |
| 298 | moveEditorGuide: (pageId, axis, index, position) => |
| 299 | set((state) => { |
| 300 | const current = state.editorGuidesByPage[pageId] |
| 301 | if (!current || index < 0 || index >= current[axis].length) return state |
| 302 | const nextAxis = [...current[axis]] |
| 303 | nextAxis[index] = Number(position.toFixed(1)) |
| 304 | return { |
| 305 | editorGuidesByPage: { |
| 306 | ...state.editorGuidesByPage, |
| 307 | [pageId]: { ...current, [axis]: nextAxis } |
| 308 | } |
| 309 | } |
| 310 | }), |
| 311 | removeEditorGuide: (pageId, axis, index) => |
| 312 | set((state) => { |
| 313 | const current = state.editorGuidesByPage[pageId] |
| 314 | if (!current || index < 0 || index >= current[axis].length) return state |
| 315 | return { |
| 316 | editorGuidesByPage: { |
| 317 | ...state.editorGuidesByPage, |
| 318 | [pageId]: { |
| 319 | ...current, |
| 320 | [axis]: current[axis].filter((_, guideIndex) => guideIndex !== index) |
| 321 | } |
| 322 | } |
| 323 | } |
| 324 | }), |
| 325 | // Fix: only reset to preview when currently in preview mode. |
| 326 | // In edit/ai-inspect mode, selecting an element should NOT change the mode. |
| 327 | setSelectedElement: (selectedSelector, selectorLabel, elementTag = '', elementText = '') => |
| 328 | set((state) => ({ |
| 329 | selectedSelector, |
| 330 | selectorLabel, |
| 331 | elementTag, |
| 332 | elementText, |
| 333 | interactionMode: |
| 334 | state.interactionMode === 'preview' ? ('preview' as InteractionMode) : state.interactionMode |
| 335 | })), |
| 336 | setEditSelectedElement: (editSelectedSelector) => set({ editSelectedSelector }), |
| 337 | clearEditSelectedElement: () => set({ editSelectedSelector: null }), |
| 338 | clearSelectedElement: () => |
| 339 | set({ |
| 340 | selectedSelector: null, |
| 341 | selectorLabel: '', |
| 342 | elementTag: '', |
| 343 | elementText: '' |
| 344 | }), |
| 345 | addPendingAssets: (assets) => |
| 346 | set((state) => ({ |
| 347 | pendingAssets: [...state.pendingAssets, ...assets] |
| 348 | })), |
| 349 | removePendingAsset: (assetId) => |
| 350 | set((state) => ({ |
| 351 | pendingAssets: state.pendingAssets.filter((asset) => asset.id !== assetId) |
| 352 | })), |
| 353 | clearPendingAssets: () => set({ pendingAssets: [] }), |
| 354 | setAssetDragActive: (assetDragActive) => set({ assetDragActive }), |
| 355 | setIsUploadingAssets: (isUploadingAssets) => set({ isUploadingAssets }), |
| 356 | bumpThumbnailVersion: (pageId) => |
| 357 | set((state) => ({ |
| 358 | thumbnailVersions: { |
| 359 | ...state.thumbnailVersions, |
| 360 | [pageId]: (state.thumbnailVersions[pageId] || 0) + 1 |
| 361 | } |
| 362 | })), |
| 363 | setAddPageDialogOpen: (addPageDialogOpen) => set({ addPageDialogOpen }), |
| 364 | setBlankPageDialogOpen: (blankPageDialogOpen) => set({ blankPageDialogOpen }), |
| 365 | setMergeSessionPagesDialogOpen: (mergeSessionPagesDialogOpen) => |
| 366 | set({ mergeSessionPagesDialogOpen }), |
| 367 | setMergeTemplatePagesDialogOpen: (mergeTemplatePagesDialogOpen) => |
| 368 | set({ mergeTemplatePagesDialogOpen }), |
| 369 | setBlankPageSourceId: (blankPageSourceId) => set({ blankPageSourceId }), |
| 370 | openBlankPageDialog: (blankPageSourceId) => |
| 371 | set({ |
| 372 | blankPageSourceId, |
| 373 | blankPageDialogOpen: true |
| 374 | }), |
| 375 | setHistoryDialogOpen: (historyDialogOpen) => set({ historyDialogOpen }), |
| 376 | openPageTitleEdit: (pageTitleEditPageId, pageTitleEditDraft) => |
| 377 | set({ |
| 378 | pageTitleEditPageId, |
| 379 | pageTitleEditDraft |
| 380 | }), |
| 381 | setPageTitleEditDraft: (pageTitleEditDraft) => set({ pageTitleEditDraft }), |
| 382 | closePageTitleEdit: () => set({ pageTitleEditPageId: null, pageTitleEditDraft: '' }), |
| 383 | setDeleteConfirmPageId: (deleteConfirmPageId) => set({ deleteConfirmPageId }), |
| 384 | setIsAddingPage: (isAddingPage) => set({ isAddingPage }), |
| 385 | setIsRetryingSinglePage: (isRetryingSinglePage) => set({ isRetryingSinglePage }), |
| 386 | setIsManagingPages: (isManagingPages) => set({ isManagingPages }), |
| 387 | toggleSidebarCollapsed: () => set((state) => ({ sidebarCollapsed: !state.sidebarCollapsed })), |
| 388 | setAssetPickerOpen: (open, type) => |
| 389 | set((state) => ({ |
| 390 | assetPickerOpen: open, |
| 391 | ...(type ? { assetPickerType: type } : { assetPickerType: state.assetPickerType }) |
| 392 | })), |
| 393 | setIsGeneratingSpeechScript: (isGeneratingSpeechScript) => set({ isGeneratingSpeechScript }), |
| 394 | setSpeechProgress: (speechProgress) => set({ speechProgress }), |
| 395 | setSpeechScriptDialogOpen: (speechScriptDialogOpen) => set({ speechScriptDialogOpen }), |
| 396 | setSpeechConfig: (speechConfig) => set({ speechConfig }), |
| 397 | finishAddPage: (selectedPageId) => |
| 398 | set((state) => ({ |
| 399 | isAddingPage: false, |
| 400 | selectedPageId: typeof selectedPageId === 'undefined' ? state.selectedPageId : selectedPageId |
| 401 | })), |
| 402 | resetForPageChange: () => |
| 403 | set({ |
| 404 | interactionMode: 'preview' as InteractionMode, |
| 405 | selectedSelector: null, |
| 406 | editSelectedSelector: null, |
| 407 | selectorLabel: '', |
| 408 | elementTag: '', |
| 409 | elementText: '' |
| 410 | }), |
| 411 | resetEditingPageState: () => |
| 412 | set({ |
| 413 | interactionMode: 'preview' as InteractionMode, |
| 414 | workspaceTab: 'preview' as SessionWorkspaceTab, |
| 415 | editorSnapEnabled: true, |
| 416 | editorGridVisible: false, |
| 417 | editorGridSize: 20, |
| 418 | editorGuidesByPage: {}, |
| 419 | selectedSelector: null, |
| 420 | editSelectedSelector: null, |
| 421 | selectorLabel: '', |
| 422 | elementTag: '', |
| 423 | elementText: '', |
| 424 | pendingAssets: [], |
| 425 | assetDragActive: false, |
| 426 | assetPickerOpen: false, |
| 427 | isGeneratingSpeechScript: false, |
| 428 | speechProgress: null, |
| 429 | speechScriptDialogOpen: false |
| 430 | }), |
| 431 | resetForSessionChange: () => |
| 432 | set({ |
| 433 | input: '', |
| 434 | aiPanelMode: 'chat', |
| 435 | chatType: 'page', |
| 436 | imagePrompt: '', |
| 437 | imageMessages: [], |
| 438 | imageMessageCache: {}, |
| 439 | loadedImageMessageKeys: {}, |
| 440 | selectedImageModelConfigId: '', |
| 441 | imageSize: '16:9', |
| 442 | imageCount: 1, |
| 443 | isGeneratingImage: false, |
| 444 | imageProgress: null, |
| 445 | selectedPageId: null, |
| 446 | interactionMode: 'preview' as InteractionMode, |
| 447 | workspaceTab: 'preview' as SessionWorkspaceTab, |
| 448 | editorSnapEnabled: true, |
| 449 | editorGridVisible: false, |
| 450 | editorGridSize: 20, |
| 451 | editorGuidesByPage: {}, |
| 452 | selectedSelector: null, |
| 453 | editSelectedSelector: null, |
| 454 | selectorLabel: '', |
| 455 | elementTag: '', |
| 456 | elementText: '', |
| 457 | pendingAssets: [], |
| 458 | assetDragActive: false, |
| 459 | isUploadingAssets: false, |
| 460 | thumbnailVersions: {}, |
| 461 | addPageDialogOpen: false, |
| 462 | blankPageDialogOpen: false, |
| 463 | mergeSessionPagesDialogOpen: false, |
| 464 | mergeTemplatePagesDialogOpen: false, |
| 465 | blankPageSourceId: '', |
| 466 | historyDialogOpen: false, |
| 467 | pageTitleEditPageId: null, |
| 468 | pageTitleEditDraft: '', |
| 469 | deleteConfirmPageId: null, |
| 470 | isAddingPage: false, |
| 471 | isRetryingSinglePage: false, |
| 472 | isManagingPages: false, |
| 473 | sidebarCollapsed: false, |
| 474 | assetPickerOpen: false, |
| 475 | isGeneratingSpeechScript: false, |
| 476 | speechProgress: null, |
| 477 | speechScriptDialogOpen: false, |
| 478 | speechConfig: { |
| 479 | scope: 'all' as const, |
| 480 | length: 'medium' as const, |
| 481 | style: 'conversational' as const |
| 482 | } |
| 483 | }) |
| 484 | })) |
| 485 |