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