返回 oh-my-ppt
ipc.ts
根目录 / src / renderer / src / lib / ipc.ts
1 import type {
2 FontSelection,
3 GenerateAddPagePayload,
4 GenerateChunkEvent,
5 GenerateRetryFailedPayload,
6 GenerateRetrySinglePagePayload,
7 GenerateStartPayload,
8 SessionPageEditAssessment,
9 ParseDocumentPlanPayload,
10 ParseImageReferencePayload,
11 ParsedDocumentPlanResult,
12 PrepareReferenceDocumentPayload,
13 PreparedReferenceDocumentResult,
14 SourceDocumentPlan,
15 SwitchSessionStylePayload,
16 RetrySessionStylePayload,
17 RetryDeckEditPayload,
18 PptxImportPayload,
19 PptxImportProgressPayload,
20 PptxImportResult,
21 UploadedAsset
22 } from '@shared/generation.js'
23 import type { UpdateAvailablePayload } from '@shared/app-update.js'
24 import type { SpeechConfig } from '@shared/speech'
25 import type { HistoryVersion, RollbackHistoryResult } from '@shared/history.js'
26 import type { HtmlThumbnailResourceType } from '@shared/thumbnail'
27 import type { IndexTransitionConfig, IndexTransitionType } from '@shared/index-transition.js'
28 import type { ElementAnimationConfig, ElementAnimationPatch } from '@shared/element-animation.js'
29 import type {
30 ThinkingStage,
31 ThinkingChatMessage,
32 ThinkingWorkspace,
33 ThinkingChatResult,
34 ThinkingPrepareGenerationResult,
35 ThinkingWorkspaceListItem
36 } from '@shared/thinking.js'
37 import type {
38 GeneratedImageAsset,
39 ImageGenerateResult,
40 ImageGeneratePayload,
41 ImagePromptGeneratePayload,
42 ImagePromptGenerateResult,
43 ImageGenerationHistoryRecord,
44 ImageModelConfig,
45 ImageModelProvider,
46 ImageModelVerificationResult
47 } from '@shared/image-generation.js'
48 import type { ThinkingParameterMode } from '@shared/model-config.js'
49 import type { ExportProgressPayload } from '@shared/export-progress.js'
50 import type { PageMergeDisabledReason } from '@shared/page-merge'
51 import type { ModelUsagePeriod, ModelUsageStats } from '@shared/model-usage'
52 import type { SlideSizePresetId } from '@shared/slide-size'
53 import type { ParsedChartDataResult } from '@shared/chart-data'
54 import type { SessionMasterConfig, SessionMasterStatus } from '@shared/master'
55 import type { SessionLayoutLibrary, SessionLayoutLibraryStatus } from '@shared/layout-master'
56
57 type IpcRendererLike = Window['electron']['ipcRenderer']
58
59 function getIpc(): IpcRendererLike {
60 const ipc = window.electron?.ipcRenderer
61 if (!ipc) {
62 const electronKeys = window.electron ? Object.keys(window.electron).join(', ') : 'none'
63 throw new Error(`Electron preload IPC is unavailable. window.electron keys: ${electronKeys}`)
64 }
65 return ipc
66 }
67
68 export interface StyleCategory {
69 name: string
70 styles: Array<{
71 id: string
72 label: string
73 description: string
74 source?: 'builtin' | 'custom' | 'override'
75 editable?: boolean
76 styleCase?: string
77 imageGenerationPrompt?: string
78 }>
79 }
80
81 export type ImageFulfillmentJobView = {
82 id: string
83 session_id: string
84 session_page_id: string
85 page_id: string
86 status: 'pending' | 'running' | 'finalizing' | 'completed' | 'degraded' | 'failed' | 'cancelled'
87 error: string | null
88 created_at: number
89 updated_at: number
90 layout_failed_count: number
91 retryable_intent_count: number
92 }
93
94 export interface StyleDetail {
95 id: string
96 styleKey?: string
97 label: string
98 name?: {
99 zh: string
100 en: string
101 }
102 description: string
103 aliases: string[]
104 styleSkill: string
105 source?: 'builtin' | 'custom' | 'override'
106 editable?: boolean
107 category?: string
108 version?: string
109 styleCase?: string
110 imageGenerationPrompt?: string
111 packageDir?: string
112 }
113
114 export interface StyleListItem {
115 id: string
116 styleKey?: string
117 label: string
118 name?: {
119 zh: string
120 en: string
121 }
122 description: string
123 aliases?: string[]
124 category: string
125 source?: 'builtin' | 'custom' | 'override'
126 editable?: boolean
127 version?: string
128 styleCase?: string
129 imageGenerationPrompt?: string
130 packageDir?: string
131 favoriteAt?: number | null
132 previewPath?: string | null
133 thumbnailPath?: string | null
134 createdAt?: number
135 updatedAt?: number
136 }
137
138 export interface HtmlThumbnailTask {
139 resourceType: HtmlThumbnailResourceType
140 resourceId: string
141 variant: string
142 status: 'queued' | 'running' | 'completed' | 'failed'
143 thumbnailPath: string | null
144 error?: string
145 }
146
147 export interface StyleParseResult {
148 label: string
149 description: string
150 category: string
151 aliases: string[]
152 styleSkill: string
153 styleCase?: string
154 imageGenerationPrompt?: string
155 }
156
157 export interface GenerateRunStateSnapshot {
158 sessionId: string
159 runId: string | null
160 status: 'idle' | 'queued' | 'running' | 'completed' | 'failed' | 'cancelled'
161 hasActiveRun: boolean
162 progress: number
163 totalPages: number
164 completedPageCount?: number
165 failedPageCount?: number
166 events: GenerateChunkEvent[]
167 error: string | null
168 startedAt: number | null
169 updatedAt: number | null
170 kind?:
171 | 'standard'
172 | 'template'
173 | 'retry'
174 | 'add-page'
175 | 'single-page-retry'
176 | 'edit'
177 | 'page-edit'
178 | 'deck-edit'
179 | 'style-switch'
180 targetPageId?: string
181 targetPageNumber?: number
182 activityKind?:
183 | 'page-edit'
184 | 'deck-edit'
185 | 'edit'
186 | 'style-switch'
187 | 'single-page-retry'
188 | 'addPage'
189 retryPayload?: GenerateStartPayload
190 }
191
192 export interface StyleSwitchJobSnapshot {
193 sessionId: string
194 runId: string | null
195 status: 'idle' | 'running' | 'completed' | 'partial' | 'failed' | 'cancelled'
196 hasActiveRun: boolean
197 progress: number
198 totalPages: number
199 completedPageCount: number
200 failedPageCount: number
201 targetStyleId: string | null
202 targetStyleName: string | null
203 pages: Array<{
204 pageId: string
205 pageNumber: number
206 title: string
207 status: 'pending' | 'running' | 'completed' | 'failed'
208 error: string | null
209 retryCount: number
210 }>
211 error: string | null
212 startedAt: number | null
213 updatedAt: number | null
214 kind: 'style-switch'
215 }
216
217 export interface ExportDeckResult {
218 success: boolean
219 cancelled?: boolean
220 path?: string
221 warnings?: string[]
222 pageCount?: number
223 durationMs?: number
224 frameCount?: number
225 }
226
227 export interface MergeSourceSessionSummary {
228 id: string
229 title: string
230 pageCount: number
231 slideSizeId: SlideSizePresetId
232 slideWidth: number
233 slideHeight: number
234 updatedAt: number
235 status: string
236 selectable: boolean
237 disabledReason?: PageMergeDisabledReason
238 }
239
240 export interface MergeTemplateSourceSummary {
241 id: string
242 title: string
243 pageCount: number
244 slideSizeId: SlideSizePresetId
245 slideWidth: number
246 slideHeight: number
247 updatedAt: number
248 thumbnailPath: string | null
249 selectable: boolean
250 disabledReason?: PageMergeDisabledReason
251 isSource: boolean
252 }
253
254 export interface MergeSourcePageSummary {
255 id: string
256 pageId: string
257 pageNumber: number
258 title: string
259 contentOutline?: string | null
260 slideSizeId: SlideSizePresetId
261 slideWidth: number
262 slideHeight: number
263 htmlPath?: string
264 sourceUrl?: string
265 status?: string
266 selectable: boolean
267 disabledReason?: PageMergeDisabledReason
268 }
269
270 export interface ImportSessionFileResult {
271 success: boolean
272 cancelled?: boolean
273 sessionId?: string
274 title?: string
275 pageCount?: number
276 warnings?: string[]
277 }
278
279 export interface HtmlEditorImportResult {
280 docId: string
281 title: string
282 htmlPath: string
283 sourcePath: string
284 designWidth: number
285 html: string
286 }
287
288 export interface HtmlEditorAiMessage {
289 role: 'user' | 'assistant'
290 content: string
291 selectedElement?: HtmlEditorAiElementContext
292 }
293
294 export interface HtmlEditorAiElementContext {
295 selector: string
296 label?: string
297 elementTag?: string
298 elementText?: string
299 html?: string
300 }
301
302 export interface HtmlEditorAiEditBatch {
303 propertyEdits: Array<Record<string, unknown>>
304 textEdits: Array<Record<string, unknown>>
305 dragEdits: Array<Record<string, unknown>>
306 deletes: Array<Record<string, unknown>>
307 addElements: Array<Record<string, unknown>>
308 }
309
310 export type HtmlEditorAiIntent = 'inspect' | 'redesign' | 'style' | 'layout' | 'content' | 'other'
311
312 export interface HtmlEditorAiPlan {
313 intent: HtmlEditorAiIntent
314 target: string
315 summary: string
316 changes: string[]
317 confirmationQuestion: string
318 edits: HtmlEditorAiEditBatch
319 }
320
321 export interface HtmlEditorAiHistoryMessage extends HtmlEditorAiMessage {
322 id: string
323 createdAt: number
324 intent?: HtmlEditorAiIntent
325 plan?: HtmlEditorAiPlan | null
326 requiresConfirmation?: boolean
327 }
328
329 export type HtmlEditorFileImportResult =
330 | { cancelled: true; reason?: 'user-cancelled' | 'storage-not-configured' }
331 | ({ cancelled: false } & HtmlEditorImportResult)
332
333 export interface TemplateListItem {
334 id: string
335 name: string
336 description: string
337 source: 'user'
338 pageCount: number
339 tags: string[]
340 slideSizeId?: import('@shared/slide-size').SlideSizePresetId
341 slideWidth?: number
342 slideHeight?: number
343 previewHtmlPath: string | null
344 thumbnailPath: string | null
345 previewPages: Array<{
346 pageNumber: number
347 pageId: string
348 title: string
349 htmlPath: string
350 }>
351 createdAt: number
352 updatedAt: number
353 }
354
355 export interface EnsureElementAnchorPayload {
356 sessionId?: string
357 htmlPath: string
358 pageId: string
359 selector: string
360 elementTag?: string
361 elementText?: string
362 reason?: 'inspect' | 'drag' | 'text-edit'
363 formula?: {
364 latex: string
365 html: string
366 displayMode: boolean
367 }
368 }
369
370 export interface EnsureElementAnchorResult {
371 success: boolean
372 selector: string
373 blockId: string
374 changed: boolean
375 }
376
377 export interface UploadAssetsPayload {
378 sessionId: string
379 files: Array<{
380 path: string
381 name?: string
382 }>
383 }
384
385 export interface UpdateElementLayoutPayload {
386 sessionId: string
387 htmlPath: string
388 pageId: string
389 selector: string
390 x: number
391 y: number
392 width?: number
393 height?: number
394 childUpdates?: Array<{
395 path: number[]
396 width?: number
397 height?: number
398 }>
399 isAbsoluteMode?: boolean
400 }
401
402 export interface UpdateElementPropertiesPayload {
403 sessionId: string
404 htmlPath: string
405 pageId: string
406 selector: string
407 patch: {
408 html?: string
409 text?: string
410 textTarget?: {
411 type: 'text-node'
412 parentSelector: string
413 textNodeIndex: number
414 text: string
415 }
416 style?: {
417 color?: string
418 fontSize?: string
419 fontWeight?: string
420 textAlign?: string
421 }
422 }
423 }
424
425 export interface CreateSessionPayload {
426 topic: string
427 styleId: string
428 modelConfigId?: string
429 visualEnabled?: boolean
430 imageModelConfigId?: string
431 pageCount?: number
432 slideSizeId?: import('@shared/slide-size').SlideSizePresetId
433 referenceDocumentPath?: string
434 fontSelection?: FontSelection
435 sourcePlan?: SourceDocumentPlan
436 }
437
438 export interface SaveSessionAsNewPayload {
439 sessionId: string
440 title: string
441 }
442
443 export interface SaveSessionAsNewResult {
444 sessionId: string
445 }
446
447 export interface ModelConfig {
448 id: string
449 name: string
450 provider: 'anthropic' | 'openai' | 'openai-responses' | 'google'
451 model: string
452 apiKey: string
453 baseUrl: string
454 maxTokens: number
455 disableTemperature: boolean
456 thinkingParameterMode: ThinkingParameterMode
457 active: boolean
458 createdAt: number
459 updatedAt: number
460 }
461
462 export type {
463 GeneratedImageAsset,
464 ImageModelConfig,
465 ImageModelProvider,
466 ImageModelVerificationResult
467 }
468
469 export interface UploadPrerequisitesResult {
470 ready: boolean
471 missing: Array<'storagePath' | 'activeModel' | 'apiKey' | 'model'>
472 message?: string
473 }
474
475 export type FontRole = 'title' | 'body'
476 export type FontScript = 'latin' | 'cjk'
477
478 export interface FontFileEntry {
479 file: string
480 weight: number
481 style: 'normal' | 'italic'
482 size?: number
483 sha256?: string
484 }
485
486 export interface FontListItem {
487 id: string
488 family: string
489 source: 'google' | 'uploaded'
490 category: string
491 role: FontRole[]
492 scripts: FontScript[]
493 files?: FontFileEntry[]
494 createdAt?: number
495 updatedAt?: number
496 }
497
498 export interface FontRegistryResponse {
499 googleFonts: FontListItem[]
500 userFonts: FontListItem[]
501 }
502
503 export interface UploadFontPayload {
504 files: Array<{
505 path: string
506 weight?: number
507 style?: 'normal' | 'italic'
508 }>
509 family: string
510 category?: string
511 role?: FontRole[]
512 scripts?: FontScript[]
513 }
514
515 export const ipc = {
516 getPlatform: (): NodeJS.Platform | 'unknown' => window.electron?.getPlatform?.() ?? 'unknown',
517 getWindowControlState: () =>
518 getIpc().invoke('window:control:getState') as Promise<{ isFullscreen: boolean }>,
519 minimizeWindow: () => getIpc().invoke('window:control:minimize') as Promise<void>,
520 toggleFullscreenWindow: () =>
521 getIpc().invoke('window:control:toggleFullscreen') as Promise<{ isFullscreen: boolean }>,
522 closeWindow: () => getIpc().invoke('window:control:close') as Promise<void>,
523 onWindowControlStateChanged: (
524 callback: (state: { isFullscreen: boolean }) => void
525 ): (() => void) => {
526 const channel = 'window:control:stateChanged'
527 const handler = (_event: unknown, state: unknown): void =>
528 callback(state as { isFullscreen: boolean })
529 getIpc().on(channel, handler)
530 return () => getIpc().removeListener(channel, handler)
531 },
532 createSession: (payload: CreateSessionPayload) =>
533 getIpc().invoke('session:create', payload) as Promise<{ sessionId: string }>,
534 listSessions: () => getIpc().invoke('session:list') as Promise<unknown[]>,
535 listMergeSourceSessions: (payload: { targetSessionId: string }) =>
536 getIpc().invoke('session:listMergeSources', payload) as Promise<MergeSourceSessionSummary[]>,
537 listMergeSourcePages: (payload: { targetSessionId: string; sourceSessionId: string }) =>
538 getIpc().invoke('session:listMergeSourcePages', payload) as Promise<MergeSourcePageSummary[]>,
539 mergeSessionPages: (payload: {
540 targetSessionId: string
541 sourceSessionId: string
542 sourcePageIds: string[]
543 }) =>
544 getIpc().invoke('session:mergePages', payload) as Promise<{
545 ok: true
546 generatedPages: Array<{
547 id: string
548 pageNumber: number
549 pageId: string
550 title: string
551 contentOutline?: string | null
552 html: string
553 htmlPath: string
554 sourceUrl?: string
555 status?: string
556 error?: string | null
557 }>
558 insertedPageIds: string[]
559 selectedPageId: string
560 }>,
561 listMergeSourceTemplates: (payload: { targetSessionId: string }) =>
562 getIpc().invoke('session:listMergeSourceTemplates', payload) as Promise<
563 MergeTemplateSourceSummary[]
564 >,
565 listMergeSourceTemplatePages: (payload: { targetSessionId: string; templateId: string }) =>
566 getIpc().invoke('session:listMergeSourcePages', {
567 targetSessionId: payload.targetSessionId,
568 sourceType: 'template',
569 templateId: payload.templateId
570 }) as Promise<MergeSourcePageSummary[]>,
571 mergeTemplatePages: (payload: {
572 targetSessionId: string
573 templateId: string
574 sourcePageIds: string[]
575 }) =>
576 getIpc().invoke('session:mergePages', {
577 targetSessionId: payload.targetSessionId,
578 sourceType: 'template',
579 templateId: payload.templateId,
580 sourcePageIds: payload.sourcePageIds
581 }) as Promise<{
582 ok: true
583 generatedPages: Array<{
584 id: string
585 pageNumber: number
586 pageId: string
587 title: string
588 contentOutline?: string | null
589 html: string
590 htmlPath: string
591 sourceUrl?: string
592 status?: string
593 error?: string | null
594 }>
595 insertedPageIds: string[]
596 selectedPageId: string
597 }>,
598 saveSessionAsNew: (payload: SaveSessionAsNewPayload): Promise<SaveSessionAsNewResult> =>
599 getIpc().invoke('session:saveAsNew', payload) as Promise<SaveSessionAsNewResult>,
600 getSessionMaster: (payload: { sessionId: string }): Promise<SessionMasterStatus> =>
601 getIpc().invoke('session:getMaster', payload) as Promise<SessionMasterStatus>,
602 saveSessionMaster: (payload: {
603 sessionId: string
604 config: SessionMasterConfig
605 }): Promise<SessionMasterStatus> =>
606 getIpc().invoke('session:saveMaster', payload) as Promise<SessionMasterStatus>,
607 setSessionMasterPageOverride: (payload: {
608 sessionId: string
609 pageId: string
610 disabled: boolean
611 }): Promise<{ disabled: boolean }> =>
612 getIpc().invoke('session:setMasterPageOverride', payload) as Promise<{ disabled: boolean }>,
613 getSessionLayoutLibrary: (payload: { sessionId: string }): Promise<SessionLayoutLibraryStatus> =>
614 getIpc().invoke('session:getLayoutLibrary', payload) as Promise<SessionLayoutLibraryStatus>,
615 saveSessionLayoutLibrary: (payload: {
616 sessionId: string
617 library: SessionLayoutLibrary
618 }): Promise<SessionLayoutLibraryStatus> =>
619 getIpc().invoke('session:saveLayoutLibrary', payload) as Promise<SessionLayoutLibraryStatus>,
620 getSession: (sessionId: string) =>
621 getIpc().invoke('session:get', sessionId) as Promise<{
622 session: unknown
623 messages: unknown[]
624 generatedPages: Array<{
625 id: string
626 pageNumber: number
627 title: string
628 contentOutline?: string | null
629 html: string
630 htmlPath?: string
631 pageId?: string
632 sourceUrl?: string
633 status?: string
634 error?: string | null
635 }>
636 }>,
637 getIndexTransition: (sessionId: string) =>
638 getIpc().invoke('session:getIndexTransition', { sessionId }) as Promise<IndexTransitionConfig>,
639 setIndexTransition: (payload: {
640 sessionId: string
641 type: IndexTransitionType
642 durationMs?: number
643 }) =>
644 getIpc().invoke('session:setIndexTransition', payload) as Promise<{
645 ok: boolean
646 transition: IndexTransitionConfig
647 }>,
648 migratePageOutlinesToSourceSkeletons: (payload: { sessionId: string }) =>
649 getIpc().invoke('session:migratePageOutlinesToSourceSkeletons', payload) as Promise<{
650 migrated: boolean
651 migratedCount: number
652 existingCount: number
653 }>,
654 reorderSessionPages: (payload: {
655 sessionId: string
656 orderedPageIds: string[]
657 selectedPageId?: string
658 }) =>
659 getIpc().invoke('session:reorderPages', payload) as Promise<{
660 ok: boolean
661 generatedPages: Array<{
662 id: string
663 pageNumber: number
664 pageId: string
665 title: string
666 contentOutline?: string | null
667 html: string
668 htmlPath?: string
669 status?: string
670 error?: string | null
671 }>
672 selectedPageId: string | null
673 }>,
674 deleteSessionPages: (payload: {
675 sessionId: string
676 pageIds: string[]
677 selectedPageId?: string
678 }) =>
679 getIpc().invoke('session:deletePages', payload) as Promise<{
680 ok: boolean
681 generatedPages: Array<{
682 id: string
683 pageNumber: number
684 pageId: string
685 title: string
686 contentOutline?: string | null
687 html: string
688 htmlPath?: string
689 status?: string
690 error?: string | null
691 }>
692 selectedPageId: string | null
693 }>,
694 createBlankSessionPage: (payload: { sessionId: string; sourcePageId: string }) =>
695 getIpc().invoke('session:createBlankPage', payload) as Promise<{
696 ok: boolean
697 generatedPages: Array<{
698 id: string
699 pageNumber: number
700 pageId: string
701 title: string
702 contentOutline?: string | null
703 html: string
704 htmlPath?: string
705 status?: string
706 error?: string | null
707 }>
708 selectedPageId: string | null
709 }>,
710 duplicateSessionPage: (payload: { sessionId: string; sourcePageId: string }) =>
711 getIpc().invoke('session:duplicatePage', payload) as Promise<{
712 ok: boolean
713 generatedPages: Array<{
714 id: string
715 pageNumber: number
716 pageId: string
717 title: string
718 contentOutline?: string | null
719 html: string
720 htmlPath?: string
721 status?: string
722 error?: string | null
723 }>
724 selectedPageId: string | null
725 }>,
726 updateSessionPageTitle: (payload: { sessionId: string; pageId: string; title: string }) =>
727 getIpc().invoke('session:updatePageTitle', payload) as Promise<{
728 ok: boolean
729 generatedPages: Array<{
730 id: string
731 pageNumber: number
732 pageId: string
733 title: string
734 contentOutline?: string | null
735 html: string
736 htmlPath?: string
737 status?: string
738 error?: string | null
739 }>
740 selectedPageId: string | null
741 }>,
742 updateSessionPageOutline: (payload: {
743 sessionId: string
744 pageId: string
745 contentOutline: string
746 }) =>
747 getIpc().invoke('session:updatePageOutline', payload) as Promise<{
748 ok: boolean
749 generatedPages: Array<{
750 id: string
751 pageNumber: number
752 pageId: string
753 title: string
754 contentOutline?: string | null
755 html: string
756 htmlPath?: string
757 status?: string
758 error?: string | null
759 }>
760 selectedPageId: string | null
761 }>,
762 getSessionMessages: (payload: {
763 sessionId: string
764 chatType: 'main' | 'page'
765 pageId?: string
766 }) => getIpc().invoke('session:getMessages', payload) as Promise<unknown[]>,
767 deleteSession: (sessionId: string) =>
768 getIpc().invoke('session:delete', sessionId) as Promise<{ success: boolean }>,
769 updateSessionTitle: (payload: { sessionId: string; title: string }) =>
770 getIpc().invoke('session:updateTitle', payload) as Promise<{ ok: boolean }>,
771 importSessionFile: () =>
772 getIpc().invoke('session:importFile') as Promise<ImportSessionFileResult>,
773 importHtmlFile: () =>
774 getIpc().invoke('html-editor:import') as Promise<HtmlEditorFileImportResult>,
775 listHtmlEditorMedia: (payload: { docId: string; mediaType: 'image' | 'video' }) =>
776 getIpc().invoke('html-editor:listMedia', payload) as Promise<{
777 assets: Array<{ fileName: string; filePath: string; relativePath: string; url: string }>
778 }>,
779 chooseAndImportHtmlMedia: (payload: { docId: string; mediaType: 'image' | 'video' }) =>
780 getIpc().invoke('html-editor:chooseAndImportMedia', payload) as Promise<
781 | { cancelled: true }
782 | { cancelled: false; filePath: string; relativePath: string; url: string }
783 >,
784 ensureHtmlAnchor: (payload: {
785 html: string
786 pageId: string
787 selector: string
788 elementTag?: string
789 formula?: { latex?: unknown; html?: unknown; displayMode?: unknown }
790 }) =>
791 getIpc().invoke('html-editor:ensureAnchor', payload) as Promise<{
792 html: string
793 selector: string
794 blockId: string
795 changed: boolean
796 }>,
797 applyHtmlEdits: (payload: {
798 html: string
799 pageId: string
800 dragEdits?: unknown[]
801 textEdits?: unknown[]
802 propertyEdits?: unknown[]
803 deletes?: unknown[]
804 addElements?: unknown[]
805 }) =>
806 getIpc().invoke('html-editor:applyEdits', payload) as Promise<{
807 html: string
808 warnings: string[]
809 }>,
810 exportHtml: (payload: { html: string; suggestedName?: string }) =>
811 getIpc().invoke('html-editor:export', payload) as Promise<
812 { cancelled: true } | { cancelled: false; path: string }
813 >,
814 cleanupHtmlEditor: (payload: { docId: string }) =>
815 getIpc().invoke('html-editor:cleanup', payload) as Promise<{ ok: boolean }>,
816 openHtmlInBrowser: (payload: { docId: string }) =>
817 getIpc().invoke('html-editor:openInBrowser', payload) as Promise<{ ok: boolean }>,
818 revealHtmlFile: (payload: { docId: string }) =>
819 getIpc().invoke('html-editor:revealFile', payload) as Promise<{ ok: boolean }>,
820 listHtmlVersions: (payload: { docId: string }) =>
821 getIpc().invoke('html-editor:listVersions', payload) as Promise<{
822 versions: Array<{ id: string; commitSha: string; message: string; createdAt: number }>
823 }>,
824 restoreHtmlVersion: (payload: { docId: string; versionId: string }) =>
825 getIpc().invoke('html-editor:restoreVersion', payload) as Promise<{ html: string }>,
826 listHtmlDocuments: () =>
827 getIpc().invoke('html-editor:listDocuments') as Promise<{
828 documents: Array<{
829 id: string
830 title: string
831 sourcePath: string | null
832 htmlPath: string
833 designWidth: number
834 updatedAt: number
835 thumbnailPath: string | null
836 }>
837 }>,
838 openHtmlDocument: (payload: { docId: string }) =>
839 getIpc().invoke('html-editor:openDocument', payload) as Promise<HtmlEditorFileImportResult>,
840 htmlEditorAiChat: (payload: {
841 documentId: string
842 documentTitle?: string
843 pageHtml?: string
844 selectedElement?: HtmlEditorAiElementContext
845 recentMessages?: HtmlEditorAiMessage[]
846 pendingPlan?: HtmlEditorAiPlan
847 userMessage: string
848 modelConfigId?: string
849 }) =>
850 getIpc().invoke('html-editor:aiChat', payload) as Promise<{
851 reply: string
852 model: string
853 intent: HtmlEditorAiIntent
854 plan: HtmlEditorAiPlan | null
855 requiresConfirmation: boolean
856 applied: boolean
857 appliedHtml?: string
858 warnings: string[]
859 }>,
860 listHtmlEditorMessages: (payload: { docId: string }) =>
861 getIpc().invoke('html-editor:listMessages', payload) as Promise<{
862 messages: HtmlEditorAiHistoryMessage[]
863 }>,
864 clearHtmlEditorMessages: (payload: { docId: string }) =>
865 getIpc().invoke('html-editor:clearMessages', payload) as Promise<{ ok: boolean }>,
866 listTemplates: () => getIpc().invoke('templates:list') as Promise<{ items: TemplateListItem[] }>,
867 createTemplateFromSession: (payload: {
868 sessionId: string
869 name?: string
870 description?: string
871 tags?: string[]
872 }) =>
873 getIpc().invoke('templates:createFromSession', payload) as Promise<{
874 success: true
875 id: string
876 }>,
877 createSessionFromTemplate: (payload: {
878 templateId: string
879 title?: string
880 modelConfigId?: string
881 pageCount?: number
882 referenceDocumentPath?: string
883 sourcePlan?: SourceDocumentPlan
884 }) =>
885 getIpc().invoke('templates:createSession', payload) as Promise<{
886 success: true
887 sessionId: string
888 }>,
889 createEditableSessionFromTemplate: (payload: {
890 templateId: string
891 title?: string
892 modelConfigId?: string
893 }) =>
894 getIpc().invoke('templates:createEditableSession', payload) as Promise<{
895 success: true
896 sessionId: string
897 }>,
898 importPptxAsTemplate: (payload: { filePath: string; name?: string; modelConfigId?: string }) =>
899 getIpc().invoke('templates:importPptx', payload) as Promise<{
900 success: true
901 id: string
902 pageCount: number
903 warnings: string[]
904 }>,
905 updateTemplateMetadata: (payload: {
906 templateId: string
907 name: string
908 description?: string
909 tags?: string[]
910 }) =>
911 getIpc().invoke('templates:updateMetadata', payload) as Promise<{
912 success: true
913 item: TemplateListItem
914 }>,
915 deleteTemplate: (templateId: string) =>
916 getIpc().invoke('templates:delete', templateId) as Promise<{
917 success: true
918 deleted: boolean
919 }>,
920 startGenerate: (payload: GenerateStartPayload) =>
921 getIpc().invoke('generate:start', payload) as Promise<{
922 success: boolean
923 runId?: string
924 alreadyRunning?: boolean
925 queued?: boolean
926 }>,
927 assessPageEdit: (payload: GenerateStartPayload) =>
928 getIpc().invoke('page-edit:assess', payload) as Promise<
929 SessionPageEditAssessment & {
930 reply: string
931 targetPageId: string
932 targetPageNumber?: number
933 }
934 >,
935 startPageEdit: (payload: GenerateStartPayload) =>
936 getIpc().invoke('page-edit:start', payload) as Promise<{
937 success: boolean
938 runId?: string
939 alreadyRunning?: boolean
940 }>,
941 getPageEditState: (sessionId: string) =>
942 getIpc().invoke('page-edit:state', sessionId) as Promise<GenerateRunStateSnapshot>,
943 listActivePageEditRuns: () =>
944 getIpc().invoke('page-edit:listActive') as Promise<GenerateRunStateSnapshot[]>,
945 cancelPageEdit: (sessionId: string) =>
946 getIpc().invoke('page-edit:cancel', sessionId) as Promise<{ success: boolean }>,
947 startDeckEdit: (payload: GenerateStartPayload) =>
948 getIpc().invoke('deck-edit:start', payload) as Promise<{
949 success: boolean
950 runId?: string
951 alreadyRunning?: boolean
952 }>,
953 getDeckEditState: (sessionId: string) =>
954 getIpc().invoke('deck-edit:state', sessionId) as Promise<GenerateRunStateSnapshot>,
955 listActiveDeckEditRuns: () =>
956 getIpc().invoke('deck-edit:listActive') as Promise<GenerateRunStateSnapshot[]>,
957 cancelDeckEdit: (sessionId: string) =>
958 getIpc().invoke('deck-edit:cancel', sessionId) as Promise<{ success: boolean }>,
959 startStyleSwitch: (payload: SwitchSessionStylePayload) =>
960 getIpc().invoke('style-switch:start', payload) as Promise<{
961 success: boolean
962 runId?: string
963 styleId: string
964 unchanged?: boolean
965 alreadyRunning?: boolean
966 }>,
967 retryStyleSwitchPage: (payload: {
968 sessionId: string
969 failedRunId?: string
970 pageId: string
971 modelConfigId?: string
972 }) =>
973 getIpc().invoke('style-switch:retryPage', payload) as Promise<{
974 success: boolean
975 runId?: string
976 styleId: string
977 alreadyRunning?: boolean
978 }>,
979 retryFailedStyleSwitchPages: (payload: RetrySessionStylePayload) =>
980 getIpc().invoke('style-switch:retryFailed', payload) as Promise<{
981 success: boolean
982 runId?: string
983 styleId: string
984 alreadyRunning?: boolean
985 failedPageCount: number
986 }>,
987 getStyleSwitchState: (sessionId: string) =>
988 getIpc().invoke('style-switch:state', sessionId) as Promise<StyleSwitchJobSnapshot>,
989 listActiveStyleSwitchRuns: () =>
990 getIpc().invoke('style-switch:listActive') as Promise<StyleSwitchJobSnapshot[]>,
991 cancelStyleSwitch: (sessionId: string) =>
992 getIpc().invoke('style-switch:cancel', sessionId) as Promise<{ success: boolean }>,
993 switchSessionStyle: (payload: SwitchSessionStylePayload) =>
994 getIpc().invoke('style-switch:start', payload) as Promise<{
995 success: boolean
996 runId?: string
997 styleId: string
998 unchanged?: boolean
999 alreadyRunning?: boolean
1000 failedPageCount?: number
1001 }>,
1002 retrySessionStyle: (payload: RetrySessionStylePayload) =>
1003 getIpc().invoke('style-switch:retryFailed', payload) as Promise<{
1004 success: boolean
1005 runId?: string
1006 styleId: string
1007 alreadyRunning?: boolean
1008 failedPageCount: number
1009 }>,
1010 retryDeckEdit: (payload: RetryDeckEditPayload) =>
1011 getIpc().invoke('generate:retryDeckEdit', payload) as Promise<{
1012 success: boolean
1013 runId?: string
1014 alreadyRunning?: boolean
1015 failedPageCount: number
1016 }>,
1017 startTemplateGenerate: (payload: GenerateStartPayload & { retry?: boolean }) =>
1018 getIpc().invoke('generate:startTemplate', payload) as Promise<{
1019 success: boolean
1020 runId?: string
1021 alreadyRunning?: boolean
1022 queued?: boolean
1023 }>,
1024 retryFailedPages: (payload: GenerateRetryFailedPayload) =>
1025 getIpc().invoke('generate:retryFailedPages', payload) as Promise<{
1026 success: boolean
1027 runId?: string
1028 alreadyRunning?: boolean
1029 queued?: boolean
1030 }>,
1031 addPage: (payload: GenerateAddPagePayload) =>
1032 getIpc().invoke('generate:addPage', payload) as Promise<{
1033 success: boolean
1034 runId?: string
1035 alreadyRunning?: boolean
1036 queued?: boolean
1037 }>,
1038 retrySinglePage: (payload: GenerateRetrySinglePagePayload) =>
1039 getIpc().invoke('generate:retrySinglePage', payload) as Promise<{
1040 success: boolean
1041 runId?: string
1042 alreadyRunning?: boolean
1043 queued?: boolean
1044 }>,
1045 getGenerateState: (sessionId: string) =>
1046 getIpc().invoke('generate:state', sessionId) as Promise<GenerateRunStateSnapshot>,
1047 listActiveGenerateRuns: () =>
1048 getIpc().invoke('generate:listActive') as Promise<GenerateRunStateSnapshot[]>,
1049 cancelGenerate: (sessionId: string) =>
1050 getIpc().invoke('generate:cancel', sessionId) as Promise<{ success: boolean }>,
1051 listHistoryVersions: (payload: { sessionId: string; limit?: number }) =>
1052 getIpc().invoke('history:listVersions', payload) as Promise<HistoryVersion[]>,
1053 rollbackToHistoryVersion: (payload: { sessionId: string; versionId: string }) =>
1054 getIpc().invoke('history:rollbackToVersion', payload) as Promise<RollbackHistoryResult>,
1055 recordHistorySnapshot: (payload: {
1056 sessionId: string
1057 type?: 'generate' | 'edit' | 'addPage' | 'retry' | 'import' | 'rollback' | 'reorder' | 'delete'
1058 scope?: 'session' | 'deck' | 'page' | 'selector' | 'shell'
1059 prompt?: string
1060 metadata?: Record<string, unknown>
1061 }) => getIpc().invoke('history:recordSnapshot', payload) as Promise<unknown>,
1062 uploadAssets: (payload: UploadAssetsPayload) =>
1063 getIpc().invoke('assets:upload', payload) as Promise<{ assets: UploadedAsset[] }>,
1064 prepareReferenceDocument: (payload: PrepareReferenceDocumentPayload) =>
1065 getIpc().invoke(
1066 'documents:prepareReference',
1067 payload
1068 ) as Promise<PreparedReferenceDocumentResult>,
1069 parseImageReferenceDocument: (payload: ParseImageReferencePayload) =>
1070 getIpc().invoke(
1071 'documents:parseImageReference',
1072 payload
1073 ) as Promise<PreparedReferenceDocumentResult>,
1074 parseDocumentPlan: (payload: ParseDocumentPlanPayload) =>
1075 getIpc().invoke('documents:parsePlan', payload) as Promise<ParsedDocumentPlanResult>,
1076 importPptx: (payload: PptxImportPayload) =>
1077 getIpc().invoke('pptx:import', payload) as Promise<PptxImportResult>,
1078 chooseAndUploadAssets: (sessionId: string, assetType: 'image' | 'video' = 'image') =>
1079 getIpc().invoke('assets:chooseAndUpload', { sessionId, assetType }) as Promise<{
1080 assets: UploadedAsset[]
1081 cancelled?: boolean
1082 }>,
1083 listAssets: (sessionId: string, assetType: 'image' | 'video') =>
1084 getIpc().invoke('assets:list', { sessionId, assetType }) as Promise<{
1085 assets: Array<{ fileName: string; relativePath: string; absolutePath: string }>
1086 }>,
1087 exportPdf: (sessionId: string) =>
1088 getIpc().invoke('export:pdf', { sessionId }) as Promise<ExportDeckResult>,
1089 exportPng: (sessionId: string) =>
1090 getIpc().invoke('export:png', { sessionId }) as Promise<ExportDeckResult>,
1091 exportLongImage: (sessionId: string) =>
1092 getIpc().invoke('export:longImage', { sessionId }) as Promise<ExportDeckResult>,
1093 exportVideo: (sessionId: string, options?: { pageId?: string }) =>
1094 getIpc().invoke('export:video', { sessionId, ...options }) as Promise<ExportDeckResult>,
1095 exportPptx: (
1096 sessionId: string,
1097 options?: {
1098 imageOnly?: boolean
1099 embedFonts?: boolean | 'auto' | 'always' | 'never'
1100 pageId?: string
1101 }
1102 ) => getIpc().invoke('export:pptx', { sessionId, ...options }) as Promise<ExportDeckResult>,
1103 exportSlidePack: (sessionId: string) =>
1104 getIpc().invoke('export:slidePack', { sessionId }) as Promise<ExportDeckResult>,
1105 exportSessionZip: (sessionId: string) =>
1106 getIpc().invoke('export:sessionZip', { sessionId }) as Promise<ExportDeckResult>,
1107 exportOutlinesMarkdown: (sessionId: string) =>
1108 getIpc().invoke('export:outlinesMarkdown', { sessionId }) as Promise<ExportDeckResult>,
1109 onExportProgress: (callback: (payload: ExportProgressPayload) => void): (() => void) => {
1110 const channel = 'export:progress'
1111 const handler = (_event: unknown, payload: unknown): void =>
1112 callback(payload as ExportProgressPayload)
1113 getIpc().on(channel, handler)
1114 return () => getIpc().removeListener(channel, handler)
1115 },
1116 getSettings: () => getIpc().invoke('settings:get') as Promise<Record<string, unknown>>,
1117 getModelUsage: (period: ModelUsagePeriod) =>
1118 getIpc().invoke('settings:getModelUsage', period) as Promise<ModelUsageStats>,
1119 listModelConfigs: () => getIpc().invoke('settings:listModelConfigs') as Promise<ModelConfig[]>,
1120 listImageModelConfigs: () => getIpc().invoke('imageModels:list') as Promise<ImageModelConfig[]>,
1121 validateUploadPrerequisites: () =>
1122 getIpc().invoke('settings:validateUploadPrerequisites') as Promise<UploadPrerequisitesResult>,
1123 listFonts: () => getIpc().invoke('fonts:list') as Promise<FontRegistryResponse>,
1124 uploadFont: (payload: UploadFontPayload) =>
1125 getIpc().invoke('fonts:upload', payload) as Promise<{ success: true; font: FontListItem }>,
1126 updateFont: (payload: {
1127 id: string
1128 family?: string
1129 category?: string
1130 role?: FontRole[]
1131 scripts?: FontScript[]
1132 }) => getIpc().invoke('fonts:update', payload) as Promise<{ success: true; font: FontListItem }>,
1133 deleteFont: (fontId: string) =>
1134 getIpc().invoke('fonts:delete', fontId) as Promise<{ success: true }>,
1135 revealFontsFolder: () => getIpc().invoke('fonts:revealFolder') as Promise<{ success: true }>,
1136 chooseFontFiles: () =>
1137 getIpc().invoke('fonts:chooseFiles') as Promise<{ canceled: boolean; filePaths: string[] }>,
1138 loadFontPreviewCss: () => getIpc().invoke('fonts:previewCss') as Promise<string>,
1139 saveSettings: (settings: Record<string, unknown>) =>
1140 getIpc().invoke('settings:save', settings) as Promise<{ success: boolean }>,
1141 upsertModelConfig: (payload: {
1142 id?: string
1143 name: string
1144 provider: 'anthropic' | 'openai' | 'openai-responses' | 'google'
1145 model: string
1146 apiKey: string
1147 baseUrl: string
1148 maxTokens?: number
1149 disableTemperature?: boolean
1150 thinkingParameterMode?: ThinkingParameterMode
1151 active?: boolean
1152 }) =>
1153 getIpc().invoke('settings:upsertModelConfig', payload) as Promise<{
1154 success: boolean
1155 id: string
1156 }>,
1157 setActiveModelConfig: (id: string) =>
1158 getIpc().invoke('settings:setActiveModelConfig', id) as Promise<{ success: boolean }>,
1159 deleteModelConfig: (id: string) =>
1160 getIpc().invoke('settings:deleteModelConfig', id) as Promise<{ success: boolean }>,
1161 upsertImageModelConfig: (payload: {
1162 id?: string
1163 name: string
1164 provider: ImageModelProvider
1165 active?: boolean
1166 modelConfig: string
1167 }) =>
1168 getIpc().invoke('imageModels:upsert', payload) as Promise<{
1169 success: boolean
1170 id: string
1171 }>,
1172 setActiveImageModelConfig: (id: string) =>
1173 getIpc().invoke('imageModels:setActive', id) as Promise<{ success: boolean }>,
1174 deleteImageModelConfig: (id: string) =>
1175 getIpc().invoke('imageModels:delete', id) as Promise<{ success: boolean }>,
1176 verifyImageModel: (payload: { provider: ImageModelProvider; modelConfig: string }) =>
1177 getIpc().invoke('imageModels:verify', payload) as Promise<ImageModelVerificationResult>,
1178 verifyApiKey: (payload: {
1179 provider: string
1180 apiKey: string
1181 model: string
1182 baseUrl: string
1183 maxTokens?: number
1184 disableTemperature?: boolean
1185 thinkingParameterMode?: ThinkingParameterMode
1186 timeoutMs: number
1187 }) =>
1188 getIpc().invoke('settings:verifyApiKey', payload) as Promise<{
1189 valid: boolean
1190 message?: string
1191 }>,
1192 fetchModelList: (payload: { provider: string; apiKey: string; baseUrl: string }) =>
1193 getIpc().invoke('settings:fetchModelList', payload) as Promise<{
1194 models: string[]
1195 message: string | null
1196 }>,
1197 chooseStoragePath: () =>
1198 getIpc().invoke('settings:chooseStoragePath') as Promise<{
1199 path: string | null
1200 error?: string
1201 }>,
1202 generateImage: (payload: ImageGeneratePayload) =>
1203 getIpc().invoke('images:generate', payload) as Promise<ImageGenerateResult>,
1204 generateImagePrompt: (payload: ImagePromptGeneratePayload) =>
1205 getIpc().invoke('images:generatePrompt', payload) as Promise<ImagePromptGenerateResult>,
1206 listImageGenerationHistory: (payload: { sessionId: string; pageId: string }) =>
1207 getIpc().invoke('images:listHistory', payload) as Promise<ImageGenerationHistoryRecord[]>,
1208 cancelImageGeneration: (sessionId: string) =>
1209 getIpc().invoke('images:cancel', sessionId) as Promise<{ success: boolean }>,
1210 getImageGenerationState: (sessionId: string) =>
1211 getIpc().invoke('images:getState', sessionId) as Promise<{
1212 runId: string
1213 sessionId: string
1214 pageId: string
1215 progress: number
1216 label: string
1217 status: 'running' | 'completed' | 'failed' | 'cancelled'
1218 error: string | null
1219 updatedAt: number
1220 } | null>,
1221 listImageFulfillmentJobs: (payload: { sessionId: string; pageId?: string }) =>
1222 getIpc().invoke('images:listFulfillmentJobs', payload) as Promise<ImageFulfillmentJobView[]>,
1223 cancelImageFulfillment: (payload: { sessionId: string; jobId: string }) =>
1224 getIpc().invoke('images:cancelFulfillment', payload) as Promise<{ success: boolean }>,
1225 retryImageFulfillment: (payload: { sessionId: string; jobId: string }) =>
1226 getIpc().invoke('images:retryFulfillment', payload) as Promise<{
1227 status: 'none' | 'completed' | 'degraded' | 'cancelled'
1228 jobId?: string
1229 error?: string
1230 reused?: boolean
1231 }>,
1232 getStyles: () =>
1233 getIpc().invoke('styles:get') as Promise<{
1234 categories: Record<
1235 string,
1236 Array<{
1237 id: string
1238 label: string
1239 description: string
1240 source?: 'builtin' | 'custom' | 'override'
1241 editable?: boolean
1242 styleCase?: string
1243 }>
1244 >
1245 defaultStyle: string
1246 }>,
1247 getStyleDetail: (styleId: string) =>
1248 getIpc().invoke('styles:getDetail', styleId) as Promise<StyleDetail>,
1249 listStyles: (payload?: { sessionId?: string }) =>
1250 getIpc().invoke('styles:list', payload) as Promise<{ items: StyleListItem[] }>,
1251 recommendStyles: (payload: { topic: string; brief?: string; modelConfigId?: string }) =>
1252 getIpc().invoke('styles:recommend', payload) as Promise<{ styleKeys: string[] }>,
1253 generateStylePreview: (payload: { styleId: string; modelConfigId?: string }) =>
1254 getIpc().invoke('styles:generatePreview', payload) as Promise<{
1255 success: boolean
1256 previewPath: string
1257 thumbnailPath: string
1258 }>,
1259 setStyleFavorite: (payload: { styleId: string; favorite: boolean }) =>
1260 getIpc().invoke('styles:setFavorite', payload) as Promise<{
1261 success: boolean
1262 styleId: string
1263 favoriteAt: number | null
1264 }>,
1265 onHtmlThumbnailChanged: (callback: (task: HtmlThumbnailTask) => void): (() => void) => {
1266 const channel = 'thumbnails:changed'
1267 const handler = (_event: unknown, task: Parameters<typeof callback>[0]): void => callback(task)
1268 getIpc().on(channel, handler)
1269 return () => getIpc().removeListener(channel, handler)
1270 },
1271 parseStyleFile: (payload: { filePath: string; modelConfigId?: string }) =>
1272 getIpc().invoke('styles:parseFile', payload) as Promise<StyleParseResult>,
1273 parseStylePptx: (payload: { filePath: string; modelConfigId?: string }) =>
1274 getIpc().invoke('styles:parsePptx', payload) as Promise<StyleParseResult>,
1275 parseStyleImage: (payload: { imageBase64: string; mimeType: string; modelConfigId?: string }) =>
1276 getIpc().invoke('styles:parseImage', payload) as Promise<StyleParseResult>,
1277 importStylePackageZip: () =>
1278 getIpc().invoke('styles:importPackageZip') as Promise<{
1279 success: boolean
1280 cancelled?: boolean
1281 id: string
1282 source: 'custom' | 'override'
1283 }>,
1284 importStylePackageDirectory: () =>
1285 getIpc().invoke('styles:importPackageDirectory') as Promise<{
1286 success: boolean
1287 cancelled?: boolean
1288 id: string
1289 source: 'custom' | 'override'
1290 }>,
1291 exportStylePackageZip: (payload: { styleId: string }) =>
1292 getIpc().invoke('styles:exportPackageZip', payload) as Promise<{
1293 success: boolean
1294 canceled?: boolean
1295 filePath?: string
1296 }>,
1297 createStyle: (payload: {
1298 label: string
1299 description: string
1300 category?: string
1301 aliases?: string[]
1302 styleSkill: string
1303 styleCase?: string
1304 }) =>
1305 getIpc().invoke('styles:create', payload) as Promise<{
1306 success: boolean
1307 id: string
1308 source: 'custom' | 'override'
1309 }>,
1310 updateStyle: (payload: {
1311 id: string
1312 label: string
1313 description: string
1314 category?: string
1315 aliases?: string[]
1316 styleSkill: string
1317 styleCase?: string
1318 }) =>
1319 getIpc().invoke('styles:update', payload) as Promise<{
1320 success: boolean
1321 id: string
1322 source: 'custom' | 'override'
1323 }>,
1324 deleteStyle: (styleId: string) =>
1325 getIpc().invoke('styles:delete', styleId) as Promise<{
1326 success: boolean
1327 deleted: boolean
1328 }>,
1329 loadPreview: (htmlPath: string, sessionId?: string) =>
1330 getIpc().invoke('preview:load', { htmlPath, sessionId }) as Promise<string>,
1331 loadPagePreview: (htmlPath: string, pageId: string, sessionId?: string) =>
1332 getIpc().invoke('preview:loadPage', { htmlPath, pageId, sessionId }) as Promise<{
1333 pageNumber: number
1334 pageId: string
1335 title: string
1336 html: string
1337 }>,
1338 updateElementLayout: (payload: UpdateElementLayoutPayload) =>
1339 getIpc().invoke('drag-editor:update-element-layout', payload) as Promise<{
1340 success: boolean
1341 }>,
1342 ensureElementAnchor: (payload: EnsureElementAnchorPayload) =>
1343 getIpc().invoke('element-anchor:ensure', payload) as Promise<EnsureElementAnchorResult>,
1344 getElementAnimation: (payload: {
1345 sessionId: string
1346 htmlPath: string
1347 pageId: string
1348 selector: string
1349 }) =>
1350 getIpc().invoke('element-animation:get', payload) as Promise<{
1351 animation: ElementAnimationConfig | null
1352 }>,
1353 setElementAnimation: (payload: {
1354 sessionId: string
1355 htmlPath: string
1356 pageId: string
1357 selector: string
1358 patch: ElementAnimationPatch
1359 }) =>
1360 getIpc().invoke('element-animation:set', payload) as Promise<{
1361 success: boolean
1362 changed: boolean
1363 animation: ElementAnimationConfig | null
1364 }>,
1365 updateElementProperties: (payload: UpdateElementPropertiesPayload) =>
1366 getIpc().invoke('text-editor:update-element-properties', payload) as Promise<{
1367 success: boolean
1368 }>,
1369 deleteElement: (payload: {
1370 sessionId: string
1371 htmlPath: string
1372 pageId: string
1373 selector: string
1374 }) =>
1375 getIpc().invoke('element-editor:delete-element', payload) as Promise<{
1376 success: boolean
1377 }>,
1378 saveEditBatch: (payload: {
1379 sessionId: string
1380 htmlPath: string
1381 pageId: string
1382 dragEdits: unknown[]
1383 textEdits: unknown[]
1384 propertyEdits?: unknown[]
1385 deletes?: unknown[]
1386 addElements?: unknown[]
1387 prompt?: string
1388 }) =>
1389 getIpc().invoke('edit:save-batch', payload) as Promise<{
1390 success: boolean
1391 dragCount: number
1392 textCount: number
1393 propertyCount?: number
1394 deleteCount: number
1395 addCount: number
1396 warnings?: string[]
1397 }>,
1398 applySyncElementToAllPages: (payload: {
1399 sessionId: string
1400 htmlPath: string
1401 pageId: string
1402 sourceHtmlFragment: string
1403 syncElementId?: string
1404 sourceBlockId?: string
1405 }) =>
1406 getIpc().invoke('element-editor:apply-sync-to-all-pages', payload) as Promise<{
1407 success: boolean
1408 syncElementId: string
1409 changedCount: number
1410 insertedCount: number
1411 updatedCount: number
1412 }>,
1413 chooseAndParseChartData: () =>
1414 getIpc().invoke('chart-data:choose-and-parse') as Promise<ParsedChartDataResult>,
1415 openFile: (filePath: string, sessionId?: string) =>
1416 getIpc().invoke('file:open', { path: filePath, sessionId }) as Promise<string>,
1417 revealFile: (filePath: string, sessionId?: string) =>
1418 getIpc().invoke('file:reveal', { path: filePath, sessionId }) as Promise<{ success: boolean }>,
1419 openInBrowser: (filePath: string, hash?: string, sessionId?: string) =>
1420 getIpc().invoke('file:openInBrowser', { path: filePath, hash, sessionId }) as Promise<{
1421 success: boolean
1422 }>,
1423 saveFile: (payload: { path: string; content: string; sessionId?: string }) =>
1424 getIpc().invoke('file:save', payload) as Promise<{ success: boolean }>,
1425 onGenerateChunk: (callback: (chunk: GenerateChunkEvent) => void): (() => void) => {
1426 const channel = 'generate:chunk'
1427 const handler = (_event: unknown, chunk: unknown): void => callback(chunk as GenerateChunkEvent)
1428 getIpc().on(channel, handler)
1429 return () => getIpc().removeListener(channel, handler)
1430 },
1431 onPptxImportProgress: (callback: (payload: PptxImportProgressPayload) => void): (() => void) => {
1432 const channel = 'pptx:import:progress'
1433 const handler = (_event: unknown, payload: unknown): void =>
1434 callback(payload as PptxImportProgressPayload)
1435 getIpc().on(channel, handler)
1436 return () => getIpc().removeListener(channel, handler)
1437 },
1438 onTemplatePptxImportProgress: (
1439 callback: (payload: PptxImportProgressPayload) => void
1440 ): (() => void) => {
1441 const channel = 'templates:importPptx:progress'
1442 const handler = (_event: unknown, payload: unknown): void =>
1443 callback(payload as PptxImportProgressPayload)
1444 getIpc().on(channel, handler)
1445 return () => getIpc().removeListener(channel, handler)
1446 },
1447 onUpdateAvailable: (callback: (payload: UpdateAvailablePayload) => void): (() => void) => {
1448 const channel = 'app:update-available'
1449 const handler = (_event: unknown, payload: unknown): void =>
1450 callback(payload as UpdateAvailablePayload)
1451 getIpc().on(channel, handler)
1452 return () => getIpc().removeListener(channel, handler)
1453 },
1454 getAppVersion: () =>
1455 getIpc().invoke('app:getVersion') as Promise<{
1456 version: string
1457 }>,
1458 openPresentation: (payload: { sessionId: string; startIndex?: number }) =>
1459 getIpc().invoke('presentation:open', payload) as Promise<{ success: boolean }>,
1460 generateSpeechScript: (sessionId: string, config: SpeechConfig & { currentPageId?: string }) =>
1461 getIpc().invoke('speech:generateScript', { sessionId, ...config }) as Promise<{
1462 success: boolean
1463 }>,
1464 getSpeechScript: (sessionId: string) =>
1465 getIpc().invoke('speech:getScript', { sessionId }) as Promise<{
1466 success: boolean
1467 script: string | null
1468 }>,
1469 openSpeechScriptFile: (sessionId: string) =>
1470 getIpc().invoke('speech:openScriptFile', { sessionId }) as Promise<{
1471 success: boolean
1472 path: string
1473 }>,
1474 clearSpeechScript: (sessionId: string) =>
1475 getIpc().invoke('speech:clearScript', { sessionId }) as Promise<{ success: boolean }>,
1476 onSpeechProgress: (
1477 callback: (payload: { sessionId: string; current: number; total: number }) => void
1478 ): (() => void) => {
1479 const channel = 'speech:progress'
1480 const handler = (_event: unknown, payload: unknown): void =>
1481 callback(payload as { sessionId: string; current: number; total: number })
1482 getIpc().on(channel, handler)
1483 return () => getIpc().removeListener(channel, handler)
1484 },
1485
1486 thinkingCreateWorkspace: () =>
1487 getIpc().invoke('thinking:createWorkspace') as Promise<ThinkingWorkspace>,
1488 thinkingGetWorkspace: (thinkingId: string) =>
1489 getIpc().invoke('thinking:getWorkspace', thinkingId) as Promise<ThinkingWorkspace>,
1490 thinkingGetLatestWorkspace: () =>
1491 getIpc().invoke('thinking:getLatestWorkspace') as Promise<ThinkingWorkspace | null>,
1492 thinkingListWorkspaces: (payload?: { limit?: number }) =>
1493 getIpc().invoke('thinking:listWorkspaces', payload || {}) as Promise<
1494 ThinkingWorkspaceListItem[]
1495 >,
1496 thinkingDeleteWorkspace: (thinkingId: string) =>
1497 getIpc().invoke('thinking:deleteWorkspace', thinkingId) as Promise<{ success: boolean }>,
1498 thinkingRevealWorkspace: (thinkingId: string) =>
1499 getIpc().invoke('thinking:revealWorkspace', thinkingId) as Promise<{ success: boolean }>,
1500 thinkingUpdatePageOutline: (payload: {
1501 thinkingId: string
1502 page: import('@shared/thinking').ThinkingPageOutlineUpdate
1503 }) =>
1504 getIpc().invoke('thinking:updatePageOutline', payload) as Promise<{
1505 success: boolean
1506 thinkingMd: string
1507 }>,
1508 thinkingUploadSources: (payload: {
1509 thinkingId: string
1510 files: Array<{ path: string; name?: string }>
1511 }) =>
1512 getIpc().invoke('thinking:uploadSources', payload) as Promise<{
1513 sources: Array<{ id: string; name: string; kind: string }>
1514 }>,
1515 thinkingRemoveSource: (payload: { thinkingId: string; sourceId: string }) =>
1516 getIpc().invoke('thinking:removeSource', payload) as Promise<{
1517 success: boolean
1518 removed: boolean
1519 }>,
1520 thinkingChat: (payload: {
1521 thinkingId: string
1522 modelConfigId?: string
1523 userMessage: string
1524 recentMessages?: ThinkingChatMessage[]
1525 attachments?: ThinkingChatMessage['attachments']
1526 }) => getIpc().invoke('thinking:chat', payload) as Promise<ThinkingChatResult>,
1527 thinkingPrepareGeneration: (payload: { thinkingId: string }) =>
1528 getIpc().invoke(
1529 'thinking:prepareGeneration',
1530 payload
1531 ) as Promise<ThinkingPrepareGenerationResult>,
1532 onThinkingStreamThinking: (
1533 callback: (payload: {
1534 thinkingId: string
1535 type: string
1536 toolName: string
1537 summary: string
1538 }) => void
1539 ): (() => void) => {
1540 const channel = 'thinking:stream:thinking'
1541 const handler = (_event: unknown, payload: unknown): void =>
1542 callback(payload as { thinkingId: string; type: string; toolName: string; summary: string })
1543 getIpc().on(channel, handler)
1544 return () => getIpc().removeListener(channel, handler)
1545 },
1546 onThinkingStreamEnd: (
1547 callback: (payload: {
1548 thinkingId: string
1549 reply: string
1550 thinkingMd: string
1551 contextMd: string
1552 stage: ThinkingStage
1553 }) => void
1554 ): (() => void) => {
1555 const channel = 'thinking:stream:end'
1556 const handler = (_event: unknown, payload: unknown): void =>
1557 callback(
1558 payload as {
1559 thinkingId: string
1560 reply: string
1561 thinkingMd: string
1562 contextMd: string
1563 stage: ThinkingStage
1564 }
1565 )
1566 getIpc().on(channel, handler)
1567 return () => getIpc().removeListener(channel, handler)
1568 }
1569 }
1570
1570 lines TYPESCRIPT