Files
CipherTalk/electron/preload.ts

536 lines
29 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { contextBridge, ipcRenderer } from 'electron'
import type { AccountProfile } from '../src/types/account'
function getMcpLaunchConfigSafe(): Promise<{
command: string
args: string[]
cwd: string
mode: 'dev' | 'packaged'
} | null> {
return new Promise((resolve) => {
const requestId = `${Date.now()}-${Math.random().toString(36).slice(2)}`
const responseChannel = `app:getMcpLaunchConfig:response:${requestId}`
const timeout = setTimeout(() => {
ipcRenderer.removeAllListeners(responseChannel)
resolve(null)
}, 600)
ipcRenderer.once(responseChannel, (_, payload) => {
clearTimeout(timeout)
resolve(payload ?? null)
})
ipcRenderer.send('app:getMcpLaunchConfig:request', { requestId })
})
}
// 暴露给渲染进程的 API
contextBridge.exposeInMainWorld('electronAPI', {
// 配置
config: {
get: (key: string) => ipcRenderer.invoke('config:get', key),
set: (key: string, value: any) => ipcRenderer.invoke('config:set', key, value),
getTldCache: () => ipcRenderer.invoke('config:getTldCache'),
setTldCache: (tlds: string[]) => ipcRenderer.invoke('config:setTldCache', tlds)
},
accounts: {
list: () => ipcRenderer.invoke('accounts:list') as Promise<AccountProfile[]>,
getActive: () => ipcRenderer.invoke('accounts:getActive') as Promise<AccountProfile | null>,
setActive: (accountId: string) => ipcRenderer.invoke('accounts:setActive', accountId) as Promise<AccountProfile | null>,
save: (profile: Omit<AccountProfile, 'id' | 'createdAt' | 'updatedAt' | 'lastUsedAt'>) => ipcRenderer.invoke('accounts:save', profile) as Promise<AccountProfile | null>,
update: (accountId: string, patch: Partial<Omit<AccountProfile, 'id' | 'createdAt' | 'updatedAt' | 'lastUsedAt'>>) =>
ipcRenderer.invoke('accounts:update', accountId, patch) as Promise<AccountProfile | null>,
delete: (accountId: string, deleteLocalData?: boolean) =>
ipcRenderer.invoke('accounts:delete', accountId, deleteLocalData) as Promise<{ success: boolean; error?: string; deleted?: AccountProfile | null; nextActiveAccountId?: string }>
},
skillInstaller: {
exportSkillZip: (skillName: string) =>
ipcRenderer.invoke('skillInstaller:exportSkillZip', skillName) as Promise<{ success: boolean; outputPath?: string; fileName?: string; version?: string; error?: string }>
},
// 数据库操作
db: {
open: (dbPath: string, key?: string) => ipcRenderer.invoke('db:open', dbPath, key),
query: (sql: string, params?: any[]) => ipcRenderer.invoke('db:query', sql, params),
close: () => ipcRenderer.invoke('db:close')
},
// 解密
decrypt: {
database: (sourcePath: string, key: string, outputPath: string) =>
ipcRenderer.invoke('decrypt:database', sourcePath, key, outputPath),
image: (imagePath: string) => ipcRenderer.invoke('decrypt:image', imagePath)
},
// 对话框
dialog: {
openFile: (options: any) => ipcRenderer.invoke('dialog:openFile', options),
saveFile: (options: any) => ipcRenderer.invoke('dialog:saveFile', options)
},
// 文件操作
file: {
delete: (filePath: string) => ipcRenderer.invoke('file:delete', filePath),
copy: (sourcePath: string, destPath: string) => ipcRenderer.invoke('file:copy', sourcePath, destPath),
writeBase64: (filePath: string, base64Data: string) => ipcRenderer.invoke('file:writeBase64', filePath, base64Data)
},
// Shell
shell: {
openPath: (path: string) => ipcRenderer.invoke('shell:openPath', path),
openExternal: (url: string) => ipcRenderer.invoke('shell:openExternal', url),
showItemInFolder: (fullPath: string) => ipcRenderer.invoke('shell:showItemInFolder', fullPath)
},
// App
app: {
getDownloadsPath: () => ipcRenderer.invoke('app:getDownloadsPath'),
getVersion: () => ipcRenderer.invoke('app:getVersion'),
getPlatformInfo: () => ipcRenderer.invoke('app:getPlatformInfo'),
getMcpLaunchConfig: () => getMcpLaunchConfigSafe(),
getUpdateState: () => ipcRenderer.invoke('app:getUpdateState'),
getUpdateSourceInfo: () => ipcRenderer.invoke('app:getUpdateSourceInfo'),
checkForUpdates: () => ipcRenderer.invoke('app:checkForUpdates'),
downloadAndInstall: () => ipcRenderer.invoke('app:downloadAndInstall'),
getStartupDbConnected: () => ipcRenderer.invoke('app:getStartupDbConnected'),
setAppIcon: (iconName: string) => ipcRenderer.invoke('app:setAppIcon', iconName),
onDownloadProgress: (callback: (progress: {
percent: number
transferred: number
total: number
bytesPerSecond: number
}) => void) => {
ipcRenderer.on('app:downloadProgress', (_, progress) => callback(progress))
return () => ipcRenderer.removeAllListeners('app:downloadProgress')
},
onUpdateAvailable: (callback: (info: {
hasUpdate: boolean
forceUpdate: boolean
currentVersion: string
version?: string
releaseNotes?: string
title?: string
message?: string
minimumSupportedVersion?: string
reason?: 'minimum-version' | 'blocked-version'
checkedAt: number
updateSource: 'github' | 'custom' | 'none'
policySource: 'github' | 'custom' | 'none'
}) => void) => {
ipcRenderer.on('app:updateAvailable', (_, info) => callback(info))
return () => ipcRenderer.removeAllListeners('app:updateAvailable')
}
},
// HTTP API
httpApi: {
getStatus: () => ipcRenderer.invoke('httpApi:getStatus'),
applySettings: (payload: { enabled: boolean; port: number; token: string }) => ipcRenderer.invoke('httpApi:applySettings', payload),
restart: () => ipcRenderer.invoke('httpApi:restart')
},
// 窗口控制
window: {
minimize: () => ipcRenderer.send('window:minimize'),
maximize: () => ipcRenderer.send('window:maximize'),
close: () => ipcRenderer.send('window:close'),
openChatWindow: () => ipcRenderer.invoke('window:openChatWindow'),
openMomentsWindow: (filterUsername?: string) => ipcRenderer.invoke('window:openMomentsWindow', filterUsername),
onMomentsFilterUser: (callback: (username: string) => void) => {
ipcRenderer.on('moments:filterUser', (_, username) => callback(username))
return () => ipcRenderer.removeAllListeners('moments:filterUser')
},
openGroupAnalyticsWindow: () => ipcRenderer.invoke('window:openGroupAnalyticsWindow'),
openAnnualReportWindow: (year: number) => ipcRenderer.invoke('window:openAnnualReportWindow', year),
openAgreementWindow: () => ipcRenderer.invoke('window:openAgreementWindow'),
openPurchaseWindow: () => ipcRenderer.invoke('window:openPurchaseWindow'),
openWelcomeWindow: (mode?: 'default' | 'add-account') => ipcRenderer.invoke('window:openWelcomeWindow', mode),
completeWelcome: () => ipcRenderer.invoke('window:completeWelcome'),
isChatWindowOpen: () => ipcRenderer.invoke('window:isChatWindowOpen'),
closeChatWindow: () => ipcRenderer.invoke('window:closeChatWindow'),
setTitleBarOverlay: (options: { symbolColor: string }) => ipcRenderer.send('window:setTitleBarOverlay', options),
openImageViewerWindow: (
imagePath: string,
liveVideoPath?: string,
imageList?: Array<{ imagePath: string; liveVideoPath?: string }>,
options?: { sessionId?: string; imageMd5?: string; imageDatName?: string }
) => ipcRenderer.invoke('window:openImageViewerWindow', imagePath, liveVideoPath, imageList, options),
openVideoPlayerWindow: (videoPath: string, videoWidth?: number, videoHeight?: number) => ipcRenderer.invoke('window:openVideoPlayerWindow', videoPath, videoWidth, videoHeight),
openBrowserWindow: (url: string, title?: string) => ipcRenderer.invoke('window:openBrowserWindow', url, title),
openAISummaryWindow: (sessionId: string, sessionName: string) => ipcRenderer.invoke('window:openAISummaryWindow', sessionId, sessionName),
openChatHistoryWindow: (sessionId: string, messageId: number) => ipcRenderer.invoke('window:openChatHistoryWindow', sessionId, messageId),
resizeToFitVideo: (videoWidth: number, videoHeight: number) => ipcRenderer.invoke('window:resizeToFitVideo', videoWidth, videoHeight),
resizeContent: (width: number, height: number) => ipcRenderer.invoke('window:resizeContent', width, height),
move: (x: number, y: number) => ipcRenderer.send('window:move', { x, y }),
splashReady: () => ipcRenderer.send('window:splashReady'),
onSplashFadeOut: (callback: () => void) => {
ipcRenderer.on('splash:fadeOut', () => callback())
return () => ipcRenderer.removeAllListeners('splash:fadeOut')
},
onImageListUpdate: (callback: (data: { imageList: Array<{ imagePath: string; liveVideoPath?: string }>, currentIndex: number }) => void) => {
const listener = (_: any, data: any) => callback(data)
ipcRenderer.on('imageViewer:setImageList', listener)
return () => { ipcRenderer.removeListener('imageViewer:setImageList', listener) }
}
},
systemAuth: {
getStatus: () => ipcRenderer.invoke('systemAuth:getStatus') as Promise<{
platform: string
available: boolean
method: 'windows-hello' | 'touch-id' | 'none'
displayName: string
error?: string
}>,
verify: (reason?: string) => ipcRenderer.invoke('systemAuth:verify', reason) as Promise<{
success: boolean
method: 'windows-hello' | 'touch-id' | 'none'
error?: string
}>
},
// 密钥获取
wxKey: {
isWeChatRunning: () => ipcRenderer.invoke('wxkey:isWeChatRunning'),
getWeChatPid: () => ipcRenderer.invoke('wxkey:getWeChatPid'),
killWeChat: () => ipcRenderer.invoke('wxkey:killWeChat'),
launchWeChat: () => ipcRenderer.invoke('wxkey:launchWeChat'),
waitForWindow: (maxWaitSeconds?: number) => ipcRenderer.invoke('wxkey:waitForWindow', maxWaitSeconds),
startGetKey: (customWechatPath?: string, dbPath?: string) => ipcRenderer.invoke('wxkey:startGetKey', customWechatPath, dbPath),
cancel: () => ipcRenderer.invoke('wxkey:cancel'),
detectCurrentAccount: (dbPath?: string, maxTimeDiffMinutes?: number) => ipcRenderer.invoke('wxkey:detectCurrentAccount', dbPath, maxTimeDiffMinutes),
onStatus: (callback: (data: { status: string; level: number }) => void) => {
ipcRenderer.on('wxkey:status', (_, data) => callback(data))
return () => ipcRenderer.removeAllListeners('wxkey:status')
}
},
// 数据库路径
dbPath: {
autoDetect: () => ipcRenderer.invoke('dbpath:autoDetect'),
scanWxids: (rootPath: string) => ipcRenderer.invoke('dbpath:scanWxids', rootPath),
getDefault: () => ipcRenderer.invoke('dbpath:getDefault'),
getBestCachePath: () => ipcRenderer.invoke('dbpath:getBestCachePath')
},
// WCDB 数据库
wcdb: {
testConnection: (dbPath: string, hexKey: string, wxid: string, isAutoConnect?: boolean) =>
ipcRenderer.invoke('wcdb:testConnection', dbPath, hexKey, wxid, isAutoConnect),
resolveValidWxid: (dbPath: string, hexKey: string) =>
ipcRenderer.invoke('wcdb:resolveValidWxid', dbPath, hexKey),
open: (dbPath: string, hexKey: string, wxid: string) =>
ipcRenderer.invoke('wcdb:open', dbPath, hexKey, wxid),
close: () => ipcRenderer.invoke('wcdb:close'),
decryptDatabase: (dbPath: string, hexKey: string, wxid: string) =>
ipcRenderer.invoke('wcdb:decryptDatabase', dbPath, hexKey, wxid),
onDecryptProgress: (callback: (data: any) => void) => {
ipcRenderer.on('wcdb:decryptProgress', (_, data) => callback(data))
return () => ipcRenderer.removeAllListeners('wcdb:decryptProgress')
}
},
// 数据管理
dataManagement: {
scanDatabases: () => ipcRenderer.invoke('dataManagement:scanDatabases'),
decryptAll: () => ipcRenderer.invoke('dataManagement:decryptAll'),
decryptSingleDatabase: (filePath: string) => ipcRenderer.invoke('dataManagement:decryptSingleDatabase', filePath),
incrementalUpdate: () => ipcRenderer.invoke('dataManagement:incrementalUpdate'),
getCurrentCachePath: () => ipcRenderer.invoke('dataManagement:getCurrentCachePath'),
getDefaultCachePath: () => ipcRenderer.invoke('dataManagement:getDefaultCachePath'),
migrateCache: (newCachePath: string) => ipcRenderer.invoke('dataManagement:migrateCache', newCachePath),
scanImages: (dirPath: string) => ipcRenderer.invoke('dataManagement:scanImages', dirPath),
decryptImages: (dirPath: string) => ipcRenderer.invoke('dataManagement:decryptImages', dirPath),
getImageDirectories: () => ipcRenderer.invoke('dataManagement:getImageDirectories'),
decryptSingleImage: (filePath: string) => ipcRenderer.invoke('dataManagement:decryptSingleImage', filePath),
checkForUpdates: () => ipcRenderer.invoke('dataManagement:checkForUpdates'),
enableAutoUpdate: (intervalSeconds?: number) => ipcRenderer.invoke('dataManagement:enableAutoUpdate', intervalSeconds),
disableAutoUpdate: () => ipcRenderer.invoke('dataManagement:disableAutoUpdate'),
autoIncrementalUpdate: (silent?: boolean) => ipcRenderer.invoke('dataManagement:autoIncrementalUpdate', silent),
onProgress: (callback: (data: any) => void) => {
ipcRenderer.on('dataManagement:progress', (_, data) => callback(data))
return () => ipcRenderer.removeAllListeners('dataManagement:progress')
},
onUpdateAvailable: (callback: (hasUpdate: boolean) => void) => {
ipcRenderer.on('dataManagement:updateAvailable', (_, hasUpdate) => callback(hasUpdate))
return () => ipcRenderer.removeAllListeners('dataManagement:updateAvailable')
}
},
// 图片解密
imageDecrypt: {
batchDetectXorKey: (dirPath: string) => ipcRenderer.invoke('imageDecrypt:batchDetectXorKey', dirPath),
decryptImage: (inputPath: string, outputPath: string, xorKey: number, aesKey?: string) =>
ipcRenderer.invoke('imageDecrypt:decryptImage', inputPath, outputPath, xorKey, aesKey)
},
// 图片解密(新 API
image: {
decrypt: (payload: { sessionId?: string; imageMd5?: string; imageDatName?: string; force?: boolean }) =>
ipcRenderer.invoke('image:decrypt', payload),
resolveCache: (payload: { sessionId?: string; imageMd5?: string; imageDatName?: string }) =>
ipcRenderer.invoke('image:resolveCache', payload),
onUpdateAvailable: (callback: (data: { cacheKey: string; imageMd5?: string; imageDatName?: string }) => void) => {
ipcRenderer.on('image:updateAvailable', (_, data) => callback(data))
return () => ipcRenderer.removeAllListeners('image:updateAvailable')
},
onCacheResolved: (callback: (data: { cacheKey: string; imageMd5?: string; imageDatName?: string; localPath: string }) => void) => {
ipcRenderer.on('image:cacheResolved', (_, data) => callback(data))
return () => ipcRenderer.removeAllListeners('image:cacheResolved')
},
deleteThumbnails: () => ipcRenderer.invoke('image:deleteThumbnails'),
countThumbnails: () => ipcRenderer.invoke('image:countThumbnails'),
},
// 视频
video: {
getVideoInfo: (videoMd5: string) => ipcRenderer.invoke('video:getVideoInfo', videoMd5),
readFile: (videoPath: string) => ipcRenderer.invoke('video:readFile', videoPath),
parseVideoMd5: (content: string) => ipcRenderer.invoke('video:parseVideoMd5', content),
parseChannelVideo: (content: string) => ipcRenderer.invoke('video:parseChannelVideo', content),
downloadChannelVideo: (videoInfo: any, key?: string) => ipcRenderer.invoke('video:downloadChannelVideo', videoInfo, key),
onDownloadProgress: (callback: (progress: any) => void) => {
const listener = (_: any, progress: any) => callback(progress)
ipcRenderer.on('video:downloadProgress', listener)
return () => ipcRenderer.removeListener('video:downloadProgress', listener)
}
},
// 图片密钥获取
imageKey: {
getImageKeys: (userDir: string) => ipcRenderer.invoke('imageKey:getImageKeys', userDir),
onProgress: (callback: (msg: string) => void) => {
ipcRenderer.on('imageKey:progress', (_, msg) => callback(msg))
return () => ipcRenderer.removeAllListeners('imageKey:progress')
}
},
// 聊天
chat: {
connect: () => ipcRenderer.invoke('chat:connect'),
getSessions: () => ipcRenderer.invoke('chat:getSessions'),
getContacts: () => ipcRenderer.invoke('chat:getContacts'),
getMessages: (sessionId: string, offset?: number, limit?: number) =>
ipcRenderer.invoke('chat:getMessages', sessionId, offset, limit),
getMessagesBefore: (
sessionId: string,
cursorSortSeq: number,
limit?: number,
cursorCreateTime?: number,
cursorLocalId?: number
) =>
ipcRenderer.invoke('chat:getMessagesBefore', sessionId, cursorSortSeq, limit, cursorCreateTime, cursorLocalId),
getMessagesAfter: (
sessionId: string,
cursorSortSeq: number,
limit?: number,
cursorCreateTime?: number,
cursorLocalId?: number
) =>
ipcRenderer.invoke('chat:getMessagesAfter', sessionId, cursorSortSeq, limit, cursorCreateTime, cursorLocalId),
getAllVoiceMessages: (sessionId: string) =>
ipcRenderer.invoke('chat:getAllVoiceMessages', sessionId),
getAllImageMessages: (sessionId: string) =>
ipcRenderer.invoke('chat:getAllImageMessages', sessionId),
getContact: (username: string) => ipcRenderer.invoke('chat:getContact', username),
getContactAvatar: (username: string) => ipcRenderer.invoke('chat:getContactAvatar', username),
resolveTransferDisplayNames: (chatroomId: string, payerUsername: string, receiverUsername: string) =>
ipcRenderer.invoke('chat:resolveTransferDisplayNames', chatroomId, payerUsername, receiverUsername),
getMyAvatarUrl: () => ipcRenderer.invoke('chat:getMyAvatarUrl'),
getMyUserInfo: () => ipcRenderer.invoke('chat:getMyUserInfo'),
downloadEmoji: (cdnUrl: string, md5?: string, productId?: string, createTime?: number, encryptUrl?: string, aesKey?: string) => ipcRenderer.invoke('chat:downloadEmoji', cdnUrl, md5, productId, createTime, encryptUrl, aesKey),
close: () => ipcRenderer.invoke('chat:close'),
refreshCache: () => ipcRenderer.invoke('chat:refreshCache'),
setCurrentSession: (sessionId: string | null) => ipcRenderer.invoke('chat:setCurrentSession', sessionId),
getSessionDetail: (sessionId: string) => ipcRenderer.invoke('chat:getSessionDetail', sessionId),
getVoiceData: (sessionId: string, msgId: string, createTime?: number) => ipcRenderer.invoke('chat:getVoiceData', sessionId, msgId, createTime),
getMessagesByDate: (sessionId: string, targetTimestamp: number, limit?: number) =>
ipcRenderer.invoke('chat:getMessagesByDate', sessionId, targetTimestamp, limit),
getMessage: (sessionId: string, localId: number) => ipcRenderer.invoke('chat:getMessage', sessionId, localId),
getDatesWithMessages: (sessionId: string, year: number, month: number) =>
ipcRenderer.invoke('chat:getDatesWithMessages', sessionId, year, month),
onSessionsUpdated: (callback: (sessions: any[]) => void) => {
const listener = (_: any, sessions: any[]) => callback(sessions)
ipcRenderer.on('chat:sessions-updated', listener)
return () => ipcRenderer.removeListener('chat:sessions-updated', listener)
},
onNewMessages: (callback: (data: { sessionId: string; messages: any[] }) => void) => {
const listener = (_: any, data: any) => callback(data)
ipcRenderer.on('chat:new-messages', listener)
return () => ipcRenderer.removeListener('chat:new-messages', listener)
}
},
// 朋友圈
sns: {
getTimeline: (limit?: number, offset?: number, usernames?: string[], keyword?: string, startTime?: number, endTime?: number) =>
ipcRenderer.invoke('sns:getTimeline', limit || 20, offset || 0, usernames, keyword, startTime, endTime),
proxyImage: (params: { url: string; key?: string | number }) =>
ipcRenderer.invoke('sns:proxyImage', params),
downloadImage: (params: { url: string; key?: string | number }) =>
ipcRenderer.invoke('sns:downloadImage', params),
downloadEmoji: (params: { url: string; encryptUrl?: string; aesKey?: string }) =>
ipcRenderer.invoke('sns:downloadEmoji', params),
writeExportFile: (filePath: string, content: string) =>
ipcRenderer.invoke('sns:writeExportFile', filePath, content),
saveMediaToDir: (params: { url: string; key?: string | number; outputDir: string; index: number; md5?: string; isAvatar?: boolean; username?: string; isEmoji?: boolean; encryptUrl?: string; aesKey?: string }) =>
ipcRenderer.invoke('sns:saveMediaToDir', params)
},
// 数据分析
analytics: {
getOverallStatistics: () => ipcRenderer.invoke('analytics:getOverallStatistics'),
getContactRankings: (limit?: number) => ipcRenderer.invoke('analytics:getContactRankings', limit),
getTimeDistribution: () => ipcRenderer.invoke('analytics:getTimeDistribution')
},
// 群聊分析
groupAnalytics: {
getGroupChats: () => ipcRenderer.invoke('groupAnalytics:getGroupChats'),
getGroupMembers: (chatroomId: string) => ipcRenderer.invoke('groupAnalytics:getGroupMembers', chatroomId),
getGroupMessageRanking: (chatroomId: string, limit?: number, startTime?: number, endTime?: number) => ipcRenderer.invoke('groupAnalytics:getGroupMessageRanking', chatroomId, limit, startTime, endTime),
getGroupActiveHours: (chatroomId: string, startTime?: number, endTime?: number) => ipcRenderer.invoke('groupAnalytics:getGroupActiveHours', chatroomId, startTime, endTime),
getGroupMediaStats: (chatroomId: string, startTime?: number, endTime?: number) => ipcRenderer.invoke('groupAnalytics:getGroupMediaStats', chatroomId, startTime, endTime)
},
// 年度报告
annualReport: {
getAvailableYears: () => ipcRenderer.invoke('annualReport:getAvailableYears'),
generateReport: (year: number) => ipcRenderer.invoke('annualReport:generateReport', year)
},
// 导出
export: {
exportSessions: (sessionIds: string[], outputDir: string, options: any) =>
ipcRenderer.invoke('export:exportSessions', sessionIds, outputDir, options),
exportSession: (sessionId: string, outputPath: string, options: any) =>
ipcRenderer.invoke('export:exportSession', sessionId, outputPath, options),
exportContacts: (outputDir: string, options: any) =>
ipcRenderer.invoke('export:exportContacts', outputDir, options),
onProgress: (callback: (data: any) => void) => {
ipcRenderer.on('export:progress', (_, data) => callback(data))
return () => ipcRenderer.removeAllListeners('export:progress')
}
},
// 激活
activation: {
getDeviceId: () => ipcRenderer.invoke('activation:getDeviceId'),
verifyCode: (code: string) => ipcRenderer.invoke('activation:verifyCode', code),
activate: (code: string) => ipcRenderer.invoke('activation:activate', code),
checkStatus: () => ipcRenderer.invoke('activation:checkStatus'),
getTypeDisplayName: (type: string | null) => ipcRenderer.invoke('activation:getTypeDisplayName', type),
clearCache: () => ipcRenderer.invoke('activation:clearCache')
},
cache: {
clearImages: () => ipcRenderer.invoke('cache:clearImages'),
clearEmojis: () => ipcRenderer.invoke('cache:clearEmojis'),
clearDatabases: () => ipcRenderer.invoke('cache:clearDatabases'),
clearAll: () => ipcRenderer.invoke('cache:clearAll'),
clearConfig: () => ipcRenderer.invoke('cache:clearConfig'),
clearCurrentAccount: (deleteLocalData?: boolean) => ipcRenderer.invoke('cache:clearCurrentAccount', deleteLocalData),
clearAllAccountConfigs: () => ipcRenderer.invoke('cache:clearAllAccountConfigs'),
getCacheSize: () => ipcRenderer.invoke('cache:getCacheSize')
},
log: {
getLogFiles: () => ipcRenderer.invoke('log:getLogFiles'),
readLogFile: (filename: string) => ipcRenderer.invoke('log:readLogFile', filename),
clearLogs: () => ipcRenderer.invoke('log:clearLogs'),
getLogSize: () => ipcRenderer.invoke('log:getLogSize'),
getLogDirectory: () => ipcRenderer.invoke('log:getLogDirectory'),
setLogLevel: (level: string) => ipcRenderer.invoke('log:setLogLevel', level),
getLogLevel: () => ipcRenderer.invoke('log:getLogLevel')
},
// 语音转文字 (STT)
stt: {
getModelStatus: () => ipcRenderer.invoke('stt:getModelStatus'),
downloadModel: () => ipcRenderer.invoke('stt:downloadModel'),
transcribe: (wavBase64: string, sessionId: string, createTime: number, force?: boolean) => ipcRenderer.invoke('stt:transcribe', wavBase64, sessionId, createTime, force),
testOnlineConfig: (overrides?: { provider?: 'openai-compatible' | 'aliyun-qwen-asr' | 'custom'; apiKey?: string; baseURL?: string; model?: string; language?: string; timeoutMs?: number }) =>
ipcRenderer.invoke('stt-online:test-config', overrides),
onDownloadProgress: (callback: (progress: { modelName: string; downloadedBytes: number; totalBytes?: number; percent?: number }) => void) => {
ipcRenderer.on('stt:downloadProgress', (_, progress) => callback(progress))
return () => ipcRenderer.removeAllListeners('stt:downloadProgress')
},
onPartialResult: (callback: (text: string) => void) => {
ipcRenderer.on('stt:partialResult', (_, text) => callback(text))
return () => ipcRenderer.removeAllListeners('stt:partialResult')
},
getCachedTranscript: (sessionId: string, createTime: number) => ipcRenderer.invoke('stt:getCachedTranscript', sessionId, createTime),
updateTranscript: (sessionId: string, createTime: number, transcript: string) => ipcRenderer.invoke('stt:updateTranscript', sessionId, createTime, transcript),
clearModel: () => ipcRenderer.invoke('stt:clearModel')
},
// 语音转文字 - Whisper GPU 加速
sttWhisper: {
detectGPU: () => ipcRenderer.invoke('stt-whisper:detect-gpu'),
checkModel: (modelType: string) => ipcRenderer.invoke('stt-whisper:check-model', modelType),
downloadModel: (modelType: string) => ipcRenderer.invoke('stt-whisper:download-model', modelType),
clearModel: (modelType: string) => ipcRenderer.invoke('stt-whisper:clear-model', modelType),
transcribe: (wavData: Buffer, options: { modelType?: string; language?: string }) =>
ipcRenderer.invoke('stt-whisper:transcribe', wavData, options),
onDownloadProgress: (callback: (progress: { downloadedBytes: number; totalBytes?: number; percent?: number }) => void) => {
ipcRenderer.on('stt-whisper:download-progress', (_, progress) => callback(progress))
return () => ipcRenderer.removeAllListeners('stt-whisper:download-progress')
},
downloadGPUComponents: () => ipcRenderer.invoke('stt-whisper:download-gpu-components'),
checkGPUComponents: () => ipcRenderer.invoke('stt-whisper:check-gpu-components'),
onGPUDownloadProgress: (callback: (progress: { currentFile: string; fileProgress: number; overallProgress: number; completedFiles: number; totalFiles: number }) => void) => {
ipcRenderer.on('stt-whisper:gpu-download-progress', (_, progress) => callback(progress))
return () => ipcRenderer.removeAllListeners('stt-whisper:gpu-download-progress')
}
},
// AI 摘要
ai: {
getProviders: () => ipcRenderer.invoke('ai:getProviders'),
getProxyStatus: () => ipcRenderer.invoke('ai:getProxyStatus'),
refreshProxy: () => ipcRenderer.invoke('ai:refreshProxy'),
testProxy: (proxyUrl: string, testUrl?: string) => ipcRenderer.invoke('ai:testProxy', proxyUrl, testUrl),
testConnection: (provider: string, apiKey: string) => ipcRenderer.invoke('ai:testConnection', provider, apiKey),
estimateCost: (messageCount: number, provider: string) => ipcRenderer.invoke('ai:estimateCost', messageCount, provider),
getUsageStats: (startDate?: string, endDate?: string) => ipcRenderer.invoke('ai:getUsageStats', startDate, endDate),
getSummaryHistory: (sessionId: string, limit?: number) => ipcRenderer.invoke('ai:getSummaryHistory', sessionId, limit),
deleteSummary: (id: number) => ipcRenderer.invoke('ai:deleteSummary', id),
renameSummary: (id: number, customName: string) => ipcRenderer.invoke('ai:renameSummary', id, customName),
cleanExpiredCache: () => ipcRenderer.invoke('ai:cleanExpiredCache'),
generateSummary: (sessionId: string, timeRange: number, options: {
provider: string
apiKey: string
model: string
detail: 'simple' | 'normal' | 'detailed'
systemPromptPreset?: 'default' | 'decision-focus' | 'action-focus' | 'risk-focus' | 'custom'
customSystemPrompt?: string
customRequirement?: string
sessionName?: string
enableThinking?: boolean
}) => ipcRenderer.invoke('ai:generateSummary', sessionId, timeRange, options),
onSummaryChunk: (callback: (chunk: string) => void) => {
ipcRenderer.on('ai:summaryChunk', (_, chunk) => callback(chunk))
return () => ipcRenderer.removeAllListeners('ai:summaryChunk')
}
}
})
// 主题由 index.html 中的内联脚本处理,这里只负责同步 localStorage
; (async () => {
try {
const theme = await ipcRenderer.invoke('config:get', 'theme') || 'cloud-dancer'
const themeMode = await ipcRenderer.invoke('config:get', 'themeMode') || 'light'
// 更新 localStorage 以供下次同步使用(主窗口场景)
try {
localStorage.setItem('theme', theme)
localStorage.setItem('themeMode', themeMode)
} catch (e) {
// localStorage 可能不可用
}
} catch (e) {
// 忽略错误
}
})()