diff --git a/electron.vite.config.1755071894873.mjs b/electron.vite.config.1755071894873.mjs new file mode 100644 index 000000000..12e52acfe --- /dev/null +++ b/electron.vite.config.1755071894873.mjs @@ -0,0 +1,104 @@ +// electron.vite.config.ts +import { resolve } from "path"; +import { defineConfig, externalizeDepsPlugin } from "electron-vite"; +import vue from "@vitejs/plugin-vue"; +import autoprefixer from "autoprefixer"; +import tailwind from "tailwindcss"; +import vueDevTools from "vite-plugin-vue-devtools"; +import svgLoader from "vite-svg-loader"; +import monacoEditorPlugin from "vite-plugin-monaco-editor-esm"; +import path from "node:path"; +var electron_vite_config_default = defineConfig({ + main: { + plugins: [ + externalizeDepsPlugin({ + exclude: ["mermaid", "dompurify"] + }) + ], + resolve: { + alias: { + "@": resolve("src/main/"), + "@shared": resolve("src/shared") + } + }, + build: { + rollupOptions: { + external: ["sharp", "@duckdb/node-api"], + output: { + inlineDynamicImports: true, + manualChunks: void 0 + // Disable automatic chunk splitting + } + } + } + }, + preload: { + plugins: [externalizeDepsPlugin()], + resolve: { + alias: { + "@shared": resolve("src/shared") + } + }, + build: { + rollupOptions: { + input: { + index: resolve("src/preload/index.ts"), + floating: resolve("src/preload/floating-preload.ts") + } + } + } + }, + renderer: { + optimizeDeps: { + include: [ + "monaco-editor", + "axios" + ] + }, + resolve: { + alias: { + "@": resolve("src/renderer/src"), + "@shell": resolve("src/renderer/shell"), + "@shared": resolve("src/shared"), + vue: "vue/dist/vue.esm-bundler.js" + } + }, + css: { + postcss: { + // @ts-ignore + plugins: [tailwind(), autoprefixer()] + } + }, + server: { + host: "0.0.0.0" + // 防止代理干扰,导致vite-electron之间ws://localhost:5713和http://localhost:5713通信失败、页面组件无法加载 + }, + plugins: [ + monacoEditorPlugin({ + languageWorkers: ["editorWorkerService", "typescript", "css", "html", "json"], + customDistPath(_root, buildOutDir, _base) { + return path.resolve(buildOutDir, "monacoeditorwork"); + } + }), + vue(), + svgLoader(), + vueDevTools({ + // use export LAUNCH_EDITOR=cursor instead + // launchEditor: 'cursor' + }) + ], + build: { + minify: "esbuild", + rollupOptions: { + input: { + shell: resolve("src/renderer/shell/index.html"), + index: resolve("src/renderer/index.html"), + floating: resolve("src/renderer/floating/index.html") + } + } + } + } +}); +export { + electron_vite_config_default as default +}; diff --git a/electron.vite.config.1755572548960.mjs b/electron.vite.config.1755572548960.mjs new file mode 100644 index 000000000..12e52acfe --- /dev/null +++ b/electron.vite.config.1755572548960.mjs @@ -0,0 +1,104 @@ +// electron.vite.config.ts +import { resolve } from "path"; +import { defineConfig, externalizeDepsPlugin } from "electron-vite"; +import vue from "@vitejs/plugin-vue"; +import autoprefixer from "autoprefixer"; +import tailwind from "tailwindcss"; +import vueDevTools from "vite-plugin-vue-devtools"; +import svgLoader from "vite-svg-loader"; +import monacoEditorPlugin from "vite-plugin-monaco-editor-esm"; +import path from "node:path"; +var electron_vite_config_default = defineConfig({ + main: { + plugins: [ + externalizeDepsPlugin({ + exclude: ["mermaid", "dompurify"] + }) + ], + resolve: { + alias: { + "@": resolve("src/main/"), + "@shared": resolve("src/shared") + } + }, + build: { + rollupOptions: { + external: ["sharp", "@duckdb/node-api"], + output: { + inlineDynamicImports: true, + manualChunks: void 0 + // Disable automatic chunk splitting + } + } + } + }, + preload: { + plugins: [externalizeDepsPlugin()], + resolve: { + alias: { + "@shared": resolve("src/shared") + } + }, + build: { + rollupOptions: { + input: { + index: resolve("src/preload/index.ts"), + floating: resolve("src/preload/floating-preload.ts") + } + } + } + }, + renderer: { + optimizeDeps: { + include: [ + "monaco-editor", + "axios" + ] + }, + resolve: { + alias: { + "@": resolve("src/renderer/src"), + "@shell": resolve("src/renderer/shell"), + "@shared": resolve("src/shared"), + vue: "vue/dist/vue.esm-bundler.js" + } + }, + css: { + postcss: { + // @ts-ignore + plugins: [tailwind(), autoprefixer()] + } + }, + server: { + host: "0.0.0.0" + // 防止代理干扰,导致vite-electron之间ws://localhost:5713和http://localhost:5713通信失败、页面组件无法加载 + }, + plugins: [ + monacoEditorPlugin({ + languageWorkers: ["editorWorkerService", "typescript", "css", "html", "json"], + customDistPath(_root, buildOutDir, _base) { + return path.resolve(buildOutDir, "monacoeditorwork"); + } + }), + vue(), + svgLoader(), + vueDevTools({ + // use export LAUNCH_EDITOR=cursor instead + // launchEditor: 'cursor' + }) + ], + build: { + minify: "esbuild", + rollupOptions: { + input: { + shell: resolve("src/renderer/shell/index.html"), + index: resolve("src/renderer/index.html"), + floating: resolve("src/renderer/floating/index.html") + } + } + } + } +}); +export { + electron_vite_config_default as default +}; diff --git a/package.json b/package.json index 5219feee4..1c819ea04 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "@google/genai": "^1.13.0", "@jxa/run": "^1.4.0", "@modelcontextprotocol/sdk": "^1.17.2", + "@nut-tree/nut-js": "^4.2.0", "axios": "^1.7.9", "better-sqlite3-multiple-ciphers": "11.10.0", "cheerio": "^1.0.0", @@ -80,6 +81,7 @@ "https-proxy-agent": "^7.0.6", "jsonrepair": "^3.13.0", "mammoth": "^1.9.0", + "marked": "^16.1.2", "mime-types": "^2.1.35", "nanoid": "^5.1.5", "ollama": "^0.5.16", @@ -152,10 +154,10 @@ "vite-plugin-monaco-editor-esm": "^2.0.2", "vite-plugin-vue-devtools": "^8.0.0", "vite-svg-loader": "^5.1.0", - "vue-renderer-markdown": "^0.0.34", "vitest": "^3.2.4", "vue": "^3.5.18", "vue-i18n": "^11.1.11", + "vue-renderer-markdown": "^0.0.34", "vue-router": "4", "vue-tsc": "^2.2.10", "vue-use-monaco": "^0.0.6", diff --git a/src/main/index.ts b/src/main/index.ts index b1eecdefc..b15f50f03 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -125,7 +125,7 @@ app.whenReady().then(async () => { targetWindow.focus() // 确保窗口置顶 // 触发更新 - presenter.upgradePresenter.checkUpdate() + // presenter.upgradePresenter.checkUpdate() }) // 监听显示/隐藏窗口事件 (从托盘或快捷键或悬浮窗口触发) diff --git a/src/main/presenter/index.ts b/src/main/presenter/index.ts index 7cd9cfc56..d77862ff9 100644 --- a/src/main/presenter/index.ts +++ b/src/main/presenter/index.ts @@ -11,7 +11,7 @@ import { LLMProviderPresenter } from './llmProviderPresenter' import { ConfigPresenter } from './configPresenter' import { ThreadPresenter } from './threadPresenter' import { DevicePresenter } from './devicePresenter' -import { UpgradePresenter } from './upgradePresenter' +// import { UpgradePresenter } from './upgradePresenter' import { FilePresenter } from './filePresenter/FilePresenter' import { McpPresenter } from './mcpPresenter' import { SyncPresenter } from './syncPresenter' @@ -45,7 +45,7 @@ export class Presenter implements IPresenter { configPresenter: ConfigPresenter threadPresenter: ThreadPresenter devicePresenter: DevicePresenter - upgradePresenter: UpgradePresenter + // upgradePresenter: UpgradePresenter shortcutPresenter: ShortcutPresenter filePresenter: FilePresenter mcpPresenter: McpPresenter @@ -77,7 +77,7 @@ export class Presenter implements IPresenter { this.configPresenter ) this.mcpPresenter = new McpPresenter(this.configPresenter) - this.upgradePresenter = new UpgradePresenter() + // this.upgradePresenter = new UpgradePresenter() this.shortcutPresenter = new ShortcutPresenter(this.configPresenter) this.filePresenter = new FilePresenter() this.syncPresenter = new SyncPresenter(this.configPresenter, this.sqlitePresenter) diff --git a/src/main/presenter/llmProviderPresenter/index.ts b/src/main/presenter/llmProviderPresenter/index.ts index 33b76b575..f9fc8b0fa 100644 --- a/src/main/presenter/llmProviderPresenter/index.ts +++ b/src/main/presenter/llmProviderPresenter/index.ts @@ -821,6 +821,41 @@ export class LLMProviderPresenter implements ILlmProviderPresenter { if (abortController.signal.aborted) break // Check after tool call returns + // 检查是否需要直接返回工具调用结果,不经过AI二次处理 + const directReturnValue = + typeof toolResponse.rawData.directReturn === 'boolean' + ? toolResponse.rawData.directReturn + : toolResponse.rawData.directReturn?.aiChange + console.log('index-toolResponse.rawData.directReturn', directReturnValue) + if (directReturnValue) { + console.log( + `[Agent Loop] Direct return tool result for ${toolCall.name}, skipping AI processing` + ) + + // 直接返回工具调用结果,不继续对话 + yield { + type: 'response', + data: { + eventId, + tool_call: 'end', + tool_call_id: toolCall.id, + tool_call_name: toolCall.name, + tool_call_params: toolCall.arguments, + tool_call_server_name: toolDef.server.name, + tool_call_server_icons: toolDef.server.icons, + tool_call_server_description: toolDef.server.description, + tool_call_response: toolResponse.content, + tool_call_response_raw: toolResponse.rawData, + direct_return: true + } + } + + // 结束agent循环 + console.log(`[Agent Loop] Ending agent loop for direct return, event: ${eventId}`) + needContinueConversation = false + break + } + // Check if permission is required if (toolResponse.rawData.requiresPermission) { console.log( @@ -919,6 +954,19 @@ export class LLMProviderPresenter implements ILlmProviderPresenter { tool_call_response_raw: toolResponse.rawData // Full raw data } } + + // 如果工具响应中包含 directReturn 标志,则直接返回结果,不继续对话 + if (toolResponse.rawData && toolResponse.rawData.directReturn === true) { + console.log( + `[LLMProviderPresenter] Tool response has directReturn flag, ending agent loop` + ) + // 发送结束事件 + yield { + type: 'end', + data: { eventId, userStop: false } + } + return // 结束代理循环 + } } else { // Non-native FC: Add tool execution record to conversation history for next LLM turn. @@ -981,6 +1029,19 @@ export class LLMProviderPresenter implements ILlmProviderPresenter { tool_call_response_raw: toolResponse.rawData // Full raw data for ThreadPresenter to store } } + + // 如果工具响应中包含 directReturn 标志,则直接返回结果,不继续对话 + if (toolResponse.rawData && toolResponse.rawData.directReturn === true) { + console.log( + `[LLMProviderPresenter] Tool response has directReturn flag, ending agent loop` + ) + // 发送结束事件 + yield { + type: 'end', + data: { eventId, userStop: false } + } + return // 结束代理循环 + } } } catch (toolError) { if (abortController.signal.aborted) break // Check after tool error diff --git a/src/main/presenter/mcpPresenter/toolManager.ts b/src/main/presenter/mcpPresenter/toolManager.ts index 1f984e075..99a886270 100644 --- a/src/main/presenter/mcpPresenter/toolManager.ts +++ b/src/main/presenter/mcpPresenter/toolManager.ts @@ -13,6 +13,37 @@ import { ServerManager } from './serverManager' import { McpClient } from './mcpClient' import { jsonrepair } from 'jsonrepair' import { getErrorMessageLabels } from '@shared/i18n' +import { ipcMain } from 'electron' +// import { useAiStore } from '@/render/src/stores/aiStore'; +// const aiStore = useAiStore(); +// console.log("MCPToolResponse123",response) +let aiChange = false +ipcMain.on('aiChangeEvent', (_event, arg) => { + console.log('收到渲染进程消息aiChangeEvent:', arg) + // 处理逻辑... + aiChange = arg +}) +import { keyboard, Key } from "@nut-tree/nut-js"; +// ipcMain.handle('simulate-capslock', (_event, action) => { +// if (action === 'press') { +// ks.sendKey('caps_lock'); +// console.log('主进程: 模拟按下 CapsLock'); +// } else if (action === 'release') { +// ks.sendKey('caps_lock'); +// console.log('主进程: 模拟释放 CapsLock'); +// } +// }); +if (!ipcMain.listenerCount('simulate-capslock')) { + ipcMain.on('simulate-capslock', async (_event, action) => { + if (action === 'press') { + await keyboard.type(Key.CapsLock); + console.log('主进程: 模拟按下 CapsLock-press'); + } else if (action === 'release') { + await keyboard.type(Key.CapsLock); + console.log('主进程: 模拟释放 CapsLock-release'); + } + }); +} export class ToolManager { private configPresenter: IConfigPresenter @@ -429,13 +460,17 @@ export class ToolManager { } else if (result.content) { formattedContent = JSON.stringify(result.content) } - const response: MCPToolResponse = { toolCallId: toolCall.id, content: formattedContent, - isError: result.isError + isError: result.isError, + directReturn: aiChange } - + console.log('MCPToolResponse123', response) + // ipcMain.on('aiChangeEvent', (event, arg) => { + // console.log('收到渲染进程消息aiChangeEvent:', arg); + // // 处理逻辑... + // }); // Trigger event eventBus.send(MCP_EVENTS.TOOL_CALL_RESULT, SendTarget.ALL_WINDOWS, response) @@ -446,7 +481,8 @@ export class ToolManager { return { toolCallId: toolCall.id, content: `Error: Failed to execute tool '${toolCall.function.name}': ${errorMessage}`, - isError: true + isError: true, + directReturn: aiChange } } } diff --git a/src/main/presenter/threadPresenter/index.ts b/src/main/presenter/threadPresenter/index.ts index d8e377891..6048b9cdb 100644 --- a/src/main/presenter/threadPresenter/index.ts +++ b/src/main/presenter/threadPresenter/index.ts @@ -1603,30 +1603,30 @@ export class ThreadPresenter implements IThreadPresenter { // 9. 如果有工具调用结果,发送工具调用结果事件 if (toolCallResponse && toolCall) { // console.log('toolCallResponse', toolCallResponse) - eventBus.sendToRenderer(STREAM_EVENTS.RESPONSE, SendTarget.ALL_WINDOWS, { - eventId: state.message.id, - content: '', - tool_call: 'start', - tool_call_id: toolCall.id, - tool_call_name: toolCall.name, - tool_call_params: toolCall.params, - tool_call_response: toolCallResponse.content, - tool_call_server_name: toolCall.server_name, - tool_call_server_icons: toolCall.server_icons, - tool_call_server_description: toolCall.server_description - }) - eventBus.sendToRenderer(STREAM_EVENTS.RESPONSE, SendTarget.ALL_WINDOWS, { - eventId: state.message.id, - content: '', - tool_call: 'running', - tool_call_id: toolCall.id, - tool_call_name: toolCall.name, - tool_call_params: toolCall.params, - tool_call_response: toolCallResponse.content, - tool_call_server_name: toolCall.server_name, - tool_call_server_icons: toolCall.server_icons, - tool_call_server_description: toolCall.server_description - }) + // eventBus.sendToRenderer(STREAM_EVENTS.RESPONSE, SendTarget.ALL_WINDOWS, { + // eventId: state.message.id, + // content: '', + // tool_call: 'start', + // tool_call_id: toolCall.id, + // tool_call_name: toolCall.name, + // tool_call_params: toolCall.params, + // tool_call_response: toolCallResponse.content, + // tool_call_server_name: toolCall.server_name, + // tool_call_server_icons: toolCall.server_icons, + // tool_call_server_description: toolCall.server_description + // }) + // eventBus.sendToRenderer(STREAM_EVENTS.RESPONSE, SendTarget.ALL_WINDOWS, { + // eventId: state.message.id, + // content: '', + // tool_call: 'running', + // tool_call_id: toolCall.id, + // tool_call_name: toolCall.name, + // tool_call_params: toolCall.params, + // tool_call_response: toolCallResponse.content, + // tool_call_server_name: toolCall.server_name, + // tool_call_server_icons: toolCall.server_icons, + // tool_call_server_description: toolCall.server_description + // }) eventBus.sendToRenderer(STREAM_EVENTS.RESPONSE, SendTarget.ALL_WINDOWS, { eventId: state.message.id, content: '', diff --git a/src/main/presenter/upgradePresenter/index.ts b/src/main/presenter/upgradePresenter/index.ts index 09c0ba9d8..dc0c2f82e 100644 --- a/src/main/presenter/upgradePresenter/index.ts +++ b/src/main/presenter/upgradePresenter/index.ts @@ -1,440 +1,440 @@ -import { app, shell } from 'electron' -import { IUpgradePresenter, UpdateStatus, UpdateProgress } from '@shared/presenter' -import { eventBus, SendTarget } from '@/eventbus' -import { UPDATE_EVENTS, WINDOW_EVENTS } from '@/events' -import electronUpdater from 'electron-updater' -import axios from 'axios' -import { compare } from 'compare-versions' -import fs from 'fs' -import path from 'path' - -const { autoUpdater } = electronUpdater - -// 版本信息接口 -interface VersionInfo { - version: string - releaseDate: string - releaseNotes: string - githubUrl: string - downloadUrl: string -} - -// 获取平台和架构信息 -const getPlatformInfo = () => { - const platform = process.platform - const arch = process.arch - let platformString = '' - - if (platform === 'win32') { - platformString = arch === 'arm64' ? 'winarm' : 'winx64' - } else if (platform === 'darwin') { - platformString = arch === 'arm64' ? 'macarm' : 'macx64' - } else if (platform === 'linux') { - platformString = arch === 'arm64' ? 'linuxarm' : 'linuxx64' - } - - return platformString -} +// import { app, shell } from 'electron' +// // import { IUpgradePresenter, UpdateStatus, UpdateProgress } from '@shared/presenter' +// import { eventBus, SendTarget } from '@/eventbus' +// import { UPDATE_EVENTS, WINDOW_EVENTS } from '@/events' +// import electronUpdater from 'electron-updater' +// import axios from 'axios' +// import { compare } from 'compare-versions' +// import fs from 'fs' +// import path from 'path' + +// const { autoUpdater } = electronUpdater + +// // 版本信息接口 +// interface VersionInfo { +// version: string +// releaseDate: string +// releaseNotes: string +// githubUrl: string +// downloadUrl: string +// } + +// // 获取平台和架构信息 +// const getPlatformInfo = () => { +// const platform = process.platform +// const arch = process.arch +// let platformString = '' + +// if (platform === 'win32') { +// platformString = arch === 'arm64' ? 'winarm' : 'winx64' +// } else if (platform === 'darwin') { +// platformString = arch === 'arm64' ? 'macarm' : 'macx64' +// } else if (platform === 'linux') { +// platformString = arch === 'arm64' ? 'linuxarm' : 'linuxx64' +// } + +// return platformString +// } // 获取版本检查的基础URL -const getVersionCheckBaseUrl = () => { - return 'https://cdn.deepchatai.cn/upgrade' -} +// const getVersionCheckBaseUrl = () => { +// return 'https://cdn.deepchatai.cn/upgrade' +// } // 获取自动更新状态文件路径 -const getUpdateMarkerFilePath = () => { - return path.join(app.getPath('userData'), 'auto_update_marker.json') -} - -export class UpgradePresenter implements IUpgradePresenter { - private _lock: boolean = false - private _status: UpdateStatus = 'not-available' - private _progress: UpdateProgress | null = null - private _error: string | null = null - private _versionInfo: VersionInfo | null = null - private _baseUrl: string - private _lastCheckTime: number = 0 // 上次检查更新的时间戳 - private _updateMarkerPath: string - private _previousUpdateFailed: boolean = false // 标记上次更新是否失败 - - constructor() { - this._baseUrl = getVersionCheckBaseUrl() - this._updateMarkerPath = getUpdateMarkerFilePath() - - // 配置自动更新 - autoUpdater.autoDownload = false // 默认不自动下载,由我们手动控制 - autoUpdater.allowDowngrade = false - autoUpdater.autoInstallOnAppQuit = true - - // 错误处理 - autoUpdater.on('error', (e) => { - console.log('自动更新失败', e.message) - this._lock = false - this._status = 'error' - this._error = e.message - eventBus.sendToRenderer(UPDATE_EVENTS.STATUS_CHANGED, SendTarget.ALL_WINDOWS, { - status: this._status, - error: this._error, - info: this._versionInfo - }) - }) - - // 检查更新状态 - autoUpdater.on('checking-for-update', () => { - console.log('正在检查更新') - }) - - // 无可用更新 - autoUpdater.on('update-not-available', () => { - console.log('无可用更新') - this._lock = false - this._status = 'not-available' - eventBus.sendToRenderer(UPDATE_EVENTS.STATUS_CHANGED, SendTarget.ALL_WINDOWS, { - status: this._status - }) - }) - - // 有可用更新 - autoUpdater.on('update-available', (info) => { - console.log('检测到新版本', info) - this._status = 'available' - - // 重要:这里不再使用info中的信息更新this._versionInfo - // 而是确保使用之前从versionUrl获取的原始信息 - console.log('使用已保存的版本信息:', this._versionInfo) - // 检测到更新后自动开始下载 - this.startDownloadUpdate() - }) - - // 下载进度 - autoUpdater.on('download-progress', (progressObj) => { - this._lock = true - this._status = 'downloading' - this._progress = { - bytesPerSecond: progressObj.bytesPerSecond, - percent: progressObj.percent, - transferred: progressObj.transferred, - total: progressObj.total - } - eventBus.sendToRenderer(UPDATE_EVENTS.STATUS_CHANGED, SendTarget.ALL_WINDOWS, { - status: this._status, - info: this._versionInfo // 使用已保存的版本信息 - }) - eventBus.sendToRenderer(UPDATE_EVENTS.PROGRESS, SendTarget.ALL_WINDOWS, this._progress) - }) - - // 下载完成 - autoUpdater.on('update-downloaded', (info) => { - console.log('更新下载完成', info) - this._lock = false - this._status = 'downloaded' - - // 写入更新标记文件 - this.writeUpdateMarker(this._versionInfo?.version || info.version) - - // 确保保存完整的更新信息 - console.log('使用已保存的版本信息:', this._versionInfo) - - eventBus.sendToRenderer(UPDATE_EVENTS.STATUS_CHANGED, SendTarget.ALL_WINDOWS, { - status: this._status, - info: this._versionInfo // 使用已保存的版本信息 - }) - }) - - // 监听应用获得焦点事件 - eventBus.on(WINDOW_EVENTS.APP_FOCUS, this.handleAppFocus.bind(this)) - - // 应用启动时检查是否有未完成的更新 - this.checkPendingUpdate() - } - - // 检查是否有未完成的自动更新 - private checkPendingUpdate(): void { - try { - if (fs.existsSync(this._updateMarkerPath)) { - const content = fs.readFileSync(this._updateMarkerPath, 'utf8') - const updateInfo = JSON.parse(content) - const currentVersion = app.getVersion() - console.log('检查未完成的更新', updateInfo, currentVersion) - - // 如果当前版本与目标版本相同,说明更新已完成 - if (updateInfo.version === currentVersion) { - // 删除标记文件 - fs.unlinkSync(this._updateMarkerPath) - return - } - - // 否则说明上次更新失败,标记为错误状态 - console.log('检测到未完成的更新', updateInfo.version) - this._status = 'error' - this._error = '上次自动更新未完成' - this._versionInfo = updateInfo - this._previousUpdateFailed = true // 标记上次更新失败 - - // 删除标记文件 - fs.unlinkSync(this._updateMarkerPath) - - // 通知渲染进程 - eventBus.sendToRenderer(UPDATE_EVENTS.STATUS_CHANGED, SendTarget.ALL_WINDOWS, { - status: this._status, - error: this._error, - info: { - version: updateInfo.version, - releaseDate: updateInfo.releaseDate, - releaseNotes: updateInfo.releaseNotes, - githubUrl: updateInfo.githubUrl, - downloadUrl: updateInfo.downloadUrl - } - }) - } - } catch (error) { - console.error('检查未完成更新失败', error) - // 出错时尝试删除标记文件 - try { - if (fs.existsSync(this._updateMarkerPath)) { - fs.unlinkSync(this._updateMarkerPath) - } - } catch (e) { - console.error('删除更新标记文件失败', e) - } - } - } - - // 写入更新标记文件 - private writeUpdateMarker(version: string): void { - try { - const updateInfo = { - version, - releaseDate: this._versionInfo?.releaseDate || '', - releaseNotes: this._versionInfo?.releaseNotes || '', - githubUrl: this._versionInfo?.githubUrl || '', - downloadUrl: this._versionInfo?.downloadUrl || '', - timestamp: Date.now() - } - - fs.writeFileSync(this._updateMarkerPath, JSON.stringify(updateInfo, null, 2), 'utf8') - console.log('写入更新标记文件成功', this._updateMarkerPath) - } catch (error) { - console.error('写入更新标记文件失败', error) - } - } - - // 处理应用获得焦点事件 - private handleAppFocus(): void { - const now = Date.now() - const twelveHoursInMs = 12 * 60 * 60 * 1000 // 12小时的毫秒数 - // 如果距离上次检查更新超过12小时,则重新检查 - if (now - this._lastCheckTime > twelveHoursInMs) { - this.checkUpdate('autoCheck') - } - } - - /** - * - * @param type 检查更新的类型,'autoCheck'表示自动检查 - * 如果不传则默认为手动检查 - * @returns - */ - async checkUpdate(type?: string): Promise { - if (this._lock) { - return - } - - try { - this._status = 'checking' - eventBus.sendToRenderer(UPDATE_EVENTS.STATUS_CHANGED, SendTarget.ALL_WINDOWS, { - status: this._status - }) - - // 首先获取版本信息文件 - const platformString = getPlatformInfo() - const randomId = Math.floor(Date.now() / 3600000) // Timestamp truncated to hour - const versionUrl = `${this._baseUrl}/${platformString}.json?noCache=${randomId}` - console.log('versionUrl', versionUrl) - const response = await axios.get(versionUrl) - const remoteVersion = response.data - const currentVersion = app.getVersion() - - // 保存完整的远程版本信息到内存中,作为唯一的标准信息源 - this._versionInfo = { - version: remoteVersion.version, - releaseDate: remoteVersion.releaseDate, - releaseNotes: remoteVersion.releaseNotes, - githubUrl: remoteVersion.githubUrl, - downloadUrl: remoteVersion.downloadUrl - } - - console.log('cache versionInfo:', this._versionInfo) - - // 更新上次检查时间 - this._lastCheckTime = Date.now() - - // 比较版本号 - if (compare(remoteVersion.version, currentVersion, '>')) { - // 有新版本 - - // 如果上次更新失败,这次不再尝试自动更新,直接进入错误状态让用户手动更新 - if (this._previousUpdateFailed) { - console.log('上次更新失败,本次不进行自动更新,改为手动更新') - this._status = 'error' - this._error = '自动更新可能不稳定,请手动下载更新' - - eventBus.sendToRenderer(UPDATE_EVENTS.STATUS_CHANGED, SendTarget.ALL_WINDOWS, { - status: this._status, - error: this._error, - info: this._versionInfo - }) - return - } - - // 设置自动更新的URL - const autoUpdateUrl = `${this._baseUrl}/v${remoteVersion.version}/${platformString}` - console.log('设置自动更新URL:', autoUpdateUrl) - autoUpdater.setFeedURL(autoUpdateUrl) - - try { - // 使用electron-updater检查更新,但不自动下载 - await autoUpdater.checkForUpdates() - } catch (err) { - console.error('自动更新检查失败,回退到手动更新', err) - // 如果自动更新失败,回退到手动更新 - this._status = 'available' - - eventBus.sendToRenderer(UPDATE_EVENTS.STATUS_CHANGED, SendTarget.ALL_WINDOWS, { - status: this._status, - info: this._versionInfo // 使用已保存的版本信息 - }) - } - } else { - // 没有新版本 - this._status = 'not-available' - eventBus.sendToRenderer(UPDATE_EVENTS.STATUS_CHANGED, SendTarget.ALL_WINDOWS, { - status: this._status, - type - }) - } - } catch (error: Error | unknown) { - this._status = 'error' - this._error = error instanceof Error ? error.message : String(error) - eventBus.sendToRenderer(UPDATE_EVENTS.STATUS_CHANGED, SendTarget.ALL_WINDOWS, { - status: this._status, - error: this._error - }) - } - } - - getUpdateStatus() { - return { - status: this._status, - progress: this._progress, - error: this._error, - updateInfo: this._versionInfo - ? { - version: this._versionInfo.version, - releaseDate: this._versionInfo.releaseDate, - releaseNotes: this._versionInfo.releaseNotes, - githubUrl: this._versionInfo.githubUrl, - downloadUrl: this._versionInfo.downloadUrl - } - : null - } - } - - async goDownloadUpgrade(type: 'github' | 'netdisk'): Promise { - if (type === 'github') { - const url = this._versionInfo?.githubUrl - if (url) { - shell.openExternal(url) - } - } else if (type === 'netdisk') { - const url = this._versionInfo?.downloadUrl - if (url) { - shell.openExternal(url) - } - } - } - - // 开始下载更新(如果手动触发) - startDownloadUpdate(): boolean { - if (this._status !== 'available') { - return false - } - try { - this._status = 'downloading' - eventBus.sendToRenderer(UPDATE_EVENTS.STATUS_CHANGED, SendTarget.ALL_WINDOWS, { - status: this._status, - info: this._versionInfo // 使用已保存的版本信息 - }) - autoUpdater.downloadUpdate() - return true - } catch (error: Error | unknown) { - this._status = 'error' - this._error = error instanceof Error ? error.message : String(error) - eventBus.sendToRenderer(UPDATE_EVENTS.STATUS_CHANGED, SendTarget.ALL_WINDOWS, { - status: this._status, - error: this._error - }) - return false - } - } - - // 执行退出并安装 - private _doQuitAndInstall(): void { - console.log('准备退出并安装更新') - try { - // 发送即将重启的消息 - eventBus.sendToRenderer(UPDATE_EVENTS.WILL_RESTART, SendTarget.ALL_WINDOWS) - // 通知需要完全退出应用 - eventBus.sendToMain(WINDOW_EVENTS.FORCE_QUIT_APP) - autoUpdater.quitAndInstall() - // 如果30秒还没完成,就强制退出重启 - setTimeout(() => { - app.quit() - }, 30000) - } catch (e) { - console.error('退出并安装失败', e) - eventBus.sendToRenderer(UPDATE_EVENTS.ERROR, SendTarget.ALL_WINDOWS, { - error: e instanceof Error ? e.message : String(e) - }) - } - } - - // 重启并更新 - restartToUpdate(): boolean { - console.log('重启并更新') - if (this._status !== 'downloaded') { - eventBus.sendToRenderer(UPDATE_EVENTS.ERROR, SendTarget.ALL_WINDOWS, { - error: '更新尚未下载完成' - }) - return false - } - try { - this._doQuitAndInstall() - return true - } catch (e) { - console.error('重启更新失败', e) - eventBus.sendToRenderer(UPDATE_EVENTS.ERROR, SendTarget.ALL_WINDOWS, { - error: e instanceof Error ? e.message : String(e) - }) - return false - } - } - - // 重启应用 - restartApp(): void { - try { - // 发送即将重启的消息 - eventBus.sendToRenderer(UPDATE_EVENTS.WILL_RESTART, SendTarget.ALL_WINDOWS) - // 给UI层一点时间保存状态 - setTimeout(() => { - app.relaunch() - app.exit() - }, 1000) - } catch (e) { - console.error('重启失败', e) - eventBus.sendToRenderer(UPDATE_EVENTS.ERROR, SendTarget.ALL_WINDOWS, { - error: e instanceof Error ? e.message : String(e) - }) - } - } -} +// const getUpdateMarkerFilePath = () => { +// return path.join(app.getPath('userData'), 'auto_update_marker.json') +// } + +// export class UpgradePresenter implements IUpgradePresenter { +// private _lock: boolean = false +// private _status: UpdateStatus = 'not-available' +// private _progress: UpdateProgress | null = null +// private _error: string | null = null +// private _versionInfo: VersionInfo | null = null +// private _baseUrl: string +// private _lastCheckTime: number = 0 // 上次检查更新的时间戳 +// private _updateMarkerPath: string +// private _previousUpdateFailed: boolean = false // 标记上次更新是否失败 + +// constructor() { +// this._baseUrl = getVersionCheckBaseUrl() +// this._updateMarkerPath = getUpdateMarkerFilePath() + +// // 配置自动更新 +// autoUpdater.autoDownload = false // 默认不自动下载,由我们手动控制 +// autoUpdater.allowDowngrade = false +// autoUpdater.autoInstallOnAppQuit = true + +// // 错误处理 +// autoUpdater.on('error', (e) => { +// console.log('自动更新失败', e.message) +// this._lock = false +// this._status = 'error' +// this._error = e.message +// eventBus.sendToRenderer(UPDATE_EVENTS.STATUS_CHANGED, SendTarget.ALL_WINDOWS, { +// status: this._status, +// error: this._error, +// info: this._versionInfo +// }) +// }) + +// // 检查更新状态 +// autoUpdater.on('checking-for-update', () => { +// console.log('正在检查更新') +// }) + +// // 无可用更新 +// autoUpdater.on('update-not-available', () => { +// console.log('无可用更新') +// this._lock = false +// this._status = 'not-available' +// eventBus.sendToRenderer(UPDATE_EVENTS.STATUS_CHANGED, SendTarget.ALL_WINDOWS, { +// status: this._status +// }) +// }) + +// // 有可用更新 +// autoUpdater.on('update-available', (info) => { +// console.log('检测到新版本', info) +// this._status = 'available' + +// // 重要:这里不再使用info中的信息更新this._versionInfo +// // 而是确保使用之前从versionUrl获取的原始信息 +// console.log('使用已保存的版本信息:', this._versionInfo) +// // 检测到更新后自动开始下载 +// this.startDownloadUpdate() +// }) + +// // 下载进度 +// autoUpdater.on('download-progress', (progressObj) => { +// this._lock = true +// this._status = 'downloading' +// this._progress = { +// bytesPerSecond: progressObj.bytesPerSecond, +// percent: progressObj.percent, +// transferred: progressObj.transferred, +// total: progressObj.total +// } +// eventBus.sendToRenderer(UPDATE_EVENTS.STATUS_CHANGED, SendTarget.ALL_WINDOWS, { +// status: this._status, +// info: this._versionInfo // 使用已保存的版本信息 +// }) +// eventBus.sendToRenderer(UPDATE_EVENTS.PROGRESS, SendTarget.ALL_WINDOWS, this._progress) +// }) + +// // 下载完成 +// autoUpdater.on('update-downloaded', (info) => { +// console.log('更新下载完成', info) +// this._lock = false +// this._status = 'downloaded' + +// // 写入更新标记文件 +// this.writeUpdateMarker(this._versionInfo?.version || info.version) + +// // 确保保存完整的更新信息 +// console.log('使用已保存的版本信息:', this._versionInfo) + +// eventBus.sendToRenderer(UPDATE_EVENTS.STATUS_CHANGED, SendTarget.ALL_WINDOWS, { +// status: this._status, +// info: this._versionInfo // 使用已保存的版本信息 +// }) +// }) + +// // 监听应用获得焦点事件 +// eventBus.on(WINDOW_EVENTS.APP_FOCUS, this.handleAppFocus.bind(this)) + +// // 应用启动时检查是否有未完成的更新 +// this.checkPendingUpdate() +// } + +// // 检查是否有未完成的自动更新 +// private checkPendingUpdate(): void { +// try { +// if (fs.existsSync(this._updateMarkerPath)) { +// const content = fs.readFileSync(this._updateMarkerPath, 'utf8') +// const updateInfo = JSON.parse(content) +// const currentVersion = app.getVersion() +// console.log('检查未完成的更新', updateInfo, currentVersion) + +// // 如果当前版本与目标版本相同,说明更新已完成 +// if (updateInfo.version === currentVersion) { +// // 删除标记文件 +// fs.unlinkSync(this._updateMarkerPath) +// return +// } + +// // 否则说明上次更新失败,标记为错误状态 +// console.log('检测到未完成的更新', updateInfo.version) +// this._status = 'error' +// this._error = '上次自动更新未完成' +// this._versionInfo = updateInfo +// this._previousUpdateFailed = true // 标记上次更新失败 + +// // 删除标记文件 +// fs.unlinkSync(this._updateMarkerPath) + +// // 通知渲染进程 +// eventBus.sendToRenderer(UPDATE_EVENTS.STATUS_CHANGED, SendTarget.ALL_WINDOWS, { +// status: this._status, +// error: this._error, +// info: { +// version: updateInfo.version, +// releaseDate: updateInfo.releaseDate, +// releaseNotes: updateInfo.releaseNotes, +// githubUrl: updateInfo.githubUrl, +// downloadUrl: updateInfo.downloadUrl +// } +// }) +// } +// } catch (error) { +// console.error('检查未完成更新失败', error) +// // 出错时尝试删除标记文件 +// try { +// if (fs.existsSync(this._updateMarkerPath)) { +// fs.unlinkSync(this._updateMarkerPath) +// } +// } catch (e) { +// console.error('删除更新标记文件失败', e) +// } +// } +// } + +// // 写入更新标记文件 +// private writeUpdateMarker(version: string): void { +// try { +// const updateInfo = { +// version, +// releaseDate: this._versionInfo?.releaseDate || '', +// releaseNotes: this._versionInfo?.releaseNotes || '', +// githubUrl: this._versionInfo?.githubUrl || '', +// downloadUrl: this._versionInfo?.downloadUrl || '', +// timestamp: Date.now() +// } + +// fs.writeFileSync(this._updateMarkerPath, JSON.stringify(updateInfo, null, 2), 'utf8') +// console.log('写入更新标记文件成功', this._updateMarkerPath) +// } catch (error) { +// console.error('写入更新标记文件失败', error) +// } +// } + +// // 处理应用获得焦点事件 +// private handleAppFocus(): void { +// const now = Date.now() +// const twelveHoursInMs = 12 * 60 * 60 * 1000 // 12小时的毫秒数 +// // 如果距离上次检查更新超过12小时,则重新检查 +// if (now - this._lastCheckTime > twelveHoursInMs) { +// this.checkUpdate('autoCheck') +// } +// } + +// /** +// * +// * @param type 检查更新的类型,'autoCheck'表示自动检查 +// * 如果不传则默认为手动检查 +// * @returns +// */ +// async checkUpdate(type?: string): Promise { +// if (this._lock) { +// return +// } + +// try { +// this._status = 'checking' +// eventBus.sendToRenderer(UPDATE_EVENTS.STATUS_CHANGED, SendTarget.ALL_WINDOWS, { +// status: this._status +// }) + +// // 首先获取版本信息文件 +// const platformString = getPlatformInfo() +// const randomId = Math.floor(Date.now() / 3600000) // Timestamp truncated to hour +// const versionUrl = `${this._baseUrl}/${platformString}.json?noCache=${randomId}` +// console.log('versionUrl', versionUrl) +// const response = await axios.get(versionUrl) +// const remoteVersion = response.data +// const currentVersion = app.getVersion() + +// // 保存完整的远程版本信息到内存中,作为唯一的标准信息源 +// this._versionInfo = { +// version: remoteVersion.version, +// releaseDate: remoteVersion.releaseDate, +// releaseNotes: remoteVersion.releaseNotes, +// githubUrl: remoteVersion.githubUrl, +// downloadUrl: remoteVersion.downloadUrl +// } + +// console.log('cache versionInfo:', this._versionInfo) + +// // 更新上次检查时间 +// this._lastCheckTime = Date.now() + +// // 比较版本号 +// if (compare(remoteVersion.version, currentVersion, '>')) { +// // 有新版本 + +// // 如果上次更新失败,这次不再尝试自动更新,直接进入错误状态让用户手动更新 +// if (this._previousUpdateFailed) { +// console.log('上次更新失败,本次不进行自动更新,改为手动更新') +// this._status = 'error' +// this._error = '自动更新可能不稳定,请手动下载更新' + +// eventBus.sendToRenderer(UPDATE_EVENTS.STATUS_CHANGED, SendTarget.ALL_WINDOWS, { +// status: this._status, +// error: this._error, +// info: this._versionInfo +// }) +// return +// } + +// // 设置自动更新的URL +// const autoUpdateUrl = `${this._baseUrl}/v${remoteVersion.version}/${platformString}` +// console.log('设置自动更新URL:', autoUpdateUrl) +// autoUpdater.setFeedURL(autoUpdateUrl) + +// try { +// // 使用electron-updater检查更新,但不自动下载 +// await autoUpdater.checkForUpdates() +// } catch (err) { +// console.error('自动更新检查失败,回退到手动更新', err) +// // 如果自动更新失败,回退到手动更新 +// this._status = 'available' + +// eventBus.sendToRenderer(UPDATE_EVENTS.STATUS_CHANGED, SendTarget.ALL_WINDOWS, { +// status: this._status, +// info: this._versionInfo // 使用已保存的版本信息 +// }) +// } +// } else { +// // 没有新版本 +// this._status = 'not-available' +// eventBus.sendToRenderer(UPDATE_EVENTS.STATUS_CHANGED, SendTarget.ALL_WINDOWS, { +// status: this._status, +// type +// }) +// } +// } catch (error: Error | unknown) { +// this._status = 'error' +// this._error = error instanceof Error ? error.message : String(error) +// eventBus.sendToRenderer(UPDATE_EVENTS.STATUS_CHANGED, SendTarget.ALL_WINDOWS, { +// status: this._status, +// error: this._error +// }) +// } +// } + +// getUpdateStatus() { +// return { +// status: this._status, +// progress: this._progress, +// error: this._error, +// updateInfo: this._versionInfo +// ? { +// version: this._versionInfo.version, +// releaseDate: this._versionInfo.releaseDate, +// releaseNotes: this._versionInfo.releaseNotes, +// githubUrl: this._versionInfo.githubUrl, +// downloadUrl: this._versionInfo.downloadUrl +// } +// : null +// } +// } + +// async goDownloadUpgrade(type: 'github' | 'netdisk'): Promise { +// if (type === 'github') { +// const url = this._versionInfo?.githubUrl +// if (url) { +// shell.openExternal(url) +// } +// } else if (type === 'netdisk') { +// const url = this._versionInfo?.downloadUrl +// if (url) { +// shell.openExternal(url) +// } +// } +// } + +// // 开始下载更新(如果手动触发) +// startDownloadUpdate(): boolean { +// if (this._status !== 'available') { +// return false +// } +// try { +// this._status = 'downloading' +// eventBus.sendToRenderer(UPDATE_EVENTS.STATUS_CHANGED, SendTarget.ALL_WINDOWS, { +// status: this._status, +// info: this._versionInfo // 使用已保存的版本信息 +// }) +// autoUpdater.downloadUpdate() +// return true +// } catch (error: Error | unknown) { +// this._status = 'error' +// this._error = error instanceof Error ? error.message : String(error) +// eventBus.sendToRenderer(UPDATE_EVENTS.STATUS_CHANGED, SendTarget.ALL_WINDOWS, { +// status: this._status, +// error: this._error +// }) +// return false +// } +// } + +// // 执行退出并安装 +// private _doQuitAndInstall(): void { +// console.log('准备退出并安装更新') +// try { +// // 发送即将重启的消息 +// eventBus.sendToRenderer(UPDATE_EVENTS.WILL_RESTART, SendTarget.ALL_WINDOWS) +// // 通知需要完全退出应用 +// eventBus.sendToMain(WINDOW_EVENTS.FORCE_QUIT_APP) +// autoUpdater.quitAndInstall() +// // 如果30秒还没完成,就强制退出重启 +// setTimeout(() => { +// app.quit() +// }, 30000) +// } catch (e) { +// console.error('退出并安装失败', e) +// eventBus.sendToRenderer(UPDATE_EVENTS.ERROR, SendTarget.ALL_WINDOWS, { +// error: e instanceof Error ? e.message : String(e) +// }) +// } +// } + +// // 重启并更新 +// restartToUpdate(): boolean { +// console.log('重启并更新') +// if (this._status !== 'downloaded') { +// eventBus.sendToRenderer(UPDATE_EVENTS.ERROR, SendTarget.ALL_WINDOWS, { +// error: '更新尚未下载完成' +// }) +// return false +// } +// try { +// this._doQuitAndInstall() +// return true +// } catch (e) { +// console.error('重启更新失败', e) +// eventBus.sendToRenderer(UPDATE_EVENTS.ERROR, SendTarget.ALL_WINDOWS, { +// error: e instanceof Error ? e.message : String(e) +// }) +// return false +// } +// } + +// // 重启应用 +// restartApp(): void { +// try { +// // 发送即将重启的消息 +// eventBus.sendToRenderer(UPDATE_EVENTS.WILL_RESTART, SendTarget.ALL_WINDOWS) +// // 给UI层一点时间保存状态 +// setTimeout(() => { +// app.relaunch() +// app.exit() +// }, 1000) +// } catch (e) { +// console.error('重启失败', e) +// eventBus.sendToRenderer(UPDATE_EVENTS.ERROR, SendTarget.ALL_WINDOWS, { +// error: e instanceof Error ? e.message : String(e) +// }) +// } +// } +// } diff --git a/src/renderer/src/App.vue b/src/renderer/src/App.vue index 9bc2b2d67..67029691d 100644 --- a/src/renderer/src/App.vue +++ b/src/renderer/src/App.vue @@ -1,7 +1,7 @@