| 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 | chooseStoragePath: () => |
| 1193 | getIpc().invoke('settings:chooseStoragePath') as Promise<{ |
| 1194 | path: string | null |
| 1195 | error?: string |
| 1196 | }>, |
| 1197 | generateImage: (payload: ImageGeneratePayload) => |
| 1198 | getIpc().invoke('images:generate', payload) as Promise<ImageGenerateResult>, |
| 1199 | generateImagePrompt: (payload: ImagePromptGeneratePayload) => |
| 1200 | getIpc().invoke('images:generatePrompt', payload) as Promise<ImagePromptGenerateResult>, |
| 1201 | listImageGenerationHistory: (payload: { sessionId: string; pageId: string }) => |
| 1202 | getIpc().invoke('images:listHistory', payload) as Promise<ImageGenerationHistoryRecord[]>, |
| 1203 | cancelImageGeneration: (sessionId: string) => |
| 1204 | getIpc().invoke('images:cancel', sessionId) as Promise<{ success: boolean }>, |
| 1205 | getImageGenerationState: (sessionId: string) => |
| 1206 | getIpc().invoke('images:getState', sessionId) as Promise<{ |
| 1207 | runId: string |
| 1208 | sessionId: string |
| 1209 | pageId: string |
| 1210 | progress: number |
| 1211 | label: string |
| 1212 | status: 'running' | 'completed' | 'failed' | 'cancelled' |
| 1213 | error: string | null |
| 1214 | updatedAt: number |
| 1215 | } | null>, |
| 1216 | listImageFulfillmentJobs: (payload: { sessionId: string; pageId?: string }) => |
| 1217 | getIpc().invoke('images:listFulfillmentJobs', payload) as Promise<ImageFulfillmentJobView[]>, |
| 1218 | cancelImageFulfillment: (payload: { sessionId: string; jobId: string }) => |
| 1219 | getIpc().invoke('images:cancelFulfillment', payload) as Promise<{ success: boolean }>, |
| 1220 | retryImageFulfillment: (payload: { sessionId: string; jobId: string }) => |
| 1221 | getIpc().invoke('images:retryFulfillment', payload) as Promise<{ |
| 1222 | status: 'none' | 'completed' | 'degraded' | 'cancelled' |
| 1223 | jobId?: string |
| 1224 | error?: string |
| 1225 | reused?: boolean |
| 1226 | }>, |
| 1227 | getStyles: () => |
| 1228 | getIpc().invoke('styles:get') as Promise<{ |
| 1229 | categories: Record< |
| 1230 | string, |
| 1231 | Array<{ |
| 1232 | id: string |
| 1233 | label: string |
| 1234 | description: string |
| 1235 | source?: 'builtin' | 'custom' | 'override' |
| 1236 | editable?: boolean |
| 1237 | styleCase?: string |
| 1238 | }> |
| 1239 | > |
| 1240 | defaultStyle: string |
| 1241 | }>, |
| 1242 | getStyleDetail: (styleId: string) => |
| 1243 | getIpc().invoke('styles:getDetail', styleId) as Promise<StyleDetail>, |
| 1244 | listStyles: (payload?: { sessionId?: string }) => |
| 1245 | getIpc().invoke('styles:list', payload) as Promise<{ items: StyleListItem[] }>, |
| 1246 | recommendStyles: (payload: { topic: string; brief?: string; modelConfigId?: string }) => |
| 1247 | getIpc().invoke('styles:recommend', payload) as Promise<{ styleKeys: string[] }>, |
| 1248 | generateStylePreview: (payload: { styleId: string; modelConfigId?: string }) => |
| 1249 | getIpc().invoke('styles:generatePreview', payload) as Promise<{ |
| 1250 | success: boolean |
| 1251 | previewPath: string |
| 1252 | thumbnailPath: string |
| 1253 | }>, |
| 1254 | setStyleFavorite: (payload: { styleId: string; favorite: boolean }) => |
| 1255 | getIpc().invoke('styles:setFavorite', payload) as Promise<{ |
| 1256 | success: boolean |
| 1257 | styleId: string |
| 1258 | favoriteAt: number | null |
| 1259 | }>, |
| 1260 | onHtmlThumbnailChanged: (callback: (task: HtmlThumbnailTask) => void): (() => void) => { |
| 1261 | const channel = 'thumbnails:changed' |
| 1262 | const handler = (_event: unknown, task: Parameters<typeof callback>[0]): void => callback(task) |
| 1263 | getIpc().on(channel, handler) |
| 1264 | return () => getIpc().removeListener(channel, handler) |
| 1265 | }, |
| 1266 | parseStyleFile: (payload: { filePath: string; modelConfigId?: string }) => |
| 1267 | getIpc().invoke('styles:parseFile', payload) as Promise<StyleParseResult>, |
| 1268 | parseStylePptx: (payload: { filePath: string; modelConfigId?: string }) => |
| 1269 | getIpc().invoke('styles:parsePptx', payload) as Promise<StyleParseResult>, |
| 1270 | parseStyleImage: (payload: { imageBase64: string; mimeType: string; modelConfigId?: string }) => |
| 1271 | getIpc().invoke('styles:parseImage', payload) as Promise<StyleParseResult>, |
| 1272 | importStylePackageZip: () => |
| 1273 | getIpc().invoke('styles:importPackageZip') as Promise<{ |
| 1274 | success: boolean |
| 1275 | cancelled?: boolean |
| 1276 | id: string |
| 1277 | source: 'custom' | 'override' |
| 1278 | }>, |
| 1279 | importStylePackageDirectory: () => |
| 1280 | getIpc().invoke('styles:importPackageDirectory') as Promise<{ |
| 1281 | success: boolean |
| 1282 | cancelled?: boolean |
| 1283 | id: string |
| 1284 | source: 'custom' | 'override' |
| 1285 | }>, |
| 1286 | exportStylePackageZip: (payload: { styleId: string }) => |
| 1287 | getIpc().invoke('styles:exportPackageZip', payload) as Promise<{ |
| 1288 | success: boolean |
| 1289 | canceled?: boolean |
| 1290 | filePath?: string |
| 1291 | }>, |
| 1292 | createStyle: (payload: { |
| 1293 | label: string |
| 1294 | description: string |
| 1295 | category?: string |
| 1296 | aliases?: string[] |
| 1297 | styleSkill: string |
| 1298 | styleCase?: string |
| 1299 | }) => |
| 1300 | getIpc().invoke('styles:create', payload) as Promise<{ |
| 1301 | success: boolean |
| 1302 | id: string |
| 1303 | source: 'custom' | 'override' |
| 1304 | }>, |
| 1305 | updateStyle: (payload: { |
| 1306 | id: string |
| 1307 | label: string |
| 1308 | description: string |
| 1309 | category?: string |
| 1310 | aliases?: string[] |
| 1311 | styleSkill: string |
| 1312 | styleCase?: string |
| 1313 | }) => |
| 1314 | getIpc().invoke('styles:update', payload) as Promise<{ |
| 1315 | success: boolean |
| 1316 | id: string |
| 1317 | source: 'custom' | 'override' |
| 1318 | }>, |
| 1319 | deleteStyle: (styleId: string) => |
| 1320 | getIpc().invoke('styles:delete', styleId) as Promise<{ |
| 1321 | success: boolean |
| 1322 | deleted: boolean |
| 1323 | }>, |
| 1324 | loadPreview: (htmlPath: string, sessionId?: string) => |
| 1325 | getIpc().invoke('preview:load', { htmlPath, sessionId }) as Promise<string>, |
| 1326 | loadPagePreview: (htmlPath: string, pageId: string, sessionId?: string) => |
| 1327 | getIpc().invoke('preview:loadPage', { htmlPath, pageId, sessionId }) as Promise<{ |
| 1328 | pageNumber: number |
| 1329 | pageId: string |
| 1330 | title: string |
| 1331 | html: string |
| 1332 | }>, |
| 1333 | updateElementLayout: (payload: UpdateElementLayoutPayload) => |
| 1334 | getIpc().invoke('drag-editor:update-element-layout', payload) as Promise<{ |
| 1335 | success: boolean |
| 1336 | }>, |
| 1337 | ensureElementAnchor: (payload: EnsureElementAnchorPayload) => |
| 1338 | getIpc().invoke('element-anchor:ensure', payload) as Promise<EnsureElementAnchorResult>, |
| 1339 | getElementAnimation: (payload: { |
| 1340 | sessionId: string |
| 1341 | htmlPath: string |
| 1342 | pageId: string |
| 1343 | selector: string |
| 1344 | }) => |
| 1345 | getIpc().invoke('element-animation:get', payload) as Promise<{ |
| 1346 | animation: ElementAnimationConfig | null |
| 1347 | }>, |
| 1348 | setElementAnimation: (payload: { |
| 1349 | sessionId: string |
| 1350 | htmlPath: string |
| 1351 | pageId: string |
| 1352 | selector: string |
| 1353 | patch: ElementAnimationPatch |
| 1354 | }) => |
| 1355 | getIpc().invoke('element-animation:set', payload) as Promise<{ |
| 1356 | success: boolean |
| 1357 | changed: boolean |
| 1358 | animation: ElementAnimationConfig | null |
| 1359 | }>, |
| 1360 | updateElementProperties: (payload: UpdateElementPropertiesPayload) => |
| 1361 | getIpc().invoke('text-editor:update-element-properties', payload) as Promise<{ |
| 1362 | success: boolean |
| 1363 | }>, |
| 1364 | deleteElement: (payload: { |
| 1365 | sessionId: string |
| 1366 | htmlPath: string |
| 1367 | pageId: string |
| 1368 | selector: string |
| 1369 | }) => |
| 1370 | getIpc().invoke('element-editor:delete-element', payload) as Promise<{ |
| 1371 | success: boolean |
| 1372 | }>, |
| 1373 | saveEditBatch: (payload: { |
| 1374 | sessionId: string |
| 1375 | htmlPath: string |
| 1376 | pageId: string |
| 1377 | dragEdits: unknown[] |
| 1378 | textEdits: unknown[] |
| 1379 | propertyEdits?: unknown[] |
| 1380 | deletes?: unknown[] |
| 1381 | addElements?: unknown[] |
| 1382 | prompt?: string |
| 1383 | }) => |
| 1384 | getIpc().invoke('edit:save-batch', payload) as Promise<{ |
| 1385 | success: boolean |
| 1386 | dragCount: number |
| 1387 | textCount: number |
| 1388 | propertyCount?: number |
| 1389 | deleteCount: number |
| 1390 | addCount: number |
| 1391 | warnings?: string[] |
| 1392 | }>, |
| 1393 | applySyncElementToAllPages: (payload: { |
| 1394 | sessionId: string |
| 1395 | htmlPath: string |
| 1396 | pageId: string |
| 1397 | sourceHtmlFragment: string |
| 1398 | syncElementId?: string |
| 1399 | sourceBlockId?: string |
| 1400 | }) => |
| 1401 | getIpc().invoke('element-editor:apply-sync-to-all-pages', payload) as Promise<{ |
| 1402 | success: boolean |
| 1403 | syncElementId: string |
| 1404 | changedCount: number |
| 1405 | insertedCount: number |
| 1406 | updatedCount: number |
| 1407 | }>, |
| 1408 | chooseAndParseChartData: () => |
| 1409 | getIpc().invoke('chart-data:choose-and-parse') as Promise<ParsedChartDataResult>, |
| 1410 | openFile: (filePath: string, sessionId?: string) => |
| 1411 | getIpc().invoke('file:open', { path: filePath, sessionId }) as Promise<string>, |
| 1412 | revealFile: (filePath: string, sessionId?: string) => |
| 1413 | getIpc().invoke('file:reveal', { path: filePath, sessionId }) as Promise<{ success: boolean }>, |
| 1414 | openInBrowser: (filePath: string, hash?: string, sessionId?: string) => |
| 1415 | getIpc().invoke('file:openInBrowser', { path: filePath, hash, sessionId }) as Promise<{ |
| 1416 | success: boolean |
| 1417 | }>, |
| 1418 | saveFile: (payload: { path: string; content: string; sessionId?: string }) => |
| 1419 | getIpc().invoke('file:save', payload) as Promise<{ success: boolean }>, |
| 1420 | onGenerateChunk: (callback: (chunk: GenerateChunkEvent) => void): (() => void) => { |
| 1421 | const channel = 'generate:chunk' |
| 1422 | const handler = (_event: unknown, chunk: unknown): void => callback(chunk as GenerateChunkEvent) |
| 1423 | getIpc().on(channel, handler) |
| 1424 | return () => getIpc().removeListener(channel, handler) |
| 1425 | }, |
| 1426 | onPptxImportProgress: (callback: (payload: PptxImportProgressPayload) => void): (() => void) => { |
| 1427 | const channel = 'pptx:import:progress' |
| 1428 | const handler = (_event: unknown, payload: unknown): void => |
| 1429 | callback(payload as PptxImportProgressPayload) |
| 1430 | getIpc().on(channel, handler) |
| 1431 | return () => getIpc().removeListener(channel, handler) |
| 1432 | }, |
| 1433 | onTemplatePptxImportProgress: ( |
| 1434 | callback: (payload: PptxImportProgressPayload) => void |
| 1435 | ): (() => void) => { |
| 1436 | const channel = 'templates:importPptx:progress' |
| 1437 | const handler = (_event: unknown, payload: unknown): void => |
| 1438 | callback(payload as PptxImportProgressPayload) |
| 1439 | getIpc().on(channel, handler) |
| 1440 | return () => getIpc().removeListener(channel, handler) |
| 1441 | }, |
| 1442 | onUpdateAvailable: (callback: (payload: UpdateAvailablePayload) => void): (() => void) => { |
| 1443 | const channel = 'app:update-available' |
| 1444 | const handler = (_event: unknown, payload: unknown): void => |
| 1445 | callback(payload as UpdateAvailablePayload) |
| 1446 | getIpc().on(channel, handler) |
| 1447 | return () => getIpc().removeListener(channel, handler) |
| 1448 | }, |
| 1449 | getAppVersion: () => |
| 1450 | getIpc().invoke('app:getVersion') as Promise<{ |
| 1451 | version: string |
| 1452 | }>, |
| 1453 | openPresentation: (payload: { sessionId: string; startIndex?: number }) => |
| 1454 | getIpc().invoke('presentation:open', payload) as Promise<{ success: boolean }>, |
| 1455 | generateSpeechScript: (sessionId: string, config: SpeechConfig & { currentPageId?: string }) => |
| 1456 | getIpc().invoke('speech:generateScript', { sessionId, ...config }) as Promise<{ |
| 1457 | success: boolean |
| 1458 | }>, |
| 1459 | getSpeechScript: (sessionId: string) => |
| 1460 | getIpc().invoke('speech:getScript', { sessionId }) as Promise<{ |
| 1461 | success: boolean |
| 1462 | script: string | null |
| 1463 | }>, |
| 1464 | openSpeechScriptFile: (sessionId: string) => |
| 1465 | getIpc().invoke('speech:openScriptFile', { sessionId }) as Promise<{ |
| 1466 | success: boolean |
| 1467 | path: string |
| 1468 | }>, |
| 1469 | clearSpeechScript: (sessionId: string) => |
| 1470 | getIpc().invoke('speech:clearScript', { sessionId }) as Promise<{ success: boolean }>, |
| 1471 | onSpeechProgress: ( |
| 1472 | callback: (payload: { sessionId: string; current: number; total: number }) => void |
| 1473 | ): (() => void) => { |
| 1474 | const channel = 'speech:progress' |
| 1475 | const handler = (_event: unknown, payload: unknown): void => |
| 1476 | callback(payload as { sessionId: string; current: number; total: number }) |
| 1477 | getIpc().on(channel, handler) |
| 1478 | return () => getIpc().removeListener(channel, handler) |
| 1479 | }, |
| 1480 | |
| 1481 | thinkingCreateWorkspace: () => |
| 1482 | getIpc().invoke('thinking:createWorkspace') as Promise<ThinkingWorkspace>, |
| 1483 | thinkingGetWorkspace: (thinkingId: string) => |
| 1484 | getIpc().invoke('thinking:getWorkspace', thinkingId) as Promise<ThinkingWorkspace>, |
| 1485 | thinkingGetLatestWorkspace: () => |
| 1486 | getIpc().invoke('thinking:getLatestWorkspace') as Promise<ThinkingWorkspace | null>, |
| 1487 | thinkingListWorkspaces: (payload?: { limit?: number }) => |
| 1488 | getIpc().invoke('thinking:listWorkspaces', payload || {}) as Promise< |
| 1489 | ThinkingWorkspaceListItem[] |
| 1490 | >, |
| 1491 | thinkingDeleteWorkspace: (thinkingId: string) => |
| 1492 | getIpc().invoke('thinking:deleteWorkspace', thinkingId) as Promise<{ success: boolean }>, |
| 1493 | thinkingRevealWorkspace: (thinkingId: string) => |
| 1494 | getIpc().invoke('thinking:revealWorkspace', thinkingId) as Promise<{ success: boolean }>, |
| 1495 | thinkingUpdatePageOutline: (payload: { |
| 1496 | thinkingId: string |
| 1497 | page: import('@shared/thinking').ThinkingPageOutlineUpdate |
| 1498 | }) => |
| 1499 | getIpc().invoke('thinking:updatePageOutline', payload) as Promise<{ |
| 1500 | success: boolean |
| 1501 | thinkingMd: string |
| 1502 | }>, |
| 1503 | thinkingUploadSources: (payload: { |
| 1504 | thinkingId: string |
| 1505 | files: Array<{ path: string; name?: string }> |
| 1506 | }) => |
| 1507 | getIpc().invoke('thinking:uploadSources', payload) as Promise<{ |
| 1508 | sources: Array<{ id: string; name: string; kind: string }> |
| 1509 | }>, |
| 1510 | thinkingRemoveSource: (payload: { thinkingId: string; sourceId: string }) => |
| 1511 | getIpc().invoke('thinking:removeSource', payload) as Promise<{ |
| 1512 | success: boolean |
| 1513 | removed: boolean |
| 1514 | }>, |
| 1515 | thinkingChat: (payload: { |
| 1516 | thinkingId: string |
| 1517 | modelConfigId?: string |
| 1518 | userMessage: string |
| 1519 | recentMessages?: ThinkingChatMessage[] |
| 1520 | attachments?: ThinkingChatMessage['attachments'] |
| 1521 | }) => getIpc().invoke('thinking:chat', payload) as Promise<ThinkingChatResult>, |
| 1522 | thinkingPrepareGeneration: (payload: { thinkingId: string }) => |
| 1523 | getIpc().invoke( |
| 1524 | 'thinking:prepareGeneration', |
| 1525 | payload |
| 1526 | ) as Promise<ThinkingPrepareGenerationResult>, |
| 1527 | onThinkingStreamThinking: ( |
| 1528 | callback: (payload: { |
| 1529 | thinkingId: string |
| 1530 | type: string |
| 1531 | toolName: string |
| 1532 | summary: string |
| 1533 | }) => void |
| 1534 | ): (() => void) => { |
| 1535 | const channel = 'thinking:stream:thinking' |
| 1536 | const handler = (_event: unknown, payload: unknown): void => |
| 1537 | callback(payload as { thinkingId: string; type: string; toolName: string; summary: string }) |
| 1538 | getIpc().on(channel, handler) |
| 1539 | return () => getIpc().removeListener(channel, handler) |
| 1540 | }, |
| 1541 | onThinkingStreamEnd: ( |
| 1542 | callback: (payload: { |
| 1543 | thinkingId: string |
| 1544 | reply: string |
| 1545 | thinkingMd: string |
| 1546 | contextMd: string |
| 1547 | stage: ThinkingStage |
| 1548 | }) => void |
| 1549 | ): (() => void) => { |
| 1550 | const channel = 'thinking:stream:end' |
| 1551 | const handler = (_event: unknown, payload: unknown): void => |
| 1552 | callback( |
| 1553 | payload as { |
| 1554 | thinkingId: string |
| 1555 | reply: string |
| 1556 | thinkingMd: string |
| 1557 | contextMd: string |
| 1558 | stage: ThinkingStage |
| 1559 | } |
| 1560 | ) |
| 1561 | getIpc().on(channel, handler) |
| 1562 | return () => getIpc().removeListener(channel, handler) |
| 1563 | } |
| 1564 | } |
| 1565 |