diff --git a/dist-electron/main/main.js b/dist-electron/main/main.js index 5639b8d..a8c04dd 100644 --- a/dist-electron/main/main.js +++ b/dist-electron/main/main.js @@ -1,1644 +1,2 @@ -"use strict"; -const electron = require("electron"); -const OpenAI = require("openai"); -const util = require("util"); -const log = require("electron-log"); -const path = require("path"); -const fs = require("fs"); -const jsBase64 = 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("bytenode"); -const electronUpdater = require("electron-updater"); -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["WINDOW_MINIMIZE"] = "window-minimize"; - IPC_EVENTS2["WINDOW_MAXIMIZE"] = "window-maximize"; - IPC_EVENTS2["WINDOW_CLOSE"] = "window-close"; - IPC_EVENTS2["IS_WINDOW_MAXIMIZED"] = "is-window-maximized"; - 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["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 || {}); -class BaseProvider { -} -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 _transformChunk(chunk) { - const choice = chunk.choices[0]; - return { - isEnd: choice?.finish_reason === "stop", - result: choice?.delta?.content ?? "" - }; -} -class OpenAIProvider extends BaseProvider { - client; - constructor(apiKey, baseURL) { - super(); - this.client = new OpenAI({ apiKey, baseURL }); - } - async chat(messages2, model) { - 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 - }); - const responseTime = Date.now() - startTime; - logManager.logApiResponse("chat.completions.create", { success: true }, 200, responseTime); - return { - async *[Symbol.asyncIterator]() { - for await (const chunk of chunks) { - yield _transformChunk(chunk); - } - } - }; - } 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 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; - } -} -function parseOpenAISetting(setting) { - try { - return JSON.parse(jsBase64.decode(setting)); - } catch (error) { - console.error("parseOpenAISetting failed:", error); - return {}; - } -} -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(); -[ - { - id: 1, - name: "bigmodel", - title: "智谱AI", - models: ["glm-4.5-flash"], - openAISetting: { - baseURL: "https://open.bigmodel.cn/api/paas/v4", - apiKey: process.env.BIGMODEL_API_KEY || "" - }, - createdAt: (/* @__PURE__ */ new Date()).getTime(), - updatedAt: (/* @__PURE__ */ new Date()).getTime() - }, - { - id: 2, - name: "deepseek", - title: "深度求索 (DeepSeek)", - models: ["deepseek-chat"], - openAISetting: { - baseURL: "https://api.deepseek.com/v1", - apiKey: process.env.DEEPSEEK_API_KEY || "" - }, - createdAt: (/* @__PURE__ */ new Date()).getTime(), - updatedAt: (/* @__PURE__ */ new Date()).getTime() - }, - { - id: 3, - name: "siliconflow", - title: "硅基流动", - models: ["Qwen/Qwen3-8B", "deepseek-ai/DeepSeek-R1-0528-Qwen3-8B"], - openAISetting: { - baseURL: "https://api.siliconflow.cn/v1", - apiKey: process.env.SILICONFLOW_API_KEY || "" - }, - createdAt: (/* @__PURE__ */ new Date()).getTime(), - updatedAt: (/* @__PURE__ */ new Date()).getTime() - }, - { - id: 4, - name: "qianfan", - title: "百度千帆", - models: ["ernie-speed-128k", "ernie-4.0-8k", "ernie-3.5-8k"], - openAISetting: { - baseURL: "https://qianfan.baidubce.com/v2", - apiKey: process.env.QIANFAN_API_KEY || "" - }, - createdAt: (/* @__PURE__ */ new Date()).getTime(), - updatedAt: (/* @__PURE__ */ new Date()).getTime() - } -]; -const _parseProvider = () => { - let result = []; - let isBase64Parsed = false; - const providerConfig = configManager.get(CONFIG_KEYS.PROVIDER); - const mapCallback = (provider) => ({ - ...provider, - openAISetting: typeof provider.openAISetting === "string" ? parseOpenAISetting(provider.openAISetting ?? "") : provider.openAISetting - }); - try { - result = JSON.parse(jsBase64.decode(providerConfig)); - isBase64Parsed = true; - } catch (error) { - logManager.error(`parse base64 provider failed: ${error}`); - } - if (!isBase64Parsed) try { - result = JSON.parse(providerConfig); - } catch (error) { - logManager.error(`parse provider failed: ${error}`); - } - if (!result.length) return; - return result.map(mapCallback); -}; -const getProviderConfig = () => { - try { - return _parseProvider(); - } catch (error) { - logManager.error(`get provider config failed: ${error}`); - return null; - } -}; -function createProvider(name) { - const providers2 = getProviderConfig(); - if (!providers2) { - throw new Error("provider config not found"); - } - for (const provider of providers2) { - if (provider.name === name) { - if (!provider.openAISetting?.apiKey || !provider.openAISetting?.baseURL) { - throw new Error("apiKey or baseURL not found"); - } - return new OpenAIProvider(provider.openAISetting.apiKey, provider.openAISetting.baseURL); - } - } -} -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 SHARED_WINDOW_OPTIONS = { - frame: false, - titleBarStyle: "hidden", - trafficLightPosition: { x: -100, y: -100 }, - 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() { - const handleCloseWindow = (e) => { - const target = electron.BrowserWindow.fromWebContents(e.sender); - const winName = this.getName(target); - this.close(target, this._isReallyClose(winName)); - }; - const handleMinimizeWindow = (e) => { - electron.BrowserWindow.fromWebContents(e.sender)?.minimize(); - }; - const handleMaximizeWindow = (e) => { - this.toggleMax(electron.BrowserWindow.fromWebContents(e.sender)); - }; - const handleIsWindowMaximized = (e) => { - return electron.BrowserWindow.fromWebContents(e.sender)?.isMaximized() ?? false; - }; - electron.ipcMain.on(IPC_EVENTS.WINDOW_CLOSE, handleCloseWindow); - electron.ipcMain.on(IPC_EVENTS.WINDOW_MINIMIZE, handleMinimizeWindow); - electron.ipcMain.on(IPC_EVENTS.WINDOW_MAXIMIZE, handleMaximizeWindow); - electron.ipcMain.handle(IPC_EVENTS.IS_WINDOW_MAXIMIZED, handleIsWindowMaximized); - 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) { - const updateWinStatus = debounce(() => !window2?.isDestroyed() && window2?.webContents?.send(IPC_EVENTS.WINDOW_MAXIMIZE + "back", window2?.isMaximized()), 80); - window2.once("closed", () => { - this._winStates[name].onClosed.forEach((callback) => callback(window2)); - window2?.destroy(); - window2?.removeListener("resize", updateWinStatus); - this._winStates[name].instance = void 0; - this._winStates[name].isHidden = false; - logManager.info(`Window closed: ${name}`); - }); - window2.on("resize", updateWinStatus); - 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() - }; - } -} -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); - const tabManager = new TabManager(mainWindow); - tabManager.enable(); - mainWindow.on("closed", () => { - tabManager.destroy(); - }); - }); - windowManager.create(WINDOW_NAMES.MAIN, MAIN_WIN_SIZE); - electron.ipcMain.on(IPC_EVENTS.START_A_DIALOGUE, async (_event, props) => { - const { providerName, messages: messages2, messageId, selectedModel } = props; - const mainWindow = windowManager.get(WINDOW_NAMES.MAIN); - if (!mainWindow) { - throw new Error("mainWindow not found"); - } - try { - const provider = createProvider(providerName); - const chunks = await provider?.chat(messages2, selectedModel); - if (!chunks) { - throw new Error("chunks or stream not found"); - } - for await (const chunk of chunks) { - const chunkContent = { - messageId, - data: chunk - }; - mainWindow.webContents.send(IPC_EVENTS.START_A_DIALOGUE + "back" + messageId, chunkContent); - } - } catch (error) { - const errorContent = { - messageId, - data: { - isEnd: true, - isError: true, - result: error instanceof Error ? error.message : String(error) - } - }; - mainWindow.webContents.send(IPC_EVENTS.START_A_DIALOGUE + "back" + messageId, errorContent); - } - }); -} -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 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(); - 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(); -appUpdater.init(); -if (started) { - electron.app.quit(); -} -electron.app.whenReady().then(() => { - setupMainWindow(); - 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('bytenode') +require('./main.jsc') \ No newline at end of file diff --git a/dist-electron/main/main.js.bak b/dist-electron/main/main.js.bak index dde931f..6995c38 100644 --- a/dist-electron/main/main.js.bak +++ b/dist-electron/main/main.js.bak @@ -1 +1 @@ -"use strict";const o=require("electron"),oe=require("openai"),G=require("util"),h=require("electron-log"),W=require("path"),Q=require("fs"),V=require("js-base64"),O=require("node:path"),re=require("crypto"),ae=require("electron-squirrel-startup"),ce=require("net"),le=require("http"),de=require("child_process"),ue=require("events");require("bytenode");function ee(n){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(n){for(const t in n)if(t!=="default"){const i=Object.getOwnPropertyDescriptor(n,t);Object.defineProperty(e,t,i.get?i:{enumerable:!0,get:()=>n[t]})}}return e.default=n,Object.freeze(e)}const S=ee(W),v=ee(Q);var a=(n=>(n.EXTERNAL_OPEN="external-open",n.WINDOW_MINIMIZE="window-minimize",n.WINDOW_MAXIMIZE="window-maximize",n.WINDOW_CLOSE="window-close",n.IS_WINDOW_MAXIMIZED="is-window-maximized",n.APP_SET_FRAMELESS="app:set-frameless",n.APP_LOAD_PAGE="app:load-page",n.TAB_CREATE="tab:create",n.TAB_LIST="tab:list",n.TAB_NAVIGATE="tab:navigate",n.TAB_RELOAD="tab:reload",n.TAB_BACK="tab:back",n.TAB_FORWARD="tab:forward",n.TAB_SWITCH="tab:switch",n.TAB_CLOSE="tab:close",n.LOG_TO_MAIN="log-to-main",n.READ_FILE="read-file",n.INVOKE="ipc:invoke",n.INVOKE_ASYNC="ipc:invokeAsync",n.APP_MINIMIZE="app:minimize",n.APP_MAXIMIZE="app:maximize",n.APP_QUIT="app:quit",n.FILE_READ="file:read",n.FILE_WRITE="file:write",n.GET_WINDOW_ID="get-window-id",n.CUSTOM_EVENT="custom:event",n.TIME_UPDATE="time:update",n.RENDERER_IS_READY="renderer-ready",n.SHOW_CONTEXT_MENU="show-context-menu",n.START_A_DIALOGUE="start-a-dialogue",n.OPEN_WINDOW="open-window",n.LOG_DEBUG="log-debug",n.LOG_INFO="log-info",n.LOG_WARN="log-warn",n.LOG_ERROR="log-error",n.CONFIG_UPDATED="config-updated",n.SET_CONFIG="set-config",n.GET_CONFIG="get-config",n.UPDATE_CONFIG="update-config",n.SET_THEME_MODE="set-theme-mode",n.GET_THEME_MODE="get-theme-mode",n.IS_DARK_THEME="is-dark-theme",n.THEME_MODE_UPDATED="theme-mode-updated",n.EXECUTE_SCRIPT="execute-script",n.OPEN_CHANNEL="open-channel",n))(a||{});const te={width:1440,height:900,minWidth:1440,minHeight:900};var w=(n=>(n.MAIN="main",n.SETTING="setting",n.DIALOG="dialog",n.LOADING="loading",n))(w||{}),g=(n=>(n.THEME_MODE="themeMode",n.PRIMARY_COLOR="primaryColor",n.LANGUAGE="language",n.FONT_SIZE="fontSize",n.MINIMIZE_TO_TRAY="minimizeToTray",n.PROVIDER="provider",n.DEFAULT_MODEL="defaultModel",n))(g||{}),y=(n=>(n.CONVERSATION_ITEM="conversation-item",n.CONVERSATION_LIST="conversation-list",n.MESSAGE_ITEM="message-item",n))(y||{}),E=(n=>(n.PIN="pin",n.RENAME="rename",n.DEL="del",n))(E||{}),m=(n=>(n.NEW_CONVERSATION="newConversation",n.SORT_BY="sortBy",n.SORT_BY_CREATE_TIME="sortByCreateTime",n.SORT_BY_UPDATE_TIME="sortByUpdateTime",n.SORT_BY_NAME="sortByName",n.SORT_BY_MODEL="sortByModel",n.SORT_ASCENDING="sortAscending",n.SORT_DESCENDING="sortDescending",n.BATCH_OPERATIONS="batchOperations",n))(m||{}),I=(n=>(n.COPY="copy",n.DELETE="delete",n.SELECT="select",n))(I||{});class he{}const pe=G.promisify(v.readdir),ge=G.promisify(v.stat),fe=G.promisify(v.unlink);class U{static _instance;LOG_RETENTION_DAYS=7;CLEANUP_INTERVAL_MS=1440*60*1e3;constructor(){const e=S.join(o.app.getPath("userData"),"logs");try{v.existsSync(e)||v.mkdirSync(e,{recursive:!0})}catch(t){this.error("Failed to create log directory:",t)}h.transports.file.resolvePathFn=()=>{const t=new Date,i=`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")}`;return S.join(e,`${i}.log`)},h.transports.file.format="[{y}-{m}-{d} {h}:{i}:{s}.{ms}] [{level}] {text}",h.transports.file.maxSize=10*1024*1024,h.transports.console.level=process.env.NODE_ENV==="development"?"debug":"info",h.transports.file.level="debug",this._setupIpcEvents(),this._rewriteConsole(),this.info("LogService initialized successfully."),this._cleanupOldLogs(),setInterval(()=>this._cleanupOldLogs(),this.CLEANUP_INTERVAL_MS)}_setupIpcEvents(){o.ipcMain.on(a.LOG_DEBUG,(e,t,...i)=>this.debug(t,...i)),o.ipcMain.on(a.LOG_INFO,(e,t,...i)=>this.info(t,...i)),o.ipcMain.on(a.LOG_WARN,(e,t,...i)=>this.warn(t,...i)),o.ipcMain.on(a.LOG_ERROR,(e,t,...i)=>this.error(t,...i))}_rewriteConsole(){console.debug=h.debug,console.log=h.info,console.info=h.info,console.warn=h.warn,console.error=h.error}async _cleanupOldLogs(){try{const e=S.join(o.app.getPath("userData"),"logs");if(!v.existsSync(e))return;const t=new Date,i=new Date(t.getTime()-this.LOG_RETENTION_DAYS*24*60*60*1e3),s=await pe(e);let r=0;for(const c of s){if(!c.endsWith(".log"))continue;const d=S.join(e,c);try{const f=await ge(d);f.isFile()&&f.birthtime0&&this.info(`Successfully cleaned up ${r} old log files.`)}catch(e){this.error("Failed to cleanup old logs:",e)}}static getInstance(){return this._instance||(this._instance=new U),this._instance}debug(e,...t){h.debug(e,...t)}info(e,...t){h.info(e,...t)}warn(e,...t){h.warn(e,...t)}error(e,...t){h.error(e,...t)}logApiRequest(e,t={},i="POST"){this.info(`API Request: ${e}, Method: ${i}, Request: ${JSON.stringify(t)}`)}logApiResponse(e,t={},i=200,s=0){i>=400?this.error(`API Error Response: ${e}, Status: ${i}, Response Time: ${s}ms, Response: ${JSON.stringify(t)}`):this.debug(`API Response: ${e}, Status: ${i}, Response Time: ${s}ms, Response: ${JSON.stringify(t)}`)}logUserOperation(e,t="unknown",i={}){this.info(`User Operation: ${e} by ${t}, Details: ${JSON.stringify(i)}`)}}const u=U.getInstance();function me(n){const e=n.choices[0];return{isEnd:e?.finish_reason==="stop",result:e?.delta?.content??""}}class _e extends he{client;constructor(e,t){super(),this.client=new oe({apiKey:e,baseURL:t})}async chat(e,t){const i=Date.now(),s=e[e.length-1];u.logApiRequest("chat.completions.create",{model:t,lastMessage:s?.content?.substring(0,100)+(s?.content?.length>100?"...":""),messageCount:e.length},"POST");try{const r=await this.client.chat.completions.create({model:t,messages:e,stream:!0}),c=Date.now()-i;return u.logApiResponse("chat.completions.create",{success:!0},200,c),{async*[Symbol.asyncIterator](){for await(const d of r)yield me(d)}}}catch(r){const c=Date.now()-i;throw u.logApiResponse("chat.completions.create",{error:r instanceof Error?r.message:String(r)},500,c),r}}}function ne(n,e){let t=null;return function(...i){t&&clearTimeout(t),t=setTimeout(()=>{n.apply(this,i)},e)}}function H(n){if(n===null||typeof n!="object")return n;if(Array.isArray(n))return n.map(t=>H(t));const e=Object.assign({},n);for(const t in e)Object.prototype.hasOwnProperty.call(e,t)&&(e[t]=H(e[t]));return e}function we(n){try{return JSON.parse(JSON.stringify(n))}catch(e){return console.error("simpleCloneDeep failed:",e),n}}function Ae(n){try{return JSON.parse(V.decode(n))}catch(e){return console.error("parseOpenAISetting failed:",e),{}}}const Te={[g.THEME_MODE]:"system",[g.PRIMARY_COLOR]:"#BB5BE7",[g.LANGUAGE]:"zh",[g.FONT_SIZE]:14,[g.MINIMIZE_TO_TRAY]:!1,[g.PROVIDER]:"",[g.DEFAULT_MODEL]:null};class z{static _instance;_config;_configPath;_defaultConfig=Te;_listeners=[];constructor(){this._configPath=S.join(o.app.getPath("userData"),"config.json"),this._config=this._loadConfig(),this._setupIpcEvents(),u.info("ConfigService initialized successfully.")}_setupIpcEvents(){const t=ne(i=>this.update(i),200);o.ipcMain.handle(a.GET_CONFIG,(i,s)=>this.get(s)),o.ipcMain.on(a.SET_CONFIG,(i,s,r)=>this.set(s,r)),o.ipcMain.on(a.UPDATE_CONFIG,(i,s)=>t(s))}static getInstance(){return this._instance||(this._instance=new z),this._instance}_loadConfig(){try{if(v.existsSync(this._configPath)){const e=v.readFileSync(this._configPath,"utf-8"),t={...this._defaultConfig,...JSON.parse(e)};return u.info("Config loaded successfully from:",this._configPath),t}}catch(e){u.error("Failed to load config:",e)}return{...this._defaultConfig}}_saveConfig(){try{v.mkdirSync(S.dirname(this._configPath),{recursive:!0}),v.writeFileSync(this._configPath,JSON.stringify(this._config,null,2),"utf-8"),this._notifyListeners(),u.info("Config saved successfully to:",this._configPath)}catch(e){u.error("Failed to save config:",e)}}_notifyListeners(){o.BrowserWindow.getAllWindows().forEach(e=>e.webContents.send(a.CONFIG_UPDATED,this._config)),this._listeners.forEach(e=>e({...this._config}))}getConfig(){return we(this._config)}get(e){return this._config[e]}set(e,t,i=!0){!(e in this._config)||this._config[e]===t||(this._config[e]=t,u.debug(`Config set: ${e} = ${t}`),i&&this._saveConfig())}update(e,t=!0){this._config={...this._config,...e},t&&this._saveConfig()}resetToDefault(){this._config={...this._defaultConfig},u.info("Config reset to default."),this._saveConfig()}onConfigChange(e){return this._listeners.push(e),()=>this._listeners=this._listeners.filter(t=>t!==e)}}const T=z.getInstance();process.env.BIGMODEL_API_KEY,new Date().getTime(),new Date().getTime(),process.env.DEEPSEEK_API_KEY,new Date().getTime(),new Date().getTime(),process.env.SILICONFLOW_API_KEY,new Date().getTime(),new Date().getTime(),process.env.QIANFAN_API_KEY,new Date().getTime(),new Date().getTime();const ye=()=>{let n=[],e=!1;const t=T.get(g.PROVIDER),i=s=>({...s,openAISetting:typeof s.openAISetting=="string"?Ae(s.openAISetting??""):s.openAISetting});try{n=JSON.parse(V.decode(t)),e=!0}catch(s){u.error(`parse base64 provider failed: ${s}`)}if(!e)try{n=JSON.parse(t)}catch(s){u.error(`parse provider failed: ${s}`)}if(n.length)return n.map(i)},ve=()=>{try{return ye()}catch(n){return u.error(`get provider config failed: ${n}`),null}};function Me(n){const e=ve();if(!e)throw new Error("provider config not found");for(const t of e)if(t.name===n){if(!t.openAISetting?.apiKey||!t.openAISetting?.baseURL)throw new Error("apiKey or baseURL not found");return new _e(t.openAISetting.apiKey,t.openAISetting.baseURL)}}const Ee={minimize:"Minimize",maximize:"Maximize",restore:"Restore",close:"Close"},Ie={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"}},Oe={cancel:"Cancel",confirm:"Confirm"},Ce={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"}},Se={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"}},be={tooltip:"Diona Application",showWindow:"Show Window",exit:"Exit"},De={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"}},Re={title:"Diona Application"},Ne={window:Ee,main:Ie,dialog:Oe,settings:Ce,menu:Se,tray:be,timeAgo:De,app:Re},Le={minimize:"最小化",maximize:"最大化",restore:"还原",close:"关闭"},We={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:"发送"}},Be={cancel:"取消",confirm:"确认"},ke={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:"语言设置"}},xe={conversation:{newConversation:"新建对话",sortBy:"排序方式",sortByCreateTime:"按创建时间排序",sortByUpdateTime:"按更新时间排序",sortByName:"按名称排序",sortByModel:"按模型排序",sortAscending:"递增",sortDescending:"递减",pinConversation:"置顶对话",unpinConversation:"取消置顶",renameConversation:"重命名对话",delConversation:"删除对话",batchOperations:"批量操作"},message:{copyMessage:"复制消息",deleteMessage:"删除消息",selectMessage:"选择消息"}},Pe={tooltip:"迪奥娜",showWindow:"显示窗口",exit:"退出"},$e={justNow:"刚刚",minutes:"{count}分钟前",hours:"{count}小时前",days:"{count}天前",months:"{count}个月前",years:"{count}年前",weekday:{sun:"星期日",mon:"星期一",tue:"星期二",wed:"星期三",thu:"星期四",fri:"星期五",sat:"星期六"}},He={title:"迪奥娜"},Ge={window:Le,main:We,dialog:Be,settings:ke,menu:xe,tray:Pe,timeAgo:$e,app:He},Ue={en:Ne,zh:Ge};function k(){return n=>{if(n)try{const e=n?.split(".");let t=Ue[T.get(g.LANGUAGE)];for(const i of e)t=t[i];return t}catch(e){return u.error("failed to translate key:",n,e),n}}}let B;function ie(){if(B!=null)return B;const n=o.app.getAppPath();return B=O.join(n,"resources","icons","icon.ico"),B}class F{static _instance;_isDark=o.nativeTheme.shouldUseDarkColors;constructor(){const e=T.get(g.THEME_MODE);e&&(o.nativeTheme.themeSource=e,this._isDark=o.nativeTheme.shouldUseDarkColors),this._setupIpcEvent(),u.info("ThemeService initialized successfully.")}_setupIpcEvent(){o.ipcMain.handle(a.SET_THEME_MODE,(e,t)=>(o.nativeTheme.themeSource=t,T.set(g.THEME_MODE,t),o.nativeTheme.shouldUseDarkColors)),o.ipcMain.handle(a.GET_THEME_MODE,()=>o.nativeTheme.themeSource),o.ipcMain.handle(a.IS_DARK_THEME,()=>o.nativeTheme.shouldUseDarkColors),o.nativeTheme.on("updated",()=>{this._isDark=o.nativeTheme.shouldUseDarkColors,o.BrowserWindow.getAllWindows().forEach(e=>e.webContents.send(a.THEME_MODE_UPDATED,this._isDark))})}static getInstance(){return this._instance||(this._instance=new F),this._instance}get isDark(){return this._isDark}get themeMode(){return o.nativeTheme.themeSource}}const X=F.getInstance(),ze={frame:!1,titleBarStyle:"hidden",trafficLightPosition:{x:-100,y:-100},show:!1,title:"NIANXX",darkTheme:X.isDark,backgroundColor:X.isDark?"#2C2C2C":"#FFFFFF",webPreferences:{nodeIntegration:!1,contextIsolation:!0,sandbox:!0,backgroundThrottling:!1,preload:MAIN_WINDOW_VITE_DEV_SERVER_URL?O.join(process.cwd(),"dist-electron/preload/preload.js"):O.join(__dirname,"preload.js")}};class j{static _instance;_logo=ie();isDev=!!MAIN_WINDOW_VITE_DEV_SERVER_URL;_winStates={main:{instance:void 0,isHidden:!1,onCreate:[],onClosed:[]},setting:{instance:void 0,isHidden:!1,onCreate:[],onClosed:[]},dialog:{instance:void 0,isHidden:!1,onCreate:[],onClosed:[]},login:{instance:void 0,isHidden:!1,onCreate:[],onClosed:[]},loading:{instance:void 0,isHidden:!1,onCreate:[],onClosed:[]}};constructor(){this._setupIpcEvents(),u.info("WindowService initialized successfully.")}_isReallyClose(e){return e===w.MAIN?T.get(g.MINIMIZE_TO_TRAY)===!1:e!==w.SETTING}_setupIpcEvents(){const e=r=>{const c=o.BrowserWindow.fromWebContents(r.sender),d=this.getName(c);this.close(c,this._isReallyClose(d))},t=r=>{o.BrowserWindow.fromWebContents(r.sender)?.minimize()},i=r=>{this.toggleMax(o.BrowserWindow.fromWebContents(r.sender))},s=r=>o.BrowserWindow.fromWebContents(r.sender)?.isMaximized()??!1;o.ipcMain.on(a.WINDOW_CLOSE,e),o.ipcMain.on(a.WINDOW_MINIMIZE,t),o.ipcMain.on(a.WINDOW_MAXIMIZE,i),o.ipcMain.handle(a.IS_WINDOW_MAXIMIZED,s),o.ipcMain.handle(a.APP_LOAD_PAGE,(r,c)=>{const d=o.BrowserWindow.fromWebContents(r.sender);d&&this._loadPage(d,c)})}static getInstance(){return this._instance||(this._instance=new j),this._instance}create(e,t,i){if(this.get(e))return;const s=this._isHiddenWin(e);let r=this._createWinInstance(e,{...t,...i});return this.isDev&&r.webContents.openDevTools(),!s&&this._setupWinLifecycle(r,e)._loadWindowTemplate(r,e),this._listenWinReady({win:r,isHiddenWin:s,size:t}),s||(this._winStates[e].instance=r,this._winStates[e].onCreate.forEach(c=>c(r))),s&&(this._winStates[e].isHidden=!1,u.info(`Hidden window show: ${e}`)),r}_setupWinLifecycle(e,t){const i=ne(()=>!e?.isDestroyed()&&e?.webContents?.send(a.WINDOW_MAXIMIZE+"back",e?.isMaximized()),80);return e.once("closed",()=>{this._winStates[t].onClosed.forEach(s=>s(e)),e?.destroy(),e?.removeListener("resize",i),this._winStates[t].instance=void 0,this._winStates[t].isHidden=!1,u.info(`Window closed: ${t}`)}),e.on("resize",i),this}_listenWinReady(e){const t=()=>{e.win?.once("show",()=>setTimeout(()=>this._applySizeConstraints(e.win,e.size),2)),e.win?.show()};e.isHiddenWin?t():this._addLoadingView(e.win,e.size)?.(t)}_addLoadingView(e,t){let i=!1;const s=r=>{r.sender!==e?.webContents||i||(i=!0,o.ipcMain.removeListener(a.RENDERER_IS_READY,s))};return o.ipcMain.on(a.RENDERER_IS_READY,s),r=>{r()}}_applySizeConstraints(e,t){t.maxHeight&&t.maxWidth&&e.setMaximumSize(t.maxWidth,t.maxHeight),t.minHeight&&t.minWidth&&e.setMinimumSize(t.minWidth,t.minHeight)}_loadPage(e,t){if(MAIN_WINDOW_VITE_DEV_SERVER_URL)return e.loadURL(`${MAIN_WINDOW_VITE_DEV_SERVER_URL}/${t}.html`);e.loadFile(O.join(app.getAppPath(),"dist",`${t}.html`))}_loadWindowTemplate(e,t){this._loadPage(e,"index")}_handleCloseWindowState(e,t){const i=this.getName(e);i&&(t?this._winStates[i].instance=void 0:this._winStates[i].isHidden=!0),setTimeout(()=>{e[t?"close":"hide"]?.(),this._checkAndCloseAllWinodws()},210)}_checkAndCloseAllWinodws(){if(!this._winStates[w.MAIN].instance||this._winStates[w.MAIN].instance?.isDestroyed())return Object.values(this._winStates).forEach(t=>t?.instance?.close());if(!T.get(g.MINIMIZE_TO_TRAY)&&!this.get(w.MAIN)?.isVisible())return Object.values(this._winStates).forEach(t=>!t?.instance?.isVisible()&&t?.instance?.close())}_isHiddenWin(e){return this._winStates[e]&&this._winStates[e].isHidden}_createWinInstance(e,t){return this._isHiddenWin(e)?this._winStates[e].instance:new o.BrowserWindow({...ze,icon:this._logo,...t})}focus(e){if(!e)return;const t=this.getName(e);e?.isMaximized()?(e?.restore(),u.debug(`Window ${t} restored and focused`)):u.debug(`Window ${t} focused`),e?.focus()}close(e,t=!0){if(!e)return;const i=this.getName(e);u.info(`Close window: ${i}, really: ${t}`),this._handleCloseWindowState(e,t)}toggleMax(e){e&&(e.isMaximized()?e.unmaximize():e.maximize())}getName(e){if(e){for(const[t,i]of Object.entries(this._winStates))if(i?.instance===e)return t}}get(e){if(!this._winStates[e].isHidden)return this._winStates[e].instance}onWindowCreate(e,t){this._winStates[e].onCreate.push(t)}onWindowClosed(e,t){this._winStates[e].onClosed.push(t)}}const L=j.getInstance();let x=k();class Y{static _instance;_menuTemplates=new Map;_currentMenu=void 0;constructor(){this._setupIpcListener(),this._setupLanguageChangeListener(),u.info("MenuService initialized successfully.")}_setupIpcListener(){o.ipcMain.handle(a.SHOW_CONTEXT_MENU,(e,t,i)=>new Promise(s=>this.showMenu(t,()=>s(!0),i)))}_setupLanguageChangeListener(){T.onConfigChange(e=>{e[g.LANGUAGE]&&(x=k())})}static getInstance(){return this._instance||(this._instance=new Y),this._instance}register(e,t){return this._menuTemplates.set(e,t),e}showMenu(e,t,i){if(this._currentMenu)return;const s=H(this._menuTemplates.get(e));if(!s){u.warn(`Menu ${e} not found.`),t?.();return}let r=[];try{r=Array.isArray(i)?i:JSON.parse(i??"[]")}catch(l){u.error(`Failed to parse dynamicOptions for menu ${e}: ${l}`)}const c=l=>l.submenu?{...l,label:x(l?.label)??void 0,submenu:l.submenu?.map(p=>c(p))}:{...l,label:x(l?.label)??void 0},d=s.map(l=>{if(!Array.isArray(r)||!r.length)return c(l);const p=r.find(_=>_.id===l.id);if(p){const _={...l,...p};return c(_)}return l.submenu?c({...l,submenu:l.submenu?.map(_=>{const b=r.find(A=>A.id===_.id);return{..._,...b}})}):c(l)}),f=o.Menu.buildFromTemplate(d);this._currentMenu=f,f.popup({callback:()=>{this._currentMenu=void 0,t?.()}})}destroyMenu(e){this._menuTemplates.delete(e)}destroyed(){this._menuTemplates.clear(),this._currentMenu=void 0}}const P=Y.getInstance();let N=k();class q{static _instance;_tray=null;_removeLanguageListener;_setupLanguageChangeListener(){this._removeLanguageListener=T.onConfigChange(e=>{e[g.LANGUAGE]&&(N=k(),this._tray&&this._updateTray())})}_updateTray(){this._tray||(this._tray=new o.Tray(ie()));const e=()=>{const t=L.get(w.MAIN);if(t&&!t?.isDestroyed()&&t?.isVisible()&&!t?.isFocused())return t.focus();if(t?.isMinimized())return t?.restore();t?.isVisible()&&t?.isFocused()||L.create(w.MAIN,te)};this._tray.setToolTip(N("tray.tooltip")??"Diona Application"),this._tray.setContextMenu(o.Menu.buildFromTemplate([{label:N("tray.showWindow"),accelerator:"CmdOrCtrl+N",click:e},{type:"separator"},{label:N("settings.title"),click:()=>o.ipcMain.emit(`${a.OPEN_WINDOW}:${w.SETTING}`)},{role:"quit",label:N("tray.exit")}])),this._tray.removeAllListeners("click"),this._tray.on("click",e)}constructor(){this._setupLanguageChangeListener(),u.info("TrayService initialized successfully.")}static getInstance(){return this._instance||(this._instance=new q),this._instance}create(){this._tray||(this._updateTray(),o.app.on("quit",()=>{this.destroy()}))}destroy(){this._tray?.destroy(),this._tray=null,this._removeLanguageListener&&(this._removeLanguageListener(),this._removeLanguageListener=void 0)}}const Z=q.getInstance();class Fe{win;views=new Map;activeId=null;skipNextNavigate=new Map;enabled=!1;constructor(e){this.win=e,this.win.on("resize",()=>this.updateActiveBounds()),this._setupIpcEvents()}_setupIpcEvents(){o.ipcMain.handle(a.TAB_CREATE,(e,t)=>this.create(t)),o.ipcMain.handle(a.TAB_LIST,()=>this.list()),o.ipcMain.handle(a.TAB_NAVIGATE,(e,{tabId:t,url:i})=>{this.navigate(t,i)}),o.ipcMain.handle(a.TAB_RELOAD,(e,t)=>{this.reload(t)}),o.ipcMain.handle(a.TAB_BACK,(e,t)=>{this.goBack(t)}),o.ipcMain.handle(a.TAB_FORWARD,(e,t)=>{this.goForward(t)}),o.ipcMain.handle(a.TAB_SWITCH,(e,t)=>{this.switch(t)}),o.ipcMain.handle(a.TAB_CLOSE,(e,t)=>{this.close(t)})}enable(){this.enabled=!0,this.updateActiveBounds(),this.activeId&&this.attach(this.activeId)}disable(){this.enabled=!1;const e=this.activeId?this.views.get(this.activeId):null;e&&this.win.removeBrowserView(e)}destroy(){this.disable(),this.views.forEach(e=>{e.webContents.destroy()}),this.views.clear(),o.ipcMain.removeHandler(a.TAB_CREATE),o.ipcMain.removeHandler(a.TAB_LIST),o.ipcMain.removeHandler(a.TAB_NAVIGATE),o.ipcMain.removeHandler(a.TAB_RELOAD),o.ipcMain.removeHandler(a.TAB_BACK),o.ipcMain.removeHandler(a.TAB_FORWARD),o.ipcMain.removeHandler(a.TAB_SWITCH),o.ipcMain.removeHandler(a.TAB_CLOSE)}list(){return Array.from(this.views.entries()).map(([e,t])=>this.info(e,t))}create(e,t=!0){const i=re.randomUUID(),s=new o.BrowserView({webPreferences:{nodeIntegration:!1,contextIsolation:!0,sandbox:!0,preload:MAIN_WINDOW_VITE_DEV_SERVER_URL?O.join(process.cwd(),"dist-electron/preload/preload.js"):O.join(__dirname,"preload.js")}});this.views.set(i,s),this.enabled&&t&&this.attach(i);const r=e&&e.length>0?e:"about:blank";s.webContents.loadURL(r),this.bindEvents(i,s);const c=this.info(i,s);return this.win.webContents.send("tab-created",c),c}switch(e){this.views.has(e)&&(this.enabled&&this.attach(e),this.win.webContents.send("tab-switched",{tabId:e}))}close(e){const t=this.views.get(e);if(!t)return;this.activeId===e&&(this.win.removeBrowserView(t),this.activeId=null),t.webContents.destroy(),this.views.delete(e),this.win.webContents.send("tab-closed",{tabId:e});const i=this.views.keys().next().value;i&&this.switch(i)}navigate(e,t){const i=this.views.get(e);i&&(this.skipNextNavigate.set(e,!0),i.webContents.loadURL(t))}reload(e){const t=this.views.get(e);t&&t.webContents.reload()}goBack(e){const t=this.views.get(e);t&&t.webContents.canGoBack()&&t.webContents.goBack()}goForward(e){const t=this.views.get(e);t&&t.webContents.canGoForward()&&t.webContents.goForward()}attach(e){if(!this.enabled)return;const t=this.views.get(e);if(t){if(this.activeId&&this.views.get(this.activeId)){const i=this.views.get(this.activeId);this.win.removeBrowserView(i)}this.activeId=e,this.win.addBrowserView(t),this.updateActiveBounds()}}updateActiveBounds(){if(!this.enabled||!this.activeId)return;const e=this.views.get(this.activeId);if(!e)return;const[t,i]=this.win.getContentSize(),s=88,r=8,c=488,d=r,f=s+r,l=t-c-r,p=i-s-r*2;e.setBounds({x:d,y:f,width:Math.max(0,l),height:Math.max(0,p)})}bindEvents(e,t){const i=()=>this.win.webContents.send("tab-updated",this.info(e,t));t.webContents.on("did-start-loading",i),t.webContents.on("did-stop-loading",i),t.webContents.on("did-finish-load",i),t.webContents.on("page-title-updated",i),t.webContents.on("did-navigate",i),t.webContents.on("did-navigate-in-page",i),t.webContents.on("will-navigate",(s,r)=>{if(this.skipNextNavigate.get(e)){this.skipNextNavigate.set(e,!1);return}s.preventDefault(),this.create(r)}),t.webContents.setWindowOpenHandler(({url:s})=>(this.create(s),{action:"deny"}))}info(e,t){const i=t.webContents;return{id:e,url:i.getURL(),title:i.getTitle(),isLoading:i.isLoading(),canGoBack:i.canGoBack(),canGoForward:i.canGoForward()}}}const K=n=>{if(n){Z.create();return}Z.destroy()},je=n=>{const e=s=>{u.logUserOperation(`${a.SHOW_CONTEXT_MENU}:${y.CONVERSATION_ITEM}-${s}`),n.webContents.send(`${a.SHOW_CONTEXT_MENU}:${y.CONVERSATION_ITEM}`,s)};P.register(y.CONVERSATION_ITEM,[{id:E.PIN,label:"menu.conversation.pinConversation",click:()=>e(E.PIN)},{id:E.RENAME,label:"menu.conversation.renameConversation",click:()=>e(E.RENAME)},{id:E.DEL,label:"menu.conversation.delConversation",click:()=>e(E.DEL)}]);const t=s=>{u.logUserOperation(`${a.SHOW_CONTEXT_MENU}:${y.CONVERSATION_LIST}-${s}`),n.webContents.send(`${a.SHOW_CONTEXT_MENU}:${y.CONVERSATION_LIST}`,s)};P.register(y.CONVERSATION_LIST,[{id:m.NEW_CONVERSATION,label:"menu.conversation.newConversation",click:()=>t(m.NEW_CONVERSATION)},{type:"separator"},{id:m.SORT_BY,label:"menu.conversation.sortBy",submenu:[{id:m.SORT_BY_CREATE_TIME,label:"menu.conversation.sortByCreateTime",type:"radio",checked:!1,click:()=>t(m.SORT_BY_CREATE_TIME)},{id:m.SORT_BY_UPDATE_TIME,label:"menu.conversation.sortByUpdateTime",type:"radio",checked:!1,click:()=>t(m.SORT_BY_UPDATE_TIME)},{id:m.SORT_BY_NAME,label:"menu.conversation.sortByName",type:"radio",checked:!1,click:()=>t(m.SORT_BY_NAME)},{id:m.SORT_BY_MODEL,label:"menu.conversation.sortByModel",type:"radio",checked:!1,click:()=>t(m.SORT_BY_MODEL)},{type:"separator"},{id:m.SORT_ASCENDING,label:"menu.conversation.sortAscending",type:"radio",checked:!1,click:()=>t(m.SORT_ASCENDING)},{id:m.SORT_DESCENDING,label:"menu.conversation.sortDescending",type:"radio",checked:!1,click:()=>t(m.SORT_DESCENDING)}]},{id:m.BATCH_OPERATIONS,label:"menu.conversation.batchOperations",click:()=>t(m.BATCH_OPERATIONS)}]);const i=s=>{u.logUserOperation(`${a.SHOW_CONTEXT_MENU}:${y.MESSAGE_ITEM}-${s}`),n.webContents.send(`${a.SHOW_CONTEXT_MENU}:${y.MESSAGE_ITEM}`,s)};P.register(y.MESSAGE_ITEM,[{id:I.COPY,label:"menu.message.copyMessage",click:()=>i(I.COPY)},{id:I.SELECT,label:"menu.message.selectMessage",click:()=>i(I.SELECT)},{type:"separator"},{id:I.DELETE,label:"menu.message.deleteMessage",click:()=>i(I.DELETE)}])};function se(){L.onWindowCreate(w.MAIN,n=>{let e=T.get(g.MINIMIZE_TO_TRAY);T.onConfigChange(i=>{e!==i[g.MINIMIZE_TO_TRAY]&&(e=i[g.MINIMIZE_TO_TRAY],K(e))}),K(e),je(n);const t=new Fe(n);t.enable(),n.on("closed",()=>{t.destroy()})}),L.create(w.MAIN,te),o.ipcMain.on(a.START_A_DIALOGUE,async(n,e)=>{const{providerName:t,messages:i,messageId:s,selectedModel:r}=e,c=L.get(w.MAIN);if(!c)throw new Error("mainWindow not found");try{const f=await Me(t)?.chat(i,r);if(!f)throw new Error("chunks or stream not found");for await(const l of f){const p={messageId:s,data:l};c.webContents.send(a.START_A_DIALOGUE+"back"+s,p)}}catch(d){const f={messageId:s,data:{isEnd:!0,isError:!0,result:d instanceof Error?d.message:String(d)}};c.webContents.send(a.START_A_DIALOGUE+"back"+s,f)}})}function Ye(){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 qe(n){return O.join(o.app.getPath("userData"),"profiles",n)}function Xe(n){return new Promise(e=>{const t=ce.createServer();t.once("error",i=>e(!0)),t.once("listening",()=>{t.close(),e(!1)}),t.listen(n)})}async function Ze(){try{return new Promise(n=>{const e=le.get("http://127.0.0.1:9222/json/version",t=>{n(t.statusCode===200)});e.on("error",()=>n(!1)),e.setTimeout(1e3,()=>{e.destroy(),n(!1)})})}catch{return!1}}async function Ke(){const n=Ye(),e=qe("default");if(h.info(`Launching Chrome with user data dir: ${e}`),await Xe(9222)){h.info("Chrome already running on port 9222, skip launching.");return}if(await Ze()){h.info("Chrome already running, skip launching.");return}return new Promise((i,s)=>{de.spawn(n,["--remote-debugging-port=9222","--window-size=1920,1080","--window-position=0,0","--no-first-run",`--user-data-dir=${e}`,"--no-default-browser-check","about:blank"],{detached:!0,stdio:"ignore"}).on("error",s),setTimeout(()=>{i(0)},1e3)})}class Je extends ue.EventEmitter{async executeScript(e,t){const s=(r,c)=>{const d=r+c;return d.length>32768?d.slice(d.length-32768):d};return await new Promise(r=>{try{const c=t?.roomType??"",d=t?.startTime??"",f=t?.endTime??"",l=t?.operation??"",p=t?.tabIndex??"",_=t?.channels??"",b=t?.startTabIndex??"",A=o.utilityProcess.fork(e,[],{env:{...process.env,ROOM_TYPE:String(c),START_DATE:String(d),END_DATE:String(f),OPERATION:String(l),TAB_INDEX:String(p),CHANNELS:typeof _=="string"?_:JSON.stringify(_),START_TAB_INDEX:String(b)},stdio:"pipe"});let C="",D="";A.stdout&&A.stdout.on("data",M=>{const R=M.toString();C=s(C,R),h.info(`stdout: ${R}`)}),A.stderr&&A.stderr.on("data",M=>{const R=M.toString();D=s(D,R),h.info(`stderr: ${R}`)}),A.on("exit",M=>{h.info(`子进程退出,退出码 ${M}`),r({success:M===0,exitCode:M,stdoutTail:C,stderrTail:D,...M===0?{}:{error:`Script exited with code ${M}`}})})}catch(c){r({success:!1,exitCode:null,stdoutTail:"",stderrTail:"",error:c?.message||"运行 Node 脚本时出错"})}})}}const $=new Map;function J(){return o.app.isPackaged?W.join(__dirname,"scripts"):W.join(process.cwd(),"electron/scripts")}function Qe(){const n=new Je;o.ipcMain.handle(a.OPEN_CHANNEL,async(e,t)=>{try{await Ke();const i=J(),s=W.join(i,"open_all_channel.js");if($.clear(),Array.isArray(t))for(let c=0;c{try{const i=t.roomList.find(l=>l.id===t.roomType),r=[["fzName","fg_trace.js"],["mtName","mt_trace.js"],["dyHotelName","dy_hotel_trace.js"],["dyHotSpringName","dy_hot_spring_trace.js"]].filter(([l])=>i?.[l]),c=J(),d=r.map(([l,p])=>{const _=W.join(c,p);if(!Q.existsSync(_))throw new Error(`Script not found for channel ${l}: ${_}`);return{channel:l,scriptPath:_}}),f=[];for(let l=0;l{se(),Qe()});o.app.on("window-all-closed",()=>{process.platform!=="darwin"&&!T.get(g.MINIMIZE_TO_TRAY)&&(h.info("app closing due to all windows being closed"),o.app.quit())});o.app.on("activate",()=>{o.BrowserWindow.getAllWindows().length===0&&se()}); +"use strict";const s=require("electron"),ae=require("openai"),$=require("util"),h=require("electron-log"),U=require("path"),ee=require("fs"),te=require("js-base64"),C=require("node:path"),ce=require("crypto"),le=require("electron-squirrel-startup"),de=require("net"),ue=require("http"),he=require("child_process"),pe=require("events");require("bytenode");const y=require("electron-updater");function ne(n){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(n){for(const t in n)if(t!=="default"){const i=Object.getOwnPropertyDescriptor(n,t);Object.defineProperty(e,t,i.get?i:{enumerable:!0,get:()=>n[t]})}}return e.default=n,Object.freeze(e)}const S=ne(U),v=ne(ee);var r=(n=>(n.EXTERNAL_OPEN="external-open",n.WINDOW_MINIMIZE="window-minimize",n.WINDOW_MAXIMIZE="window-maximize",n.WINDOW_CLOSE="window-close",n.IS_WINDOW_MAXIMIZED="is-window-maximized",n.APP_SET_FRAMELESS="app:set-frameless",n.APP_LOAD_PAGE="app:load-page",n.TAB_CREATE="tab:create",n.TAB_LIST="tab:list",n.TAB_NAVIGATE="tab:navigate",n.TAB_RELOAD="tab:reload",n.TAB_BACK="tab:back",n.TAB_FORWARD="tab:forward",n.TAB_SWITCH="tab:switch",n.TAB_CLOSE="tab:close",n.LOG_TO_MAIN="log-to-main",n.READ_FILE="read-file",n.INVOKE="ipc:invoke",n.INVOKE_ASYNC="ipc:invokeAsync",n.APP_MINIMIZE="app:minimize",n.APP_MAXIMIZE="app:maximize",n.APP_QUIT="app:quit",n.FILE_READ="file:read",n.FILE_WRITE="file:write",n.GET_WINDOW_ID="get-window-id",n.CUSTOM_EVENT="custom:event",n.TIME_UPDATE="time:update",n.RENDERER_IS_READY="renderer-ready",n.SHOW_CONTEXT_MENU="show-context-menu",n.START_A_DIALOGUE="start-a-dialogue",n.OPEN_WINDOW="open-window",n.LOG_DEBUG="log-debug",n.LOG_INFO="log-info",n.LOG_WARN="log-warn",n.LOG_ERROR="log-error",n.CONFIG_UPDATED="config-updated",n.SET_CONFIG="set-config",n.GET_CONFIG="get-config",n.UPDATE_CONFIG="update-config",n.SET_THEME_MODE="set-theme-mode",n.GET_THEME_MODE="get-theme-mode",n.IS_DARK_THEME="is-dark-theme",n.THEME_MODE_UPDATED="theme-mode-updated",n.EXECUTE_SCRIPT="execute-script",n.OPEN_CHANNEL="open-channel",n.UPDATE_CHECK="update:check",n.UPDATE_DOWNLOAD="update:download",n.UPDATE_INSTALL="update:install",n.UPDATE_VERSION="update:version",n.UPDATE_STATUS_CHANGED="update:status-changed",n))(r||{});const ie={width:1440,height:900,minWidth:1440,minHeight:900};var w=(n=>(n.MAIN="main",n.SETTING="setting",n.DIALOG="dialog",n.LOADING="loading",n))(w||{}),g=(n=>(n.THEME_MODE="themeMode",n.PRIMARY_COLOR="primaryColor",n.LANGUAGE="language",n.FONT_SIZE="fontSize",n.MINIMIZE_TO_TRAY="minimizeToTray",n.PROVIDER="provider",n.DEFAULT_MODEL="defaultModel",n.AUTO_CHECK_UPDATE="autoCheckUpdate",n.AUTO_DOWNLOAD_UPDATE="autoDownloadUpdate",n))(g||{}),E=(n=>(n.CONVERSATION_ITEM="conversation-item",n.CONVERSATION_LIST="conversation-list",n.MESSAGE_ITEM="message-item",n))(E||{}),D=(n=>(n.PIN="pin",n.RENAME="rename",n.DEL="del",n))(D||{}),_=(n=>(n.NEW_CONVERSATION="newConversation",n.SORT_BY="sortBy",n.SORT_BY_CREATE_TIME="sortByCreateTime",n.SORT_BY_UPDATE_TIME="sortByUpdateTime",n.SORT_BY_NAME="sortByName",n.SORT_BY_MODEL="sortByModel",n.SORT_ASCENDING="sortAscending",n.SORT_DESCENDING="sortDescending",n.BATCH_OPERATIONS="batchOperations",n))(_||{}),O=(n=>(n.COPY="copy",n.DELETE="delete",n.SELECT="select",n))(O||{});class ge{}const fe=$.promisify(v.readdir),_e=$.promisify(v.stat),me=$.promisify(v.unlink);class z{static _instance;LOG_RETENTION_DAYS=7;CLEANUP_INTERVAL_MS=1440*60*1e3;constructor(){const e=S.join(s.app.getPath("userData"),"logs");try{v.existsSync(e)||v.mkdirSync(e,{recursive:!0})}catch(t){this.error("Failed to create log directory:",t)}h.transports.file.resolvePathFn=()=>{const t=new Date,i=`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")}`;return S.join(e,`${i}.log`)},h.transports.file.format="[{y}-{m}-{d} {h}:{i}:{s}.{ms}] [{level}] {text}",h.transports.file.maxSize=10*1024*1024,h.transports.console.level=process.env.NODE_ENV==="development"?"debug":"info",h.transports.file.level="debug",this._setupIpcEvents(),this._rewriteConsole(),this.info("LogService initialized successfully."),this._cleanupOldLogs(),setInterval(()=>this._cleanupOldLogs(),this.CLEANUP_INTERVAL_MS)}_setupIpcEvents(){s.ipcMain.on(r.LOG_DEBUG,(e,t,...i)=>this.debug(t,...i)),s.ipcMain.on(r.LOG_INFO,(e,t,...i)=>this.info(t,...i)),s.ipcMain.on(r.LOG_WARN,(e,t,...i)=>this.warn(t,...i)),s.ipcMain.on(r.LOG_ERROR,(e,t,...i)=>this.error(t,...i))}_rewriteConsole(){console.debug=h.debug,console.log=h.info,console.info=h.info,console.warn=h.warn,console.error=h.error}async _cleanupOldLogs(){try{const e=S.join(s.app.getPath("userData"),"logs");if(!v.existsSync(e))return;const t=new Date,i=new Date(t.getTime()-this.LOG_RETENTION_DAYS*24*60*60*1e3),o=await fe(e);let a=0;for(const c of o){if(!c.endsWith(".log"))continue;const d=S.join(e,c);try{const f=await _e(d);f.isFile()&&f.birthtime0&&this.info(`Successfully cleaned up ${a} old log files.`)}catch(e){this.error("Failed to cleanup old logs:",e)}}static getInstance(){return this._instance||(this._instance=new z),this._instance}debug(e,...t){h.debug(e,...t)}info(e,...t){h.info(e,...t)}warn(e,...t){h.warn(e,...t)}error(e,...t){h.error(e,...t)}logApiRequest(e,t={},i="POST"){this.info(`API Request: ${e}, Method: ${i}, Request: ${JSON.stringify(t)}`)}logApiResponse(e,t={},i=200,o=0){i>=400?this.error(`API Error Response: ${e}, Status: ${i}, Response Time: ${o}ms, Response: ${JSON.stringify(t)}`):this.debug(`API Response: ${e}, Status: ${i}, Response Time: ${o}ms, Response: ${JSON.stringify(t)}`)}logUserOperation(e,t="unknown",i={}){this.info(`User Operation: ${e} by ${t}, Details: ${JSON.stringify(i)}`)}}const u=z.getInstance();function we(n){const e=n.choices[0];return{isEnd:e?.finish_reason==="stop",result:e?.delta?.content??""}}class Ae extends ge{client;constructor(e,t){super(),this.client=new ae({apiKey:e,baseURL:t})}async chat(e,t){const i=Date.now(),o=e[e.length-1];u.logApiRequest("chat.completions.create",{model:t,lastMessage:o?.content?.substring(0,100)+(o?.content?.length>100?"...":""),messageCount:e.length},"POST");try{const a=await this.client.chat.completions.create({model:t,messages:e,stream:!0}),c=Date.now()-i;return u.logApiResponse("chat.completions.create",{success:!0},200,c),{async*[Symbol.asyncIterator](){for await(const d of a)yield we(d)}}}catch(a){const c=Date.now()-i;throw u.logApiResponse("chat.completions.create",{error:a instanceof Error?a.message:String(a)},500,c),a}}}function se(n,e){let t=null;return function(...i){t&&clearTimeout(t),t=setTimeout(()=>{n.apply(this,i)},e)}}function G(n){if(n===null||typeof n!="object")return n;if(Array.isArray(n))return n.map(t=>G(t));const e=Object.assign({},n);for(const t in e)Object.prototype.hasOwnProperty.call(e,t)&&(e[t]=G(e[t]));return e}function Te(n){try{return JSON.parse(JSON.stringify(n))}catch(e){return console.error("simpleCloneDeep failed:",e),n}}function ye(n){try{return JSON.parse(te.decode(n))}catch(e){return console.error("parseOpenAISetting failed:",e),{}}}const Ee={[g.THEME_MODE]:"system",[g.PRIMARY_COLOR]:"#BB5BE7",[g.LANGUAGE]:"zh",[g.FONT_SIZE]:14,[g.MINIMIZE_TO_TRAY]:!1,[g.PROVIDER]:"",[g.DEFAULT_MODEL]:null};class F{static _instance;_config;_configPath;_defaultConfig=Ee;_listeners=[];constructor(){this._configPath=S.join(s.app.getPath("userData"),"config.json"),this._config=this._loadConfig(),this._setupIpcEvents(),u.info("ConfigService initialized successfully.")}_setupIpcEvents(){const t=se(i=>this.update(i),200);s.ipcMain.handle(r.GET_CONFIG,(i,o)=>this.get(o)),s.ipcMain.on(r.SET_CONFIG,(i,o,a)=>this.set(o,a)),s.ipcMain.on(r.UPDATE_CONFIG,(i,o)=>t(o))}static getInstance(){return this._instance||(this._instance=new F),this._instance}_loadConfig(){try{if(v.existsSync(this._configPath)){const e=v.readFileSync(this._configPath,"utf-8"),t={...this._defaultConfig,...JSON.parse(e)};return u.info("Config loaded successfully from:",this._configPath),t}}catch(e){u.error("Failed to load config:",e)}return{...this._defaultConfig}}_saveConfig(){try{v.mkdirSync(S.dirname(this._configPath),{recursive:!0}),v.writeFileSync(this._configPath,JSON.stringify(this._config,null,2),"utf-8"),this._notifyListeners(),u.info("Config saved successfully to:",this._configPath)}catch(e){u.error("Failed to save config:",e)}}_notifyListeners(){s.BrowserWindow.getAllWindows().forEach(e=>e.webContents.send(r.CONFIG_UPDATED,this._config)),this._listeners.forEach(e=>e({...this._config}))}getConfig(){return Te(this._config)}get(e){return this._config[e]}set(e,t,i=!0){!(e in this._config)||this._config[e]===t||(this._config[e]=t,u.debug(`Config set: ${e} = ${t}`),i&&this._saveConfig())}update(e,t=!0){this._config={...this._config,...e},t&&this._saveConfig()}resetToDefault(){this._config={...this._defaultConfig},u.info("Config reset to default."),this._saveConfig()}onConfigChange(e){return this._listeners.push(e),()=>this._listeners=this._listeners.filter(t=>t!==e)}}const T=F.getInstance();process.env.BIGMODEL_API_KEY,new Date().getTime(),new Date().getTime(),process.env.DEEPSEEK_API_KEY,new Date().getTime(),new Date().getTime(),process.env.SILICONFLOW_API_KEY,new Date().getTime(),new Date().getTime(),process.env.QIANFAN_API_KEY,new Date().getTime(),new Date().getTime();const ve=()=>{let n=[],e=!1;const t=T.get(g.PROVIDER),i=o=>({...o,openAISetting:typeof o.openAISetting=="string"?ye(o.openAISetting??""):o.openAISetting});try{n=JSON.parse(te.decode(t)),e=!0}catch(o){u.error(`parse base64 provider failed: ${o}`)}if(!e)try{n=JSON.parse(t)}catch(o){u.error(`parse provider failed: ${o}`)}if(n.length)return n.map(i)},Me=()=>{try{return ve()}catch(n){return u.error(`get provider config failed: ${n}`),null}};function De(n){const e=Me();if(!e)throw new Error("provider config not found");for(const t of e)if(t.name===n){if(!t.openAISetting?.apiKey||!t.openAISetting?.baseURL)throw new Error("apiKey or baseURL not found");return new Ae(t.openAISetting.apiKey,t.openAISetting.baseURL)}}const Oe={minimize:"Minimize",maximize:"Maximize",restore:"Restore",close:"Close"},Ce={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"}},Ie={cancel:"Cancel",confirm:"Confirm"},Se={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"}},be={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"}},Re={tooltip:"Diona Application",showWindow:"Show Window",exit:"Exit"},Ne={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"}},Le={title:"Diona Application"},We={window:Oe,main:Ce,dialog:Ie,settings:Se,menu:be,tray:Re,timeAgo:Ne,app:Le},Ue={minimize:"最小化",maximize:"最大化",restore:"还原",close:"关闭"},ke={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:"发送"}},Be={cancel:"取消",confirm:"确认"},Pe={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:"语言设置"}},xe={conversation:{newConversation:"新建对话",sortBy:"排序方式",sortByCreateTime:"按创建时间排序",sortByUpdateTime:"按更新时间排序",sortByName:"按名称排序",sortByModel:"按模型排序",sortAscending:"递增",sortDescending:"递减",pinConversation:"置顶对话",unpinConversation:"取消置顶",renameConversation:"重命名对话",delConversation:"删除对话",batchOperations:"批量操作"},message:{copyMessage:"复制消息",deleteMessage:"删除消息",selectMessage:"选择消息"}},He={tooltip:"迪奥娜",showWindow:"显示窗口",exit:"退出"},Ge={justNow:"刚刚",minutes:"{count}分钟前",hours:"{count}小时前",days:"{count}天前",months:"{count}个月前",years:"{count}年前",weekday:{sun:"星期日",mon:"星期一",tue:"星期二",wed:"星期三",thu:"星期四",fri:"星期五",sat:"星期六"}},$e={title:"迪奥娜"},ze={window:Ue,main:ke,dialog:Be,settings:Pe,menu:xe,tray:He,timeAgo:Ge,app:$e},Fe={en:We,zh:ze};function B(){return n=>{if(n)try{const e=n?.split(".");let t=Fe[T.get(g.LANGUAGE)];for(const i of e)t=t[i];return t}catch(e){return u.error("failed to translate key:",n,e),n}}}let k;function oe(){if(k!=null)return k;const n=s.app.getAppPath();return k=C.join(n,"resources","icons","icon.ico"),k}class j{static _instance;_isDark=s.nativeTheme.shouldUseDarkColors;constructor(){const e=T.get(g.THEME_MODE);e&&(s.nativeTheme.themeSource=e,this._isDark=s.nativeTheme.shouldUseDarkColors),this._setupIpcEvent(),u.info("ThemeService initialized successfully.")}_setupIpcEvent(){s.ipcMain.handle(r.SET_THEME_MODE,(e,t)=>(s.nativeTheme.themeSource=t,T.set(g.THEME_MODE,t),s.nativeTheme.shouldUseDarkColors)),s.ipcMain.handle(r.GET_THEME_MODE,()=>s.nativeTheme.themeSource),s.ipcMain.handle(r.IS_DARK_THEME,()=>s.nativeTheme.shouldUseDarkColors),s.nativeTheme.on("updated",()=>{this._isDark=s.nativeTheme.shouldUseDarkColors,s.BrowserWindow.getAllWindows().forEach(e=>e.webContents.send(r.THEME_MODE_UPDATED,this._isDark))})}static getInstance(){return this._instance||(this._instance=new j),this._instance}get isDark(){return this._isDark}get themeMode(){return s.nativeTheme.themeSource}}const K=j.getInstance(),je={frame:!1,titleBarStyle:"hidden",trafficLightPosition:{x:-100,y:-100},show:!1,title:"NIANXX",darkTheme:K.isDark,backgroundColor:K.isDark?"#2C2C2C":"#FFFFFF",webPreferences:{nodeIntegration:!1,contextIsolation:!0,sandbox:!0,backgroundThrottling:!1,preload:MAIN_WINDOW_VITE_DEV_SERVER_URL?C.join(process.cwd(),"dist-electron/preload/preload.js"):C.join(__dirname,"preload.js")}};class Y{static _instance;_logo=oe();isDev=!!MAIN_WINDOW_VITE_DEV_SERVER_URL;_winStates={main:{instance:void 0,isHidden:!1,onCreate:[],onClosed:[]},setting:{instance:void 0,isHidden:!1,onCreate:[],onClosed:[]},dialog:{instance:void 0,isHidden:!1,onCreate:[],onClosed:[]},login:{instance:void 0,isHidden:!1,onCreate:[],onClosed:[]},loading:{instance:void 0,isHidden:!1,onCreate:[],onClosed:[]}};constructor(){this._setupIpcEvents(),u.info("WindowService initialized successfully.")}_isReallyClose(e){return e===w.MAIN?T.get(g.MINIMIZE_TO_TRAY)===!1:e!==w.SETTING}_setupIpcEvents(){const e=a=>{const c=s.BrowserWindow.fromWebContents(a.sender),d=this.getName(c);this.close(c,this._isReallyClose(d))},t=a=>{s.BrowserWindow.fromWebContents(a.sender)?.minimize()},i=a=>{this.toggleMax(s.BrowserWindow.fromWebContents(a.sender))},o=a=>s.BrowserWindow.fromWebContents(a.sender)?.isMaximized()??!1;s.ipcMain.on(r.WINDOW_CLOSE,e),s.ipcMain.on(r.WINDOW_MINIMIZE,t),s.ipcMain.on(r.WINDOW_MAXIMIZE,i),s.ipcMain.handle(r.IS_WINDOW_MAXIMIZED,o),s.ipcMain.handle(r.APP_LOAD_PAGE,(a,c)=>{const d=s.BrowserWindow.fromWebContents(a.sender);d&&this._loadPage(d,c)})}static getInstance(){return this._instance||(this._instance=new Y),this._instance}create(e,t,i){if(this.get(e))return;const o=this._isHiddenWin(e);let a=this._createWinInstance(e,{...t,...i});return this.isDev&&a.webContents.openDevTools(),!o&&this._setupWinLifecycle(a,e)._loadWindowTemplate(a,e),this._listenWinReady({win:a,isHiddenWin:o,size:t}),o||(this._winStates[e].instance=a,this._winStates[e].onCreate.forEach(c=>c(a))),o&&(this._winStates[e].isHidden=!1,u.info(`Hidden window show: ${e}`)),a}_setupWinLifecycle(e,t){const i=se(()=>!e?.isDestroyed()&&e?.webContents?.send(r.WINDOW_MAXIMIZE+"back",e?.isMaximized()),80);return e.once("closed",()=>{this._winStates[t].onClosed.forEach(o=>o(e)),e?.destroy(),e?.removeListener("resize",i),this._winStates[t].instance=void 0,this._winStates[t].isHidden=!1,u.info(`Window closed: ${t}`)}),e.on("resize",i),this}_listenWinReady(e){const t=()=>{e.win?.once("show",()=>setTimeout(()=>this._applySizeConstraints(e.win,e.size),2)),e.win?.show()};e.isHiddenWin?t():this._addLoadingView(e.win,e.size)?.(t)}_addLoadingView(e,t){let i=!1;const o=a=>{a.sender!==e?.webContents||i||(i=!0,s.ipcMain.removeListener(r.RENDERER_IS_READY,o))};return s.ipcMain.on(r.RENDERER_IS_READY,o),a=>{a()}}_applySizeConstraints(e,t){t.maxHeight&&t.maxWidth&&e.setMaximumSize(t.maxWidth,t.maxHeight),t.minHeight&&t.minWidth&&e.setMinimumSize(t.minWidth,t.minHeight)}_loadPage(e,t){if(MAIN_WINDOW_VITE_DEV_SERVER_URL)return e.loadURL(`${MAIN_WINDOW_VITE_DEV_SERVER_URL}/${t}.html`);e.loadFile(C.join(app.getAppPath(),"dist",`${t}.html`))}_loadWindowTemplate(e,t){this._loadPage(e,"index")}_handleCloseWindowState(e,t){const i=this.getName(e);i&&(t?this._winStates[i].instance=void 0:this._winStates[i].isHidden=!0),setTimeout(()=>{e[t?"close":"hide"]?.(),this._checkAndCloseAllWinodws()},210)}_checkAndCloseAllWinodws(){if(!this._winStates[w.MAIN].instance||this._winStates[w.MAIN].instance?.isDestroyed())return Object.values(this._winStates).forEach(t=>t?.instance?.close());if(!T.get(g.MINIMIZE_TO_TRAY)&&!this.get(w.MAIN)?.isVisible())return Object.values(this._winStates).forEach(t=>!t?.instance?.isVisible()&&t?.instance?.close())}_isHiddenWin(e){return this._winStates[e]&&this._winStates[e].isHidden}_createWinInstance(e,t){return this._isHiddenWin(e)?this._winStates[e].instance:new s.BrowserWindow({...je,icon:this._logo,...t})}focus(e){if(!e)return;const t=this.getName(e);e?.isMaximized()?(e?.restore(),u.debug(`Window ${t} restored and focused`)):u.debug(`Window ${t} focused`),e?.focus()}close(e,t=!0){if(!e)return;const i=this.getName(e);u.info(`Close window: ${i}, really: ${t}`),this._handleCloseWindowState(e,t)}toggleMax(e){e&&(e.isMaximized()?e.unmaximize():e.maximize())}getName(e){if(e){for(const[t,i]of Object.entries(this._winStates))if(i?.instance===e)return t}}get(e){if(!this._winStates[e].isHidden)return this._winStates[e].instance}onWindowCreate(e,t){this._winStates[e].onCreate.push(t)}onWindowClosed(e,t){this._winStates[e].onClosed.push(t)}}const W=Y.getInstance();let P=B();class q{static _instance;_menuTemplates=new Map;_currentMenu=void 0;constructor(){this._setupIpcListener(),this._setupLanguageChangeListener(),u.info("MenuService initialized successfully.")}_setupIpcListener(){s.ipcMain.handle(r.SHOW_CONTEXT_MENU,(e,t,i)=>new Promise(o=>this.showMenu(t,()=>o(!0),i)))}_setupLanguageChangeListener(){T.onConfigChange(e=>{e[g.LANGUAGE]&&(P=B())})}static getInstance(){return this._instance||(this._instance=new q),this._instance}register(e,t){return this._menuTemplates.set(e,t),e}showMenu(e,t,i){if(this._currentMenu)return;const o=G(this._menuTemplates.get(e));if(!o){u.warn(`Menu ${e} not found.`),t?.();return}let a=[];try{a=Array.isArray(i)?i:JSON.parse(i??"[]")}catch(l){u.error(`Failed to parse dynamicOptions for menu ${e}: ${l}`)}const c=l=>l.submenu?{...l,label:P(l?.label)??void 0,submenu:l.submenu?.map(p=>c(p))}:{...l,label:P(l?.label)??void 0},d=o.map(l=>{if(!Array.isArray(a)||!a.length)return c(l);const p=a.find(m=>m.id===l.id);if(p){const m={...l,...p};return c(m)}return l.submenu?c({...l,submenu:l.submenu?.map(m=>{const b=a.find(A=>A.id===m.id);return{...m,...b}})}):c(l)}),f=s.Menu.buildFromTemplate(d);this._currentMenu=f,f.popup({callback:()=>{this._currentMenu=void 0,t?.()}})}destroyMenu(e){this._menuTemplates.delete(e)}destroyed(){this._menuTemplates.clear(),this._currentMenu=void 0}}const x=q.getInstance();let L=B();class X{static _instance;_tray=null;_removeLanguageListener;_setupLanguageChangeListener(){this._removeLanguageListener=T.onConfigChange(e=>{e[g.LANGUAGE]&&(L=B(),this._tray&&this._updateTray())})}_updateTray(){this._tray||(this._tray=new s.Tray(oe()));const e=()=>{const t=W.get(w.MAIN);if(t&&!t?.isDestroyed()&&t?.isVisible()&&!t?.isFocused())return t.focus();if(t?.isMinimized())return t?.restore();t?.isVisible()&&t?.isFocused()||W.create(w.MAIN,ie)};this._tray.setToolTip(L("tray.tooltip")??"Diona Application"),this._tray.setContextMenu(s.Menu.buildFromTemplate([{label:L("tray.showWindow"),accelerator:"CmdOrCtrl+N",click:e},{type:"separator"},{label:L("settings.title"),click:()=>s.ipcMain.emit(`${r.OPEN_WINDOW}:${w.SETTING}`)},{role:"quit",label:L("tray.exit")}])),this._tray.removeAllListeners("click"),this._tray.on("click",e)}constructor(){this._setupLanguageChangeListener(),u.info("TrayService initialized successfully.")}static getInstance(){return this._instance||(this._instance=new X),this._instance}create(){this._tray||(this._updateTray(),s.app.on("quit",()=>{this.destroy()}))}destroy(){this._tray?.destroy(),this._tray=null,this._removeLanguageListener&&(this._removeLanguageListener(),this._removeLanguageListener=void 0)}}const J=X.getInstance();class Ye{win;views=new Map;activeId=null;skipNextNavigate=new Map;enabled=!1;constructor(e){this.win=e,this.win.on("resize",()=>this.updateActiveBounds()),this._setupIpcEvents()}_setupIpcEvents(){s.ipcMain.handle(r.TAB_CREATE,(e,t)=>this.create(t)),s.ipcMain.handle(r.TAB_LIST,()=>this.list()),s.ipcMain.handle(r.TAB_NAVIGATE,(e,{tabId:t,url:i})=>{this.navigate(t,i)}),s.ipcMain.handle(r.TAB_RELOAD,(e,t)=>{this.reload(t)}),s.ipcMain.handle(r.TAB_BACK,(e,t)=>{this.goBack(t)}),s.ipcMain.handle(r.TAB_FORWARD,(e,t)=>{this.goForward(t)}),s.ipcMain.handle(r.TAB_SWITCH,(e,t)=>{this.switch(t)}),s.ipcMain.handle(r.TAB_CLOSE,(e,t)=>{this.close(t)})}enable(){this.enabled=!0,this.updateActiveBounds(),this.activeId&&this.attach(this.activeId)}disable(){this.enabled=!1;const e=this.activeId?this.views.get(this.activeId):null;e&&this.win.removeBrowserView(e)}destroy(){this.disable(),this.views.forEach(e=>{e.webContents.destroy()}),this.views.clear(),s.ipcMain.removeHandler(r.TAB_CREATE),s.ipcMain.removeHandler(r.TAB_LIST),s.ipcMain.removeHandler(r.TAB_NAVIGATE),s.ipcMain.removeHandler(r.TAB_RELOAD),s.ipcMain.removeHandler(r.TAB_BACK),s.ipcMain.removeHandler(r.TAB_FORWARD),s.ipcMain.removeHandler(r.TAB_SWITCH),s.ipcMain.removeHandler(r.TAB_CLOSE)}list(){return Array.from(this.views.entries()).map(([e,t])=>this.info(e,t))}create(e,t=!0){const i=ce.randomUUID(),o=new s.BrowserView({webPreferences:{nodeIntegration:!1,contextIsolation:!0,sandbox:!0,preload:MAIN_WINDOW_VITE_DEV_SERVER_URL?C.join(process.cwd(),"dist-electron/preload/preload.js"):C.join(__dirname,"preload.js")}});this.views.set(i,o),this.enabled&&t&&this.attach(i);const a=e&&e.length>0?e:"about:blank";o.webContents.loadURL(a),this.bindEvents(i,o);const c=this.info(i,o);return this.win.webContents.send("tab-created",c),c}switch(e){this.views.has(e)&&(this.enabled&&this.attach(e),this.win.webContents.send("tab-switched",{tabId:e}))}close(e){const t=this.views.get(e);if(!t)return;this.activeId===e&&(this.win.removeBrowserView(t),this.activeId=null),t.webContents.destroy(),this.views.delete(e),this.win.webContents.send("tab-closed",{tabId:e});const i=this.views.keys().next().value;i&&this.switch(i)}navigate(e,t){const i=this.views.get(e);i&&(this.skipNextNavigate.set(e,!0),i.webContents.loadURL(t))}reload(e){const t=this.views.get(e);t&&t.webContents.reload()}goBack(e){const t=this.views.get(e);t&&t.webContents.canGoBack()&&t.webContents.goBack()}goForward(e){const t=this.views.get(e);t&&t.webContents.canGoForward()&&t.webContents.goForward()}attach(e){if(!this.enabled)return;const t=this.views.get(e);if(t){if(this.activeId&&this.views.get(this.activeId)){const i=this.views.get(this.activeId);this.win.removeBrowserView(i)}this.activeId=e,this.win.addBrowserView(t),this.updateActiveBounds()}}updateActiveBounds(){if(!this.enabled||!this.activeId)return;const e=this.views.get(this.activeId);if(!e)return;const[t,i]=this.win.getContentSize(),o=88,a=8,c=488,d=a,f=o+a,l=t-c-a,p=i-o-a*2;e.setBounds({x:d,y:f,width:Math.max(0,l),height:Math.max(0,p)})}bindEvents(e,t){const i=()=>this.win.webContents.send("tab-updated",this.info(e,t));t.webContents.on("did-start-loading",i),t.webContents.on("did-stop-loading",i),t.webContents.on("did-finish-load",i),t.webContents.on("page-title-updated",i),t.webContents.on("did-navigate",i),t.webContents.on("did-navigate-in-page",i),t.webContents.on("will-navigate",(o,a)=>{if(this.skipNextNavigate.get(e)){this.skipNextNavigate.set(e,!1);return}o.preventDefault(),this.create(a)}),t.webContents.setWindowOpenHandler(({url:o})=>(this.create(o),{action:"deny"}))}info(e,t){const i=t.webContents;return{id:e,url:i.getURL(),title:i.getTitle(),isLoading:i.isLoading(),canGoBack:i.canGoBack(),canGoForward:i.canGoForward()}}}const Q=n=>{if(n){J.create();return}J.destroy()},qe=n=>{const e=o=>{u.logUserOperation(`${r.SHOW_CONTEXT_MENU}:${E.CONVERSATION_ITEM}-${o}`),n.webContents.send(`${r.SHOW_CONTEXT_MENU}:${E.CONVERSATION_ITEM}`,o)};x.register(E.CONVERSATION_ITEM,[{id:D.PIN,label:"menu.conversation.pinConversation",click:()=>e(D.PIN)},{id:D.RENAME,label:"menu.conversation.renameConversation",click:()=>e(D.RENAME)},{id:D.DEL,label:"menu.conversation.delConversation",click:()=>e(D.DEL)}]);const t=o=>{u.logUserOperation(`${r.SHOW_CONTEXT_MENU}:${E.CONVERSATION_LIST}-${o}`),n.webContents.send(`${r.SHOW_CONTEXT_MENU}:${E.CONVERSATION_LIST}`,o)};x.register(E.CONVERSATION_LIST,[{id:_.NEW_CONVERSATION,label:"menu.conversation.newConversation",click:()=>t(_.NEW_CONVERSATION)},{type:"separator"},{id:_.SORT_BY,label:"menu.conversation.sortBy",submenu:[{id:_.SORT_BY_CREATE_TIME,label:"menu.conversation.sortByCreateTime",type:"radio",checked:!1,click:()=>t(_.SORT_BY_CREATE_TIME)},{id:_.SORT_BY_UPDATE_TIME,label:"menu.conversation.sortByUpdateTime",type:"radio",checked:!1,click:()=>t(_.SORT_BY_UPDATE_TIME)},{id:_.SORT_BY_NAME,label:"menu.conversation.sortByName",type:"radio",checked:!1,click:()=>t(_.SORT_BY_NAME)},{id:_.SORT_BY_MODEL,label:"menu.conversation.sortByModel",type:"radio",checked:!1,click:()=>t(_.SORT_BY_MODEL)},{type:"separator"},{id:_.SORT_ASCENDING,label:"menu.conversation.sortAscending",type:"radio",checked:!1,click:()=>t(_.SORT_ASCENDING)},{id:_.SORT_DESCENDING,label:"menu.conversation.sortDescending",type:"radio",checked:!1,click:()=>t(_.SORT_DESCENDING)}]},{id:_.BATCH_OPERATIONS,label:"menu.conversation.batchOperations",click:()=>t(_.BATCH_OPERATIONS)}]);const i=o=>{u.logUserOperation(`${r.SHOW_CONTEXT_MENU}:${E.MESSAGE_ITEM}-${o}`),n.webContents.send(`${r.SHOW_CONTEXT_MENU}:${E.MESSAGE_ITEM}`,o)};x.register(E.MESSAGE_ITEM,[{id:O.COPY,label:"menu.message.copyMessage",click:()=>i(O.COPY)},{id:O.SELECT,label:"menu.message.selectMessage",click:()=>i(O.SELECT)},{type:"separator"},{id:O.DELETE,label:"menu.message.deleteMessage",click:()=>i(O.DELETE)}])};function re(){W.onWindowCreate(w.MAIN,n=>{let e=T.get(g.MINIMIZE_TO_TRAY);T.onConfigChange(i=>{e!==i[g.MINIMIZE_TO_TRAY]&&(e=i[g.MINIMIZE_TO_TRAY],Q(e))}),Q(e),qe(n);const t=new Ye(n);t.enable(),n.on("closed",()=>{t.destroy()})}),W.create(w.MAIN,ie),s.ipcMain.on(r.START_A_DIALOGUE,async(n,e)=>{const{providerName:t,messages:i,messageId:o,selectedModel:a}=e,c=W.get(w.MAIN);if(!c)throw new Error("mainWindow not found");try{const f=await De(t)?.chat(i,a);if(!f)throw new Error("chunks or stream not found");for await(const l of f){const p={messageId:o,data:l};c.webContents.send(r.START_A_DIALOGUE+"back"+o,p)}}catch(d){const f={messageId:o,data:{isEnd:!0,isError:!0,result:d instanceof Error?d.message:String(d)}};c.webContents.send(r.START_A_DIALOGUE+"back"+o,f)}})}function Xe(){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 Ze(n){return C.join(s.app.getPath("userData"),"profiles",n)}function Ke(n){return new Promise(e=>{const t=de.createServer();t.once("error",i=>e(!0)),t.once("listening",()=>{t.close(),e(!1)}),t.listen(n)})}async function Je(){try{return new Promise(n=>{const e=ue.get("http://127.0.0.1:9222/json/version",t=>{n(t.statusCode===200)});e.on("error",()=>n(!1)),e.setTimeout(1e3,()=>{e.destroy(),n(!1)})})}catch{return!1}}async function Qe(){const n=Xe(),e=Ze("default");if(h.info(`Launching Chrome with user data dir: ${e}`),await Ke(9222)){h.info("Chrome already running on port 9222, skip launching.");return}if(await Je()){h.info("Chrome already running, skip launching.");return}return new Promise((i,o)=>{he.spawn(n,["--remote-debugging-port=9222","--window-size=1920,1080","--window-position=0,0","--no-first-run",`--user-data-dir=${e}`,"--no-default-browser-check","about:blank"],{detached:!0,stdio:"ignore"}).on("error",o),setTimeout(()=>{i(0)},1e3)})}class Ve extends pe.EventEmitter{async executeScript(e,t){const o=(a,c)=>{const d=a+c;return d.length>32768?d.slice(d.length-32768):d};return await new Promise(a=>{try{const c=t?.roomType??"",d=t?.startTime??"",f=t?.endTime??"",l=t?.operation??"",p=t?.tabIndex??"",m=t?.channels??"",b=t?.startTabIndex??"",A=s.utilityProcess.fork(e,[],{env:{...process.env,ROOM_TYPE:String(c),START_DATE:String(d),END_DATE:String(f),OPERATION:String(l),TAB_INDEX:String(p),CHANNELS:typeof m=="string"?m:JSON.stringify(m),START_TAB_INDEX:String(b)},stdio:"pipe"});let I="",R="";A.stdout&&A.stdout.on("data",M=>{const N=M.toString();I=o(I,N),h.info(`stdout: ${N}`)}),A.stderr&&A.stderr.on("data",M=>{const N=M.toString();R=o(R,N),h.info(`stderr: ${N}`)}),A.on("exit",M=>{h.info(`子进程退出,退出码 ${M}`),a({success:M===0,exitCode:M,stdoutTail:I,stderrTail:R,...M===0?{}:{error:`Script exited with code ${M}`}})})}catch(c){a({success:!1,exitCode:null,stdoutTail:"",stderrTail:"",error:c?.message||"运行 Node 脚本时出错"})}})}}const H=new Map;function V(){return s.app.isPackaged?U.join(__dirname,"scripts"):U.join(process.cwd(),"electron/scripts")}function et(){const n=new Ve;s.ipcMain.handle(r.OPEN_CHANNEL,async(e,t)=>{try{await Qe();const i=V(),o=U.join(i,"open_all_channel.js");if(H.clear(),Array.isArray(t))for(let c=0;c{try{const i=t.roomList.find(l=>l.id===t.roomType),a=[["fzName","fg_trace.js"],["mtName","mt_trace.js"],["dyHotelName","dy_hotel_trace.js"],["dyHotSpringName","dy_hot_spring_trace.js"]].filter(([l])=>i?.[l]),c=V(),d=a.map(([l,p])=>{const m=U.join(c,p);if(!ee.existsSync(m))throw new Error(`Script not found for channel ${l}: ${m}`);return{channel:l,scriptPath:m}}),f=[];for(let l=0;lthis.sendToRenderer(r.UPDATE_STATUS_CHANGED,{status:"checking"})),y.autoUpdater.on("update-available",e=>this.sendToRenderer(r.UPDATE_STATUS_CHANGED,{status:"available",info:e})),y.autoUpdater.on("update-not-available",()=>this.sendToRenderer(r.UPDATE_STATUS_CHANGED,{status:"not-available"})),y.autoUpdater.on("download-progress",e=>this.sendToRenderer(r.UPDATE_STATUS_CHANGED,{status:"downloading",progress:e})),y.autoUpdater.on("update-downloaded",e=>this.sendToRenderer(r.UPDATE_STATUS_CHANGED,{status:"downloaded",info:e})),y.autoUpdater.on("error",e=>this.sendToRenderer(r.UPDATE_STATUS_CHANGED,{status:"error",error:e.message}))}sendToRenderer(e,t){s.BrowserWindow.getAllWindows().forEach(i=>{i.isDestroyed()||i.webContents.send(e,t)})}registerHandlers(){s.ipcMain.handle(r.UPDATE_CHECK,()=>s.app.isPackaged?y.autoUpdater.checkForUpdates():(this.sendToRenderer(r.UPDATE_STATUS_CHANGED,{status:"checking"}),setTimeout(()=>{this.sendToRenderer(r.UPDATE_STATUS_CHANGED,{status:"not-available"})},1500),null)),s.ipcMain.handle(r.UPDATE_DOWNLOAD,()=>s.app.isPackaged?y.autoUpdater.downloadUpdate():null),s.ipcMain.handle(r.UPDATE_INSTALL,()=>s.app.isPackaged?y.autoUpdater.quitAndInstall():null),s.ipcMain.handle(r.UPDATE_VERSION,()=>s.app.getVersion())}}const tt=Z.getInstance();tt.init();le&&s.app.quit();s.app.whenReady().then(()=>{re(),et()});s.app.on("window-all-closed",()=>{process.platform!=="darwin"&&!T.get(g.MINIMIZE_TO_TRAY)&&(h.info("app closing due to all windows being closed"),s.app.quit())});s.app.on("activate",()=>{s.BrowserWindow.getAllWindows().length===0&&re()}); diff --git a/dist-electron/main/main.jsc b/dist-electron/main/main.jsc index a5dff2d..32107a2 100644 Binary files a/dist-electron/main/main.jsc and b/dist-electron/main/main.jsc differ diff --git a/dist-electron/preload/preload.js b/dist-electron/preload/preload.js index 0164736..59832c9 100644 --- a/dist-electron/preload/preload.js +++ b/dist-electron/preload/preload.js @@ -1,99 +1 @@ -"use strict"; -const electron = require("electron"); -var IPC_EVENTS = /* @__PURE__ */ ((IPC_EVENTS2) => { - IPC_EVENTS2["EXTERNAL_OPEN"] = "external-open"; - IPC_EVENTS2["WINDOW_MINIMIZE"] = "window-minimize"; - IPC_EVENTS2["WINDOW_MAXIMIZE"] = "window-maximize"; - IPC_EVENTS2["WINDOW_CLOSE"] = "window-close"; - IPC_EVENTS2["IS_WINDOW_MAXIMIZED"] = "is-window-maximized"; - 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["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 api = { - versions: process.versions, - external: { - open: (url) => electron.ipcRenderer.invoke("external-open", url) - }, - closeWindow: () => electron.ipcRenderer.send(IPC_EVENTS.WINDOW_CLOSE), - minimizeWindow: () => electron.ipcRenderer.send(IPC_EVENTS.WINDOW_MINIMIZE), - maximizeWindow: () => electron.ipcRenderer.send(IPC_EVENTS.WINDOW_MAXIMIZE), - onWindowMaximized: (callback) => electron.ipcRenderer.on(IPC_EVENTS.WINDOW_MAXIMIZE + "back", (_, isMaximized) => callback(isMaximized)), - isWindowMaximized: () => electron.ipcRenderer.invoke(IPC_EVENTS.IS_WINDOW_MAXIMIZED), - viewIsReady: () => electron.ipcRenderer.send(IPC_EVENTS.RENDERER_IS_READY), - app: { - setFrameless: (route) => electron.ipcRenderer.invoke(IPC_EVENTS.APP_SET_FRAMELESS, route), - loadPage: (page) => electron.ipcRenderer.invoke(IPC_EVENTS.APP_LOAD_PAGE, page) - }, - // 通过 IPC 调用主进程 - readFile: (filePath) => electron.ipcRenderer.invoke(IPC_EVENTS.READ_FILE, filePath), - // 异步调用(映射为 electron 的 invoke) - invoke: (channel, ...args) => electron.ipcRenderer.invoke(channel, ...args), - // 异步调用(为了兼容老代码) - invokeAsync: (channel, ...args) => electron.ipcRenderer.invoke(channel, ...args), - // 监听主进程消息 - on: (event, callback) => { - const subscription = (_event, ...args) => callback(...args); - electron.ipcRenderer.on(event, subscription); - return () => electron.ipcRenderer.removeListener(event, subscription); - }, - // 发送消息到主进程 - send: (channel, ...args) => electron.ipcRenderer.send(channel, ...args), - // 获取窗口ID - getCurrentWindowId: () => electron.ipcRenderer.sendSync(IPC_EVENTS.GET_WINDOW_ID), - // 发送日志 - logger: { - debug: (message, ...meta) => electron.ipcRenderer.send(IPC_EVENTS.LOG_DEBUG, message, ...meta), - info: (message, ...meta) => electron.ipcRenderer.send(IPC_EVENTS.LOG_INFO, message, ...meta), - warn: (message, ...meta) => electron.ipcRenderer.send(IPC_EVENTS.LOG_WARN, message, ...meta), - error: (message, ...meta) => electron.ipcRenderer.send(IPC_EVENTS.LOG_ERROR, message, ...meta) - }, - // 执行脚本 - executeScript: (params) => electron.ipcRenderer.invoke(IPC_EVENTS.EXECUTE_SCRIPT, params), - // 打开渠道 - openChannel: (channels) => electron.ipcRenderer.invoke(IPC_EVENTS.OPEN_CHANNEL, channels) -}; -electron.contextBridge.exposeInMainWorld("api", api); +"use strict";const n=require("electron");var i=(e=>(e.EXTERNAL_OPEN="external-open",e.WINDOW_MINIMIZE="window-minimize",e.WINDOW_MAXIMIZE="window-maximize",e.WINDOW_CLOSE="window-close",e.IS_WINDOW_MAXIMIZED="is-window-maximized",e.APP_SET_FRAMELESS="app:set-frameless",e.APP_LOAD_PAGE="app:load-page",e.TAB_CREATE="tab:create",e.TAB_LIST="tab:list",e.TAB_NAVIGATE="tab:navigate",e.TAB_RELOAD="tab:reload",e.TAB_BACK="tab:back",e.TAB_FORWARD="tab:forward",e.TAB_SWITCH="tab:switch",e.TAB_CLOSE="tab:close",e.LOG_TO_MAIN="log-to-main",e.READ_FILE="read-file",e.INVOKE="ipc:invoke",e.INVOKE_ASYNC="ipc:invokeAsync",e.APP_MINIMIZE="app:minimize",e.APP_MAXIMIZE="app:maximize",e.APP_QUIT="app:quit",e.FILE_READ="file:read",e.FILE_WRITE="file:write",e.GET_WINDOW_ID="get-window-id",e.CUSTOM_EVENT="custom:event",e.TIME_UPDATE="time:update",e.RENDERER_IS_READY="renderer-ready",e.SHOW_CONTEXT_MENU="show-context-menu",e.START_A_DIALOGUE="start-a-dialogue",e.OPEN_WINDOW="open-window",e.LOG_DEBUG="log-debug",e.LOG_INFO="log-info",e.LOG_WARN="log-warn",e.LOG_ERROR="log-error",e.CONFIG_UPDATED="config-updated",e.SET_CONFIG="set-config",e.GET_CONFIG="get-config",e.UPDATE_CONFIG="update-config",e.SET_THEME_MODE="set-theme-mode",e.GET_THEME_MODE="get-theme-mode",e.IS_DARK_THEME="is-dark-theme",e.THEME_MODE_UPDATED="theme-mode-updated",e.EXECUTE_SCRIPT="execute-script",e.OPEN_CHANNEL="open-channel",e.UPDATE_CHECK="update:check",e.UPDATE_DOWNLOAD="update:download",e.UPDATE_INSTALL="update:install",e.UPDATE_VERSION="update:version",e.UPDATE_STATUS_CHANGED="update:status-changed",e))(i||{});const a={versions:process.versions,external:{open:e=>n.ipcRenderer.invoke("external-open",e)},closeWindow:()=>n.ipcRenderer.send(i.WINDOW_CLOSE),minimizeWindow:()=>n.ipcRenderer.send(i.WINDOW_MINIMIZE),maximizeWindow:()=>n.ipcRenderer.send(i.WINDOW_MAXIMIZE),onWindowMaximized:e=>n.ipcRenderer.on(i.WINDOW_MAXIMIZE+"back",(r,d)=>e(d)),isWindowMaximized:()=>n.ipcRenderer.invoke(i.IS_WINDOW_MAXIMIZED),viewIsReady:()=>n.ipcRenderer.send(i.RENDERER_IS_READY),app:{setFrameless:e=>n.ipcRenderer.invoke(i.APP_SET_FRAMELESS,e),loadPage:e=>n.ipcRenderer.invoke(i.APP_LOAD_PAGE,e)},readFile:e=>n.ipcRenderer.invoke(i.READ_FILE,e),invoke:(e,...r)=>n.ipcRenderer.invoke(e,...r),invokeAsync:(e,...r)=>n.ipcRenderer.invoke(e,...r),on:(e,r)=>{const d=(t,...o)=>r(...o);return n.ipcRenderer.on(e,d),()=>n.ipcRenderer.removeListener(e,d)},send:(e,...r)=>n.ipcRenderer.send(e,...r),getCurrentWindowId:()=>n.ipcRenderer.sendSync(i.GET_WINDOW_ID),logger:{debug:(e,...r)=>n.ipcRenderer.send(i.LOG_DEBUG,e,...r),info:(e,...r)=>n.ipcRenderer.send(i.LOG_INFO,e,...r),warn:(e,...r)=>n.ipcRenderer.send(i.LOG_WARN,e,...r),error:(e,...r)=>n.ipcRenderer.send(i.LOG_ERROR,e,...r)},executeScript:e=>n.ipcRenderer.invoke(i.EXECUTE_SCRIPT,e),openChannel:e=>n.ipcRenderer.invoke(i.OPEN_CHANNEL,e)};n.contextBridge.exposeInMainWorld("api",a); diff --git a/dist/assets/index-CvK9ZwFP.css b/dist/assets/index-CvK9ZwFP.css deleted file mode 100644 index 5c4f0c7..0000000 --- a/dist/assets/index-CvK9ZwFP.css +++ /dev/null @@ -1 +0,0 @@ -[data-v-eceb1c86] .el-steps--simple{background:0 0}[data-v-eceb1c86] .el-step.is-simple .el-step__arrow:before{background:#525866;width:2px}[data-v-eceb1c86] .el-step.is-simple .el-step__arrow:after{background:#525866;width:2px}[data-v-eceb1c86] .el-step__head.is-process .el-step__icon.is-text{border-color:#2b7fff}[data-v-eceb1c86] .el-step__head.is-success{color:#1fc16b}[data-v-eceb1c86] .el-step__head.is-success .el-step__icon.is-text{background:#1fc16b}[data-v-eceb1c86] .el-step__head.is-success .el-step__icon.is-text{color:#fff;border-color:#1fc16b}[data-v-eceb1c86] .el-step__title.is-success{color:#525866;font-size:14px;font-weight:500}[data-v-eceb1c86] .el-textarea .el-input__count.is-outside{top:auto;bottom:10px;right:15px}[data-v-6ab79709] .el-button.button{background:linear-gradient(#4a8ff9,#0000),#2b7fff;border:1px solid #1447e6;border-radius:8px;padding-top:18px;padding-bottom:18px;box-shadow:0 1px 1px #0000000d,inset 0 0 0 1px #ffffff3d}[data-v-84bf9ea3] .el-table tr td:nth-of-type(1){background:#eff6ff!important}[data-v-84bf9ea3] .el-tag{color:#525866;background:#fff!important;border:1px solid #e5e8ee!important;border-radius:6px!important}[data-v-4c31200f] .el-tabs__item{color:#99a0ae}[data-v-4c31200f] .el-tabs__item.is-active{color:#2b7fff}[data-v-4c31200f] .el-tabs__active-bar{background-color:#2b7fff} diff --git a/dist/assets/index-tccZ5UIk.css b/dist/assets/index-tccZ5UIk.css deleted file mode 100644 index 46fb464..0000000 --- a/dist/assets/index-tccZ5UIk.css +++ /dev/null @@ -1 +0,0 @@ -.task[data-v-74ff89ed]{z-index:1;transition:all .2s linear;position:relative}.task .success[data-v-74ff89ed]{color:#1fc16b;background-color:#e0faec}.task .error[data-v-74ff89ed]{color:#fb3748;background-color:#ffebec}.task .warning[data-v-74ff89ed]{color:#fa7319;background-color:#fff3eb}.task[data-v-74ff89ed]:hover{z-index:2;transform:translateY(-2px);box-shadow:0 10px 20px #0000001a}.task-tab .text[data-v-be5e193a]{color:#525866;cursor:pointer;font-size:14px}.task-tab .active[data-v-be5e193a]{color:#2b7fff;background:#fff;border-radius:8px;position:relative}.task-tab .active[data-v-be5e193a]:after{content:"";border:1px solid #2b7fff;border-radius:8px;width:100%;height:100%;position:absolute;top:0;left:0}.task-tab .text[data-v-33324fcc]{color:#525866;cursor:pointer;font-size:14px}.task-tab .active[data-v-33324fcc]{color:#2b7fff;background:#fff;border-radius:8px;position:relative}.task-tab .active[data-v-33324fcc]:after{content:"";border:1px solid #2b7fff;border-radius:8px;width:100%;height:100%;position:absolute;top:0;left:0}pre code.hljs{padding:1em;display:block;overflow-x:auto}code.hljs{padding:3px 5px}.hljs{color:#24292e;background:#fff}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#d73a49}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#6f42c1}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-variable,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id{color:#005cc5}.hljs-regexp,.hljs-string,.hljs-meta .hljs-string{color:#032f62}.hljs-built_in,.hljs-symbol{color:#e36209}.hljs-comment,.hljs-code,.hljs-formula{color:#6a737d}.hljs-name,.hljs-quote,.hljs-selector-tag,.hljs-selector-pseudo{color:#22863a}.hljs-subst{color:#24292e}.hljs-section{color:#005cc5;font-weight:700}.hljs-bullet{color:#735c0f}.hljs-emphasis{color:#24292e;font-style:italic}.hljs-strong{color:#24292e;font-weight:700}.hljs-addition{color:#22863a;background-color:#f0fff4}.hljs-deletion{color:#b31d28;background-color:#ffeef0}.wave[data-v-5b734189]{text-align:center;width:30px;position:relative}.dot[data-v-5b734189]{background:#333;border-radius:50%;width:3px;height:3px;margin-right:3px;animation:1.3s linear infinite wave-5b734189;display:inline-block}.dot[data-v-5b734189]:nth-child(2){animation-delay:-1.1s}.dot[data-v-5b734189]:nth-child(3){animation-delay:-.9s}@keyframes wave-5b734189{0%,60%,to{transform:initial}30%{transform:translateY(-5px)}} diff --git a/dist/index.html b/dist/index.html index d64847e..54eda75 100644 --- a/dist/index.html +++ b/dist/index.html @@ -1,17 +1,17 @@ - - - - - NIANXX - - - - - - -
- - + + + + + NIANXX + + + + + + +
+ + diff --git a/package.json b/package.json index cc3a98e..6d8b195 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "start": "vite", "build": "vite build && electron-builder", "build:vite": "vite build", - "package": "electron-builder", + "package": "vite build", "icons": "zx scripts/generate-icons.mjs", "uv:download": "zx scripts/download-bundled-uv.mjs", "uv:download:mac": "zx scripts/download-bundled-uv.mjs --platform=mac", @@ -18,10 +18,14 @@ "uv:download:all": "zx scripts/download-bundled-uv.mjs --all", "node:download:win": "zx scripts/download-bundled-node.mjs --platform=win", "prep:win-binaries": "pnpm run uv:download:win && pnpm run node:download:win", - "package:win": "pnpm run prep:win-binaries && vite build && electron-builder --win --publish never", - "package:mac": "vite build && electron-builder --mac --publish never", - "package:linux": "vite build && electron-builder --linux --publish never", - "release": "vite build && electron-builder --publish always", + "package:win": "pnpm run prep:win-binaries && pnpm run package && electron-builder --win --publish never", + "package:mac": "pnpm run package && electron-builder --mac --publish never", + "package:mac:local": "SKIP_AFTERPACK_CLEANUP=1 pnpm run package && electron-builder --mac --publish never", + "package:mac:x64": "pnpm run package && electron-builder --mac --x64 --publish never", + "package:mac:arm64": "pnpm run package && electron-builder --mac --arm64 --publish never", + "package:mac:fast": "pnpm run package && electron-builder --mac --publish never --config.compression=normal", + "package:linux": "pnpm run package && electron-builder --linux --publish never", + "release": "pnpm run package && electron-builder --publish always", "lint": "eslint --ext .ts,.tsx .", "typecheck": "tsc --noEmit", "openapi": "dotenv -e .env -- openapi-ts", diff --git a/package_mac_diagnosis_report.md b/package_mac_diagnosis_report.md new file mode 100644 index 0000000..83eb773 --- /dev/null +++ b/package_mac_diagnosis_report.md @@ -0,0 +1,145 @@ +# package:mac 打包诊断报告 + +**诊断时间**: 2026-04-11 +**项目路径**: `/Users/duanshuwen/Documents/workspace/electron/zn-ai` +**执行命令**: `pnpm run package:mac` + +--- + +## 1. 结论摘要 + +**本次打包失败不是因为项目源码缺少文件或 `electron-builder.yml` 配置项缺失,而是因为** + +1. **网络下载中断**:`electron-builder` 在构建 DMG 时需要下载 `dmgbuild-bundle-x86_64`,该请求被服务器中断(`ReadError: The server aborted pending request`)。 +2. **并发锁竞争**:`mac.target` 同时配置了 `x64` 与 `arm64` 两种架构,`electron-builder` 并行打包时两个进程争抢 `proper-lockfile` 的缓存锁,导致下载失败后的清理阶段抛出 `Lock file is already being held` 错误。 +3. **(非致命)代码签名证书已过期**:本地唯一的 `Apple Development` 证书状态为 `CSSMERR_TP_CERT_EXPIRED`,因此 `electron-builder` 跳过了正式签名,仅做了 ad-hoc 签名。这不会导致打包中断,但会影响最终 app 在 macOS Gatekeeper 下的可运行性。 + +--- + +## 2. 关键错误日志 + +```text + • downloading release=dmg-builder@1.2.0 file=dmgbuild-bundle-x86_64-75c8a6c.tar.gz +... +ReadError: The server aborted pending request +... +Error: Lock file is already being held + at /Users/duanshuwen/Documents/workspace/electron/zn-ai/node_modules/proper-lockfile/lib/lockfile.js:68:47 +... + ELIFECYCLE  Command failed with exit code 1. +``` + +--- + +## 3. 配置与文件检查结果 + +| 检查项 | 状态 | 说明 | +|--------|------|------| +| `package.json` | 正常 | 存在且 `package:mac` 脚本定义正确 | +| `electron-builder.yml` | 正常 | 配置完整,`mac` / `dmg` / `win` / `linux` 分平台配置齐全 | +| `resources/icons/icon.icns` | 正常 | `mac.icon` 指向的文件存在 | +| `entitlements.mac.plist` | 正常 | 文件存在并已配置 | +| `dist` / `dist-electron` | 正常 | `vite build` 阶段成功通过,产物已生成 | +| 发布产物 | 部分生成 | `zip`(x64 / arm64)已成功生成,但 DMG(x64 / arm64)因下载失败未完成 | + +--- + +## 4. 详细原因分析 + +### 4.1 网络问题导致 dmg-builder 下载中断 +`electron-builder` 在首次构建 DMG 时需要拉取预编译的 `dmg-builder` bundle(`dmgbuild-bundle-x86_64-75c8a6c.tar.gz`)。由于该资源托管在 GitHub / AWS CDN 上,在国内网络环境下极易出现连接被重置或请求被服务器中断的情况。 + +### 4.2 并发构建加剧锁竞争 +`electron-builder.yml` 中配置了同时打包两种架构: + +```yaml +mac: + target: + - target: dmg + arch: + - x64 + - arm64 + - target: zip + arch: + - x64 + - arm64 +``` + +这导致 `electron-builder` 会并行启动多个打包任务。当它们同时尝试下载/解压同一个 `dmg-builder` 缓存时,`proper-lockfile` 产生竞争;一旦其中一个下载失败,另一个在清理阶段也会因无法获取锁而报错,最终整体构建失败。 + +### 4.3 代码签名证书过期(仅提示,不中断构建) +日志中出现的: + +```text +skipped macOS application code signing reason=cannot find valid "Developer ID Application" identity ... +"Apple Development: 562304744@qq.com (Z27TQS657B)" (CSSMERR_TP_CERT_EXPIRED) +``` + +这说明本地证书已过期,不会影响打包流程本身,但生成的 `.app` 没有有效的开发者签名,分发后用户打开时会遇到 "已损坏,无法打开" 的 Gatekeeper 提示。 + +--- + +## 5. 解决方案与建议 + +### 方案 A:分架构单独打包(推荐,最稳定) +先避免并发锁竞争,同时降低单次构建对网络的敏感度: + +```bash +# 仅打包 arm64(Apple Silicon) +cd zn-ai +npx electron-builder --mac --arm64 --publish never + +# 仅打包 x64(Intel) +npx electron-builder --mac --x64 --publish never +``` + +若网络仍不稳定,可配置代理或手动下载缓存(见方案 B)。 + +### 方案 B:配置 electron-builder 缓存 +如果网络受限,可预先设置缓存目录并手动下载所需 bundle: + +```bash +export ELECTRON_BUILDER_CACHE="$HOME/.electron-builder-cache" +``` + +然后手动将 `dmgbuild-bundle-x86_64-75c8a6c.tar.gz` 放置到缓存目录的 `cache/dmg-builder` 子目录下,再执行打包。 + +### 方案 C:修改 `electron-builder.yml` 降低并发 +在 `mac.target` 里先只保留当前常用架构(如 `arm64`),需要时再切换: + +```yaml +mac: + target: + - target: dmg + arch: + - arm64 + - target: zip + arch: + - arm64 +``` + +### 方案 D:修复代码签名(分发前必须) +若计划将应用分发给其他用户,需要: +1. 在 Apple Developer 后台续期或重新申请 **Developer ID Application** 证书; +2. 将 `notarize` 改为 `true` 并配置正确的 `teamId`; +3. 在构建机器上安装新证书后重新打包。 + +--- + +## 6. 附录:构建产物现状 + +截至诊断时,`release/` 目录下已生成: + +- `NIANXX-1.0.0-mac-x64.zip` (133 MB) +- `NIANXX-1.0.0-mac-arm64.zip` (129 MB) +- 对应的 `.blockmap` 文件 + +**未生成**: +- `NIANXX-1.0.0-mac-x64.dmg` +- `NIANXX-1.0.0-mac-arm64.dmg` + +原因即上述网络下载中断 + 并发锁竞争。 + +--- + +*报告结束* diff --git a/scripts/after-pack.cjs b/scripts/after-pack.cjs index 202ed20..0b2bf9d 100644 --- a/scripts/after-pack.cjs +++ b/scripts/after-pack.cjs @@ -142,19 +142,23 @@ module.exports = async function afterPack(context) { await ensureDependency(dep); } - // 3. Clean up unnecessary development files from node_modules - const nodeModulesDest = path.join(appOutDir, 'node_modules'); - if (await fs.pathExists(nodeModulesDest)) { - console.log('Cleaning up development files in node_modules...'); - const removed = cleanupUnnecessaryFiles(nodeModulesDest); - console.log(`Removed ${removed} unnecessary files/directories from node_modules.`); - } + // 3. Clean up unnecessary development files from node_modules (skip if SKIP_AFTERPACK_CLEANUP is set) + if (!process.env.SKIP_AFTERPACK_CLEANUP) { + const nodeModulesDest = path.join(appOutDir, 'node_modules'); + if (await fs.pathExists(nodeModulesDest)) { + console.log('Cleaning up development files in node_modules...'); + const removed = cleanupUnnecessaryFiles(nodeModulesDest); + console.log(`Removed ${removed} unnecessary files/directories from node_modules.`); + } - // 4. Clean up unnecessary files in scripts directory - if (await fs.pathExists(scriptsDest)) { - console.log('Cleaning up development files in scripts directory...'); - const removedScripts = cleanupUnnecessaryFiles(scriptsDest); - console.log(`Removed ${removedScripts} unnecessary files/directories from scripts.`); + // 4. Clean up unnecessary files in scripts directory + if (await fs.pathExists(scriptsDest)) { + console.log('Cleaning up development files in scripts directory...'); + const removedScripts = cleanupUnnecessaryFiles(scriptsDest); + console.log(`Removed ${removedScripts} unnecessary files/directories from scripts.`); + } + } else { + console.log('Skipping afterPack cleanup (SKIP_AFTERPACK_CLEANUP is set)'); } // 5. Optional: platform-specific native package cleanup