- Reorganize project structure with new electron and shared directories - Add comprehensive i18n support with Chinese, English, and Japanese locales - Update build configurations and TypeScript paths for new structure - Add various UI components including chat interface and task management - Include Windows release binaries and localization files - Update dependencies and fix import paths throughout the codebase
6 lines
34 KiB
JavaScript
6 lines
34 KiB
JavaScript
"use strict";const o=require("electron"),oe=require("openai"),U=require("util"),h=require("electron-log"),B=require("path"),V=require("fs"),Q=require("js-base64"),E=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 D=ee(B),v=ee(V);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||{}),I=(n=>(n.PIN="pin",n.RENAME="rename",n.DEL="del",n))(I||{}),_=(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))(_||{}),C=(n=>(n.COPY="copy",n.DELETE="delete",n.SELECT="select",n))(C||{});class he{}const pe=U.promisify(v.readdir),ge=U.promisify(v.stat),fe=U.promisify(v.unlink);class F{static _instance;LOG_RETENTION_DAYS=7;CLEANUP_INTERVAL_MS=1440*60*1e3;constructor(){const e=D.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 D.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=D.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=D.join(e,c);try{const f=await ge(d);f.isFile()&&f.birthtime<i&&(await fe(d),r++)}catch(f){this.error(`Failed to delete old log file ${d}:`,f)}}r>0&&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 F),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=F.getInstance();function _e(n){const e=n.choices[0];return{isEnd:e?.finish_reason==="stop",result:e?.delta?.content??""}}class me 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 _e(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 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 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(Q.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=D.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(D.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(Q.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 me(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"}},Ce={cancel:"Cancel",confirm:"Confirm"},Oe={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"}},De={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"}},Se={tooltip:"Diona Application",showWindow:"Show Window",exit:"Exit"},be={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:Ce,settings:Oe,menu:De,tray:Se,timeAgo:be,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:"选择消息"}},$e={tooltip:"迪奥娜",showWindow:"显示窗口",exit:"退出"},Pe={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:$e,timeAgo:Pe,app:He},Ue={en:Ne,zh:Ge};function x(){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 k;function ie(){return k!=null||(k=E.join(__dirname,"/public/logo.ico")),k}class j{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 j),this._instance}get isDark(){return this._isDark}get themeMode(){return o.nativeTheme.themeSource}}const L=j.getInstance(),Fe={frame:!1,titleBarStyle:"hidden",trafficLightPosition:{x:-100,y:-100},show:!1,title:"NIANXX",darkTheme:L.isDark,backgroundColor:L.isDark?"#2C2C2C":"#FFFFFF",webPreferences:{nodeIntegration:!1,contextIsolation:!0,sandbox:!0,backgroundThrottling:!1,preload:MAIN_WINDOW_VITE_DEV_SERVER_URL?E.join(process.cwd(),"dist-electron/preload/preload.js"):E.join(__dirname,"preload.js")}};class Y{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 Y),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=new o.WebContentsView,s=!1;e.contentView?.addChildView(i),i.setBounds({x:0,y:0,width:t.width,height:t.height}),MAIN_WINDOW_VITE_DEV_SERVER_URL?i.webContents.loadURL(`${MAIN_WINDOW_VITE_DEV_SERVER_URL}/loading.html`):i.webContents.loadFile(E.join(__dirname,`../renderer/${MAIN_WINDOW_VITE_NAME}/loading.html`));const r=c=>{c.sender!==e?.webContents||s||(s=!0,e.contentView.removeChildView(i),o.ipcMain.removeListener(a.RENDERER_IS_READY,r),i=void 0)};return o.ipcMain.on(a.RENDERER_IS_READY,r),c=>i?.webContents.once("dom-ready",()=>{i?.webContents.insertCSS(`body {
|
|
background-color: ${L.isDark?"#2C2C2C":"#FFFFFF"} !important;
|
|
--stop-color-start: ${L.isDark?"#A0A0A0":"#7F7F7F"} !important;
|
|
--stop-color-end: ${L.isDark?"#A0A0A0":"#7F7F7F"} !important;
|
|
}`),c()})}_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(E.join(__dirname,`../renderer/${MAIN_WINDOW_VITE_NAME}/${t}.html`))}_loadWindowTemplate(e,t){const i=t===w.LOADING?"loading":"index";this._loadPage(e,i)}_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({...Fe,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 $=x();class q{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())})}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 s=G(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:$(l?.label)??void 0,submenu:l.submenu?.map(p=>c(p))}:{...l,label:$(l?.label)??void 0},d=s.map(l=>{if(!Array.isArray(r)||!r.length)return c(l);const p=r.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 S=r.find(A=>A.id===m.id);return{...m,...S}})}):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=q.getInstance();let N=x();class X{static _instance;_tray=null;_removeLanguageListener;_setupLanguageChangeListener(){this._removeLanguageListener=T.onConfigChange(e=>{e[g.LANGUAGE]&&(N=x(),this._tray&&this._updateTray())})}_updateTray(){this._tray||(this._tray=new o.Tray(ie()));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,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 X),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=X.getInstance();class ze{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?E.join(process.cwd(),"dist-electron/preload/preload.js"):E.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:I.PIN,label:"menu.conversation.pinConversation",click:()=>e(I.PIN)},{id:I.RENAME,label:"menu.conversation.renameConversation",click:()=>e(I.RENAME)},{id:I.DEL,label:"menu.conversation.delConversation",click:()=>e(I.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:_.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=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:C.COPY,label:"menu.message.copyMessage",click:()=>i(C.COPY)},{id:C.SELECT,label:"menu.message.selectMessage",click:()=>i(C.SELECT)},{type:"separator"},{id:C.DELETE,label:"menu.message.deleteMessage",click:()=>i(C.DELETE)}])};function se(){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],K(e))}),K(e),je(n);const t=new ze(n);t.enable(),n.on("closed",()=>{t.destroy()})}),W.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=W.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 E.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??"",m=t?.channels??"",S=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 m=="string"?m:JSON.stringify(m),START_TAB_INDEX:String(S)},stdio:"pipe"});let O="",b="";A.stdout&&A.stdout.on("data",M=>{const R=M.toString();O=s(O,R),h.info(`stdout: ${R}`)}),A.stderr&&A.stderr.on("data",M=>{const R=M.toString();b=s(b,R),h.info(`stderr: ${R}`)}),A.on("exit",M=>{h.info(`子进程退出,退出码 ${M}`),r({success:M===0,exitCode:M,stdoutTail:O,stderrTail:b,...M===0?{}:{error:`Script exited with code ${M}`}})})}catch(c){r({success:!1,exitCode:null,stdoutTail:"",stderrTail:"",error:c?.message||"运行 Node 脚本时出错"})}})}}const H=new Map;function J(){return o.app.isPackaged?B.join(__dirname,"scripts"):B.join(process.cwd(),"electron/scripts")}function Ve(){const n=new Je;o.ipcMain.handle(a.OPEN_CHANNEL,async(e,t)=>{try{await Ke();const i=J(),s=B.join(i,"open_all_channel.js");if(H.clear(),Array.isArray(t))for(let c=0;c<t.length;c++){const d=t[c]?.channelName;d&&H.set(String(d),c)}return{success:!0,result:await n.executeScript(s,{channels:t})}}catch(i){return{success:!1,error:i?.message||"open channel failed"}}}),o.ipcMain.handle(a.EXECUTE_SCRIPT,async(e,t)=>{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 m=B.join(c,p);if(!V.existsSync(m))throw new Error(`Script not found for channel ${l}: ${m}`);return{channel:l,scriptPath:m}}),f=[];for(let l=0;l<d.length;l++){const p=d[l],m={fzName:"fliggy",mtName:"meituan",dyHotelName:"douyin",dyHotSpringName:"douyin"},S={fliggy:0,meituan:1,douyin:2},A=m[p.channel],O=A?H.get(A)??S[A]??l:l;h.info(`Launching script for channel ${p.channel}: ${p.scriptPath} (tabIndex: ${O})`);const b=await n.executeScript(p.scriptPath,{roomType:i[p.channel],startTime:t.startTime,endTime:t.endTime,operation:t.operation,tabIndex:O});f.push({channel:p.channel,scriptPath:p.scriptPath,...b})}return{success:!0,result:f}}catch(i){return{success:!1,error:i.message}}})}ae&&o.app.quit();o.app.whenReady().then(()=>{se(),Ve()});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()});
|