diff --git a/dist-electron/main/main.js b/dist-electron/main/main.js index 1c7656d..2a3e22d 100644 --- a/dist-electron/main/main.js +++ b/dist-electron/main/main.js @@ -1,2551 +1,7 @@ "use strict"; -var __create = Object.create; -var __defProp = Object.defineProperty; -var __getOwnPropDesc = Object.getOwnPropertyDescriptor; -var __getOwnPropNames = Object.getOwnPropertyNames; -var __getProtoOf = Object.getPrototypeOf; -var __hasOwnProp = Object.prototype.hasOwnProperty; -var __copyProps = (to, from, except, desc) => { - if (from && typeof from === "object" || typeof from === "function") { - for (let key of __getOwnPropNames(from)) - if (!__hasOwnProp.call(to, key) && key !== except) - __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); - } - return to; -}; -var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( - // If the importer is in node compatibility mode or this is not an ESM - // file that has been converted to a CommonJS file using a Babel- - // compatible transform (i.e. "__esModule" has not been set), then set - // "default" to the CommonJS "module.exports" for node compatibility. - isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, - mod -)); -const electron = require("electron"); -const util = require("util"); -const log = require("electron-log"); -const path = require("path"); -const fs = require("fs"); -require("js-base64"); -const path$1 = require("node:path"); -const crypto = require("crypto"); -const started = require("electron-squirrel-startup"); -const net = require("net"); -const http = require("http"); -const child_process = require("child_process"); -const events = require("events"); +require("electron"); +require("./main-D4EDpIiu.js"); +require("electron-squirrel-startup"); +require("electron-log"); require("bytenode"); -const electronUpdater = require("electron-updater"); -const axios = require("axios"); -const OpenAI = require("openai"); -function _interopNamespaceDefault(e) { - const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } }); - if (e) { - for (const k in e) { - if (k !== "default") { - const d = Object.getOwnPropertyDescriptor(e, k); - Object.defineProperty(n, k, d.get ? d : { - enumerable: true, - get: () => e[k] - }); - } - } - } - n.default = e; - return Object.freeze(n); -} -const path__namespace = /* @__PURE__ */ _interopNamespaceDefault(path); -const fs__namespace = /* @__PURE__ */ _interopNamespaceDefault(fs); -var IPC_EVENTS = /* @__PURE__ */ ((IPC_EVENTS2) => { - IPC_EVENTS2["EXTERNAL_OPEN"] = "external-open"; - IPC_EVENTS2["APP_SET_FRAMELESS"] = "app:set-frameless"; - IPC_EVENTS2["APP_LOAD_PAGE"] = "app:load-page"; - IPC_EVENTS2["TAB_CREATE"] = "tab:create"; - IPC_EVENTS2["TAB_LIST"] = "tab:list"; - IPC_EVENTS2["TAB_NAVIGATE"] = "tab:navigate"; - IPC_EVENTS2["TAB_RELOAD"] = "tab:reload"; - IPC_EVENTS2["TAB_BACK"] = "tab:back"; - IPC_EVENTS2["TAB_FORWARD"] = "tab:forward"; - IPC_EVENTS2["TAB_SWITCH"] = "tab:switch"; - IPC_EVENTS2["TAB_CLOSE"] = "tab:close"; - IPC_EVENTS2["LOG_TO_MAIN"] = "log-to-main"; - IPC_EVENTS2["READ_FILE"] = "read-file"; - IPC_EVENTS2["INVOKE"] = "ipc:invoke"; - IPC_EVENTS2["INVOKE_ASYNC"] = "ipc:invokeAsync"; - IPC_EVENTS2["APP_MINIMIZE"] = "app:minimize"; - IPC_EVENTS2["APP_MAXIMIZE"] = "app:maximize"; - IPC_EVENTS2["APP_QUIT"] = "app:quit"; - IPC_EVENTS2["FILE_READ"] = "file:read"; - IPC_EVENTS2["FILE_WRITE"] = "file:write"; - IPC_EVENTS2["GET_WINDOW_ID"] = "get-window-id"; - IPC_EVENTS2["CUSTOM_EVENT"] = "custom:event"; - IPC_EVENTS2["TIME_UPDATE"] = "time:update"; - IPC_EVENTS2["RENDERER_IS_READY"] = "renderer-ready"; - IPC_EVENTS2["SHOW_CONTEXT_MENU"] = "show-context-menu"; - IPC_EVENTS2["START_A_DIALOGUE"] = "start-a-dialogue"; - IPC_EVENTS2["OPEN_WINDOW"] = "open-window"; - IPC_EVENTS2["LOG_DEBUG"] = "log-debug"; - IPC_EVENTS2["LOG_INFO"] = "log-info"; - IPC_EVENTS2["LOG_WARN"] = "log-warn"; - IPC_EVENTS2["LOG_ERROR"] = "log-error"; - IPC_EVENTS2["CONFIG_UPDATED"] = "config-updated"; - IPC_EVENTS2["SET_CONFIG"] = "set-config"; - IPC_EVENTS2["GET_CONFIG"] = "get-config"; - IPC_EVENTS2["UPDATE_CONFIG"] = "update-config"; - IPC_EVENTS2["SET_THEME_MODE"] = "set-theme-mode"; - IPC_EVENTS2["GET_THEME_MODE"] = "get-theme-mode"; - IPC_EVENTS2["IS_DARK_THEME"] = "is-dark-theme"; - IPC_EVENTS2["THEME_MODE_UPDATED"] = "theme-mode-updated"; - IPC_EVENTS2["EXECUTE_SCRIPT"] = "execute-script"; - IPC_EVENTS2["OPEN_CHANNEL"] = "open-channel"; - IPC_EVENTS2["SCRIPT_LIST"] = "script:list"; - IPC_EVENTS2["SCRIPT_GET"] = "script:get"; - IPC_EVENTS2["SCRIPT_SAVE"] = "script:save"; - IPC_EVENTS2["SCRIPT_DELETE"] = "script:delete"; - IPC_EVENTS2["SCRIPT_TOGGLE"] = "script:toggle"; - IPC_EVENTS2["SCRIPT_RUN"] = "script:run"; - IPC_EVENTS2["SCRIPT_RECORD_START"] = "script:record-start"; - IPC_EVENTS2["SCRIPT_RECORD_STOP"] = "script:record-stop"; - IPC_EVENTS2["SCRIPT_CODEGEN"] = "script:codegen"; - IPC_EVENTS2["GATEWAY_RPC"] = "gateway:rpc"; - IPC_EVENTS2["GATEWAY_EVENT"] = "gateway:event"; - IPC_EVENTS2["UPDATE_CHECK"] = "update:check"; - IPC_EVENTS2["UPDATE_DOWNLOAD"] = "update:download"; - IPC_EVENTS2["UPDATE_INSTALL"] = "update:install"; - IPC_EVENTS2["UPDATE_VERSION"] = "update:version"; - IPC_EVENTS2["UPDATE_STATUS_CHANGED"] = "update:status-changed"; - return IPC_EVENTS2; -})(IPC_EVENTS || {}); -const MAIN_WIN_SIZE = { - width: 1440, - height: 900, - minWidth: 1440, - minHeight: 900 -}; -var WINDOW_NAMES = /* @__PURE__ */ ((WINDOW_NAMES2) => { - WINDOW_NAMES2["MAIN"] = "main"; - WINDOW_NAMES2["SETTING"] = "setting"; - WINDOW_NAMES2["DIALOG"] = "dialog"; - WINDOW_NAMES2["LOADING"] = "loading"; - return WINDOW_NAMES2; -})(WINDOW_NAMES || {}); -var CONFIG_KEYS = /* @__PURE__ */ ((CONFIG_KEYS2) => { - CONFIG_KEYS2["THEME_MODE"] = "themeMode"; - CONFIG_KEYS2["PRIMARY_COLOR"] = "primaryColor"; - CONFIG_KEYS2["LANGUAGE"] = "language"; - CONFIG_KEYS2["FONT_SIZE"] = "fontSize"; - CONFIG_KEYS2["MINIMIZE_TO_TRAY"] = "minimizeToTray"; - CONFIG_KEYS2["PROVIDER"] = "provider"; - CONFIG_KEYS2["DEFAULT_MODEL"] = "defaultModel"; - CONFIG_KEYS2["AUTO_CHECK_UPDATE"] = "autoCheckUpdate"; - CONFIG_KEYS2["AUTO_DOWNLOAD_UPDATE"] = "autoDownloadUpdate"; - return CONFIG_KEYS2; -})(CONFIG_KEYS || {}); -var MENU_IDS = /* @__PURE__ */ ((MENU_IDS2) => { - MENU_IDS2["CONVERSATION_ITEM"] = "conversation-item"; - MENU_IDS2["CONVERSATION_LIST"] = "conversation-list"; - MENU_IDS2["MESSAGE_ITEM"] = "message-item"; - return MENU_IDS2; -})(MENU_IDS || {}); -var CONVERSATION_ITEM_MENU_IDS = /* @__PURE__ */ ((CONVERSATION_ITEM_MENU_IDS2) => { - CONVERSATION_ITEM_MENU_IDS2["PIN"] = "pin"; - CONVERSATION_ITEM_MENU_IDS2["RENAME"] = "rename"; - CONVERSATION_ITEM_MENU_IDS2["DEL"] = "del"; - return CONVERSATION_ITEM_MENU_IDS2; -})(CONVERSATION_ITEM_MENU_IDS || {}); -var CONVERSATION_LIST_MENU_IDS = /* @__PURE__ */ ((CONVERSATION_LIST_MENU_IDS2) => { - CONVERSATION_LIST_MENU_IDS2["NEW_CONVERSATION"] = "newConversation"; - CONVERSATION_LIST_MENU_IDS2["SORT_BY"] = "sortBy"; - CONVERSATION_LIST_MENU_IDS2["SORT_BY_CREATE_TIME"] = "sortByCreateTime"; - CONVERSATION_LIST_MENU_IDS2["SORT_BY_UPDATE_TIME"] = "sortByUpdateTime"; - CONVERSATION_LIST_MENU_IDS2["SORT_BY_NAME"] = "sortByName"; - CONVERSATION_LIST_MENU_IDS2["SORT_BY_MODEL"] = "sortByModel"; - CONVERSATION_LIST_MENU_IDS2["SORT_ASCENDING"] = "sortAscending"; - CONVERSATION_LIST_MENU_IDS2["SORT_DESCENDING"] = "sortDescending"; - CONVERSATION_LIST_MENU_IDS2["BATCH_OPERATIONS"] = "batchOperations"; - return CONVERSATION_LIST_MENU_IDS2; -})(CONVERSATION_LIST_MENU_IDS || {}); -var MESSAGE_ITEM_MENU_IDS = /* @__PURE__ */ ((MESSAGE_ITEM_MENU_IDS2) => { - MESSAGE_ITEM_MENU_IDS2["COPY"] = "copy"; - MESSAGE_ITEM_MENU_IDS2["DELETE"] = "delete"; - MESSAGE_ITEM_MENU_IDS2["SELECT"] = "select"; - return MESSAGE_ITEM_MENU_IDS2; -})(MESSAGE_ITEM_MENU_IDS || {}); -const readdirAsync = util.promisify(fs__namespace.readdir); -const statAsync = util.promisify(fs__namespace.stat); -const unlinkAsync = util.promisify(fs__namespace.unlink); -class LogService { - static _instance; - // 日志保留天数,默认7天 - LOG_RETENTION_DAYS = 7; - // 清理间隔,默认24小时(毫秒) - CLEANUP_INTERVAL_MS = 24 * 60 * 60 * 1e3; - constructor() { - const logPath = path__namespace.join(electron.app.getPath("userData"), "logs"); - try { - if (!fs__namespace.existsSync(logPath)) { - fs__namespace.mkdirSync(logPath, { recursive: true }); - } - } catch (err) { - this.error("Failed to create log directory:", err); - } - log.transports.file.resolvePathFn = () => { - const today = /* @__PURE__ */ new Date(); - const formattedDate = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, "0")}-${String(today.getDate()).padStart(2, "0")}`; - return path__namespace.join(logPath, `${formattedDate}.log`); - }; - log.transports.file.format = "[{y}-{m}-{d} {h}:{i}:{s}.{ms}] [{level}] {text}"; - log.transports.file.maxSize = 10 * 1024 * 1024; - log.transports.console.level = process.env.NODE_ENV === "development" ? "debug" : "info"; - log.transports.file.level = "debug"; - this._setupIpcEvents(); - this._rewriteConsole(); - this.info("LogService initialized successfully."); - this._cleanupOldLogs(); - setInterval(() => this._cleanupOldLogs(), this.CLEANUP_INTERVAL_MS); - } - _setupIpcEvents() { - electron.ipcMain.on(IPC_EVENTS.LOG_DEBUG, (_e, message, ...meta) => this.debug(message, ...meta)); - electron.ipcMain.on(IPC_EVENTS.LOG_INFO, (_e, message, ...meta) => this.info(message, ...meta)); - electron.ipcMain.on(IPC_EVENTS.LOG_WARN, (_e, message, ...meta) => this.warn(message, ...meta)); - electron.ipcMain.on(IPC_EVENTS.LOG_ERROR, (_e, message, ...meta) => this.error(message, ...meta)); - } - _rewriteConsole() { - console.debug = log.debug; - console.log = log.info; - console.info = log.info; - console.warn = log.warn; - console.error = log.error; - } - async _cleanupOldLogs() { - try { - const logPath = path__namespace.join(electron.app.getPath("userData"), "logs"); - if (!fs__namespace.existsSync(logPath)) return; - const now = /* @__PURE__ */ new Date(); - const expirationDate = new Date(now.getTime() - this.LOG_RETENTION_DAYS * 24 * 60 * 60 * 1e3); - const files = await readdirAsync(logPath); - let deletedCount = 0; - for (const file of files) { - if (!file.endsWith(".log")) continue; - const filePath = path__namespace.join(logPath, file); - try { - const stats = await statAsync(filePath); - if (stats.isFile() && stats.birthtime < expirationDate) { - await unlinkAsync(filePath); - deletedCount++; - } - } catch (error) { - this.error(`Failed to delete old log file ${filePath}:`, error); - } - } - if (deletedCount > 0) { - this.info(`Successfully cleaned up ${deletedCount} old log files.`); - } - } catch (err) { - this.error("Failed to cleanup old logs:", err); - } - } - static getInstance() { - if (!this._instance) { - this._instance = new LogService(); - } - return this._instance; - } - /** - * 记录调试信息 - * @param {string} message - 日志消息 - * @param {any[]} meta - 附加的元数据 - */ - debug(message, ...meta) { - log.debug(message, ...meta); - } - /** - * 记录一般信息 - * @param {string} message - 日志消息 - * @param {any[]} meta - 附加的元数据 - */ - info(message, ...meta) { - log.info(message, ...meta); - } - /** - * 记录警告信息 - * @param {string} message - 日志消息 - * @param {any[]} meta - 附加的元数据 - */ - warn(message, ...meta) { - log.warn(message, ...meta); - } - /** - * 记录错误信息 - * @param {string} message - 日志消息 - * @param {any[]} meta - 附加的元数据,通常是错误对象 - */ - error(message, ...meta) { - log.error(message, ...meta); - } - logApiRequest(endpoint, data = {}, method = "POST") { - this.info(`API Request: ${endpoint}, Method: ${method}, Request: ${JSON.stringify(data)}`); - } - logApiResponse(endpoint, response = {}, statusCode = 200, responseTime = 0) { - if (statusCode >= 400) { - this.error(`API Error Response: ${endpoint}, Status: ${statusCode}, Response Time: ${responseTime}ms, Response: ${JSON.stringify(response)}`); - } else { - this.debug(`API Response: ${endpoint}, Status: ${statusCode}, Response Time: ${responseTime}ms, Response: ${JSON.stringify(response)}`); - } - } - logUserOperation(operation, userId = "unknown", details = {}) { - this.info(`User Operation: ${operation} by ${userId}, Details: ${JSON.stringify(details)}`); - } -} -const logManager = LogService.getInstance(); -function debounce(fn, delay) { - let timer = null; - return function(...args) { - if (timer) { - clearTimeout(timer); - } - timer = setTimeout(() => { - fn.apply(this, args); - }, delay); - }; -} -function cloneDeep(obj) { - if (obj === null || typeof obj !== "object") { - return obj; - } - if (Array.isArray(obj)) { - return obj.map((item) => cloneDeep(item)); - } - const clone = Object.assign({}, obj); - for (const key in clone) { - if (Object.prototype.hasOwnProperty.call(clone, key)) { - clone[key] = cloneDeep(clone[key]); - } - } - return clone; -} -function simpleCloneDeep(obj) { - try { - return JSON.parse(JSON.stringify(obj)); - } catch (error) { - console.error("simpleCloneDeep failed:", error); - return obj; - } -} -const DEFAULT_CONFIG = { - [CONFIG_KEYS.THEME_MODE]: "system", - [CONFIG_KEYS.PRIMARY_COLOR]: "#BB5BE7", - [CONFIG_KEYS.LANGUAGE]: "zh", - [CONFIG_KEYS.FONT_SIZE]: 14, - [CONFIG_KEYS.MINIMIZE_TO_TRAY]: false, - [CONFIG_KEYS.PROVIDER]: "", - [CONFIG_KEYS.DEFAULT_MODEL]: null -}; -class ConfigService { - static _instance; - _config; - _configPath; - _defaultConfig = DEFAULT_CONFIG; - _listeners = []; - constructor() { - this._configPath = path__namespace.join(electron.app.getPath("userData"), "config.json"); - this._config = this._loadConfig(); - this._setupIpcEvents(); - logManager.info("ConfigService initialized successfully."); - } - _setupIpcEvents() { - const duration = 200; - const handelUpdate = debounce((val) => this.update(val), duration); - electron.ipcMain.handle(IPC_EVENTS.GET_CONFIG, (_, key) => this.get(key)); - electron.ipcMain.on(IPC_EVENTS.SET_CONFIG, (_, key, val) => this.set(key, val)); - electron.ipcMain.on(IPC_EVENTS.UPDATE_CONFIG, (_, updates) => handelUpdate(updates)); - } - static getInstance() { - if (!this._instance) { - this._instance = new ConfigService(); - } - return this._instance; - } - _loadConfig() { - try { - if (fs__namespace.existsSync(this._configPath)) { - const configContent = fs__namespace.readFileSync(this._configPath, "utf-8"); - const config = { ...this._defaultConfig, ...JSON.parse(configContent) }; - logManager.info("Config loaded successfully from:", this._configPath); - return config; - } - } catch (error) { - logManager.error("Failed to load config:", error); - } - return { ...this._defaultConfig }; - } - _saveConfig() { - try { - fs__namespace.mkdirSync(path__namespace.dirname(this._configPath), { recursive: true }); - fs__namespace.writeFileSync(this._configPath, JSON.stringify(this._config, null, 2), "utf-8"); - this._notifyListeners(); - logManager.info("Config saved successfully to:", this._configPath); - } catch (error) { - logManager.error("Failed to save config:", error); - } - } - _notifyListeners() { - electron.BrowserWindow.getAllWindows().forEach((win) => win.webContents.send(IPC_EVENTS.CONFIG_UPDATED, this._config)); - this._listeners.forEach((listener) => listener({ ...this._config })); - } - getConfig() { - return simpleCloneDeep(this._config); - } - get(key) { - return this._config[key]; - } - set(key, value, autoSave = true) { - if (!(key in this._config)) return; - const oldValue = this._config[key]; - if (oldValue === value) return; - this._config[key] = value; - logManager.debug(`Config set: ${key} = ${value}`); - autoSave && this._saveConfig(); - } - update(updates, autoSave = true) { - this._config = { ...this._config, ...updates }; - autoSave && this._saveConfig(); - } - resetToDefault() { - this._config = { ...this._defaultConfig }; - logManager.info("Config reset to default."); - this._saveConfig(); - } - onConfigChange(listener) { - this._listeners.push(listener); - return () => this._listeners = this._listeners.filter((l) => l !== listener); - } -} -const configManager = ConfigService.getInstance(); -const window$1 = { "minimize": "Minimize", "maximize": "Maximize", "restore": "Restore", "close": "Close" }; -const main$1 = { "welcome": { "helloMessage": "Hello, I'm Diona" }, "conversation": { "placeholder": "Type a message...", "newConversation": "New Conversation", "selectModel": "Please select model", "createConversation": "Create Conversation", "searchPlaceholder": "Search conversations...", "goSettings": "Go to", "settings": "Settings Window", "addModel": "to add a model", "dialog": { "title": "Confirm Deletion", "content": "Are you sure you want to delete this conversation?", "content_1": "Are you sure you want to delete the selected conversations? This action cannot be undone." }, "operations": { "pin": "Pin Selected", "del": "Delete Selected", "selectAll": "Select All", "cancel": "Cancel" } }, "sidebar": { "conversations": "Conversations", "settings": "Settings", "help": "Help" }, "message": { "dialog": { "title": "Confirm Deletion", "messageDelete": "Are you sure you want to delete this message?", "batchDelete": "Are you sure you want to delete the selected messages?", "copySuccess": "Copied successfully" }, "batchActions": { "deleteSelected": "Delete Selected" }, "rendering": "Thinking...", "stoppedGeneration": "(Stopped generating)", "sending": "Sending", "stopGeneration": "Stop generating", "send": "Send" } }; -const dialog$1 = { "cancel": "Cancel", "confirm": "Confirm" }; -const settings$1 = { "title": "Settings", "base": "Basic Settings", "provider": { "modelConfig": "Model Configuration" }, "theme": { "label": "Theme Settings", "dark": "Dark Theme", "light": "Light Theme", "system": "System Theme", "primaryColor": "Primary Color" }, "appearance": { "fontSize": "Font Size", "fontSizeOptions": { "10": "Tiny (10px)", "12": "Small (12px)", "14": "Normal (14px)", "16": "Medium (16px)", "18": "Large (18px)", "20": "Larger (20px)", "24": "Extra Large (24px)" } }, "behavior": { "minimizeToTray": "Minimize to tray when closed" }, "language": { "label": "Language" }, "providers": { "defaultModel": "Default Model", "apiKey": "API Key", "apiUrl": "API URL" } }; -const menu$1 = { "conversation": { "newConversation": "New Conversation", "sortBy": "Sort By", "sortByCreateTime": "Sort by Creation Time", "sortByUpdateTime": "Sort by Update Time", "sortByName": "Sort by Name", "sortByModel": "Sort by Model", "sortAscending": "Ascending", "sortDescending": "Descending", "pinConversation": "Pin Conversation", "unpinConversation": "Unpin Conversation", "renameConversation": "Rename Conversation", "delConversation": "Delete Conversation", "batchOperations": "Batch Operations" }, "message": { "copyMessage": "Copy Message", "deleteMessage": "Delete Message", "selectMessage": "Select Message" } }; -const tray$1 = { "tooltip": "Diona Application", "showWindow": "Show Window", "exit": "Exit" }; -const timeAgo$1 = { "justNow": "Just now", "minutes": "{count} minutes ago", "hours": "{count} hours ago", "days": "{count} days ago", "months": "{count} months ago", "years": "{count} years ago", "weekday": { "sun": "Sunday", "mon": "Monday", "tue": "Tuesday", "wed": "Wednesday", "thu": "Thursday", "fri": "Friday", "sat": "Saturday" } }; -const app$1 = { "title": "Diona Application" }; -const en = { - window: window$1, - main: main$1, - dialog: dialog$1, - settings: settings$1, - menu: menu$1, - tray: tray$1, - timeAgo: timeAgo$1, - app: app$1 -}; -const window = { "minimize": "最小化", "maximize": "最大化", "restore": "还原", "close": "关闭" }; -const main = { "welcome": { "helloMessage": "你好,我是迪奥娜" }, "conversation": { "placeholder": "输入消息...", "newConversation": "新对话", "selectModel": "请选择模型", "createConversation": "创建对话", "searchPlaceholder": "搜索对话...", "goSettings": "快去", "settings": "设置窗口", "addModel": "添加模型", "dialog": { "title": "确认删除", "content": "确定要删除这个对话吗?", "content_1": "确定要删除选中的对话吗?此操作不可撤销。" }, "operations": { "pin": "置顶所选", "del": "删除所选", "selectAll": "全选", "cancel": "取消" } }, "sidebar": { "conversations": "对话", "settings": "设置", "help": "帮助" }, "message": { "dialog": { "title": "确认删除", "messageDelete": "确认删除该条消息?", "batchDelete": "确认删除选中的消息?", "copySuccess": "复制成功" }, "batchActions": { "deleteSelected": "删除选中项" }, "rendering": "思考中...", "stoppedGeneration": "(已停止生成)", "sending": "发送中", "stopGeneration": "停止生成", "send": "发送" } }; -const dialog = { "cancel": "取消", "confirm": "确认" }; -const settings = { "title": "设置", "base": "基础设置", "provider": { "modelConfig": "模型配置" }, "providers": { "defaultModel": "默认模型", "apiKey": "API密钥", "apiUrl": "API地址" }, "theme": { "label": "主题设置", "dark": "深色主题", "light": "浅色主题", "system": "跟随系统", "primaryColor": "主题颜色" }, "appearance": { "fontSize": "字体大小", "fontSizeOptions": { "10": "极小 (10px)", "12": "小 (12px)", "14": "正常 (14px)", "16": "中 (16px)", "18": "大 (18px)", "20": "较大 (20px)", "24": "超大 (24px)" } }, "behavior": { "minimizeToTray": "关闭时最小化到托盘" }, "language": { "label": "语言设置" } }; -const menu = { "conversation": { "newConversation": "新建对话", "sortBy": "排序方式", "sortByCreateTime": "按创建时间排序", "sortByUpdateTime": "按更新时间排序", "sortByName": "按名称排序", "sortByModel": "按模型排序", "sortAscending": "递增", "sortDescending": "递减", "pinConversation": "置顶对话", "unpinConversation": "取消置顶", "renameConversation": "重命名对话", "delConversation": "删除对话", "batchOperations": "批量操作" }, "message": { "copyMessage": "复制消息", "deleteMessage": "删除消息", "selectMessage": "选择消息" } }; -const tray = { "tooltip": "迪奥娜", "showWindow": "显示窗口", "exit": "退出" }; -const timeAgo = { "justNow": "刚刚", "minutes": "{count}分钟前", "hours": "{count}小时前", "days": "{count}天前", "months": "{count}个月前", "years": "{count}年前", "weekday": { "sun": "星期日", "mon": "星期一", "tue": "星期二", "wed": "星期三", "thu": "星期四", "fri": "星期五", "sat": "星期六" } }; -const app = { "title": "迪奥娜" }; -const zh = { - window, - main, - dialog, - settings, - menu, - tray, - timeAgo, - app -}; -const messages = { en, zh }; -function createTranslator() { - return (key) => { - if (!key) return void 0; - try { - const keys = key?.split("."); - let result = messages[configManager.get(CONFIG_KEYS.LANGUAGE)]; - for (const _key of keys) { - result = result[_key]; - } - return result; - } catch (e) { - logManager.error("failed to translate key:", key, e); - return key; - } - }; -} -let logo = void 0; -function createLogo() { - if (logo != null) { - return logo; - } - const appPath = electron.app.getAppPath(); - const iconPath = path$1.join(appPath, "resources", "icons", "icon.ico"); - logo = iconPath; - return logo; -} -class ThemeService { - static _instance; - _isDark = electron.nativeTheme.shouldUseDarkColors; - constructor() { - const themeMode = configManager.get(CONFIG_KEYS.THEME_MODE); - if (themeMode) { - electron.nativeTheme.themeSource = themeMode; - this._isDark = electron.nativeTheme.shouldUseDarkColors; - } - this._setupIpcEvent(); - logManager.info("ThemeService initialized successfully."); - } - _setupIpcEvent() { - electron.ipcMain.handle(IPC_EVENTS.SET_THEME_MODE, (_e, mode) => { - electron.nativeTheme.themeSource = mode; - configManager.set(CONFIG_KEYS.THEME_MODE, mode); - return electron.nativeTheme.shouldUseDarkColors; - }); - electron.ipcMain.handle(IPC_EVENTS.GET_THEME_MODE, () => { - return electron.nativeTheme.themeSource; - }); - electron.ipcMain.handle(IPC_EVENTS.IS_DARK_THEME, () => { - return electron.nativeTheme.shouldUseDarkColors; - }); - electron.nativeTheme.on("updated", () => { - this._isDark = electron.nativeTheme.shouldUseDarkColors; - electron.BrowserWindow.getAllWindows().forEach( - (win) => win.webContents.send(IPC_EVENTS.THEME_MODE_UPDATED, this._isDark) - ); - }); - } - static getInstance() { - if (!this._instance) { - this._instance = new ThemeService(); - } - return this._instance; - } - get isDark() { - return this._isDark; - } - get themeMode() { - return electron.nativeTheme.themeSource; - } -} -const themeManager = ThemeService.getInstance(); -const isMac = process.platform === "darwin"; -const isWindows = process.platform === "win32"; -const useCustomTitleBar = isWindows; -const SHARED_WINDOW_OPTIONS = { - frame: isMac || !useCustomTitleBar, - titleBarStyle: isMac ? "hiddenInset" : useCustomTitleBar ? "hidden" : "default", - trafficLightPosition: isMac ? { x: 16, y: 16 } : void 0, - show: false, - title: "NIANXX", - darkTheme: themeManager.isDark, - backgroundColor: themeManager.isDark ? "#2C2C2C" : "#FFFFFF", - webPreferences: { - nodeIntegration: false, - // 禁用 Node.js 集成,提高安全性 - contextIsolation: true, - // 启用上下文隔离,防止渲染进程访问主进程 API - sandbox: true, - // 启用沙箱模式,进一步增强安全性 - backgroundThrottling: false, - preload: path$1.join(process.cwd(), "dist-electron/preload/preload.js") - } -}; -class WindowService { - static _instance; - _logo = createLogo(); - isDev = true; - _winStates = { - main: { instance: void 0, isHidden: false, onCreate: [], onClosed: [] }, - setting: { instance: void 0, isHidden: false, onCreate: [], onClosed: [] }, - dialog: { instance: void 0, isHidden: false, onCreate: [], onClosed: [] }, - login: { instance: void 0, isHidden: false, onCreate: [], onClosed: [] }, - loading: { instance: void 0, isHidden: false, onCreate: [], onClosed: [] } - }; - constructor() { - this._setupIpcEvents(); - logManager.info("WindowService initialized successfully."); - } - _isReallyClose(windowName) { - if (windowName === WINDOW_NAMES.MAIN) return configManager.get(CONFIG_KEYS.MINIMIZE_TO_TRAY) === false; - if (windowName === WINDOW_NAMES.SETTING) return false; - return true; - } - _setupIpcEvents() { - electron.ipcMain.handle(IPC_EVENTS.APP_LOAD_PAGE, (e, page) => { - const win = electron.BrowserWindow.fromWebContents(e.sender); - if (win) this._loadPage(win, page); - }); - } - static getInstance() { - if (!this._instance) { - this._instance = new WindowService(); - } - return this._instance; - } - create(name, size, moreOpts) { - if (this.get(name)) return; - const isHiddenWin = this._isHiddenWin(name); - let window2 = this._createWinInstance(name, { ...size, ...moreOpts }); - if (this.isDev) window2.webContents.openDevTools(); - !isHiddenWin && this._setupWinLifecycle(window2, name)._loadWindowTemplate(window2, name); - this._listenWinReady({ - win: window2, - isHiddenWin, - size - }); - if (!isHiddenWin) { - this._winStates[name].instance = window2; - this._winStates[name].onCreate.forEach((callback) => callback(window2)); - } - if (isHiddenWin) { - this._winStates[name].isHidden = false; - logManager.info(`Hidden window show: ${name}`); - } - return window2; - } - _setupWinLifecycle(window2, name) { - window2.once("closed", () => { - this._winStates[name].onClosed.forEach((callback) => callback(window2)); - window2?.destroy(); - this._winStates[name].instance = void 0; - this._winStates[name].isHidden = false; - logManager.info(`Window closed: ${name}`); - }); - return this; - } - _listenWinReady(params) { - const onReady = () => { - params.win?.once("show", () => setTimeout(() => this._applySizeConstraints(params.win, params.size), 2)); - params.win?.show(); - }; - if (!params.isHiddenWin) { - const loadingHandler = this._addLoadingView(params.win, params.size); - loadingHandler?.(onReady); - } else { - onReady(); - } - } - _addLoadingView(window2, size) { - let rendererIsReady = false; - const onRendererIsReady = (e) => { - if (e.sender !== window2?.webContents || rendererIsReady) return; - rendererIsReady = true; - electron.ipcMain.removeListener(IPC_EVENTS.RENDERER_IS_READY, onRendererIsReady); - }; - electron.ipcMain.on(IPC_EVENTS.RENDERER_IS_READY, onRendererIsReady); - return (cb) => { - cb(); - }; - } - _applySizeConstraints(win, size) { - if (size.maxHeight && size.maxWidth) { - win.setMaximumSize(size.maxWidth, size.maxHeight); - } - if (size.minHeight && size.minWidth) { - win.setMinimumSize(size.minWidth, size.minHeight); - } - } - _loadPage(window2, pageName) { - { - return window2.loadURL(`${"http://localhost:5173"}/${pageName}.html`); - } - } - _loadWindowTemplate(window2, name) { - const page = "index"; - this._loadPage(window2, page); - } - _handleCloseWindowState(target, really) { - const name = this.getName(target); - if (name) { - if (!really) this._winStates[name].isHidden = true; - else this._winStates[name].instance = void 0; - } - setTimeout(() => { - target[really ? "close" : "hide"]?.(); - this._checkAndCloseAllWinodws(); - }, 210); - } - _checkAndCloseAllWinodws() { - if (!this._winStates[WINDOW_NAMES.MAIN].instance || this._winStates[WINDOW_NAMES.MAIN].instance?.isDestroyed()) - return Object.values(this._winStates).forEach((win) => win?.instance?.close()); - const minimizeToTray = configManager.get(CONFIG_KEYS.MINIMIZE_TO_TRAY); - if (!minimizeToTray && !this.get(WINDOW_NAMES.MAIN)?.isVisible()) - return Object.values(this._winStates).forEach((win) => !win?.instance?.isVisible() && win?.instance?.close()); - } - _isHiddenWin(name) { - return this._winStates[name] && this._winStates[name].isHidden; - } - _createWinInstance(name, opts) { - return this._isHiddenWin(name) ? this._winStates[name].instance : new electron.BrowserWindow({ - ...SHARED_WINDOW_OPTIONS, - icon: this._logo, - ...opts - }); - } - focus(target) { - if (!target) return; - const name = this.getName(target); - if (target?.isMaximized()) { - target?.restore(); - logManager.debug(`Window ${name} restored and focused`); - } else { - logManager.debug(`Window ${name} focused`); - } - target?.focus(); - } - close(target, really = true) { - if (!target) return; - const name = this.getName(target); - logManager.info(`Close window: ${name}, really: ${really}`); - this._handleCloseWindowState(target, really); - } - toggleMax(target) { - if (!target) return; - target.isMaximized() ? target.unmaximize() : target.maximize(); - } - getName(target) { - if (!target) return; - for (const [name, win] of Object.entries(this._winStates)) { - if (win?.instance === target) return name; - } - } - get(name) { - if (this._winStates[name].isHidden) return void 0; - return this._winStates[name].instance; - } - onWindowCreate(name, callback) { - this._winStates[name].onCreate.push(callback); - } - onWindowClosed(name, callback) { - this._winStates[name].onClosed.push(callback); - } -} -const windowManager = WindowService.getInstance(); -let t$1 = createTranslator(); -class MenuService { - static _instance; - _menuTemplates = /* @__PURE__ */ new Map(); - _currentMenu = void 0; - constructor() { - this._setupIpcListener(); - this._setupLanguageChangeListener(); - logManager.info("MenuService initialized successfully."); - } - _setupIpcListener() { - electron.ipcMain.handle(IPC_EVENTS.SHOW_CONTEXT_MENU, (_, menuId, dynamicOptions) => new Promise((resolve) => this.showMenu(menuId, () => resolve(true), dynamicOptions))); - } - _setupLanguageChangeListener() { - configManager.onConfigChange((config) => { - if (!config[CONFIG_KEYS.LANGUAGE]) return; - t$1 = createTranslator(); - }); - } - static getInstance() { - if (!this._instance) - this._instance = new MenuService(); - return this._instance; - } - register(menuId, template) { - this._menuTemplates.set(menuId, template); - return menuId; - } - showMenu(menuId, onClose, dynamicOptions) { - if (this._currentMenu) return; - const template = cloneDeep(this._menuTemplates.get(menuId)); - if (!template) { - logManager.warn(`Menu ${menuId} not found.`); - onClose?.(); - return; - } - let _dynamicOptions = []; - try { - _dynamicOptions = Array.isArray(dynamicOptions) ? dynamicOptions : JSON.parse(dynamicOptions ?? "[]"); - } catch (error) { - logManager.error(`Failed to parse dynamicOptions for menu ${menuId}: ${error}`); - } - const translationItem = (item) => { - if (item.submenu) { - return { - ...item, - label: t$1(item?.label) ?? void 0, - submenu: item.submenu?.map((item2) => translationItem(item2)) - }; - } - return { - ...item, - label: t$1(item?.label) ?? void 0 - }; - }; - const localizedTemplate = template.map((item) => { - if (!Array.isArray(_dynamicOptions) || !_dynamicOptions.length) { - return translationItem(item); - } - const dynamicItem = _dynamicOptions.find((_item) => _item.id === item.id); - if (dynamicItem) { - const mergedItem = { ...item, ...dynamicItem }; - return translationItem(mergedItem); - } - if (item.submenu) { - return translationItem({ - ...item, - submenu: item.submenu?.map((__item) => { - const dynamicItem2 = _dynamicOptions.find((_item) => _item.id === __item.id); - return { ...__item, ...dynamicItem2 }; - }) - }); - } - return translationItem(item); - }); - const menu2 = electron.Menu.buildFromTemplate(localizedTemplate); - this._currentMenu = menu2; - menu2.popup({ - callback: () => { - this._currentMenu = void 0; - onClose?.(); - } - }); - } - destroyMenu(menuId) { - this._menuTemplates.delete(menuId); - } - destroyed() { - this._menuTemplates.clear(); - this._currentMenu = void 0; - } -} -const menuManager = MenuService.getInstance(); -let t = createTranslator(); -class TrayService { - static _instance; - _tray = null; - _removeLanguageListener; - _setupLanguageChangeListener() { - this._removeLanguageListener = configManager.onConfigChange((config) => { - if (!config[CONFIG_KEYS.LANGUAGE]) return; - t = createTranslator(); - if (this._tray) { - this._updateTray(); - } - }); - } - _updateTray() { - if (!this._tray) { - this._tray = new electron.Tray(createLogo()); - } - const showWindow = () => { - const mainWindow = windowManager.get(WINDOW_NAMES.MAIN); - if (mainWindow && !mainWindow?.isDestroyed() && mainWindow?.isVisible() && !mainWindow?.isFocused()) { - return mainWindow.focus(); - } - if (mainWindow?.isMinimized()) { - return mainWindow?.restore(); - } - if (mainWindow?.isVisible() && mainWindow?.isFocused()) return; - windowManager.create(WINDOW_NAMES.MAIN, MAIN_WIN_SIZE); - }; - this._tray.setToolTip(t("tray.tooltip") ?? "Diona Application"); - this._tray.setContextMenu(electron.Menu.buildFromTemplate([ - { label: t("tray.showWindow"), accelerator: "CmdOrCtrl+N", click: showWindow }, - { type: "separator" }, - { label: t("settings.title"), click: () => electron.ipcMain.emit(`${IPC_EVENTS.OPEN_WINDOW}:${WINDOW_NAMES.SETTING}`) }, - { role: "quit", label: t("tray.exit") } - ])); - this._tray.removeAllListeners("click"); - this._tray.on("click", showWindow); - } - constructor() { - this._setupLanguageChangeListener(); - logManager.info("TrayService initialized successfully."); - } - static getInstance() { - if (!this._instance) { - this._instance = new TrayService(); - } - return this._instance; - } - create() { - if (this._tray) return; - this._updateTray(); - electron.app.on("quit", () => { - this.destroy(); - }); - } - destroy() { - this._tray?.destroy(); - this._tray = null; - if (this._removeLanguageListener) { - this._removeLanguageListener(); - this._removeLanguageListener = void 0; - } - } -} -const trayManager = TrayService.getInstance(); -class TabManager { - win; - views = /* @__PURE__ */ new Map(); - activeId = null; - skipNextNavigate = /* @__PURE__ */ new Map(); - enabled = false; - constructor(win) { - this.win = win; - this.win.on("resize", () => this.updateActiveBounds()); - this._setupIpcEvents(); - } - _setupIpcEvents() { - electron.ipcMain.handle(IPC_EVENTS.TAB_CREATE, (_e, url) => { - const info = this.create(url); - return info; - }); - electron.ipcMain.handle(IPC_EVENTS.TAB_LIST, () => { - return this.list(); - }); - electron.ipcMain.handle(IPC_EVENTS.TAB_NAVIGATE, (_e, { tabId, url }) => { - this.navigate(tabId, url); - }); - electron.ipcMain.handle(IPC_EVENTS.TAB_RELOAD, (_e, tabId) => { - this.reload(tabId); - }); - electron.ipcMain.handle(IPC_EVENTS.TAB_BACK, (_e, tabId) => { - this.goBack(tabId); - }); - electron.ipcMain.handle(IPC_EVENTS.TAB_FORWARD, (_e, tabId) => { - this.goForward(tabId); - }); - electron.ipcMain.handle(IPC_EVENTS.TAB_SWITCH, (_e, tabId) => { - this.switch(tabId); - }); - electron.ipcMain.handle(IPC_EVENTS.TAB_CLOSE, (_e, tabId) => { - this.close(tabId); - }); - } - enable() { - this.enabled = true; - this.updateActiveBounds(); - if (this.activeId) this.attach(this.activeId); - } - disable() { - this.enabled = false; - const view = this.activeId ? this.views.get(this.activeId) : null; - if (view) this.win.removeBrowserView(view); - } - destroy() { - this.disable(); - this.views.forEach((view) => { - view.webContents.destroy(); - }); - this.views.clear(); - electron.ipcMain.removeHandler(IPC_EVENTS.TAB_CREATE); - electron.ipcMain.removeHandler(IPC_EVENTS.TAB_LIST); - electron.ipcMain.removeHandler(IPC_EVENTS.TAB_NAVIGATE); - electron.ipcMain.removeHandler(IPC_EVENTS.TAB_RELOAD); - electron.ipcMain.removeHandler(IPC_EVENTS.TAB_BACK); - electron.ipcMain.removeHandler(IPC_EVENTS.TAB_FORWARD); - electron.ipcMain.removeHandler(IPC_EVENTS.TAB_SWITCH); - electron.ipcMain.removeHandler(IPC_EVENTS.TAB_CLOSE); - } - list() { - return Array.from(this.views.entries()).map(([id, view]) => this.info(id, view)); - } - create(url, active = true) { - const id = crypto.randomUUID(); - const view = new electron.BrowserView({ - webPreferences: { - nodeIntegration: false, - contextIsolation: true, - sandbox: true, - preload: path$1.join(process.cwd(), "dist-electron/preload/preload.js") - } - }); - this.views.set(id, view); - if (this.enabled && active) this.attach(id); - const target = url && url.length > 0 ? url : "about:blank"; - view.webContents.loadURL(target); - this.bindEvents(id, view); - const info = this.info(id, view); - this.win.webContents.send("tab-created", info); - return info; - } - switch(tabId) { - if (!this.views.has(tabId)) return; - if (this.enabled) this.attach(tabId); - this.win.webContents.send("tab-switched", { tabId }); - } - close(tabId) { - const view = this.views.get(tabId); - if (!view) return; - if (this.activeId === tabId) { - this.win.removeBrowserView(view); - this.activeId = null; - } - view.webContents.destroy(); - this.views.delete(tabId); - this.win.webContents.send("tab-closed", { tabId }); - const next = this.views.keys().next().value; - if (next) this.switch(next); - } - navigate(tabId, url) { - const view = this.views.get(tabId); - if (!view) return; - this.skipNextNavigate.set(tabId, true); - view.webContents.loadURL(url); - } - reload(tabId) { - const view = this.views.get(tabId); - if (!view) return; - view.webContents.reload(); - } - goBack(tabId) { - const view = this.views.get(tabId); - if (!view) return; - if (view.webContents.canGoBack()) view.webContents.goBack(); - } - goForward(tabId) { - const view = this.views.get(tabId); - if (!view) return; - if (view.webContents.canGoForward()) view.webContents.goForward(); - } - attach(tabId) { - if (!this.enabled) return; - const view = this.views.get(tabId); - if (!view) return; - if (this.activeId && this.views.get(this.activeId)) { - const prev = this.views.get(this.activeId); - this.win.removeBrowserView(prev); - } - this.activeId = tabId; - this.win.addBrowserView(view); - this.updateActiveBounds(); - } - updateActiveBounds() { - if (!this.enabled || !this.activeId) return; - const view = this.views.get(this.activeId); - if (!view) return; - const [winWidth, winHeight] = this.win.getContentSize(); - const HEADER_HEIGHT = 88; - const PADDING = 8; - const RIGHT_PANEL_WIDTH = 392 + 80 + 8 + 8; - const x = PADDING; - const y = HEADER_HEIGHT + PADDING; - const width = winWidth - RIGHT_PANEL_WIDTH - PADDING; - const height = winHeight - HEADER_HEIGHT - PADDING * 2; - view.setBounds({ - x, - y, - width: Math.max(0, width), - height: Math.max(0, height) - }); - } - bindEvents(id, view) { - const send = () => this.win.webContents.send("tab-updated", this.info(id, view)); - view.webContents.on("did-start-loading", send); - view.webContents.on("did-stop-loading", send); - view.webContents.on("did-finish-load", send); - view.webContents.on("page-title-updated", send); - view.webContents.on("did-navigate", send); - view.webContents.on("did-navigate-in-page", send); - view.webContents.on("will-navigate", (event, url) => { - if (this.skipNextNavigate.get(id)) { - this.skipNextNavigate.set(id, false); - return; - } - event.preventDefault(); - this.create(url); - }); - view.webContents.setWindowOpenHandler(({ url }) => { - this.create(url); - return { action: "deny" }; - }); - } - info(id, view) { - const wc = view.webContents; - return { - id, - url: wc.getURL(), - title: wc.getTitle(), - isLoading: wc.isLoading(), - canGoBack: wc.canGoBack(), - canGoForward: wc.canGoForward() - }; - } -} -function registerWindowHandlers(mainWindow) { - electron.ipcMain.handle("window:minimize", () => { - mainWindow.minimize(); - }); - electron.ipcMain.handle("window:maximize", () => { - if (mainWindow.isMaximized()) { - mainWindow.unmaximize(); - } else { - mainWindow.maximize(); - } - }); - electron.ipcMain.handle("window:close", () => { - mainWindow.close(); - }); - electron.ipcMain.handle("window:isMaximized", () => { - return mainWindow.isMaximized(); - }); -} -const handleTray = (minimizeToTray) => { - if (minimizeToTray) { - trayManager.create(); - return; - } - trayManager.destroy(); -}; -const registerMenus = (window2) => { - const conversationItemMenuItemClick = (id) => { - logManager.logUserOperation(`${IPC_EVENTS.SHOW_CONTEXT_MENU}:${MENU_IDS.CONVERSATION_ITEM}-${id}`); - window2.webContents.send(`${IPC_EVENTS.SHOW_CONTEXT_MENU}:${MENU_IDS.CONVERSATION_ITEM}`, id); - }; - menuManager.register(MENU_IDS.CONVERSATION_ITEM, [ - { - id: CONVERSATION_ITEM_MENU_IDS.PIN, - label: "menu.conversation.pinConversation", - click: () => conversationItemMenuItemClick(CONVERSATION_ITEM_MENU_IDS.PIN) - }, - { - id: CONVERSATION_ITEM_MENU_IDS.RENAME, - label: "menu.conversation.renameConversation", - click: () => conversationItemMenuItemClick(CONVERSATION_ITEM_MENU_IDS.RENAME) - }, - { - id: CONVERSATION_ITEM_MENU_IDS.DEL, - label: "menu.conversation.delConversation", - click: () => conversationItemMenuItemClick(CONVERSATION_ITEM_MENU_IDS.DEL) - } - ]); - const conversationListMenuItemClick = (id) => { - logManager.logUserOperation(`${IPC_EVENTS.SHOW_CONTEXT_MENU}:${MENU_IDS.CONVERSATION_LIST}-${id}`); - window2.webContents.send(`${IPC_EVENTS.SHOW_CONTEXT_MENU}:${MENU_IDS.CONVERSATION_LIST}`, id); - }; - menuManager.register(MENU_IDS.CONVERSATION_LIST, [ - { - id: CONVERSATION_LIST_MENU_IDS.NEW_CONVERSATION, - label: "menu.conversation.newConversation", - click: () => conversationListMenuItemClick(CONVERSATION_LIST_MENU_IDS.NEW_CONVERSATION) - }, - { type: "separator" }, - { - id: CONVERSATION_LIST_MENU_IDS.SORT_BY, - label: "menu.conversation.sortBy", - submenu: [ - { id: CONVERSATION_LIST_MENU_IDS.SORT_BY_CREATE_TIME, label: "menu.conversation.sortByCreateTime", type: "radio", checked: false, click: () => conversationListMenuItemClick(CONVERSATION_LIST_MENU_IDS.SORT_BY_CREATE_TIME) }, - { id: CONVERSATION_LIST_MENU_IDS.SORT_BY_UPDATE_TIME, label: "menu.conversation.sortByUpdateTime", type: "radio", checked: false, click: () => conversationListMenuItemClick(CONVERSATION_LIST_MENU_IDS.SORT_BY_UPDATE_TIME) }, - { id: CONVERSATION_LIST_MENU_IDS.SORT_BY_NAME, label: "menu.conversation.sortByName", type: "radio", checked: false, click: () => conversationListMenuItemClick(CONVERSATION_LIST_MENU_IDS.SORT_BY_NAME) }, - { id: CONVERSATION_LIST_MENU_IDS.SORT_BY_MODEL, label: "menu.conversation.sortByModel", type: "radio", checked: false, click: () => conversationListMenuItemClick(CONVERSATION_LIST_MENU_IDS.SORT_BY_MODEL) }, - { type: "separator" }, - { id: CONVERSATION_LIST_MENU_IDS.SORT_ASCENDING, label: "menu.conversation.sortAscending", type: "radio", checked: false, click: () => conversationListMenuItemClick(CONVERSATION_LIST_MENU_IDS.SORT_ASCENDING) }, - { id: CONVERSATION_LIST_MENU_IDS.SORT_DESCENDING, label: "menu.conversation.sortDescending", type: "radio", checked: false, click: () => conversationListMenuItemClick(CONVERSATION_LIST_MENU_IDS.SORT_DESCENDING) } - ] - }, - { - id: CONVERSATION_LIST_MENU_IDS.BATCH_OPERATIONS, - label: "menu.conversation.batchOperations", - click: () => conversationListMenuItemClick(CONVERSATION_LIST_MENU_IDS.BATCH_OPERATIONS) - } - ]); - const messageItemMenuItemClick = (id) => { - logManager.logUserOperation(`${IPC_EVENTS.SHOW_CONTEXT_MENU}:${MENU_IDS.MESSAGE_ITEM}-${id}`); - window2.webContents.send(`${IPC_EVENTS.SHOW_CONTEXT_MENU}:${MENU_IDS.MESSAGE_ITEM}`, id); - }; - menuManager.register(MENU_IDS.MESSAGE_ITEM, [ - { - id: MESSAGE_ITEM_MENU_IDS.COPY, - label: "menu.message.copyMessage", - click: () => messageItemMenuItemClick(MESSAGE_ITEM_MENU_IDS.COPY) - }, - { - id: MESSAGE_ITEM_MENU_IDS.SELECT, - label: "menu.message.selectMessage", - click: () => messageItemMenuItemClick(MESSAGE_ITEM_MENU_IDS.SELECT) - }, - { type: "separator" }, - { - id: MESSAGE_ITEM_MENU_IDS.DELETE, - label: "menu.message.deleteMessage", - click: () => messageItemMenuItemClick(MESSAGE_ITEM_MENU_IDS.DELETE) - } - ]); -}; -function setupMainWindow() { - windowManager.onWindowCreate(WINDOW_NAMES.MAIN, (mainWindow) => { - let minimizeToTray = configManager.get(CONFIG_KEYS.MINIMIZE_TO_TRAY); - configManager.onConfigChange((config) => { - if (minimizeToTray === config[CONFIG_KEYS.MINIMIZE_TO_TRAY]) return; - minimizeToTray = config[CONFIG_KEYS.MINIMIZE_TO_TRAY]; - handleTray(minimizeToTray); - }); - handleTray(minimizeToTray); - registerMenus(mainWindow); - registerWindowHandlers(mainWindow); - const tabManager = new TabManager(mainWindow); - tabManager.enable(); - mainWindow.on("closed", () => { - tabManager.destroy(); - }); - }); - windowManager.create(WINDOW_NAMES.MAIN, MAIN_WIN_SIZE); -} -function getChromePath() { - if (process.platform === "win32") { - return "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"; - } - if (process.platform === "darwin") { - return "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"; - } - if (process.platform === "linux") { - return "google-chrome"; - } -} -function getProfileDir(accountId) { - return path$1.join(electron.app.getPath("userData"), `profiles`, accountId); -} -function isPortInUse(port) { - return new Promise((resolve) => { - const server = net.createServer(); - server.once("error", (err) => resolve(true)); - server.once("listening", () => { - server.close(); - resolve(false); - }); - server.listen(port); - }); -} -async function isChromeRunning() { - try { - return new Promise((resolve) => { - const req = http.get("http://127.0.0.1:9222/json/version", (res) => { - resolve(res.statusCode === 200); - }); - req.on("error", () => resolve(false)); - req.setTimeout(1e3, () => { - req.destroy(); - resolve(false); - }); - }); - } catch (error) { - return false; - } -} -async function launchLocalChrome() { - const chromePath = getChromePath(); - const profileDir = getProfileDir("default"); - log.info(`Launching Chrome with user data dir: ${profileDir}`); - const portInUse = await isPortInUse(9222); - if (portInUse) { - log.info("Chrome already running on port 9222, skip launching."); - return; - } - if (await isChromeRunning()) { - log.info("Chrome already running, skip launching."); - return; - } - return new Promise((resolve, reject) => { - const chromeProcess = child_process.spawn(chromePath, [ - "--remote-debugging-port=9222", - "--window-size=1920,1080", - "--window-position=0,0", - "--no-first-run", - `--user-data-dir=${profileDir}`, - "--no-default-browser-check", - "about:blank" - // '--window-maximized', - ], { - detached: true, - stdio: "ignore" - }); - chromeProcess.on("error", reject); - setTimeout(() => { - resolve(0); - }, 1e3); - }); -} -class executeScriptService extends events.EventEmitter { - // 执行脚本 - async executeScript(scriptPath, options) { - const MAX_TAIL = 32 * 1024; - const appendTail = (current, chunk) => { - const next = current + chunk; - return next.length > MAX_TAIL ? next.slice(next.length - MAX_TAIL) : next; - }; - return await new Promise((resolve) => { - try { - const roomType = options?.roomType ?? ""; - const startTime = options?.startTime ?? ""; - const endTime = options?.endTime ?? ""; - const operation = options?.operation ?? ""; - const tabIndex = options?.tabIndex ?? ""; - const channels = options?.channels ?? ""; - const startTabIndex = options?.startTabIndex ?? ""; - const child = electron.utilityProcess.fork(scriptPath, [], { - env: { - ...process.env, - ROOM_TYPE: String(roomType), - START_DATE: String(startTime), - END_DATE: String(endTime), - OPERATION: String(operation), - TAB_INDEX: String(tabIndex), - CHANNELS: typeof channels === "string" ? channels : JSON.stringify(channels), - START_TAB_INDEX: String(startTabIndex) - }, - stdio: "pipe" - }); - let stdoutTail = ""; - let stderrTail = ""; - if (child.stdout) { - child.stdout.on("data", (data) => { - const text = data.toString(); - stdoutTail = appendTail(stdoutTail, text); - log.info(`stdout: ${text}`); - }); - } - if (child.stderr) { - child.stderr.on("data", (data) => { - const text = data.toString(); - stderrTail = appendTail(stderrTail, text); - log.info(`stderr: ${text}`); - }); - } - child.on("exit", (code) => { - log.info(`子进程退出,退出码 ${code}`); - resolve({ - success: code === 0, - exitCode: code, - stdoutTail, - stderrTail, - ...code === 0 ? {} : { error: `Script exited with code ${code}` } - }); - }); - } catch (error) { - resolve({ - success: false, - exitCode: null, - stdoutTail: "", - stderrTail: "", - error: error?.message || "运行 Node 脚本时出错" - }); - } - }); - } -} -const META_FILENAME = "scripts.meta.json"; -function getScriptsDir$1() { - return electron.app.isPackaged ? path.join(__dirname, "scripts") : path.join(process.cwd(), "electron/scripts"); -} -function ensureScriptsDir() { - const dir = getScriptsDir$1(); - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true }); - } -} -function getMetaPath() { - return path.join(getScriptsDir$1(), META_FILENAME); -} -function readMeta() { - const metaPath = getMetaPath(); - if (!fs.existsSync(metaPath)) { - return { scripts: [] }; - } - try { - const raw = fs.readFileSync(metaPath, "utf-8"); - const parsed = JSON.parse(raw); - if (parsed && Array.isArray(parsed.scripts)) { - return parsed; - } - } catch (err) { - log.warn("[script-store-service] Failed to read meta:", err); - } - return { scripts: [] }; -} -function writeMeta(meta) { - ensureScriptsDir(); - const metaPath = getMetaPath(); - fs.writeFileSync(metaPath, JSON.stringify(meta, null, 2), "utf-8"); -} -function sanitizeFilename(name) { - return name.toLowerCase().replace(/[^a-z0-9\u4e00-\u9fa5]+/g, "-").replace(/^-+|-+$/g, "") || "script"; -} -function generateUniqueFilename(name, existingNames) { - const base = sanitizeFilename(name); - let filename = `${base}.mjs`; - let counter = 1; - while (existingNames.has(filename)) { - filename = `${base}-${counter}.mjs`; - counter++; - } - return filename; -} -function seedScripts() { - const scriptsDir = getScriptsDir$1(); - const metaPath = getMetaPath(); - if (fs.existsSync(metaPath)) { - return; - } - if (!fs.existsSync(scriptsDir)) { - log.info("[script-store-service] Scripts directory does not exist, skipping seed."); - return; - } - const meta = { scripts: [] }; - const scriptFiles = fs.readdirSync(scriptsDir).filter((f) => f.endsWith(".mjs")); - for (const file of scriptFiles) { - try { - const name = file.replace(/\.mjs$/, ""); - const now = (/* @__PURE__ */ new Date()).toISOString(); - meta.scripts.push({ - id: `seed-${name}`, - name, - description: "", - filename: file, - enabled: true, - channel: "", - createdAt: now, - updatedAt: now - }); - } catch (err) { - log.warn("[script-store-service] Failed to seed script", file, err); - } - } - writeMeta(meta); - log.info("[script-store-service] Seeded scripts:", meta.scripts.length); -} -function initScriptStoreService() { - ensureScriptsDir(); - seedScripts(); -} -function listScripts() { - const meta = readMeta(); - return meta.scripts.map((item) => enrichWithCode(item)).sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()); -} -function getScript(id) { - const meta = readMeta(); - const item = meta.scripts.find((s) => s.id === id); - if (!item) return null; - return enrichWithCode(item); -} -function getScriptPathById(id) { - const meta = readMeta(); - const item = meta.scripts.find((s) => s.id === id); - if (!item) return null; - return path.join(getScriptsDir$1(), item.filename); -} -function saveScript(input) { - const meta = readMeta(); - const scriptsDir = getScriptsDir$1(); - const existingNames = new Set(meta.scripts.map((s) => s.filename)); - const now = (/* @__PURE__ */ new Date()).toISOString(); - if (input.id) { - const index = meta.scripts.findIndex((s) => s.id === input.id); - if (index >= 0) { - const existing = meta.scripts[index]; - const filePath2 = path.join(scriptsDir, existing.filename); - fs.writeFileSync(filePath2, input.code, "utf-8"); - meta.scripts[index] = { - ...existing, - name: input.name, - description: input.description, - channel: input.channel, - enabled: input.enabled, - updatedAt: now - }; - writeMeta(meta); - return enrichWithCode(meta.scripts[index]); - } - } - const filename = generateUniqueFilename(input.name, existingNames); - const filePath = path.join(scriptsDir, filename); - fs.writeFileSync(filePath, input.code, "utf-8"); - const id = `script-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`; - const item = { - id, - name: input.name, - description: input.description, - filename, - enabled: input.enabled, - channel: input.channel, - createdAt: now, - updatedAt: now - }; - meta.scripts.push(item); - writeMeta(meta); - return enrichWithCode(item); -} -function deleteScript(id) { - const meta = readMeta(); - const index = meta.scripts.findIndex((s) => s.id === id); - if (index === -1) return false; - const item = meta.scripts[index]; - const filePath = path.join(getScriptsDir$1(), item.filename); - if (fs.existsSync(filePath)) { - try { - fs.unlinkSync(filePath); - } catch (err) { - log.warn("[script-store-service] Failed to delete script file:", err); - } - } - meta.scripts.splice(index, 1); - writeMeta(meta); - return true; -} -function toggleScript(id, enabled) { - const meta = readMeta(); - const index = meta.scripts.findIndex((s) => s.id === id); - if (index === -1) return false; - meta.scripts[index].enabled = enabled; - meta.scripts[index].updatedAt = (/* @__PURE__ */ new Date()).toISOString(); - writeMeta(meta); - return true; -} -function updateLastRun(id, lastRun) { - const meta = readMeta(); - const index = meta.scripts.findIndex((s) => s.id === id); - if (index === -1) return false; - meta.scripts[index].lastRun = lastRun; - meta.scripts[index].updatedAt = (/* @__PURE__ */ new Date()).toISOString(); - writeMeta(meta); - return true; -} -function enrichWithCode(item) { - const scriptsDir = getScriptsDir$1(); - const filePath = path.join(scriptsDir, item.filename); - let code = ""; - try { - if (fs.existsSync(filePath)) { - code = fs.readFileSync(filePath, "utf-8"); - } - } catch (err) { - log.warn("[script-store-service] Failed to read script file:", err); - } - return { - ...item, - code - }; -} -const executor = new executeScriptService(); -async function runScriptById(id, channel) { - const scriptPath = getScriptPathById(id); - if (!scriptPath) { - return { - success: false, - exitCode: null, - stdoutTail: "", - stderrTail: "", - error: "Script not found" - }; - } - const result = await executor.executeScript(scriptPath, { - SCRIPT_ID: id, - CHANNEL: channel || "" - }); - updateLastRun(id, { - time: (/* @__PURE__ */ new Date()).toISOString(), - success: result.success, - error: result.error - }); - return result; -} -const openedTabIndexByChannelName = /* @__PURE__ */ new Map(); -function getScriptsDir() { - return electron.app.isPackaged ? path.join(__dirname, "scripts") : path.join(process.cwd(), "electron/scripts"); -} -function runTaskOperationService() { - const executeScriptServiceInstance = new executeScriptService(); - const playwrightCoreDir = path.dirname(require.resolve("playwright-core")); - const cliPath = path.join(playwrightCoreDir, "cli.js"); - let recorderProc = null; - electron.ipcMain.handle(IPC_EVENTS.SCRIPT_LIST, async () => { - try { - return listScripts(); - } catch (error) { - log.error("[SCRIPT_LIST] error:", error); - throw error; - } - }); - electron.ipcMain.handle(IPC_EVENTS.SCRIPT_GET, async (_event, id) => { - try { - return getScript(id); - } catch (error) { - log.error("[SCRIPT_GET] error:", error); - throw error; - } - }); - electron.ipcMain.handle(IPC_EVENTS.SCRIPT_SAVE, async (_event, input) => { - try { - return saveScript(input); - } catch (error) { - log.error("[SCRIPT_SAVE] error:", error); - throw error; - } - }); - electron.ipcMain.handle(IPC_EVENTS.SCRIPT_DELETE, async (_event, id) => { - try { - return deleteScript(id); - } catch (error) { - log.error("[SCRIPT_DELETE] error:", error); - throw error; - } - }); - electron.ipcMain.handle(IPC_EVENTS.SCRIPT_TOGGLE, async (_event, id, enabled) => { - try { - return toggleScript(id, enabled); - } catch (error) { - log.error("[SCRIPT_TOGGLE] error:", error); - throw error; - } - }); - electron.ipcMain.handle(IPC_EVENTS.SCRIPT_RUN, async (_event, id) => { - try { - const script = getScript(id); - return await runScriptById(id, script?.channel); - } catch (error) { - log.error("[SCRIPT_RUN] error:", error); - return { success: false, exitCode: null, stdoutTail: "", stderrTail: "", error: error?.message || "Run failed" }; - } - }); - electron.ipcMain.handle(IPC_EVENTS.SCRIPT_RECORD_START, async (_event, url) => { - try { - if (recorderProc) { - recorderProc.kill("SIGINT"); - recorderProc = null; - } - const targetUrl = url || "about:blank"; - recorderProc = child_process.spawn(process.execPath, [cliPath, "codegen", "--target", "javascript", "--channel", "chrome", "--viewport-size", "1920,1080", "--color-scheme", "light", targetUrl], { - env: { ...process.env, ELECTRON_RUN_AS_NODE: "1" }, - stdio: "pipe" - }); - recorderProc.on("error", (err) => { - log.error("[SCRIPT_RECORD_START] Failed to start codegen process:", err); - }); - recorderProc.on("exit", (code, signal) => { - log.info(`[SCRIPT_RECORD_START] Process exited code=${code} signal=${signal}`); - recorderProc = null; - }); - recorderProc.stdout?.on("data", (data) => { - log.info(`[SCRIPT_RECORD_START] stdout: ${data.toString()}`); - }); - recorderProc.stderr?.on("data", (data) => { - log.error(`[SCRIPT_RECORD_START] stderr: ${data.toString()}`); - }); - return { success: true }; - } catch (error) { - log.error("[SCRIPT_RECORD_START] error:", error); - return { success: false, error: error?.message || "Recording start failed" }; - } - }); - electron.ipcMain.handle(IPC_EVENTS.SCRIPT_RECORD_STOP, async () => { - try { - if (recorderProc) { - recorderProc.kill("SIGINT"); - recorderProc = null; - } - return { success: true, code: "" }; - } catch (error) { - log.error("[SCRIPT_RECORD_STOP] error:", error); - return { success: false, error: error?.message || "Recording stop failed" }; - } - }); - electron.ipcMain.handle(IPC_EVENTS.SCRIPT_CODEGEN, async (_event, id, url) => { - try { - const script = getScript(id); - if (!script) { - return { success: false, error: "Script not found" }; - } - const scriptsDir = getScriptsDir(); - const scriptPath = path.join(scriptsDir, script.filename); - const targetUrl = url || "about:blank"; - log.info(`[SCRIPT_CODEGEN] Starting codegen for script ${id} at ${scriptPath} with url ${targetUrl}`); - return await new Promise((resolve) => { - const proc = child_process.spawn(process.execPath, [cliPath, "codegen", "--target", "javascript", "--channel", "chrome", "-o", scriptPath, targetUrl], { - env: { ...process.env, ELECTRON_RUN_AS_NODE: "1" }, - stdio: "pipe" - }); - proc.on("exit", () => { - try { - let generatedCode = fs.readFileSync(scriptPath, "utf-8"); - if (generatedCode.includes("require('playwright')") && !generatedCode.includes("createRequire")) { - generatedCode = `import { createRequire } from 'node:module'; -const require = createRequire(import.meta.url); - -${generatedCode}`; - } - fs.writeFileSync(scriptPath, generatedCode, "utf-8"); - saveScript({ - id, - name: script.name, - description: script.description, - code: generatedCode, - channel: script.channel, - enabled: script.enabled - }); - resolve({ success: true, code: generatedCode }); - } catch (err) { - log.error("[SCRIPT_CODEGEN] Failed to process generated code:", err); - resolve({ success: false, error: err?.message || "Failed to process generated code" }); - } - }); - proc.on("error", (err) => { - log.error("[SCRIPT_CODEGEN] Failed to start codegen:", err); - resolve({ success: false, error: err.message }); - }); - }); - } catch (error) { - log.error("[SCRIPT_CODEGEN] error:", error); - return { success: false, error: error?.message || "Codegen failed" }; - } - }); - electron.ipcMain.handle(IPC_EVENTS.OPEN_CHANNEL, async (_event, channels) => { - try { - await launchLocalChrome(); - const scriptsDir = getScriptsDir(); - const scriptPath = path.join(scriptsDir, "open_all_channel.js"); - openedTabIndexByChannelName.clear(); - if (Array.isArray(channels)) { - for (let i = 0; i < channels.length; i++) { - const name = channels[i]?.channelName; - if (name) openedTabIndexByChannelName.set(String(name), i); - } - } - const result = await executeScriptServiceInstance.executeScript(scriptPath, { channels }); - return { success: true, result }; - } catch (error) { - return { success: false, error: error?.message || "open channel failed" }; - } - }); - electron.ipcMain.handle(IPC_EVENTS.EXECUTE_SCRIPT, async (_event, options) => { - try { - const roomType = options.roomList.find((item) => item.id === options.roomType); - const pairs = [ - ["fzName", "fg_trace.js"], - ["mtName", "mt_trace.js"], - ["dyHotelName", "dy_hotel_trace.js"], - ["dyHotSpringName", "dy_hot_spring_trace.js"] - ]; - const scriptEntries = pairs.filter(([prop]) => roomType?.[prop]); - const scriptsDir = getScriptsDir(); - const scriptPaths = scriptEntries.map(([channel, fileName]) => { - const p = path.join(scriptsDir, fileName); - if (!fs.existsSync(p)) { - throw new Error(`Script not found for channel ${channel}: ${p}`); - } - return { channel, scriptPath: p }; - }); - const results = []; - for (let i = 0; i < scriptPaths.length; i++) { - const item = scriptPaths[i]; - const channelNameMap = { - fzName: "fliggy", - mtName: "meituan", - dyHotelName: "douyin", - dyHotSpringName: "douyin" - }; - const defaultTabIndexMap = { - fliggy: 0, - meituan: 1, - douyin: 2 - }; - const mappedName = channelNameMap[item.channel]; - const tabIndex = mappedName ? openedTabIndexByChannelName.get(mappedName) ?? defaultTabIndexMap[mappedName] ?? i : i; - log.info(`Launching script for channel ${item.channel}: ${item.scriptPath} (tabIndex: ${tabIndex})`); - const result = await executeScriptServiceInstance.executeScript(item.scriptPath, { - roomType: roomType[item.channel], - startTime: options.startTime, - endTime: options.endTime, - operation: options.operation, - tabIndex - }); - results.push({ - channel: item.channel, - scriptPath: item.scriptPath, - ...result - }); - } - return { success: true, result: results }; - } catch (error) { - return { success: false, error: error.message }; - } - }); -} -class AppUpdater { - mainWindow = null; - static _instance; - _initialized = false; - constructor() { - electronUpdater.autoUpdater.autoDownload = false; - } - init() { - if (this._initialized) return; - this._initialized = true; - this.setupListeners(); - this.registerHandlers(); - } - static getInstance() { - if (!this._instance) { - this._instance = new AppUpdater(); - } - return this._instance; - } - setMainWindow(window2) { - this.mainWindow = window2; - } - setupListeners() { - electronUpdater.autoUpdater.on("checking-for-update", () => this.sendToRenderer(IPC_EVENTS.UPDATE_STATUS_CHANGED, { status: "checking" })); - electronUpdater.autoUpdater.on("update-available", (info) => this.sendToRenderer(IPC_EVENTS.UPDATE_STATUS_CHANGED, { status: "available", info })); - electronUpdater.autoUpdater.on("update-not-available", () => this.sendToRenderer(IPC_EVENTS.UPDATE_STATUS_CHANGED, { status: "not-available" })); - electronUpdater.autoUpdater.on("download-progress", (progress) => this.sendToRenderer(IPC_EVENTS.UPDATE_STATUS_CHANGED, { status: "downloading", progress })); - electronUpdater.autoUpdater.on("update-downloaded", (info) => this.sendToRenderer(IPC_EVENTS.UPDATE_STATUS_CHANGED, { status: "downloaded", info })); - electronUpdater.autoUpdater.on("error", (error) => this.sendToRenderer(IPC_EVENTS.UPDATE_STATUS_CHANGED, { status: "error", error: error.message })); - } - sendToRenderer(channel, data) { - electron.BrowserWindow.getAllWindows().forEach((win) => { - if (!win.isDestroyed()) { - win.webContents.send(channel, data); - } - }); - } - registerHandlers() { - electron.ipcMain.handle(IPC_EVENTS.UPDATE_CHECK, () => { - if (electron.app.isPackaged) { - return electronUpdater.autoUpdater.checkForUpdates(); - } else { - this.sendToRenderer(IPC_EVENTS.UPDATE_STATUS_CHANGED, { status: "checking" }); - setTimeout(() => { - this.sendToRenderer(IPC_EVENTS.UPDATE_STATUS_CHANGED, { status: "not-available" }); - }, 1500); - return null; - } - }); - electron.ipcMain.handle(IPC_EVENTS.UPDATE_DOWNLOAD, () => { - if (electron.app.isPackaged) { - return electronUpdater.autoUpdater.downloadUpdate(); - } - return null; - }); - electron.ipcMain.handle(IPC_EVENTS.UPDATE_INSTALL, () => { - if (electron.app.isPackaged) { - return electronUpdater.autoUpdater.quitAndInstall(); - } - return null; - }); - electron.ipcMain.handle(IPC_EVENTS.UPDATE_VERSION, () => { - return electron.app.getVersion(); - }); - } -} -const appUpdater = AppUpdater.getInstance(); -const PROVIDER_TYPE_INFO = [ - { - id: "anthropic", - name: "Anthropic", - icon: "🤖", - placeholder: "sk-ant-api03-...", - model: "Claude", - requiresApiKey: true, - docsUrl: "https://platform.claude.com/docs/en/api/overview" - }, - { - id: "openai", - name: "OpenAI", - icon: "💚", - placeholder: "sk-proj-...", - model: "GPT", - requiresApiKey: true, - isOAuth: true, - supportsApiKey: true, - defaultModelId: "gpt-5.4", - showModelId: true, - showModelIdInDevModeOnly: true, - modelIdPlaceholder: "gpt-5.4", - apiKeyUrl: "https://platform.openai.com/api-keys" - }, - { - id: "google", - name: "Google", - icon: "🔷", - placeholder: "AIza...", - model: "Gemini", - requiresApiKey: true, - isOAuth: true, - supportsApiKey: true, - defaultModelId: "gemini-3-pro-preview", - showModelId: true, - showModelIdInDevModeOnly: true, - modelIdPlaceholder: "gemini-3-pro-preview", - apiKeyUrl: "https://aistudio.google.com/app/apikey" - }, - { id: "openrouter", name: "OpenRouter", icon: "🌐", placeholder: "sk-or-v1-...", model: "Multi-Model", requiresApiKey: true, showModelId: true, modelIdPlaceholder: "openai/gpt-5.4", defaultModelId: "openai/gpt-5.4", docsUrl: "https://openrouter.ai/models" }, - { id: "minimax-portal-cn", name: "MiniMax (CN)", icon: "☁️", placeholder: "sk-...", model: "MiniMax", requiresApiKey: false, isOAuth: true, supportsApiKey: true, defaultModelId: "MiniMax-M2.7", showModelId: true, showModelIdInDevModeOnly: true, modelIdPlaceholder: "MiniMax-M2.7", apiKeyUrl: "https://platform.minimaxi.com/" }, - { id: "moonshot", name: "Moonshot (CN)", icon: "🌙", placeholder: "sk-...", model: "Kimi", requiresApiKey: true, defaultBaseUrl: "https://api.moonshot.cn/v1", defaultModelId: "kimi-k2.5", docsUrl: "https://platform.moonshot.cn/" }, - { id: "siliconflow", name: "SiliconFlow (CN)", icon: "🌊", placeholder: "sk-...", model: "Multi-Model", requiresApiKey: true, defaultBaseUrl: "https://api.siliconflow.cn/v1", showModelId: true, showModelIdInDevModeOnly: true, modelIdPlaceholder: "deepseek-ai/DeepSeek-V3", defaultModelId: "deepseek-ai/DeepSeek-V3", docsUrl: "https://docs.siliconflow.cn/cn/userguide/introduction" }, - { id: "deepseek", name: "DeepSeek", icon: "🐋", placeholder: "sk-...", model: "DeepSeek", requiresApiKey: true, defaultBaseUrl: "https://api.deepseek.com/v1", showModelId: true, modelIdPlaceholder: "deepseek-chat", defaultModelId: "deepseek-chat", apiKeyUrl: "https://platform.deepseek.com/api_keys", docsUrl: "https://api-docs.deepseek.com/" }, - { id: "minimax-portal", name: "MiniMax (Global)", icon: "☁️", placeholder: "sk-...", model: "MiniMax", requiresApiKey: false, isOAuth: true, supportsApiKey: true, defaultModelId: "MiniMax-M2.7", showModelId: true, showModelIdInDevModeOnly: true, modelIdPlaceholder: "MiniMax-M2.7", apiKeyUrl: "https://platform.minimax.io" }, - { id: "modelstudio", name: "Model Studio", icon: "☁️", placeholder: "sk-...", model: "Qwen", requiresApiKey: true, defaultBaseUrl: "https://coding.dashscope.aliyuncs.com/v1", showBaseUrl: true, defaultModelId: "qwen3.5-plus", showModelId: true, showModelIdInDevModeOnly: true, modelIdPlaceholder: "qwen3.5-plus", apiKeyUrl: "https://bailian.console.aliyun.com/", hidden: true }, - { id: "ark", name: "ByteDance Ark", icon: "A", placeholder: "your-ark-api-key", model: "Doubao", requiresApiKey: true, defaultBaseUrl: "https://ark.cn-beijing.volces.com/api/v3", showBaseUrl: true, showModelId: true, modelIdPlaceholder: "ep-20260228000000-xxxxx", docsUrl: "https://www.volcengine.com/", codePlanPresetBaseUrl: "https://ark.cn-beijing.volces.com/api/coding/v3", codePlanPresetModelId: "ark-code-latest", codePlanDocsUrl: "https://www.volcengine.com/docs/82379/1928261?lang=zh" }, - { id: "ollama", name: "Ollama", icon: "🦙", placeholder: "Not required", requiresApiKey: false, defaultBaseUrl: "http://localhost:11434/v1", showBaseUrl: true, showModelId: true, modelIdPlaceholder: "qwen3:latest" }, - { - id: "custom", - name: "Custom", - icon: "⚙️", - placeholder: "API key...", - requiresApiKey: true, - showBaseUrl: true, - showModelId: true, - modelIdPlaceholder: "your-provider/model-id", - docsUrl: "https://icnnp7d0dymg.feishu.cn/wiki/BmiLwGBcEiloZDkdYnGc8RWnn6d#Ee1ldfvKJoVGvfxc32mcILwenth", - docsUrlZh: "https://icnnp7d0dymg.feishu.cn/wiki/BmiLwGBcEiloZDkdYnGc8RWnn6d#IWQCdfe5fobGU3xf3UGcgbLynGh" - } -]; -function getProviderTypeInfo(type) { - return PROVIDER_TYPE_INFO.find((t2) => t2.id === type); -} -const defaultStore = { - accounts: [], - defaultAccountId: null -}; -const storePath = path__namespace.join(electron.app.getPath("userData"), "provider-accounts.json"); -const keysPath = path__namespace.join(electron.app.getPath("userData"), "provider-keys.json"); -function readJson(filePath, defaultValue) { - try { - if (fs__namespace.existsSync(filePath)) { - return JSON.parse(fs__namespace.readFileSync(filePath, "utf-8")); - } - } catch (e) { - logManager.error(`Failed to read ${filePath}:`, e); - } - return defaultValue; -} -function writeJson(filePath, data) { - try { - fs__namespace.mkdirSync(path__namespace.dirname(filePath), { recursive: true }); - fs__namespace.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8"); - } catch (e) { - logManager.error(`Failed to write ${filePath}:`, e); - } -} -function getStore() { - return readJson(storePath, defaultStore); -} -function saveStore(store) { - writeJson(storePath, store); -} -function getKeys() { - return readJson(keysPath, {}); -} -function saveKeys(keys) { - writeJson(keysPath, keys); -} -function mapToProviderWithKeyInfo(account) { - const keys = getKeys(); - const hasKey = !!keys[account.id]; - return { - id: account.id, - name: account.label, - type: account.vendorId, - baseUrl: account.baseUrl, - apiProtocol: account.apiProtocol, - headers: account.headers, - model: account.model, - fallbackModels: account.fallbackModels, - fallbackProviderIds: account.fallbackAccountIds, - enabled: account.enabled, - createdAt: account.createdAt, - updatedAt: account.updatedAt, - hasKey, - keyMasked: hasKey ? "••••••••" : null - }; -} -const listeners = []; -function onProviderChange(listener) { - listeners.push(listener); - return () => { - const idx = listeners.indexOf(listener); - if (idx > -1) listeners.splice(idx, 1); - }; -} -function notifyChange() { - listeners.forEach((l) => l()); -} -function mapToVendorInfo(info) { - return { - ...info, - category: info.id === "ollama" ? "local" : info.id === "custom" ? "custom" : "compatible", - supportedAuthModes: info.requiresApiKey ? info.isOAuth ? ["api_key", "oauth_browser"] : ["api_key"] : info.isOAuth ? ["local", "oauth_browser"] : ["local"], - defaultAuthMode: info.requiresApiKey ? "api_key" : "local", - supportsMultipleAccounts: true - }; -} -function sanitizeAccount(account) { - let model = account.model; - if (model) { - if (model === "deepseek-chat/deepseek-reasoner" || model.startsWith("deepseek-chat/")) { - model = "deepseek-chat"; - } else if (model.startsWith("deepseek-reasoner/")) { - model = "deepseek-reasoner"; - } - } - if (model !== account.model) { - return { ...account, model }; - } - return account; -} -const providerApiService = { - getVendors() { - return PROVIDER_TYPE_INFO.map(mapToVendorInfo); - }, - getAccounts() { - return getStore().accounts.map(sanitizeAccount); - }, - getProviders() { - return getStore().accounts.map(sanitizeAccount).map(mapToProviderWithKeyInfo); - }, - getDefault() { - return { accountId: getStore().defaultAccountId }; - }, - createAccount(body) { - const store = getStore(); - const account = { ...body.account, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }; - store.accounts.push(account); - if (body.apiKey) { - const keys = getKeys(); - keys[account.id] = body.apiKey; - saveKeys(keys); - } - saveStore(store); - notifyChange(); - return { success: true }; - }, - updateAccount(accountId, body) { - const store = getStore(); - const idx = store.accounts.findIndex((a) => a.id === accountId); - if (idx === -1) return { success: false, error: "Account not found" }; - store.accounts[idx] = { - ...store.accounts[idx], - ...body.updates, - updatedAt: (/* @__PURE__ */ new Date()).toISOString() - }; - if (body.apiKey) { - const keys = getKeys(); - keys[accountId] = body.apiKey; - saveKeys(keys); - } - saveStore(store); - notifyChange(); - return { success: true }; - }, - deleteAccount(accountId) { - const store = getStore(); - store.accounts = store.accounts.filter((a) => a.id !== accountId); - if (store.defaultAccountId === accountId) store.defaultAccountId = null; - saveStore(store); - const keys = getKeys(); - delete keys[accountId]; - saveKeys(keys); - notifyChange(); - return { success: true }; - }, - setDefault(body) { - const store = getStore(); - const accountExists = store.accounts.some((a) => a.id === body.accountId); - if (!accountExists) { - return { success: false, error: "Account not found" }; - } - store.defaultAccountId = body.accountId; - saveStore(store); - notifyChange(); - return { success: true }; - }, - validateApiKey(body) { - if (!body.apiKey || body.apiKey.trim().length === 0) { - return { valid: false, error: "API key is required" }; - } - return { valid: true }; - }, - getApiKey(providerId) { - const keys = getKeys(); - return { apiKey: keys[providerId] || null }; - }, - deleteApiKey(accountId) { - const keys = getKeys(); - delete keys[accountId]; - saveKeys(keys); - notifyChange(); - return { success: true }; - }, - getUsageHistory() { - return []; - } -}; -class BaseProvider { -} -function _transformChunk(chunk) { - const choice = chunk.choices[0]; - return { - isEnd: choice?.finish_reason != null, - result: choice?.delta?.content ?? "" - }; -} -class OpenAIProvider extends BaseProvider { - client; - constructor(apiKey, baseURL, headers) { - super(); - this.client = new OpenAI({ apiKey, baseURL, defaultHeaders: headers }); - } - async chat(messages2, model, options) { - const startTime = Date.now(); - const lastMessage = messages2[messages2.length - 1]; - logManager.logApiRequest("chat.completions.create", { - model, - lastMessage: lastMessage?.content?.substring(0, 100) + (lastMessage?.content?.length > 100 ? "..." : ""), - messageCount: messages2.length - }, "POST"); - try { - const chunks = await this.client.chat.completions.create({ - model, - messages: messages2, - stream: true - }, { - signal: options?.signal - }); - return { - async *[Symbol.asyncIterator]() { - try { - for await (const chunk of chunks) { - if (options?.signal?.aborted) break; - yield _transformChunk(chunk); - } - const responseTime = Date.now() - startTime; - logManager.logApiResponse("chat.completions.create", { success: true }, 200, responseTime); - } catch (error) { - const responseTime = Date.now() - startTime; - logManager.logApiResponse("chat.completions.create", { error: error instanceof Error ? error.message : String(error) }, 500, responseTime); - throw error; - } - } - }; - } catch (error) { - const responseTime = Date.now() - startTime; - logManager.logApiResponse("chat.completions.create", { error: error instanceof Error ? error.message : String(error) }, 500, responseTime); - throw error; - } - } -} -function createProvider(accountId) { - const account = providerApiService.getAccounts().find((a) => a.id === accountId); - if (!account) { - throw new Error(`Provider account ${accountId} not found`); - } - const apiKeyResult = providerApiService.getApiKey(accountId); - const apiKey = apiKeyResult.apiKey; - if (!apiKey) { - throw new Error(`API key for account ${accountId} not found`); - } - const baseURL = account.baseUrl || getProviderTypeInfo(account.vendorId)?.defaultBaseUrl; - if (!baseURL) { - throw new Error(`Base URL for account ${accountId} not found`); - } - switch (account.apiProtocol) { - case "anthropic-messages": - throw new Error("Anthropic provider not yet implemented"); - case "openai-completions": - case "openai-responses": - default: - return new OpenAIProvider(apiKey, baseURL, account.headers); - } -} -let sessionsFilePath = null; -function getSessionsFilePath() { - if (!sessionsFilePath) { - sessionsFilePath = path__namespace.join(electron.app.getPath("userData"), "chat-sessions.json"); - } - return sessionsFilePath; -} -class SessionStore { - sessions = /* @__PURE__ */ new Map(); - loaded = false; - ensureLoaded() { - if (this.loaded) return; - this.loaded = true; - this.loadFromDisk(); - } - loadFromDisk() { - try { - const filePath = getSessionsFilePath(); - if (fs__namespace.existsSync(filePath)) { - const data = JSON.parse(fs__namespace.readFileSync(filePath, "utf-8")); - for (const [key, entry] of Object.entries(data)) { - this.sessions.set(key, { - ...entry, - activeRun: void 0 - }); - } - } - } catch (e) { - logManager.error("Failed to load sessions from disk:", e); - } - } - saveToDisk() { - try { - const filePath = getSessionsFilePath(); - const data = {}; - for (const [key, entry] of this.sessions) { - data[key] = { - key: entry.key, - messages: entry.messages, - updatedAt: entry.updatedAt - }; - } - fs__namespace.mkdirSync(path__namespace.dirname(filePath), { recursive: true }); - fs__namespace.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8"); - } catch (e) { - logManager.error("Failed to save sessions to disk:", e); - } - } - getOrCreate(key) { - this.ensureLoaded(); - let session = this.sessions.get(key); - if (!session) { - session = { - key, - messages: [], - updatedAt: Date.now() - }; - this.sessions.set(key, session); - } - return session; - } - get(key) { - this.ensureLoaded(); - return this.sessions.get(key); - } - getAllKeys() { - this.ensureLoaded(); - return Array.from(this.sessions.keys()); - } - appendMessage(key, message) { - const session = this.getOrCreate(key); - session.messages.push(message); - session.updatedAt = Date.now(); - this.saveToDisk(); - } - getMessages(key, limit = 50) { - const session = this.get(key); - if (!session) return []; - return session.messages.slice(-limit); - } - setActiveRun(key, runId, abortController) { - const session = this.getOrCreate(key); - session.activeRun = { runId, abortController }; - } - clearActiveRun(key) { - const session = this.sessions.get(key); - if (session) { - session.activeRun = void 0; - } - } - getActiveRun(key) { - return this.sessions.get(key)?.activeRun; - } - deleteSession(key) { - this.sessions.delete(key); - this.saveToDisk(); - } -} -const sessionStore = new SessionStore(); -function buildChatMessages(sessionMessages) { - return sessionMessages.map((msg) => { - if (!msg.role || !msg.content) return null; - const role = msg.role; - if (role === "user" || role === "assistant" || role === "system") { - return { - role, - content: typeof msg.content === "string" ? msg.content : "" - }; - } - return null; - }).filter((m) => m !== null); -} -async function processChatStream(sessionKey, runId, provider, model, messages2, signal, broadcast) { - let assistantContent = ""; - try { - const chunks = await provider.chat(messages2, model, { signal }); - for await (const chunk of chunks) { - if (signal.aborted) break; - if (chunk.result) { - assistantContent += chunk.result; - broadcast({ - type: "chat:delta", - sessionKey, - runId, - delta: chunk.result - }); - } - if (chunk.isEnd) { - break; - } - } - if (!signal.aborted) { - const finalMessage = { - role: "assistant", - content: assistantContent, - timestamp: Date.now() - }; - sessionStore.appendMessage(sessionKey, finalMessage); - sessionStore.clearActiveRun(sessionKey); - broadcast({ - type: "chat:final", - sessionKey, - runId, - message: finalMessage - }); - } - } catch (error) { - sessionStore.clearActiveRun(sessionKey); - broadcast({ - type: "chat:error", - sessionKey, - runId, - error: error instanceof Error ? error.message : String(error) - }); - } -} -function handleChatSend(params, broadcast) { - const { sessionKey, message, options } = params; - const runId = crypto.randomUUID(); - sessionStore.appendMessage(sessionKey, { - ...message, - timestamp: message.timestamp || Date.now() - }); - const accountId = options?.providerAccountId || providerApiService.getDefault().accountId; - if (!accountId) { - throw new Error("No provider account selected"); - } - const account = providerApiService.getAccounts().find((a) => a.id === accountId); - if (!account) { - throw new Error(`Provider account ${accountId} not found`); - } - const model = account.model; - if (!model) { - throw new Error(`Provider account ${accountId} has no model configured`); - } - const session = sessionStore.getOrCreate(sessionKey); - const messages2 = buildChatMessages(session.messages); - const abortController = new AbortController(); - sessionStore.setActiveRun(sessionKey, runId, abortController); - const provider = createProvider(accountId); - processChatStream(sessionKey, runId, provider, model, messages2, abortController.signal, broadcast).catch( - (err) => { - logManager.error("Unexpected error in processChatStream:", err); - sessionStore.clearActiveRun(sessionKey); - broadcast({ - type: "chat:error", - sessionKey, - runId, - error: err instanceof Error ? err.message : String(err) - }); - } - ); - return { runId }; -} -function handleChatHistory(params) { - return sessionStore.getMessages(params.sessionKey, params.limit ?? 50); -} -function handleChatAbort(params, broadcast) { - const activeRun = sessionStore.getActiveRun(params.sessionKey); - if (activeRun) { - activeRun.abortController.abort(); - sessionStore.clearActiveRun(params.sessionKey); - broadcast({ - type: "chat:aborted", - sessionKey: params.sessionKey, - runId: activeRun.runId - }); - } -} -function handleSessionList() { - return sessionStore.getAllKeys(); -} -function handleProviderList() { - return { - accounts: providerApiService.getAccounts(), - defaultAccountId: providerApiService.getDefault().accountId - }; -} -function handleProviderGetDefault() { - return providerApiService.getDefault(); -} -class GatewayManager { - initialized = false; - async init() { - if (this.initialized) return; - this.initialized = true; - logManager.info("GatewayManager initialized"); - this.broadcast({ type: "gateway:status", status: "connected" }); - } - async rpc(method, params) { - if (!this.initialized) { - await this.init(); - } - logManager.info(`Gateway RPC: ${method}`, params); - switch (method) { - case "chat.send": - return handleChatSend(params, (event) => this.broadcast(event)); - case "chat.history": - return handleChatHistory(params); - case "chat.abort": - return handleChatAbort(params, (event) => this.broadcast(event)); - case "session.list": - return handleSessionList(); - case "provider.list": - return handleProviderList(); - case "provider.getDefault": - return handleProviderGetDefault(); - default: - throw new Error(`Unknown gateway RPC method: ${method}`); - } - } - broadcast(event) { - const mainWindow = electron.BrowserWindow.getAllWindows().find( - (win) => windowManager.getName(win) === "main" - ); - if (mainWindow && !mainWindow.isDestroyed()) { - mainWindow.webContents.send("gateway:event", event); - } - } - reloadProviders() { - logManager.info("GatewayManager reloading providers"); - } -} -const gatewayManager = new GatewayManager(); -appUpdater.init(); -const HOST_API_BASE_URL = "http://8.138.234.141/ingress"; -async function handleLocalProviderApi(path2, method, body) { - const parsedBody = typeof body === "string" && body ? JSON.parse(body) : body; - if (path2 === "/api/provider-vendors" && method === "GET") { - return { success: true, ok: true, json: providerApiService.getVendors(), data: providerApiService.getVendors() }; - } - if (path2 === "/api/provider-accounts" && method === "GET") { - return { success: true, ok: true, json: providerApiService.getAccounts(), data: providerApiService.getAccounts() }; - } - if (path2 === "/api/providers" && method === "GET") { - return { success: true, ok: true, json: providerApiService.getProviders(), data: providerApiService.getProviders() }; - } - if (path2 === "/api/provider-accounts/default" && method === "GET") { - return { success: true, ok: true, json: providerApiService.getDefault(), data: providerApiService.getDefault() }; - } - if (path2 === "/api/provider-accounts" && method === "POST") { - const result = providerApiService.createAccount(parsedBody || {}); - return { success: true, ok: true, json: result, data: result }; - } - if (path2 === "/api/provider-accounts/default" && method === "PUT") { - const result = providerApiService.setDefault(parsedBody || {}); - return { success: result.success, ok: result.success, json: result, data: result }; - } - if (path2.startsWith("/api/provider-accounts/") && method === "PUT") { - const id = decodeURIComponent(path2.replace("/api/provider-accounts/", "")); - const result = providerApiService.updateAccount(id, parsedBody || {}); - return { success: result.success, ok: result.success, json: result, data: result }; - } - if (path2.startsWith("/api/provider-accounts/") && method === "DELETE") { - const id = decodeURIComponent(path2.replace("/api/provider-accounts/", "")); - const result = providerApiService.deleteAccount(id); - return { success: result.success, ok: result.success, json: result, data: result }; - } - if (path2 === "/api/providers/default" && method === "PUT") { - const result = providerApiService.setDefault({ accountId: parsedBody?.providerId }); - return { success: result.success, ok: result.success, json: result, data: result }; - } - if (path2.startsWith("/api/providers/") && path2.endsWith("/api-key") && method === "GET") { - const id = decodeURIComponent(path2.replace("/api/providers/", "").replace("/api-key", "")); - const result = providerApiService.getApiKey(id); - return { success: true, ok: true, json: result, data: result }; - } - if (path2.startsWith("/api/providers/") && method === "PUT") { - const id = decodeURIComponent(path2.replace("/api/providers/", "")); - const result = providerApiService.updateAccount(id, parsedBody || {}); - return { success: result.success, ok: result.success, json: result, data: result }; - } - if (path2.startsWith("/api/providers/") && method === "DELETE") { - const [rawId, query] = path2.replace("/api/providers/", "").split("?"); - const id = decodeURIComponent(rawId); - if (query && query.includes("apiKeyOnly=1")) { - const result2 = providerApiService.deleteApiKey(id); - return { success: result2.success, ok: result2.success, json: result2, data: result2 }; - } - const result = providerApiService.deleteAccount(id); - return { success: result.success, ok: result.success, json: result, data: result }; - } - if (path2 === "/api/providers/validate" && method === "POST") { - const result = await providerApiService.validateApiKey(parsedBody || {}); - return { success: true, ok: true, json: result, data: result }; - } - if (path2 === "/api/usage/recent-token-history" && method === "GET") { - return { success: true, ok: true, json: providerApiService.getUsageHistory(), data: providerApiService.getUsageHistory() }; - } - return null; -} -electron.ipcMain.handle("hostapi:fetch", async (_event, { path: path2, method, headers, body }) => { - const localResult = await handleLocalProviderApi(path2, method || "GET", body); - if (localResult) return localResult; - const url = `${HOST_API_BASE_URL}${path2}`; - try { - const response = await axios({ - url, - method: method || "GET", - headers: { - "Content-Type": "application/json", - ...headers - }, - data: body ?? void 0, - timeout: 3e4 - }); - return { - success: true, - ok: true, - json: response.data, - data: response.data - }; - } catch (error) { - if (error.response) { - return { - success: false, - ok: false, - status: error.response.status, - error: error.response.data?.message || error.message, - text: error.response.statusText, - data: error.response.data - }; - } - return { - success: false, - ok: false, - error: error.message || "Unknown error" - }; - } -}); -electron.ipcMain.handle("gateway:rpc", async (_event, method, params) => { - return gatewayManager.rpc(method, params); -}); -if (started) { - electron.app.quit(); -} -electron.app.whenReady().then(() => { - gatewayManager.init(); - onProviderChange(() => { - gatewayManager.reloadProviders(); - }); - setupMainWindow(); - initScriptStoreService(); - runTaskOperationService(); -}); -electron.app.on("window-all-closed", () => { - if (process.platform !== "darwin" && !configManager.get(CONFIG_KEYS.MINIMIZE_TO_TRAY)) { - log.info("app closing due to all windows being closed"); - electron.app.quit(); - } -}); -electron.app.on("activate", () => { - if (electron.BrowserWindow.getAllWindows().length === 0) { - setupMainWindow(); - } -}); +require("axios"); diff --git a/dist/index.html b/dist/index.html index 693abfa..4330d23 100644 --- a/dist/index.html +++ b/dist/index.html @@ -1,17 +1,17 @@ - - -
- -