├── .gitignore ├── README.md ├── build ├── extraResources │ ├── chromeExtension │ │ └── read.txt │ ├── dll │ │ └── myDllDemo.dll │ └── read.txt ├── icons │ ├── 128x128.png │ ├── 16x16.png │ ├── 256x256.png │ ├── 32x32.png │ ├── 48x48.png │ ├── 512x512.png │ ├── 64x64.png │ ├── icon.icns │ ├── icon.ico │ └── icon.png └── script │ └── installer.nsh ├── data ├── sqlite.db └── system.json ├── electron ├── addon │ ├── app │ │ └── index.js │ ├── autoUpdater │ │ └── index.js │ ├── awaken │ │ └── index.js │ ├── chromeExtension │ │ └── index.js │ ├── security │ │ └── index.js │ └── tray │ │ └── index.js ├── common │ ├── appConfig.js │ └── initCreateSql.js ├── config │ ├── bin.js │ ├── builder.json │ ├── config.default.js │ ├── config.local.js │ ├── config.prod.js │ └── nodemon.json ├── controller │ ├── app.js │ ├── example.js │ ├── file.js │ └── storage.js ├── index.js ├── jobs │ └── example │ │ ├── hello.js │ │ └── timer.js ├── preload │ ├── bridge.js │ └── index.js └── service │ ├── app.js │ ├── example.js │ ├── file.js │ ├── storage.js │ └── user.js ├── frontend ├── .env.development ├── .env.production ├── .gitignore ├── base.less ├── index.html ├── package.json ├── postcss.config.js ├── src │ ├── App.vue │ ├── api │ │ └── main.js │ ├── assets │ │ ├── css │ │ │ └── tailwind.css │ │ ├── global.less │ │ ├── home │ │ │ ├── bilibili.png │ │ │ └── dy.png │ │ └── icon │ │ │ ├── log.svg │ │ │ └── logo.png │ ├── components │ │ └── global │ │ │ ├── Footer.vue │ │ │ ├── PageOperation.vue │ │ │ ├── TitleBar.vue │ │ │ └── index.js │ ├── layout │ │ ├── components │ │ │ ├── AppMain.vue │ │ │ ├── Logo.vue │ │ │ ├── NavBar.vue │ │ │ └── Version.vue │ │ └── index.vue │ ├── main.js │ ├── router │ │ ├── index.js │ │ └── routerMap.js │ ├── store │ │ └── app.js │ ├── utils │ │ └── ipcRenderer.js │ └── views │ │ ├── components │ │ ├── InputSearch.vue │ │ └── WebCard.vue │ │ ├── home │ │ ├── audio │ │ │ └── index.vue │ │ ├── index.vue │ │ └── setting │ │ │ └── index.vue │ │ ├── read │ │ ├── ChaptersDrawer.vue │ │ ├── EditModal.vue │ │ ├── detail.vue │ │ └── index.vue │ │ ├── settings │ │ └── index.vue │ │ ├── study │ │ └── index.vue │ │ └── web │ │ ├── EditModal.vue │ │ └── index.vue ├── tailwind.config.js └── vite.config.js ├── main.js ├── package-lock.json ├── package.json └── public ├── dist ├── assets │ ├── WebCard-54f3b803.css │ ├── WebCard-c39805ae.js │ ├── detail-75f1420c.css │ ├── detail-c5047e38.js │ ├── index-3dc9b7be.css │ ├── index-520f1754.css │ ├── index-7178b9ee.js │ ├── index-8d47797c.css │ ├── index-9a4e248e.css │ ├── index-9ec7f2bd.js │ ├── index-a13539ee.js │ ├── index-b00bd2c4.css │ ├── index-c580b1b6.js │ ├── index-c73cc8f4.js │ ├── index-e19c5bd5.js │ └── lodash-022d070e.js └── index.html ├── html ├── loading.html └── view_example.html ├── images ├── app-logo.svg ├── example │ ├── img.png │ ├── img2.png │ ├── img3.png │ ├── img4.png │ ├── img5.png │ └── logo.png └── tray.png └── ssl ├── localhost+1.key └── localhost+1.pem /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | out 3 | logs 4 | node_modules 5 | frontend/node_modules 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

🎉🎉🎉 ToReduce V1.0.0已发布! 🎉🎉🎉

3 |
4 |
5 | 6 |
7 | 8 |
9 | 10 |
11 |

一个简单、好用的、新一代摸鱼神器

12 |
13 |
14 | 15 | ## 📋 介绍 16 | 17 | > ToReduce 是一款基于 Electron-Egg 开发的跨平台多摸鱼软件,为了上班族打造的上班必备神器,使用此软件可以让上班倍感轻松,远离EMO 18 | 19 | ## 👦 谁可以使用 20 | - 996社畜 21 | - 在职考编 22 | - 在职考研 23 | 24 | ## ✈️ 应用下载 25 | - win64:[下载](https://wwyt.lanzn.com/ilhVl2huxhsd) 26 | - win32:[下载](https://wwyt.lanzn.com/i1ufD2hv41ab) 27 | 28 | ## 🐶 应用截图 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | ## 📺 技术 38 | 1. vue3 39 | 2. electron 40 | 3. splite 用于数据存储 41 | 4. [electron-egg](https://github.com/dromara/electron-egg) 42 | 43 | ## 👦 联系我 44 | 邮箱:980460365@qq.com 45 | -------------------------------------------------------------------------------- /build/extraResources/chromeExtension/read.txt: -------------------------------------------------------------------------------- 1 | chrome应用商店ctx文件,解压后,放置在此目录中,打包时会将资源加入安装包内。 -------------------------------------------------------------------------------- /build/extraResources/dll/myDllDemo.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lazycce/ToReduce/a889b034c44924350cc043b06d81ffc75a394db3/build/extraResources/dll/myDllDemo.dll -------------------------------------------------------------------------------- /build/extraResources/read.txt: -------------------------------------------------------------------------------- 1 | 建议第三方软件放置在此目录中,打包时会将资源加入安装包内。 -------------------------------------------------------------------------------- /build/icons/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lazycce/ToReduce/a889b034c44924350cc043b06d81ffc75a394db3/build/icons/128x128.png -------------------------------------------------------------------------------- /build/icons/16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lazycce/ToReduce/a889b034c44924350cc043b06d81ffc75a394db3/build/icons/16x16.png -------------------------------------------------------------------------------- /build/icons/256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lazycce/ToReduce/a889b034c44924350cc043b06d81ffc75a394db3/build/icons/256x256.png -------------------------------------------------------------------------------- /build/icons/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lazycce/ToReduce/a889b034c44924350cc043b06d81ffc75a394db3/build/icons/32x32.png -------------------------------------------------------------------------------- /build/icons/48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lazycce/ToReduce/a889b034c44924350cc043b06d81ffc75a394db3/build/icons/48x48.png -------------------------------------------------------------------------------- /build/icons/512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lazycce/ToReduce/a889b034c44924350cc043b06d81ffc75a394db3/build/icons/512x512.png -------------------------------------------------------------------------------- /build/icons/64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lazycce/ToReduce/a889b034c44924350cc043b06d81ffc75a394db3/build/icons/64x64.png -------------------------------------------------------------------------------- /build/icons/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lazycce/ToReduce/a889b034c44924350cc043b06d81ffc75a394db3/build/icons/icon.icns -------------------------------------------------------------------------------- /build/icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lazycce/ToReduce/a889b034c44924350cc043b06d81ffc75a394db3/build/icons/icon.ico -------------------------------------------------------------------------------- /build/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lazycce/ToReduce/a889b034c44924350cc043b06d81ffc75a394db3/build/icons/icon.png -------------------------------------------------------------------------------- /build/script/installer.nsh: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lazycce/ToReduce/a889b034c44924350cc043b06d81ffc75a394db3/build/script/installer.nsh -------------------------------------------------------------------------------- /data/sqlite.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lazycce/ToReduce/a889b034c44924350cc043b06d81ffc75a394db3/data/sqlite.db -------------------------------------------------------------------------------- /data/system.json: -------------------------------------------------------------------------------- 1 | { 2 | "cache": { 3 | "config": { 4 | "env": "local", 5 | "name": "ToReduce", 6 | "baseDir": "D:\\project\\gitee\\thief\\electron", 7 | "HOME": "D:\\project\\gitee\\thief", 8 | "rundir": "D:\\project\\gitee\\thief\\electron\\run", 9 | "dump": { 10 | "ignore": {} 11 | }, 12 | "homeDir": "D:\\project\\gitee\\thief", 13 | "root": "D:\\project\\gitee\\thief", 14 | "appUserDataDir": "C:\\Users\\haotian lu\\AppData\\Roaming\\ToReduce", 15 | "userHome": "C:\\Users\\haotian lu", 16 | "appVersion": "1.0.0", 17 | "isPackaged": false, 18 | "execDir": "D:\\project\\gitee\\thief", 19 | "logger": { 20 | "type": "application", 21 | "dir": "D:\\project\\gitee\\thief\\logs", 22 | "encoding": "utf8", 23 | "env": "local", 24 | "level": "INFO", 25 | "consoleLevel": "INFO", 26 | "disableConsoleAfterReady": false, 27 | "outputJSON": false, 28 | "buffer": true, 29 | "appLogName": "ee.log", 30 | "coreLogName": "ee-core.log", 31 | "agentLogName": "ee-agent.log", 32 | "errorLogName": "ee-error.log", 33 | "coreLogger": {}, 34 | "allowDebugAtProd": false, 35 | "enablePerformanceTimer": false, 36 | "rotator": "day" 37 | }, 38 | "customLogger": {}, 39 | "httpclient": { 40 | "enableDNSCache": false, 41 | "dnsCacheLookupInterval": 10000, 42 | "dnsCacheMaxLength": 1000, 43 | "request": { 44 | "timeout": 5000 45 | }, 46 | "httpAgent": { 47 | "keepAlive": true, 48 | "freeSocketTimeout": 4000, 49 | "maxSockets": 9007199254740991, 50 | "maxFreeSockets": 256 51 | }, 52 | "httpsAgent": { 53 | "keepAlive": true, 54 | "freeSocketTimeout": 4000, 55 | "maxSockets": 9007199254740991, 56 | "maxFreeSockets": 256 57 | } 58 | }, 59 | "developmentMode": { 60 | "default": "vue", 61 | "mode": { 62 | "vue": { 63 | "protocol": "http://", 64 | "hostname": "localhost", 65 | "port": 8080 66 | }, 67 | "react": { 68 | "protocol": "http://", 69 | "hostname": "localhost", 70 | "port": 3000 71 | }, 72 | "html": { 73 | "protocol": "http://", 74 | "hostname": "localhost", 75 | "indexPage": "index.html" 76 | } 77 | } 78 | }, 79 | "socketServer": { 80 | "enable": false, 81 | "port": 7070, 82 | "path": "/socket.io/", 83 | "connectTimeout": 45000, 84 | "pingTimeout": 30000, 85 | "pingInterval": 25000, 86 | "maxHttpBufferSize": 100000000, 87 | "transports": [ 88 | "polling", 89 | "websocket" 90 | ], 91 | "cors": { 92 | "origin": true 93 | }, 94 | "channel": "c1" 95 | }, 96 | "httpServer": { 97 | "enable": false, 98 | "https": { 99 | "enable": false, 100 | "key": "/public/ssl/localhost+1.key", 101 | "cert": "/public/ssl/localhost+1.pem" 102 | }, 103 | "protocol": "http://", 104 | "host": "127.0.0.1", 105 | "port": 7071, 106 | "cors": { 107 | "origin": "*" 108 | }, 109 | "body": { 110 | "multipart": true, 111 | "formidable": { 112 | "keepExtensions": true 113 | } 114 | }, 115 | "filterRequest": { 116 | "uris": [ 117 | "favicon.ico" 118 | ], 119 | "returnData": "" 120 | } 121 | }, 122 | "mainServer": { 123 | "protocol": "file://", 124 | "indexPath": "/public/dist/index.html", 125 | "host": "localhost", 126 | "port": 7072, 127 | "open": false, 128 | "options": {}, 129 | "ssl": { 130 | "key": "", 131 | "cert": "" 132 | } 133 | }, 134 | "openAppMenu": false, 135 | "hardGpu": { 136 | "enable": true 137 | }, 138 | "storage": { 139 | "dir": "D:\\project\\gitee\\thief\\data" 140 | }, 141 | "addons": { 142 | "window": { 143 | "enable": true 144 | }, 145 | "tray": { 146 | "enable": true, 147 | "title": "ToReduce", 148 | "icon": "/public/images/tray.png" 149 | }, 150 | "security": { 151 | "enable": true 152 | }, 153 | "awaken": { 154 | "enable": true, 155 | "protocol": "ee", 156 | "args": [] 157 | }, 158 | "autoUpdater": { 159 | "enable": true, 160 | "windows": true, 161 | "macOS": true, 162 | "linux": true, 163 | "options": { 164 | "provider": "generic", 165 | "url": "http://files.dahanbao.cn/" 166 | }, 167 | "force": true 168 | } 169 | }, 170 | "exception": { 171 | "mainExit": false, 172 | "childExit": true, 173 | "rendererExit": true 174 | }, 175 | "cross": {}, 176 | "jobs": { 177 | "messageLog": true 178 | }, 179 | "openDevTools": { 180 | "mode": "undocked" 181 | }, 182 | "windowsOption": { 183 | "title": "ToReduce", 184 | "width": 750, 185 | "height": 470, 186 | "resizable": false, 187 | "webPreferences": { 188 | "contextIsolation": false, 189 | "nodeIntegration": true, 190 | "webviewTag": true 191 | }, 192 | "frame": false, 193 | "show": false, 194 | "icon": "D:\\project\\gitee\\thief\\public\\images\\app-logo.svg" 195 | }, 196 | "remoteUrl": { 197 | "enable": false, 198 | "url": "http://electron-egg.kaka996.com/" 199 | }, 200 | "coreMiddlewares": [], 201 | "coreMiddleware": [], 202 | "appMiddlewares": [], 203 | "appMiddleware": [] 204 | } 205 | } 206 | } -------------------------------------------------------------------------------- /electron/addon/app/index.js: -------------------------------------------------------------------------------- 1 | const { app, globalShortcut } = require('electron'); 2 | const Services = require('ee-core/services'); 3 | const CoreWindow = require('ee-core/electron/window'); 4 | 5 | /** 6 | * AppAddon 7 | */ 8 | class AppAddon { 9 | create () { 10 | const main = CoreWindow.getMainWindow(); 11 | globalShortcut.register('Control+Q', () => { 12 | app.quit(); // 强制退出应用 13 | }); 14 | globalShortcut.register('Control+Tab', () => { 15 | if (main.isVisible()) { 16 | main.hide(); 17 | } else { 18 | main.show(); 19 | main.focus(); 20 | } 21 | }); 22 | globalShortcut.register('Control+1', () => { 23 | const opacity = main.getOpacity(); 24 | Services.get('app').setWindowOpacity({ 25 | opacity: Math.max(0.01, opacity - 0.01) 26 | }) 27 | }) 28 | globalShortcut.register('Control+2', () => { 29 | const opacity = main.getOpacity(); 30 | Services.get('app').setWindowOpacity({ 31 | opacity: Math.min(1, opacity + 0.01) 32 | }) 33 | }) 34 | } 35 | } 36 | 37 | AppAddon.toString = () => '[class AppAddon]'; 38 | module.exports = AppAddon; 39 | -------------------------------------------------------------------------------- /electron/addon/autoUpdater/index.js: -------------------------------------------------------------------------------- 1 | const { app: electronApp } = require('electron'); 2 | const { autoUpdater } = require("electron-updater"); 3 | const is = require('ee-core/utils/is'); 4 | const Log = require('ee-core/log'); 5 | const Conf = require('ee-core/config'); 6 | const CoreWindow = require('ee-core/electron/window'); 7 | const Electron = require('ee-core/electron'); 8 | 9 | /** 10 | * 自动升级插件 11 | * @class 12 | */ 13 | class AutoUpdaterAddon { 14 | 15 | /** 16 | * 创建 17 | */ 18 | create () { 19 | Log.info('[addon:autoUpdater] load'); 20 | const cfg = Conf.getValue('addons.autoUpdater'); 21 | if ((is.windows() && cfg.windows) 22 | || (is.macOS() && cfg.macOS) 23 | || (is.linux() && cfg.linux)) 24 | { 25 | // continue 26 | } else { 27 | return 28 | } 29 | Log.info("[addon:autoUpdater] 检查是否更新"); 30 | // 是否检查更新 31 | if (cfg.force) { 32 | this.checkUpdate(); 33 | } 34 | 35 | const status = { 36 | error: -1, 37 | available: 1, 38 | noAvailable: 2, 39 | downloading: 3, 40 | downloaded: 4, 41 | } 42 | 43 | const version = electronApp.getVersion(); 44 | Log.info('[addon:autoUpdater] current version: ', version); 45 | 46 | // 设置下载服务器地址 47 | let server = cfg.options.url; 48 | let lastChar = server.substring(server.length - 1); 49 | server = lastChar === '/' ? server : server + "/"; 50 | //Log.info('[addon:autoUpdater] server: ', server); 51 | cfg.options.url = server; 52 | 53 | // 是否后台自动下载 54 | autoUpdater.autoDownload = cfg.force ? true : false; 55 | 56 | try { 57 | autoUpdater.setFeedURL(cfg.options); 58 | } catch (error) { 59 | Log.error('[addon:autoUpdater] setFeedURL error : ', error); 60 | } 61 | 62 | autoUpdater.on('checking-for-update', () => { 63 | //sendStatusToWindow('正在检查更新...'); 64 | }) 65 | autoUpdater.on('update-available', (info) => { 66 | info.status = status.available; 67 | info.desc = '有可用更新'; 68 | this.sendStatusToWindow(info); 69 | }) 70 | autoUpdater.on('update-not-available', (info) => { 71 | info.status = status.noAvailable; 72 | info.desc = '没有可用更新'; 73 | this.sendStatusToWindow(info); 74 | }) 75 | autoUpdater.on('error', (err) => { 76 | let info = { 77 | status: status.error, 78 | desc: err 79 | } 80 | this.sendStatusToWindow(info); 81 | }) 82 | autoUpdater.on('download-progress', (progressObj) => { 83 | let percentNumber = parseInt(progressObj.percent); 84 | let totalSize = this.bytesChange(progressObj.total); 85 | let transferredSize = this.bytesChange(progressObj.transferred); 86 | let text = '已下载 ' + percentNumber + '%'; 87 | text = text + ' (' + transferredSize + "/" + totalSize + ')'; 88 | 89 | let info = { 90 | status: status.downloading, 91 | desc: text, 92 | percentNumber: percentNumber, 93 | totalSize: totalSize, 94 | transferredSize: transferredSize 95 | } 96 | Log.info('[addon:autoUpdater] progress: ', text); 97 | this.sendStatusToWindow(info); 98 | }) 99 | autoUpdater.on('update-downloaded', (info) => { 100 | info.status = status.downloaded; 101 | info.desc = '下载完成'; 102 | this.sendStatusToWindow(info); 103 | 104 | // 托盘插件默认会阻止窗口关闭,这里设置允许关闭窗口 105 | Electron.extra.closeWindow = true; 106 | 107 | autoUpdater.quitAndInstall(); 108 | // const mainWindow = CoreWindow.getMainWindow(); 109 | // if (mainWindow) { 110 | // mainWindow.destroy() 111 | // } 112 | // electronApp.appQuit() 113 | }); 114 | } 115 | 116 | /** 117 | * 检查更新 118 | */ 119 | checkUpdate () { 120 | autoUpdater.checkForUpdates(); 121 | } 122 | 123 | /** 124 | * 下载更新 125 | */ 126 | download () { 127 | autoUpdater.downloadUpdate(); 128 | } 129 | 130 | /** 131 | * 向前端发消息 132 | */ 133 | sendStatusToWindow(content = {}) { 134 | const textJson = JSON.stringify(content); 135 | const channel = 'app.updater'; 136 | const win = CoreWindow.getMainWindow(); 137 | win.webContents.send(channel, textJson); 138 | } 139 | 140 | /** 141 | * 单位转换 142 | */ 143 | bytesChange (limit) { 144 | let size = ""; 145 | if(limit < 0.1 * 1024){ 146 | size = limit.toFixed(2) + "B"; 147 | }else if(limit < 0.1 * 1024 * 1024){ 148 | size = (limit/1024).toFixed(2) + "KB"; 149 | }else if(limit < 0.1 * 1024 * 1024 * 1024){ 150 | size = (limit/(1024 * 1024)).toFixed(2) + "MB"; 151 | }else{ 152 | size = (limit/(1024 * 1024 * 1024)).toFixed(2) + "GB"; 153 | } 154 | 155 | let sizeStr = size + ""; 156 | let index = sizeStr.indexOf("."); 157 | let dou = sizeStr.substring(index + 1 , index + 3); 158 | if(dou == "00"){ 159 | return sizeStr.substring(0, index) + sizeStr.substring(index + 3, index + 5); 160 | } 161 | 162 | return size; 163 | } 164 | } 165 | 166 | AutoUpdaterAddon.toString = () => '[class AutoUpdaterAddon]'; 167 | module.exports = AutoUpdaterAddon; 168 | -------------------------------------------------------------------------------- /electron/addon/awaken/index.js: -------------------------------------------------------------------------------- 1 | const { app: electronApp } = require('electron'); 2 | const Log = require('ee-core/log'); 3 | const Conf = require('ee-core/config'); 4 | const Services = require('ee-core/services'); 5 | 6 | /** 7 | * 唤醒插件 8 | * @class 9 | */ 10 | class AwakenAddon { 11 | 12 | constructor() { 13 | this.protocol = ''; 14 | } 15 | 16 | /** 17 | * 创建 18 | */ 19 | create () { 20 | Log.info('[addon:awaken] load'); 21 | 22 | const cfg = Conf.getValue('addons.awaken'); 23 | this.protocol = cfg.protocol; 24 | 25 | electronApp.setAsDefaultProtocolClient(this.protocol); 26 | 27 | this.handleArgv(process.argv); 28 | electronApp.on('second-instance', (event, argv) => { 29 | if (process.platform === 'win32') { 30 | this.handleArgv(argv) 31 | } 32 | }) 33 | 34 | // 仅用于macOS 35 | electronApp.on('open-url', (event, urlStr) => { 36 | this.handleUrl(urlStr) 37 | }) 38 | 39 | // 初始化表 40 | Services.get('storage').init(); 41 | } 42 | 43 | /** 44 | * 参数处理 45 | */ 46 | handleArgv(argv) { 47 | const offset = electronApp.isPackaged ? 1 : 2; 48 | const url = argv.find((arg, i) => i >= offset && arg.startsWith(this.protocol)); 49 | this.handleUrl(url) 50 | } 51 | 52 | /** 53 | * url解析 54 | */ 55 | handleUrl(awakeUrlStr) { 56 | if (!awakeUrlStr || awakeUrlStr.length === 0) { 57 | return 58 | } 59 | const {hostname, pathname, search} = new URL(awakeUrlStr); 60 | let awakeUrlInfo = { 61 | urlStr: awakeUrlStr, 62 | urlHost: hostname, 63 | urlPath: pathname, 64 | urlParams: search && search.slice(1) 65 | } 66 | Log.info('[addon:awaken] awakeUrlInfo:', awakeUrlInfo); 67 | } 68 | } 69 | 70 | AwakenAddon.toString = () => '[class AwakenAddon]'; 71 | module.exports = AwakenAddon; 72 | -------------------------------------------------------------------------------- /electron/addon/chromeExtension/index.js: -------------------------------------------------------------------------------- 1 | const { app, session } = require('electron'); 2 | const _ = require('lodash'); 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const Log = require('ee-core/log'); 6 | 7 | /** 8 | * 扩展插件 (electron自身对该功能并不完全支持,官方也不建议使用) 9 | * @class 10 | */ 11 | class ChromeExtensionAddon { 12 | 13 | constructor() { 14 | } 15 | 16 | /** 17 | * 创建 18 | */ 19 | async create () { 20 | Log.info('[addon:chromeExtension] load'); 21 | 22 | const extensionIds = this.getAllIds(); 23 | for (let i = 0; i < extensionIds.length; i++) { 24 | await this.load(extensionIds[i]); 25 | } 26 | } 27 | 28 | /** 29 | * 获取扩展id列表(crx解压后的目录名,即是该扩展的id) 30 | */ 31 | getAllIds () { 32 | const extendsionDir = this.getDirectory(); 33 | const ids = this.getDirs(extendsionDir); 34 | 35 | return ids; 36 | } 37 | 38 | /** 39 | * 扩展所在目录 40 | */ 41 | getDirectory () { 42 | let extensionDirPath = ''; 43 | let variablePath = 'build'; // 打包前路径 44 | if (app.isPackaged) { 45 | variablePath = '..'; // 打包后路径 46 | } 47 | extensionDirPath = path.join(app.getAppPath(), variablePath, "extraResources", "chromeExtension"); 48 | 49 | return extensionDirPath; 50 | } 51 | 52 | /** 53 | * 加载扩展 54 | */ 55 | async load (extensionId = '') { 56 | if (_.isEmpty(extensionId)) { 57 | return false 58 | } 59 | 60 | try { 61 | const extensionPath = path.join(this.getDirectory(), extensionId); 62 | Log.info('[addon:chromeExtension] extensionPath:', extensionPath); 63 | await session.defaultSession.loadExtension(extensionPath, { allowFileAccess: true }); 64 | } catch (e) { 65 | Log.info('[addon:chromeExtension] load extension error extensionId:%s, errorInfo:%s', extensionId, e.toString()); 66 | return false 67 | } 68 | 69 | return true 70 | } 71 | 72 | /** 73 | * 获取目录下所有文件夹 74 | */ 75 | getDirs(dir) { 76 | if (!dir) { 77 | return []; 78 | } 79 | 80 | const components = []; 81 | const files = fs.readdirSync(dir); 82 | files.forEach(function(item, index) { 83 | const stat = fs.lstatSync(dir + '/' + item); 84 | if (stat.isDirectory() === true) { 85 | components.push(item); 86 | } 87 | }); 88 | 89 | return components; 90 | }; 91 | } 92 | 93 | ChromeExtensionAddon.toString = () => '[class ChromeExtensionAddon]'; 94 | module.exports = ChromeExtensionAddon; -------------------------------------------------------------------------------- /electron/addon/security/index.js: -------------------------------------------------------------------------------- 1 | const Log = require('ee-core/log'); 2 | const EE = require('ee-core/ee'); 3 | 4 | /** 5 | * 安全插件 6 | * @class 7 | */ 8 | class SecurityAddon { 9 | 10 | constructor() { 11 | } 12 | 13 | /** 14 | * 创建 15 | */ 16 | create () { 17 | Log.info('[addon:security] load'); 18 | const { CoreApp } = EE; 19 | const runWithDebug = process.argv.find(function(e){ 20 | let isHasDebug = e.includes("--inspect") || e.includes("--inspect-brk") || e.includes("--remote-debugging-port"); 21 | return isHasDebug; 22 | }) 23 | 24 | // 不允许远程调试 25 | if (runWithDebug) { 26 | Log.error('[error] Remote debugging is not allowed, runWithDebug:', runWithDebug); 27 | CoreApp.appQuit(); 28 | } 29 | } 30 | } 31 | 32 | SecurityAddon.toString = () => '[class SecurityAddon]'; 33 | module.exports = SecurityAddon; -------------------------------------------------------------------------------- /electron/addon/tray/index.js: -------------------------------------------------------------------------------- 1 | const { Tray, Menu } = require('electron'); 2 | const path = require('path'); 3 | const Ps = require('ee-core/ps'); 4 | const Log = require('ee-core/log'); 5 | const Electron = require('ee-core/electron'); 6 | const CoreWindow = require('ee-core/electron/window'); 7 | const Conf = require('ee-core/config'); 8 | const EE = require('ee-core/ee'); 9 | 10 | /** 11 | * 托盘插件 12 | * @class 13 | */ 14 | class TrayAddon { 15 | 16 | constructor() { 17 | this.tray = null; 18 | } 19 | 20 | /** 21 | * 创建托盘 22 | */ 23 | create () { 24 | // 开发环境,代码热更新开启时,会导致托盘中有残影 25 | if (Ps.isDev() && Ps.isHotReload()) return; 26 | 27 | Log.info('[addon:tray] load'); 28 | const { CoreApp } = EE; 29 | const cfg = Conf.getValue('addons.tray'); 30 | const mainWindow = CoreWindow.getMainWindow(); 31 | 32 | // 托盘图标 33 | let iconPath = path.join(Ps.getHomeDir(), cfg.icon); 34 | 35 | // 托盘菜单功能列表 36 | let trayMenuTemplate = [ 37 | { 38 | label: '显示', 39 | click: function () { 40 | mainWindow.show(); 41 | } 42 | }, 43 | { 44 | label: '退出', 45 | click: function () { 46 | CoreApp.appQuit(); 47 | } 48 | } 49 | ] 50 | 51 | // 点击关闭,最小化到托盘 52 | mainWindow.on('close', (event) => { 53 | if (Electron.extra.closeWindow === true) { 54 | return; 55 | } 56 | mainWindow.hide(); 57 | event.preventDefault(); 58 | }); 59 | 60 | // 实例化托盘 61 | this.tray = new Tray(iconPath); 62 | this.tray.setToolTip(cfg.title); 63 | const contextMenu = Menu.buildFromTemplate(trayMenuTemplate); 64 | this.tray.setContextMenu(contextMenu); 65 | this.tray.on('double-click', () => { 66 | mainWindow.show() 67 | }) 68 | } 69 | } 70 | 71 | TrayAddon.toString = () => '[class TrayAddon]'; 72 | module.exports = TrayAddon; 73 | -------------------------------------------------------------------------------- /electron/common/appConfig.js: -------------------------------------------------------------------------------- 1 | 2 | const phoneWindowSize = { 3 | width: 425, 4 | height: 650 5 | } 6 | 7 | const pcWindowSize = { 8 | width: 750, 9 | height: 470, 10 | } 11 | 12 | module.exports = { 13 | phoneWindowSize, 14 | pcWindowSize 15 | } 16 | -------------------------------------------------------------------------------- /electron/common/initCreateSql.js: -------------------------------------------------------------------------------- 1 | const createSysWebSql = ` 2 | CREATE TABLE sys_web 3 | ( 4 | id INTEGER PRIMARY KEY AUTOINCREMENT, 5 | name CHAR(50), 6 | icon Text, 7 | url Text, 8 | showType CHAR(50), 9 | category CHAR(50), 10 | sort INT 11 | ); 12 | ` 13 | 14 | const createSysNovel = ` 15 | CREATE TABLE sys_novel 16 | ( 17 | id INTEGER PRIMARY KEY AUTOINCREMENT, 18 | name CHAR(50), 19 | contentType CHAR(50), 20 | category CHAR(50), 21 | progress INT, 22 | wordReadCount INT, 23 | totalWordCount INT, 24 | charactersReadCount INT, 25 | chaptersCount INT, 26 | sort INT 27 | ); 28 | ` 29 | 30 | const createSysNovelDetail = ` 31 | CREATE TABLE sys_novel_detail 32 | ( 33 | novelId INTEGER, 34 | content Text 35 | ); 36 | ` 37 | 38 | const createSysConfig = ` 39 | CREATE TABLE sys_config 40 | ( 41 | key CHAR(50), 42 | value CHAR(50) 43 | ) 44 | ` 45 | 46 | const sysWebTable = 'sys_web' 47 | const sysNovelTable = 'sys_novel' 48 | const sysNovelDetailTable = 'sys_novel_detail' 49 | const sysConfigTable = 'sys_config' 50 | 51 | const initWebData = [{ 52 | name: '抖音', 53 | icon: '', 54 | url: 'https://www.douyin.com/?is_from_mobile_home=1&recommend=1', 55 | category: 'entertainment', 56 | showType: 'phone', 57 | sort: 1 58 | }, { 59 | name: 'B站', 60 | icon: '', 61 | url: 'https://m.bilibili.com/', 62 | category: 'entertainment', 63 | showType: 'phone', 64 | sort: 2 65 | }, { 66 | name: '百度', 67 | icon: '', 68 | url: 'https://www.baidu.com', 69 | category: 'study', 70 | showType: 'phone', 71 | sort: 1 72 | }] 73 | 74 | const initSysConfigData = [{ 75 | key: 'alwaysOnTop', 76 | value: '0' 77 | }] 78 | 79 | module.exports = { 80 | createSysWebSql, 81 | createSysNovel, 82 | createSysNovelDetail, 83 | createSysConfig, 84 | sysWebTable, 85 | sysNovelTable, 86 | sysNovelDetailTable, 87 | sysConfigTable, 88 | initWebData, 89 | initSysConfigData 90 | } 91 | -------------------------------------------------------------------------------- /electron/config/bin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ee-bin 配置 3 | * 仅适用于开发环境 4 | */ 5 | module.exports = { 6 | /** 7 | * development serve ("frontend" "electron" ) 8 | * ee-bin dev 9 | */ 10 | dev: { 11 | frontend: { 12 | directory: './frontend', 13 | cmd: 'npm', 14 | args: ['run', 'dev'], 15 | protocol: 'http://', 16 | hostname: 'localhost', 17 | port: 8888, 18 | indexPath: 'index.html' 19 | }, 20 | electron: { 21 | directory: './', 22 | cmd: 'electron', 23 | args: ['.', '--env=local', '--color=always'], 24 | } 25 | }, 26 | 27 | /** 28 | * 构建 29 | * ee-bin build 30 | */ 31 | build: { 32 | frontend: { 33 | directory: './frontend', 34 | cmd: 'npm', 35 | args: ['run', 'build'], 36 | } 37 | }, 38 | 39 | /** 40 | * 移动资源 41 | * ee-bin move 42 | */ 43 | move: { 44 | frontend_dist: { 45 | dist: './frontend/dist', 46 | target: './public/dist' 47 | } 48 | }, 49 | 50 | /** 51 | * 预发布模式(prod) 52 | * ee-bin start 53 | */ 54 | start: { 55 | directory: './', 56 | cmd: 'electron', 57 | args: ['.', '--env=prod'] 58 | }, 59 | 60 | /** 61 | * 加密 62 | */ 63 | encrypt: { 64 | type: 'confusion', 65 | files: [ 66 | 'electron/**/*.(js|json)', 67 | '!electron/config/encrypt.js', 68 | '!electron/config/nodemon.json', 69 | '!electron/config/builder.json', 70 | '!electron/config/bin.json', 71 | ], 72 | fileExt: ['.js'], 73 | confusionOptions: { 74 | compact: true, 75 | stringArray: true, 76 | stringArrayEncoding: ['none'], 77 | deadCodeInjection: false, 78 | } 79 | }, 80 | 81 | /** 82 | * 执行自定义命令 83 | * ee-bin exec 84 | */ 85 | exec: { 86 | node_v: { 87 | directory: './', 88 | cmd: 'node', 89 | args: ['-v'], 90 | }, 91 | npm_v: { 92 | directory: './', 93 | cmd: 'npm', 94 | args: ['-v'], 95 | }, 96 | }, 97 | }; 98 | -------------------------------------------------------------------------------- /electron/config/builder.json: -------------------------------------------------------------------------------- 1 | { 2 | "productName": "ToReduce", 3 | "appId": "appId", 4 | "copyright": "© 2024 ToReduce Technology Co., Ltd.", 5 | "directories": { 6 | "output": "out" 7 | }, 8 | "asar": true, 9 | "files": [ 10 | "**/*", 11 | "!frontend/", 12 | "!run/", 13 | "!logs/", 14 | "!data/" 15 | ], 16 | "extraResources": { 17 | "from": "build/extraResources/", 18 | "to": "extraResources" 19 | }, 20 | "nsis": { 21 | "oneClick": false, 22 | "allowElevation": true, 23 | "allowToChangeInstallationDirectory": true, 24 | "installerIcon": "build/icons/icon.ico", 25 | "uninstallerIcon": "build/icons/icon.ico", 26 | "installerHeaderIcon": "build/icons/icon.ico", 27 | "createDesktopShortcut": true, 28 | "createStartMenuShortcut": true, 29 | "shortcutName": "ToReduce" 30 | }, 31 | "publish": [ 32 | { 33 | "provider": "generic", 34 | "url": "https://github.com/wallace5303/electron-egg" 35 | } 36 | ], 37 | "mac": { 38 | "icon": "build/icons/icon.icns", 39 | "artifactName": "${productName}-${os}-${version}-${arch}.${ext}", 40 | "darkModeSupport": true, 41 | "hardenedRuntime": false 42 | }, 43 | "win": { 44 | "icon": "build/icons/icon.ico", 45 | "artifactName": "${productName}-${os}-${version}-${arch}.${ext}", 46 | "target": [ 47 | { 48 | "target": "nsis" 49 | } 50 | ] 51 | }, 52 | "linux": { 53 | "icon": "build/icons/icon.icns", 54 | "artifactName": "${productName}-${os}-${version}-${arch}.${ext}", 55 | "target": [ 56 | "deb" 57 | ], 58 | "category": "Utility" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /electron/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | 5 | /** 6 | * 默认配置 7 | */ 8 | module.exports = (appInfo) => { 9 | 10 | const config = {}; 11 | 12 | /** 13 | * 开发者工具 14 | */ 15 | config.openDevTools = false; 16 | 17 | /** 18 | * 应用程序顶部菜单 19 | */ 20 | config.openAppMenu = false; 21 | 22 | /** 23 | * 主窗口 24 | */ 25 | config.windowsOption = { 26 | title: 'ToReduce', 27 | width: 750, 28 | height: 470, 29 | resizable: false, 30 | // alwaysOnTop: true, 31 | webPreferences: { 32 | //webSecurity: false, 33 | contextIsolation: false, // false -> 可在渲染进程中使用electron的api,true->需要bridge.js(contextBridge) 34 | nodeIntegration: true, 35 | webviewTag: true, 36 | //preload: path.join(appInfo.baseDir, 'preload', 'bridge.js'), 37 | }, 38 | frame: false, 39 | show: false, 40 | icon: path.join(appInfo.home, 'public', 'images', 'app-logo.svg'), 41 | }; 42 | 43 | /** 44 | * ee框架日志 45 | */ 46 | config.logger = { 47 | encoding: 'utf8', 48 | level: 'INFO', 49 | outputJSON: false, 50 | buffer: true, 51 | enablePerformanceTimer: false, 52 | rotator: 'day', 53 | appLogName: 'ee.log', 54 | coreLogName: 'ee-core.log', 55 | errorLogName: 'ee-error.log' 56 | } 57 | 58 | /** 59 | * 远程模式-web地址 60 | */ 61 | config.remoteUrl = { 62 | enable: false, 63 | url: 'http://electron-egg.kaka996.com/' 64 | }; 65 | 66 | /** 67 | * 内置socket服务 68 | */ 69 | config.socketServer = { 70 | enable: false, 71 | port: 7070, 72 | path: "/socket.io/", 73 | connectTimeout: 45000, 74 | pingTimeout: 30000, 75 | pingInterval: 25000, 76 | maxHttpBufferSize: 1e8, 77 | transports: ["polling", "websocket"], 78 | cors: { 79 | origin: true, 80 | }, 81 | channel: 'c1' 82 | }; 83 | 84 | /** 85 | * 内置http服务 86 | */ 87 | config.httpServer = { 88 | enable: false, 89 | https: { 90 | enable: false, 91 | key: '/public/ssl/localhost+1.key', 92 | cert: '/public/ssl/localhost+1.pem' 93 | }, 94 | host: '127.0.0.1', 95 | port: 7071, 96 | cors: { 97 | origin: "*" 98 | }, 99 | body: { 100 | multipart: true, 101 | formidable: { 102 | keepExtensions: true 103 | } 104 | }, 105 | filterRequest: { 106 | uris: [ 107 | 'favicon.ico' 108 | ], 109 | returnData: '' 110 | } 111 | }; 112 | 113 | /** 114 | * 主进程 115 | */ 116 | config.mainServer = { 117 | protocol: 'file://', 118 | indexPath: '/public/dist/index.html', 119 | }; 120 | 121 | /** 122 | * 硬件加速 123 | */ 124 | config.hardGpu = { 125 | enable: true 126 | }; 127 | 128 | /** 129 | * 异常捕获 130 | */ 131 | config.exception = { 132 | mainExit: false, 133 | childExit: true, 134 | rendererExit: true, 135 | }; 136 | 137 | /** 138 | * jobs 139 | */ 140 | config.jobs = { 141 | messageLog: true 142 | }; 143 | 144 | /** 145 | * 插件功能 146 | */ 147 | config.addons = { 148 | window: { 149 | enable: true, 150 | }, 151 | tray: { 152 | enable: true, 153 | title: 'ToReduce', 154 | icon: '/public/images/tray.png' 155 | }, 156 | security: { 157 | enable: true, 158 | }, 159 | awaken: { 160 | enable: true, 161 | protocol: 'ee', 162 | args: [] 163 | }, 164 | autoUpdater: { 165 | enable: true, 166 | windows: true, 167 | macOS: true, 168 | linux: true, 169 | options: { 170 | provider: 'generic', 171 | url: '' 172 | }, 173 | force: true, 174 | } 175 | }; 176 | 177 | return { 178 | ...config 179 | }; 180 | } 181 | -------------------------------------------------------------------------------- /electron/config/config.local.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * 开发环境配置,覆盖 config.default.js 5 | */ 6 | module.exports = (appInfo) => { 7 | const config = {}; 8 | 9 | /** 10 | * 开发者工具 11 | */ 12 | config.openDevTools = { 13 | mode: 'undocked' 14 | }; 15 | 16 | /** 17 | * 应用程序顶部菜单 18 | */ 19 | config.openAppMenu = false; 20 | 21 | /** 22 | * jobs 23 | */ 24 | config.jobs = { 25 | messageLog: true 26 | }; 27 | 28 | return { 29 | ...config 30 | }; 31 | }; 32 | -------------------------------------------------------------------------------- /electron/config/config.prod.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * 生产环境配置,覆盖 config.default.js 5 | */ 6 | module.exports = (appInfo) => { 7 | const config = {}; 8 | 9 | /** 10 | * 开发者工具 11 | */ 12 | config.openDevTools = false; 13 | 14 | /** 15 | * 应用程序顶部菜单 16 | */ 17 | config.openAppMenu = false; 18 | 19 | /** 20 | * jobs 21 | */ 22 | config.jobs = { 23 | messageLog: false 24 | }; 25 | 26 | return { 27 | ...config 28 | }; 29 | }; 30 | -------------------------------------------------------------------------------- /electron/config/nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": [ 3 | "electron/", 4 | "main.js" 5 | ], 6 | "ignore": [], 7 | "ext": "js,json", 8 | "verbose": true, 9 | "exec": "electron . --env=local --hot-reload=1", 10 | "restartable": "hr", 11 | "colours": true, 12 | "events": {} 13 | } -------------------------------------------------------------------------------- /electron/controller/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { Controller } = require('ee-core'); 4 | const Log = require('ee-core/log'); 5 | const Services = require('ee-core/services'); 6 | const {nativeTheme} = require("electron/main"); 7 | const { 8 | phoneWindowSize, 9 | pcWindowSize 10 | } = require('../common/appConfig'); 11 | 12 | /** 13 | * app 14 | * @class 15 | */ 16 | class AppController extends Controller { 17 | 18 | /** 19 | * setWindowOpacity 20 | */ 21 | async setWindowOpacity (args) { 22 | await Services.get('app').setWindowOpacity(args); 23 | } 24 | 25 | async setWindowAlwaysOnTop(args) { 26 | await Services.get('app').setWindowAlwaysOnTop(args); 27 | } 28 | 29 | async themeToggle(args) { 30 | await Services.get('app').themeToggle(args); 31 | } 32 | 33 | async setWindowSize(args) { 34 | await Services.get('app').setWindowSize(args); 35 | } 36 | 37 | async setWindowSizeByType({ type }) { 38 | console.log('type', type) 39 | const typeMap = { 40 | pc: pcWindowSize, 41 | phone: phoneWindowSize 42 | } 43 | await Services.get('app').setWindowSize(typeMap[type]) 44 | } 45 | 46 | async hideWindow() { 47 | await Services.get('app').hideWindow(); 48 | } 49 | } 50 | 51 | AppController.toString = () => '[class AppController]'; 52 | module.exports = AppController; 53 | -------------------------------------------------------------------------------- /electron/controller/example.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { Controller } = require('ee-core'); 4 | const Log = require('ee-core/log'); 5 | const Services = require('ee-core/services'); 6 | 7 | /** 8 | * example 9 | * @class 10 | */ 11 | class ExampleController extends Controller { 12 | 13 | constructor(ctx) { 14 | super(ctx); 15 | } 16 | 17 | 18 | /** 19 | * 所有方法接收两个参数 20 | * @param args 前端传的参数 21 | * @param event - ipc通信时才有值。详情见:控制器文档 22 | */ 23 | 24 | /** 25 | * test 26 | */ 27 | async test () { 28 | const result = await Services.get('example').test('electron'); 29 | Log.info('service result:', result); 30 | 31 | return 'hello electron-egg'; 32 | } 33 | } 34 | 35 | ExampleController.toString = () => '[class ExampleController]'; 36 | module.exports = ExampleController; -------------------------------------------------------------------------------- /electron/controller/file.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { Controller } = require('ee-core'); 4 | const Log = require('ee-core/log'); 5 | const Services = require('ee-core/services'); 6 | 7 | /** 8 | * FileController 9 | * @class 10 | */ 11 | class FileController extends Controller { 12 | 13 | async getLocalTxtFileContent (args) { 14 | return await Services.get('file').getLocalTxtFileContent(args); 15 | } 16 | } 17 | 18 | FileController.toString = () => '[class FileController]'; 19 | module.exports = FileController; 20 | -------------------------------------------------------------------------------- /electron/controller/storage.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { Controller } = require('ee-core'); 4 | const Log = require('ee-core/log'); 5 | const Services = require('ee-core/services'); 6 | const { nativeTheme} = require("electron/main"); 7 | 8 | /** 9 | * StorageController 10 | * @class 11 | */ 12 | class StorageController extends Controller { 13 | 14 | async init() { 15 | return await Services.get('storage').init(); 16 | } 17 | 18 | async reset() { 19 | return await Services.get('storage').reset(); 20 | } 21 | 22 | async selectWebList(args) { 23 | return await Services.get('storage').selectWebList(args); 24 | } 25 | 26 | async addWeb(args) { 27 | return await Services.get('storage').addWeb(args); 28 | } 29 | 30 | async selectNovelList(args) { 31 | return await Services.get('storage').selectNovelList(args); 32 | } 33 | 34 | async addNovel(args) { 35 | await Services.get('storage').addNovel(args) 36 | } 37 | 38 | async getNovelDetail(args) { 39 | return await Services.get('storage').getNovelDetail(args.novelId) 40 | } 41 | } 42 | 43 | StorageController.toString = () => '[class StorageController]'; 44 | module.exports = StorageController; 45 | -------------------------------------------------------------------------------- /electron/index.js: -------------------------------------------------------------------------------- 1 | const { Application } = require('ee-core'); 2 | 3 | class Index extends Application { 4 | 5 | constructor() { 6 | super(); 7 | // this === eeApp; 8 | } 9 | 10 | /** 11 | * core app have been loaded 12 | */ 13 | async ready () { 14 | // do some things 15 | } 16 | 17 | /** 18 | * electron app ready 19 | */ 20 | async electronAppReady () { 21 | // do some things 22 | } 23 | 24 | /** 25 | * main window have been loaded 26 | */ 27 | async windowReady () { 28 | // do some things 29 | // 延迟加载,无白屏 30 | const winOpt = this.config.windowsOption; 31 | if (winOpt.show == false) { 32 | const win = this.electron.mainWindow; 33 | win.once('ready-to-show', () => { 34 | win.show(); 35 | win.focus(); 36 | }) 37 | } 38 | } 39 | 40 | /** 41 | * before app close 42 | */ 43 | async beforeClose () { 44 | // do some things 45 | 46 | } 47 | } 48 | 49 | Index.toString = () => '[class Index]'; 50 | module.exports = Index; -------------------------------------------------------------------------------- /electron/jobs/example/hello.js: -------------------------------------------------------------------------------- 1 | const Log = require('ee-core/log'); 2 | 3 | exports.welcome = function () { 4 | Log.info('[child-process] [jobs/example/hello] welcome ! '); 5 | } -------------------------------------------------------------------------------- /electron/jobs/example/timer.js: -------------------------------------------------------------------------------- 1 | const Job = require('ee-core/jobs/baseJobClass'); 2 | const Log = require('ee-core/log'); 3 | const Ps = require('ee-core/ps'); 4 | 5 | /** 6 | * example - TimerJob 7 | * @class 8 | */ 9 | class TimerJob extends Job { 10 | 11 | constructor(params) { 12 | super(); 13 | this.params = params; 14 | } 15 | 16 | /** 17 | * handle()方法是必要的,且会被自动调用 18 | */ 19 | async handle () { 20 | Log.info("[child-process] TimerJob params: ", this.params); 21 | 22 | if (Ps.isChildJob()) { 23 | Ps.exit(); 24 | } 25 | } 26 | } 27 | 28 | TimerJob.toString = () => '[class TimerJob]'; 29 | module.exports = TimerJob; 30 | -------------------------------------------------------------------------------- /electron/preload/bridge.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 如果启用了上下文隔离,渲染进程无法使用electron的api, 3 | * 可通过contextBridge 导出api给渲染进程使用 4 | */ 5 | 6 | const { contextBridge, ipcRenderer } = require('electron') 7 | 8 | contextBridge.exposeInMainWorld('electron', { 9 | ipcRenderer: ipcRenderer, 10 | }) -------------------------------------------------------------------------------- /electron/preload/index.js: -------------------------------------------------------------------------------- 1 | /************************************************* 2 | ** preload为预加载模块,该文件将会在程序启动时加载 ** 3 | *************************************************/ 4 | const Addon = require('ee-core/addon'); 5 | 6 | /** 7 | * 预加载模块入口 8 | */ 9 | module.exports = async () => { 10 | 11 | // 示例功能模块,可选择性使用和修改 12 | Addon.get('autoUpdater').create(); 13 | Addon.get('awaken').create(); 14 | Addon.get('tray').create(); 15 | Addon.get('security').create(); 16 | Addon.get('app').create(); 17 | } 18 | -------------------------------------------------------------------------------- /electron/service/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const CoreWindow = require('ee-core/electron/window'); 4 | const { Service } = require('ee-core'); 5 | const { nativeTheme } = require('electron/main') 6 | 7 | /** 8 | * 示例服务(service层为单例) 9 | * @class 10 | */ 11 | class AppService extends Service { 12 | 13 | /** 14 | * 设置窗口透明度 15 | * @param args 16 | */ 17 | async setWindowOpacity(args) { 18 | const { opacity } = args 19 | const mainWindow = CoreWindow.getMainWindow(); 20 | const max = Math.max(0.02, opacity); 21 | mainWindow.setOpacity(max); 22 | } 23 | 24 | async setWindowAlwaysOnTop(args) { 25 | const { flag } = args; 26 | const mainWindow = CoreWindow.getMainWindow(); 27 | mainWindow.setAlwaysOnTop(flag); 28 | } 29 | 30 | async themeToggle() { 31 | nativeTheme.themeSource = "dark" 32 | } 33 | 34 | /** 35 | * 设置窗体大小 36 | */ 37 | async setWindowSize(args) { 38 | const { width, height } = args; 39 | const mainWindow = CoreWindow.getMainWindow(); 40 | mainWindow.setResizable(true); 41 | mainWindow.setSize(width, height, true); 42 | } 43 | 44 | /** 45 | * 隐藏缩小窗体 46 | */ 47 | async hideWindow() { 48 | const mainWindow = CoreWindow.getMainWindow(); 49 | mainWindow.hide(); 50 | } 51 | } 52 | 53 | AppService.toString = () => '[class AppService]'; 54 | module.exports = AppService; 55 | -------------------------------------------------------------------------------- /electron/service/example.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { Service } = require('ee-core'); 4 | 5 | /** 6 | * 示例服务(service层为单例) 7 | * @class 8 | */ 9 | class ExampleService extends Service { 10 | 11 | constructor(ctx) { 12 | super(ctx); 13 | } 14 | 15 | /** 16 | * test 17 | */ 18 | async test(args) { 19 | let obj = { 20 | status:'ok', 21 | params: args 22 | } 23 | 24 | return obj; 25 | } 26 | } 27 | 28 | ExampleService.toString = () => '[class ExampleService]'; 29 | module.exports = ExampleService; -------------------------------------------------------------------------------- /electron/service/file.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { Service } = require('ee-core'); 4 | const fs = require('fs'); 5 | 6 | /** 7 | * 文件读取 8 | * @class 9 | */ 10 | class FileService extends Service { 11 | 12 | async getLocalTxtFileContent (args) { 13 | console.log(args) 14 | const { localPath, contentType } = args 15 | try { 16 | return fs.readFileSync(localPath, contentType); 17 | } catch (error) { 18 | console.log(error) 19 | return { error: error.message }; 20 | } 21 | } 22 | } 23 | 24 | module.exports = FileService; 25 | -------------------------------------------------------------------------------- /electron/service/storage.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { Service } = require('ee-core'); 4 | const Storage = require('ee-core/storage'); 5 | const { app } = require('electron'); 6 | const _ = require('lodash'); 7 | const { 8 | sysWebTable, 9 | sysNovelTable, 10 | sysNovelDetailTable, 11 | createSysWebSql, 12 | createSysNovel, 13 | createSysNovelDetail, 14 | createSysConfig, 15 | initWebData, 16 | initSysConfigData 17 | } = require('./../common/initCreateSql') 18 | const {sysConfigTable} = require("../common/initCreateSql"); 19 | const CoreWindow = require('ee-core/electron/window'); 20 | 21 | /** 22 | * 数据存储 23 | * @class 24 | */ 25 | class StorageService extends Service { 26 | 27 | constructor (ctx) { 28 | super(ctx); 29 | 30 | // sqlite数据库 31 | let sqliteOptions = { 32 | driver: 'sqlite', 33 | default: { 34 | timeout: 6000, 35 | verbose: console.log // 打印sql语法 36 | } 37 | } 38 | this.sqliteDB = Storage.connection('sqlite.db', sqliteOptions); 39 | } 40 | 41 | async init() { 42 | await this.checkAndCreateTable(sysWebTable, createSysWebSql); 43 | await this.checkAndCreateTable(sysNovelTable, createSysNovel); 44 | await this.checkAndCreateTable(sysNovelDetailTable, createSysNovelDetail); 45 | await this.checkAndCreateTable(sysConfigTable, createSysConfig) 46 | // 初始化数据 47 | const webList = await this.selectAllWebList() 48 | if (webList.length > 0) return; 49 | initWebData.forEach(item => { 50 | this.addWeb(item); 51 | }) 52 | } 53 | 54 | async reset() { 55 | await this.deleteTable(sysWebTable); 56 | await this.deleteTable(sysNovelTable); 57 | await this.deleteTable(sysNovelDetailTable); 58 | await this.init() 59 | app.quit(); 60 | } 61 | 62 | async deleteTable (tableName = '') { 63 | await this.sqliteDB.db.exec(`DROP TABLE IF EXISTS ${tableName}`) 64 | } 65 | 66 | async checkAndCreateTable(tableName = '', createTableSql = '') { 67 | if (_.isEmpty(tableName)) { 68 | throw new Error(`table name is required`); 69 | } 70 | 71 | if (_.isEmpty(createTableSql)) { 72 | throw new Error(`create table sql is error`); 73 | } 74 | 75 | // 检查表是否存在 76 | const userTable = this.sqliteDB.db.prepare('SELECT * FROM sqlite_master WHERE type=? AND name = ?'); 77 | const result = userTable.get('table', tableName); 78 | if (result) { 79 | return; 80 | } 81 | 82 | this.sqliteDB.db.exec(createTableSql); 83 | } 84 | 85 | async selectAllWebList() { 86 | const selectSysWeb = await this.sqliteDB.db.prepare(`SELECT * FROM ${sysWebTable}`); 87 | return selectSysWeb.all(); 88 | } 89 | 90 | async selectWebList(data) { 91 | const { category } = data; 92 | const selectSysWeb = this.sqliteDB.db.prepare(`SELECT * FROM ${sysWebTable} WHERE category = @category`); 93 | return selectSysWeb.all({ 94 | category: category 95 | }); 96 | } 97 | 98 | async addWeb(data) { 99 | const insert = this.sqliteDB.db.prepare(`INSERT INTO ${sysWebTable} (name, url, category, showType, sort) VALUES (@name, @url, @category, @showType, @sort)`); 100 | insert.run(data); 101 | return true; 102 | } 103 | 104 | async selectNovelList() { 105 | const selectSysNovel = this.sqliteDB.db.prepare(`SELECT * FROM ${sysNovelTable}`); 106 | return selectSysNovel.all(); 107 | } 108 | 109 | async addNovel(data) { 110 | this.sqliteDB.db.exec('BEGIN'); 111 | try { 112 | const insert = this.sqliteDB.db.prepare(`INSERT INTO ${sysNovelTable} (name, contentType, progress) VALUES (@name, @contentType, @progress)`); 113 | const { lastInsertRowid } = await insert.run(data); 114 | await this.addNovelDetail({ 115 | novelId: lastInsertRowid, 116 | content: data.content 117 | }); 118 | this.sqliteDB.db.exec('COMMIT'); 119 | } catch (e) { 120 | this.sqliteDB.db.exec('ROLLBACK'); 121 | } 122 | 123 | } 124 | 125 | async addNovelDetail(data) { 126 | const insert = this.sqliteDB.db.prepare(`INSERT INTO ${sysNovelDetailTable} (novelId, content) VALUES (@novelId, @content)`); 127 | insert.run(data); 128 | return true; 129 | } 130 | 131 | async getNovelDetail(novelId) { 132 | const selectSysNovelDetail = this.sqliteDB.db.prepare(`SELECT * FROM ${sysNovelDetailTable} WHERE novelId = @novelId`); 133 | return selectSysNovelDetail.all({ 134 | novelId 135 | }); 136 | } 137 | 138 | async selectAllConfigList() { 139 | const selectSysConfig = await this.sqliteDB.db.prepare(`SELECT * FROM ${sysConfigTable}`); 140 | return selectSysConfig.all(); 141 | } 142 | 143 | async addConfig(data) { 144 | console.log('addConfig', data); 145 | const insert = await this.sqliteDB.db.prepare(`INSERT INTO ${sysConfigTable} (key, value) VALUES (@key, @value`); 146 | insert.run(data); 147 | return true; 148 | } 149 | } 150 | 151 | module.exports = StorageService; 152 | -------------------------------------------------------------------------------- /electron/service/user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const CoreWindow = require('ee-core/electron/window'); 4 | const { Service } = require('ee-core'); 5 | const { nativeTheme } = require('electron/main') 6 | const Storage = require('ee-core/storage'); 7 | const _ = require('lodash'); 8 | 9 | /** 10 | * 用户服务(service层为单例) 11 | * @class 12 | */ 13 | class UserService extends Service { 14 | 15 | } 16 | 17 | UserService.toString = () => '[class UserService]'; 18 | module.exports = UserService; 19 | -------------------------------------------------------------------------------- /frontend/.env.development: -------------------------------------------------------------------------------- 1 | VITE_TITLE="" 2 | VITE_GO_URL="http://localhost:8081" -------------------------------------------------------------------------------- /frontend/.env.production: -------------------------------------------------------------------------------- 1 | VITE_TITLE="" 2 | VITE_GO_URL="http://www.test.com" -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | package-lock.json 7 | logs 8 | -------------------------------------------------------------------------------- /frontend/base.less: -------------------------------------------------------------------------------- 1 | @design-width: 1920; 2 | @base-font-size: 16px; 3 | 4 | html { 5 | font-size: calc((100vw / @design-width) * @base-font-size); 6 | } 7 | 8 | body { 9 | margin: 0; 10 | padding: 0; 11 | font-family: Arial, sans-serif; 12 | } 13 | -------------------------------------------------------------------------------- /frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 87 | 88 | 89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ToReduce", 3 | "version": "1.0.1", 4 | "scripts": { 5 | "dev": "vite --host --port 8888", 6 | "serve": "vite --host --port 8888", 7 | "build-staging": "vite build --mode staging", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "@vicons/ionicons5": "^0.12.0", 13 | "vue": "^3.2.33", 14 | "vue-router": "^4.0.14" 15 | }, 16 | "devDependencies": { 17 | "@vicons/material": "^0.12.0", 18 | "@vitejs/plugin-vue": "^4.2.3", 19 | "@vue/compiler-sfc": "^3.2.33", 20 | "autoprefixer": "^10.4.20", 21 | "less": "^4.1.2", 22 | "less-loader": "^10.2.0", 23 | "naive-ui": "^2.40.1", 24 | "postcss": "^8.4.49", 25 | "tailwindcss": "^3.4.16", 26 | "terser": "^5.19.1", 27 | "vfonts": "^0.0.3", 28 | "vite": "^4.4.4" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /frontend/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [require('tailwindcss')], 3 | } 4 | -------------------------------------------------------------------------------- /frontend/src/App.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 24 | 29 | -------------------------------------------------------------------------------- /frontend/src/api/main.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * 主进程与渲染进程通信频道定义 4 | * Definition of communication channels between main process and rendering process 5 | */ 6 | const ipcApiRoute = { 7 | setWindowOpacity: 'controller.app.setWindowOpacity', 8 | setWindowSize: 'controller.app.setWindowSize', 9 | hideWindow: 'controller.app.hideWindow', 10 | themeToggle: 'controller.app.themeToggle', 11 | reset: 'controller.storage.reset', 12 | addTestDataSqlite: 'controller.storage.test', 13 | selectWebList: 'controller.storage.selectWebList', 14 | selectNovelList: 'controller.storage.selectNovelList', 15 | addWeb: 'controller.storage.addWeb', 16 | addNovel: 'controller.storage.addNovel', 17 | getNovelDetail: 'controller.storage.getNovelDetail', 18 | setWindowSizeByType: 'controller.app.setWindowSizeByType', 19 | setWindowAlwaysOnTop: 'controller.app.setWindowAlwaysOnTop' 20 | } 21 | 22 | export { 23 | ipcApiRoute 24 | } 25 | 26 | -------------------------------------------------------------------------------- /frontend/src/assets/css/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /frontend/src/assets/global.less: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin: 0; 3 | padding: 0; 4 | width: 100%; 5 | height: 100%; /* 确保 html 和 body 高度为 100% */ 6 | overflow: hidden; /* 防止滚动条 */ 7 | } 8 | 9 | /* Remove margins and padding from all elements */ 10 | pre { 11 | margin: 0; 12 | box-sizing: border-box; 13 | } 14 | 15 | 16 | #app { 17 | font-family: Avenir, Helvetica, Arial, sans-serif; 18 | -webkit-font-smoothing: antialiased; 19 | -moz-osx-font-smoothing: grayscale; 20 | height: 100%; 21 | } 22 | 23 | body { 24 | background: #f0f3f8 !important; 25 | } 26 | 27 | .app-container { 28 | padding: 16px 24px; 29 | } 30 | 31 | pre { 32 | padding: 0 !important; 33 | } 34 | 35 | svg { 36 | -webkit-app-region: no-drag; /* 按钮不可拖动 */ 37 | } 38 | -------------------------------------------------------------------------------- /frontend/src/assets/home/bilibili.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lazycce/ToReduce/a889b034c44924350cc043b06d81ffc75a394db3/frontend/src/assets/home/bilibili.png -------------------------------------------------------------------------------- /frontend/src/assets/home/dy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lazycce/ToReduce/a889b034c44924350cc043b06d81ffc75a394db3/frontend/src/assets/home/dy.png -------------------------------------------------------------------------------- /frontend/src/assets/icon/log.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /frontend/src/assets/icon/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lazycce/ToReduce/a889b034c44924350cc043b06d81ffc75a394db3/frontend/src/assets/icon/logo.png -------------------------------------------------------------------------------- /frontend/src/components/global/Footer.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 15 | -------------------------------------------------------------------------------- /frontend/src/components/global/PageOperation.vue: -------------------------------------------------------------------------------- 1 | 7 | 38 | 43 | -------------------------------------------------------------------------------- /frontend/src/components/global/TitleBar.vue: -------------------------------------------------------------------------------- 1 | 28 | 130 | 153 | -------------------------------------------------------------------------------- /frontend/src/components/global/index.js: -------------------------------------------------------------------------------- 1 | const modules = import.meta.globEager('./*.vue') 2 | const map = {} 3 | Object.keys(modules).forEach(file => { 4 | const modulesName = file.replace('./', '').replace('.vue', '') 5 | map[modulesName] = modules[file].default 6 | }) 7 | const globalComponents = { 8 | ...map, 9 | } 10 | export default globalComponents 11 | -------------------------------------------------------------------------------- /frontend/src/layout/components/AppMain.vue: -------------------------------------------------------------------------------- 1 | 12 | 14 | -------------------------------------------------------------------------------- /frontend/src/layout/components/Logo.vue: -------------------------------------------------------------------------------- 1 | 15 | 17 | 30 | -------------------------------------------------------------------------------- /frontend/src/layout/components/NavBar.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 87 | 158 | -------------------------------------------------------------------------------- /frontend/src/layout/components/Version.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 17 | -------------------------------------------------------------------------------- /frontend/src/layout/index.vue: -------------------------------------------------------------------------------- 1 | 14 | 22 | 27 | -------------------------------------------------------------------------------- /frontend/src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue'; 2 | import App from './App.vue'; 3 | import {createPinia} from 'pinia' 4 | import components from './components/global'; 5 | import Router from './router/index'; 6 | 7 | import "@/assets/css/tailwind.css"; 8 | import "@/assets/global.less"; 9 | 10 | import naive from 'naive-ui' 11 | 12 | const app = createApp(App) 13 | const pina = createPinia() 14 | app.config.productionTip = false 15 | 16 | for (const i in components) { 17 | app.component(i, components[i]) 18 | } 19 | 20 | app.use(Router).use(pina).use(naive).mount('#app') 21 | -------------------------------------------------------------------------------- /frontend/src/router/index.js: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHashHistory } from 'vue-router' 2 | import routerMap from './routerMap' 3 | 4 | const Router = createRouter({ 5 | history: createWebHashHistory(), 6 | routes: routerMap, 7 | }) 8 | 9 | export default Router 10 | -------------------------------------------------------------------------------- /frontend/src/router/routerMap.js: -------------------------------------------------------------------------------- 1 | import Layout from '@/layout/index.vue' 2 | 3 | /** 4 | * 基础路由 5 | * @type { *[] } 6 | */ 7 | 8 | const constantRouterMap = [ 9 | { 10 | path: '/', 11 | component: Layout, 12 | redirect: { name: 'home' }, 13 | children: [ 14 | { 15 | path: '/home', 16 | name: 'home', 17 | component: () => import('@/views/home/index.vue') 18 | }, 19 | { 20 | path: '/study', 21 | name: 'study', 22 | component: () => import('@/views/study/index.vue') 23 | }, 24 | { 25 | path: '/read', 26 | name: 'read', 27 | component: () => import('@/views/read/index.vue') 28 | }, 29 | { 30 | path: '/settings', 31 | name: 'settings', 32 | component: () => import('@/views/settings/index.vue') 33 | } 34 | ] 35 | }, 36 | { 37 | path: '/web', 38 | name: 'web', 39 | component: () => import('@/views/web/index.vue') 40 | }, 41 | { 42 | path: '/readDetail', 43 | name: 'readDetail', 44 | component: () => import('@/views/read/detail.vue') 45 | } 46 | ] 47 | 48 | export default constantRouterMap 49 | -------------------------------------------------------------------------------- /frontend/src/store/app.js: -------------------------------------------------------------------------------- 1 | import { defineStore } from "pinia"; 2 | import { ref } from "vue"; 3 | 4 | export const useAppStore = defineStore('app',()=>{ 5 | // 窗体透明度 6 | const windowOpacity = ref(100); 7 | 8 | // 窗体置顶 9 | const windowAlwaysOnTop = ref(false); 10 | 11 | const setWindowOpacity = (opacity) => { 12 | windowOpacity.value = opacity; 13 | } 14 | 15 | const setWindowAlwaysOnTop = (flag) => { 16 | windowAlwaysOnTop.value = flag; 17 | } 18 | 19 | return { 20 | setWindowOpacity, 21 | setWindowAlwaysOnTop, 22 | windowOpacity, 23 | windowAlwaysOnTop 24 | } 25 | }) 26 | -------------------------------------------------------------------------------- /frontend/src/utils/ipcRenderer.js: -------------------------------------------------------------------------------- 1 | const Renderer = (window.require && window.require('electron')) || window.electron || {}; 2 | 3 | /** 4 | * ipc 5 | * 官方api说明:https://www.electronjs.org/zh/docs/latest/api/ipc-renderer 6 | * 7 | * 属性/方法 8 | * ipc.invoke(channel, param) - 发送异步消息(invoke/handle 模型) 9 | * ipc.sendSync(channel, param) - 发送同步消息(send/on 模型) 10 | * ipc.on(channel, listener) - 监听 channel, 当新消息到达,调用 listener 11 | * ipc.once(channel, listener) - 添加一次性 listener 函数 12 | * ipc.removeListener(channel, listener) - 为特定的 channel 从监听队列中删除特定的 listener 监听者 13 | * ipc.removeAllListeners(channel) - 移除所有的监听器,当指定 channel 时只移除与其相关的所有监听器 14 | * ipc.send(channel, ...args) - 通过channel向主进程发送异步消息 15 | * ipc.postMessage(channel, message, [transfer]) - 发送消息到主进程 16 | * ipc.sendTo(webContentsId, channel, ...args) - 通过 channel 发送消息到带有 webContentsId 的窗口 17 | * ipc.sendToHost(channel, ...args) - 消息会被发送到 host 页面上的 元素 18 | */ 19 | 20 | /** 21 | * ipc 22 | */ 23 | const ipc = Renderer.ipcRenderer || undefined; 24 | 25 | /** 26 | * 是否为EE环境 27 | */ 28 | const isEE = ipc ? true : false; 29 | 30 | export { 31 | Renderer, ipc, isEE 32 | }; 33 | 34 | -------------------------------------------------------------------------------- /frontend/src/views/components/InputSearch.vue: -------------------------------------------------------------------------------- 1 | 11 | 26 | -------------------------------------------------------------------------------- /frontend/src/views/components/WebCard.vue: -------------------------------------------------------------------------------- 1 | 6 | 26 | 40 | -------------------------------------------------------------------------------- /frontend/src/views/home/audio/index.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /frontend/src/views/home/index.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 48 | 69 | -------------------------------------------------------------------------------- /frontend/src/views/home/setting/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 6 | -------------------------------------------------------------------------------- /frontend/src/views/read/ChaptersDrawer.vue: -------------------------------------------------------------------------------- 1 | 14 | 42 | 51 | -------------------------------------------------------------------------------- /frontend/src/views/read/EditModal.vue: -------------------------------------------------------------------------------- 1 | 56 | 159 | 167 | -------------------------------------------------------------------------------- /frontend/src/views/read/detail.vue: -------------------------------------------------------------------------------- 1 | 24 | 123 | 135 | -------------------------------------------------------------------------------- /frontend/src/views/read/index.vue: -------------------------------------------------------------------------------- 1 | 36 | 76 | 102 | -------------------------------------------------------------------------------- /frontend/src/views/settings/index.vue: -------------------------------------------------------------------------------- 1 | 41 | 48 | -------------------------------------------------------------------------------- /frontend/src/views/study/index.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 48 | 56 | -------------------------------------------------------------------------------- /frontend/src/views/web/EditModal.vue: -------------------------------------------------------------------------------- 1 | 40 | 92 | -------------------------------------------------------------------------------- /frontend/src/views/web/index.vue: -------------------------------------------------------------------------------- 1 | 26 | 86 | 102 | -------------------------------------------------------------------------------- /frontend/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'], 4 | theme: { 5 | extend: {} 6 | }, 7 | corePlugins: { 8 | preflight: false 9 | }, 10 | plugins: [] 11 | } 12 | -------------------------------------------------------------------------------- /frontend/vite.config.js: -------------------------------------------------------------------------------- 1 | import vue from '@vitejs/plugin-vue' 2 | import { defineConfig } from 'vite' 3 | 4 | import path from 'path' 5 | // https://vitejs.dev/config/ 6 | export default defineConfig(({ command, mode }) => { 7 | return { 8 | // 项目插件 9 | plugins: [ 10 | vue(), 11 | ], 12 | // 基础配置 13 | base: './', 14 | publicDir: 'public', 15 | resolve: { 16 | alias: { 17 | '@': path.resolve(__dirname, 'src'), 18 | }, 19 | }, 20 | css: { 21 | preprocessorOptions: { 22 | less: { 23 | modifyVars: { 24 | '@border-color-base': '#dce3e8', 25 | }, 26 | javascriptEnabled: true, 27 | }, 28 | }, 29 | }, 30 | build: { 31 | outDir: 'dist', 32 | assetsDir: 'assets', 33 | assetsInlineLimit: 4096, 34 | cssCodeSplit: true, 35 | brotliSize: false, 36 | sourcemap: false, 37 | minify: 'terser', 38 | terserOptions: { 39 | compress: { 40 | // 生产环境去除console及debug 41 | drop_console: false, 42 | drop_debugger: true, 43 | }, 44 | }, 45 | }, 46 | } 47 | }) 48 | 49 | 50 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | const { ElectronEgg } = require('ee-core'); 2 | new ElectronEgg(); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ToReduce", 3 | "version": "1.0.0", 4 | "description": "A fast, desktop software development framework", 5 | "main": "main.js", 6 | "scripts": { 7 | "dev": "ee-bin dev", 8 | "dev-frontend": "ee-bin dev --serve=frontend", 9 | "dev-electron": "ee-bin dev --serve=electron", 10 | "build-frontend": "ee-bin build --cmds=frontend && ee-bin move --flag=frontend_dist", 11 | "start": "ee-bin start", 12 | "rd": "ee-bin move --flag=frontend_dist", 13 | "encrypt": "ee-bin encrypt", 14 | "clean": "ee-bin clean", 15 | "icon": "ee-bin icon", 16 | "reload": "nodemon --config ./electron/config/nodemon.json", 17 | "rebuild": "electron-rebuild", 18 | "re-sqlite": "electron-rebuild -f -w better-sqlite3", 19 | "build-w": "electron-builder --config=./electron/config/builder.json -w=nsis --x64", 20 | "build-w-32": "electron-builder --config=./electron/config/builder.json -w=nsis --ia32", 21 | "build-w-64": "electron-builder --config=./electron/config/builder.json -w=nsis --x64", 22 | "build-w-arm64": "electron-builder --config=./electron/config/builder.json -w=nsis --arm64", 23 | "build-we": "electron-builder --config=./electron/config/builder.json -w=portable --x64", 24 | "build-wz": "electron-builder --config=./electron/config/builder.json -w=7z --x64", 25 | "build-wz-32": "electron-builder --config=./electron/config/builder.json -w=7z --ia32", 26 | "build-wz-64": "electron-builder --config=./electron/config/builder.json -w=7z --x64", 27 | "build-wz-arm64": "electron-builder --config=./electron/config/builder.json -w=7z --arm64", 28 | "build-m": "electron-builder --config=./electron/config/builder.json -m", 29 | "build-m-arm64": "electron-builder --config=./electron/config/builder.json -m --arm64", 30 | "build-l": "electron-builder --config=./electron/config/builder.json -l=deb --x64", 31 | "build-l-32": "electron-builder --config=./electron/config/builder.json -l=deb --ia32", 32 | "build-l-64": "electron-builder --config=./electron/config/builder.json -l=deb --x64", 33 | "build-l-arm64": "electron-builder --config=./electron/config/builder.json -l=deb --arm64", 34 | "build-l-armv7l": "electron-builder --config=./electron/config/builder.json -l=deb --armv7l", 35 | "build-lr-64": "electron-builder --config=./electron/config/builder.json -l=rpm --x64", 36 | "build-lp-64": "electron-builder --config=./electron/config/builder.json -l=pacman --x64", 37 | "test": "set DEBUG=* && electron . --env=local" 38 | }, 39 | "repository": "https://github.com/dromara/electron-egg.git", 40 | "keywords": [ 41 | "Electron", 42 | "electron-egg", 43 | "ElectronEgg" 44 | ], 45 | "author": "Toduce", 46 | "license": "Apache", 47 | "devDependencies": { 48 | "@electron/rebuild": "^3.7.1", 49 | "debug": "^4.3.3", 50 | "ee-bin": "1.8.0", 51 | "electron": "^21.4.4", 52 | "electron-builder": "^23.6.0", 53 | "electron-rebuild": "^3.2.9", 54 | "eslint": "^5.13.0", 55 | "eslint-plugin-prettier": "^3.0.1", 56 | "less": "^4.2.0", 57 | "naive-ui": "^2.40.1", 58 | "nodemon": "^2.0.16" 59 | }, 60 | "dependencies": { 61 | "@vitejs/plugin-vue": "^5.2.0", 62 | "better-sqlite3": "^8.4.0", 63 | "dayjs": "^1.10.7", 64 | "ee-core": "2.11.0", 65 | "electron-updater": "^5.3.0", 66 | "lodash": "^4.17.21", 67 | "pinia": "^2.2.8", 68 | "vite": "^2.9.18", 69 | "vue-router": "^4.4.5" 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /public/dist/assets/WebCard-54f3b803.css: -------------------------------------------------------------------------------- 1 | .light-green[data-v-7d414856]{font-size:18px;font-weight:700;height:108px;width:108px;border-radius:8px;background-color:#0080001f;display:flex;justify-content:center;align-items:center;cursor:pointer} 2 | -------------------------------------------------------------------------------- /public/dist/assets/WebCard-c39805ae.js: -------------------------------------------------------------------------------- 1 | import{r as a,a as e,c as s,b as t,w as l,g as n,N as u,h as r,j as d,k as o,l as p,_ as c,u as i,m,t as v,n as _}from"./index-e19c5bd5.js";const h={__name:"InputSearch",emits:["search","add"],setup(c,{emit:i}){const m=a(""),v=i,_=()=>{v("search",m.value)};return(a,c)=>(e(),s("div",null,[t(n(p),null,{default:l((()=>[t(n(u),{onKeyup:c[0]||(c[0]=r((a=>_()),["enter","native"])),placeholder:"请输入网址",style:{width:"60%"},value:m.value,"onUpdate:value":c[1]||(c[1]=a=>m.value=a)},null,8,["value"]),t(n(d),{type:"primary",onClick:c[2]||(c[2]=a=>_())},{default:l((()=>c[3]||(c[3]=[o(" 搜索 ")]))),_:1})])),_:1})]))}},y=["onClick"],k=c({__name:"WebCard",props:{data:{type:Object,default:()=>[]}},setup(a){const t=i();return(l,n)=>(e(!0),s(_,null,m(a.data,((a,l)=>(e(),s("div",{class:"light-green",key:l,onClick:e=>(a=>{console.log("item",a),t.push({path:"/web",query:a})})(a)},v(a.name),9,y)))),128))}},[["__scopeId","data-v-7d414856"]]);export{k as W,h as _}; 2 | -------------------------------------------------------------------------------- /public/dist/assets/detail-75f1420c.css: -------------------------------------------------------------------------------- 1 | .item[data-v-b934beb3]{cursor:pointer;height:24px;padding:6px;border-bottom:1px solid #eee}.content[data-v-bc984df8]{padding:0 12px!important;background:#eee;white-space:pre-wrap;font-weight:700}.read-wrapper[data-v-bc984df8]{position:relative;overflow:auto} 2 | -------------------------------------------------------------------------------- /public/dist/assets/detail-c5047e38.js: -------------------------------------------------------------------------------- 1 | import{d as e,a as t,c as a,e as l,M as n,_ as s,r,q as i,w as o,b as c,g as d,C as h,t as u,O as v,P as p,H as w,o as m,Q as f,R as y,p as k,S as x,i as g,f as _,k as C,n as z,m as I,T as b}from"./index-e19c5bd5.js";const M={xmlns:"http://www.w3.org/2000/svg","xmlns:xlink":"http://www.w3.org/1999/xlink",viewBox:"0 0 512 512"},E=[l("circle",{cx:"256",cy:"256",r:"64",fill:"currentColor"},null,-1),l("path",{d:"M490.84 238.6c-26.46-40.92-60.79-75.68-99.27-100.53C349 110.55 302 96 255.66 96c-42.52 0-84.33 12.15-124.27 36.11c-40.73 24.43-77.63 60.12-109.68 106.07a31.92 31.92 0 0 0-.64 35.54c26.41 41.33 60.4 76.14 98.28 100.65C162 402 207.9 416 255.66 416c46.71 0 93.81-14.43 136.2-41.72c38.46-24.77 72.72-59.66 99.08-100.92a32.2 32.2 0 0 0-.1-34.76zM256 352a96 96 0 1 1 96-96a96.11 96.11 0 0 1-96 96z",fill:"currentColor"},null,-1)],L=e({name:"Eye",render:function(e,l){return t(),a("svg",M,E)}}),S={xmlns:"http://www.w3.org/2000/svg","xmlns:xlink":"http://www.w3.org/1999/xlink",viewBox:"0 0 512 512"},B=[n('',6)],D=e({name:"Library",render:function(e,l){return t(),a("svg",S,B)}}),H=["onClick"],N=s({__name:"ChaptersDrawer",props:{chapterList:{type:Array,default:()=>[]}},emits:"select",setup(e,{expose:a,emit:n}){const s=n,w=r(!1);return a({show:()=>{w.value=!0}}),(a,n)=>(t(),i(d(p),{show:w.value,"onUpdate:show":n[0]||(n[0]=e=>w.value=e),placement:"bottom"},{default:o((()=>[c(d(v),{title:"章节"},{default:o((()=>[c(d(h),{"item-size":24,items:e.chapterList},{default:o((({item:e})=>[l("div",{class:"item",onClick:t=>(e=>{s("select",e),w.value=!1})(e)},u(e),9,H)])),_:1},8,["items"])])),_:1})])),_:1},8,["show"]))}},[["__scopeId","data-v-b934beb3"]]),V={style:{height:"100%"}},q={class:"content"},O=["id"],R=s({__name:"detail",setup(e){const n=r(""),s=r([]),i=r([]),h=w(),v=r({});m((()=>{v.value=JSON.parse(h.query.data),(async e=>{const t=await g.invoke(_.getNovelDetail,{novelId:e});t&&t.length>0&&(n.value=t[0].content,i.value=(e=>{const t=/(第[一二三四五六七八九十百千零\d]+章\s*[^\n]*)/g,a=[];let l;for(;null!==(l=t.exec(e));)a.push({key:l[0].trim(),startIndex:l.index});const n=[];for(let s=0;se.key)))})(v.value.id)}));const p=e=>{"Escape"===e.key&&(g.invoke(_.setWindowSize,{width:750,height:470}),E.value=!0)};f((()=>{window.addEventListener("keydown",p)})),y((()=>{window.removeEventListener("keydown",p)}));const M=e=>{const t=document.getElementById(e);null==t||t.scrollIntoView({behavior:"smooth"})},E=r(!0),S=r();return(e,n)=>{const r=k("NIcon"),h=k("TitleBar");return t(),a("div",V,[c(h,{hidden:!E.value},{"right-action":o((()=>[c(r,{title:"章节选择",size:22,onClick:n[0]||(n[0]=e=>{S.value.show()})},{default:o((()=>[c(d(D))])),_:1}),c(r,{title:"专注模式",size:22,onClick:n[1]||(n[1]=e=>(g.invoke(_.setWindowSize,{width:300,height:200}),void(E.value=!1)))},{default:o((()=>[c(d(L))])),_:1})])),_:1},8,["hidden"]),c(d(b),{class:"read-wrapper",style:x({height:E.value?"calc(100% - 42px)":"100%"})},{default:o((()=>[l("pre",q,[n[3]||(n[3]=C(" ")),(t(!0),a(z,null,I(i.value,(e=>(t(),a("div",{id:e.key},[n[2]||(n[2]=C("\n ")),l("div",null,u(e.key),1),C("\n "+u(e.content)+"\n ",1)],8,O)))),256)),n[4]||(n[4]=C("\n "))])])),_:1},8,["style"]),c(N,{"chapter-list":s.value,ref_key:"chaptersDrawerRef",ref:S,onSelect:M},null,8,["chapter-list"])])}}},[["__scopeId","data-v-bc984df8"]]);export{R as default}; 2 | -------------------------------------------------------------------------------- /public/dist/assets/index-3dc9b7be.css: -------------------------------------------------------------------------------- 1 | .n-config-provider{height:100%}html,body{margin:0;padding:0;width:100%;height:100%;overflow:hidden}pre{margin:0;box-sizing:border-box}#app{font-family:Avenir,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;height:100%}body{background:#f0f3f8!important}.app-container{padding:16px 24px}pre{padding:0!important}svg{-webkit-app-region:no-drag}.footer{position:absolute;bottom:12px;left:0;right:0;text-align:center;color:#000000a6}.top-action{display:flex}.hidden-title-bar{padding:8px 12px;position:fixed;z-index:1000;top:0;left:0;right:0;height:24px;-webkit-app-region:drag}.title-bar{padding:8px 12px;position:sticky;z-index:1000;top:0;height:24px;-webkit-app-region:drag}.title-bar svg{cursor:pointer}.layout[data-v-076b24f6]{display:flex;flex-direction:row;flex:auto}.layout-default-background[data-v-076b24f6]{background:#f5f7f9}.layout .layout-sider[data-v-076b24f6]{min-height:100vh;box-shadow:2px 0 8px #1d23290d;position:relative;z-index:13;transition:all .2s ease-in-out}.layout .layout-sider-fix[data-v-076b24f6]{position:fixed;top:0;left:0}.layout .ant-layout[data-v-076b24f6]{overflow:hidden}.layout .layout-right-fix[data-v-076b24f6]{overflow-x:hidden;padding-left:200px;min-height:100vh;transition:all .2s ease-in-out}.layout .layout-content[data-v-076b24f6]{flex:auto;min-height:100vh}.layout .n-layout-header.n-layout-header--absolute-positioned[data-v-076b24f6]{z-index:11}.layout .n-layout-footer[data-v-076b24f6]{background:none}.layout-content-main[data-v-076b24f6]{margin:0 10px 10px;position:relative;padding-top:64px}.layout-content-main-fix[data-v-076b24f6]{padding-top:64px}.fluid-header[data-v-076b24f6]{padding-top:0}.main-view-fix[data-v-076b24f6]{padding-top:44px}.noMultiTabs[data-v-076b24f6]{padding-top:0}.nav-logo{display:flex;gap:4px;align-items:center;color:#06b359;position:fixed;bottom:20px;left:20px;font-size:18px;font-weight:700}.version[data-v-a786045a]{position:absolute;padding:16px 20px 0;top:0;color:#00000073;right:0;left:0;-webkit-app-region:drag;height:24px}.n-layout{height:100%}@tailwind base;@tailwind components;@tailwind utilities; 2 | -------------------------------------------------------------------------------- /public/dist/assets/index-520f1754.css: -------------------------------------------------------------------------------- 1 | .svg{cursor:pointer}.web-container{height:calc(100% - 40px);display:flex;flex-direction:column}.web-container .top-actions{flex-shrink:0}.web-container .web-content{flex-grow:1} 2 | -------------------------------------------------------------------------------- /public/dist/assets/index-7178b9ee.js: -------------------------------------------------------------------------------- 1 | import{d as a,u as s,r as e,o as t,a as n,c as r,b as o,e as i,F as c,i as d,f as p,_ as u}from"./index-e19c5bd5.js";import{_ as l,W as m}from"./WebCard-c39805ae.js";const f={class:"app-container"},v={class:"wrap"},_=u(a({__name:"index",setup(a){const u=s(),_=a=>{a&&u.push({path:"/web",query:{url:a,category:"entertainment"}})},b=e([]);return t((()=>{(async()=>{b.value=await d.invoke(p.selectWebList,{category:"entertainment"})})()})),(a,s)=>(n(),r("div",f,[o(l,{onSearch:_}),i("div",v,[o(m,{data:b.value},null,8,["data"])]),o(c)]))}}),[["__scopeId","data-v-ff8a5d40"]]);export{_ as default}; 2 | -------------------------------------------------------------------------------- /public/dist/assets/index-8d47797c.css: -------------------------------------------------------------------------------- 1 | .wrap[data-v-ff8a5d40]{margin-top:20px;display:flex;flex-wrap:wrap;gap:20px}.light-green[data-v-ff8a5d40]{font-size:18px;font-weight:700;height:108px;width:108px;border-radius:8px;background-color:#0080001f;display:flex;justify-content:center;align-items:center;cursor:pointer} 2 | -------------------------------------------------------------------------------- /public/dist/assets/index-9a4e248e.css: -------------------------------------------------------------------------------- 1 | .wrap[data-v-e032b68d]{margin-top:20px;display:flex;flex-wrap:wrap;gap:20px} 2 | -------------------------------------------------------------------------------- /public/dist/assets/index-9ec7f2bd.js: -------------------------------------------------------------------------------- 1 | import{d as e,a,c as t,e as l,_ as n,r as s,p as o,q as r,w as u,b as i,g as d,s as c,j as p,k as v,v as f,x as m,N as g,y,z as _,t as h,A as w,B as k,i as x,f as b,u as T,o as C,C as j}from"./index-e19c5bd5.js";import{_ as B}from"./lodash-022d070e.js";const U={xmlns:"http://www.w3.org/2000/svg","xmlns:xlink":"http://www.w3.org/1999/xlink",viewBox:"0 0 512 512"},q=[l("path",{fill:"none",stroke:"currentColor","stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"32",d:"M256 112v288"},null,-1),l("path",{fill:"none",stroke:"currentColor","stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"32",d:"M400 256H112"},null,-1)],N=e({name:"Add",render:function(e,l){return a(),t("svg",U,q)}}),z={class:"preview"},G=n({__name:"EditModal",emits:["success"],setup(e,{expose:t,emit:n}){const T=n,C=s(!1),j=s({name:"",type:"",content:"",contentType:"GBK",progress:0}),U=s(""),q=s(!1);s([]);const G=s(null),K=s(),M=()=>{var e;null==(e=K.value)||e.validate((e=>{e||(async()=>{await x.invoke(b.addNovel,B.cloneDeep(j.value)),C.value=!1,T("success")})()}))},A=e=>{const{file:a}=e;G.value=a,F(a)},F=e=>{q.value=!0;try{if(e&&"text/plain"===e.type){const a=new FileReader;a.onload=e=>{j.value.content=e.target.result,U.value=e.target.result<=200?e.target.result:e.target.result.substring(0,200)},a.readAsText(e.file,j.value.contentType),j.value.name=e.file.name.replace(".txt","")}}finally{q.value=!1}},R=()=>{F(G.value)},D=()=>{j.value={name:"",type:"",content:"",contentType:"GBK",progress:0},q.value=!1},I={name:{required:!0,message:"请输入小说名称",trigger:"blur"},content:{required:!0,message:"请选择txt文件",trigger:"change"},contentType:{required:!0,message:"请选择文件编码",trigger:"change"}};return t({show:()=>{C.value=!0}}),(e,t)=>{const n=o("n-icon"),s=o("NUpload");return a(),r(d(k),{to:"body",show:C.value,"onUpdate:show":t[3]||(t[3]=e=>C.value=e),onClose:D},{default:u((()=>[i(d(w),{style:{width:"350px"}},{footer:u((()=>[i(d(c),{justify:"center"},{default:u((()=>[i(d(p),{loading:q.value,type:"primary",onClick:t[2]||(t[2]=e=>M())},{default:u((()=>t[6]||(t[6]=[v("确 定")]))),_:1},8,["loading"])])),_:1})])),default:u((()=>[i(d(f),{ref_key:"formRef",ref:K,model:j.value,"label-placement":"left","label-width":"auto",size:"small",rules:I},{default:u((()=>[i(d(m),{label:"选择文件",path:"content"},{default:u((()=>[i(s,{max:1,"response-type":"blob",accept:".txt","show-file-list":!0,"on-change":A},{default:u((()=>[i(d(p),{size:"small",type:"primary"},{icon:u((()=>[i(n,null,{default:u((()=>[i(d(N))])),_:1})])),_:1})])),_:1})])),_:1}),i(d(m),{label:"小说名称",path:"name"},{default:u((()=>[i(d(g),{value:j.value.name,"onUpdate:value":t[0]||(t[0]=e=>j.value.name=e),placeholder:"请输入小说名称"},null,8,["value"])])),_:1}),i(d(m),{label:"文件编码",path:"contentType"},{default:u((()=>[i(d(y),{value:j.value.contentType,"onUpdate:value":t[1]||(t[1]=e=>j.value.contentType=e),onChange:R},{default:u((()=>[i(d(c),null,{default:u((()=>[i(d(_),{value:"GBK"},{default:u((()=>t[4]||(t[4]=[v(" GBK ")]))),_:1}),i(d(_),{value:"UTF-8"},{default:u((()=>t[5]||(t[5]=[v(" UTF-8 ")]))),_:1})])),_:1})])),_:1},8,["value"])])),_:1}),i(d(m),{label:"内容预览"},{default:u((()=>[l("div",z,h(U.value),1)])),_:1})])),_:1},8,["model"])])),_:1})])),_:1},8,["show"])}}},[["__scopeId","data-v-59bdcb08"]]),K={class:"app-container"},M=["onClick"],A={class:"title"},F=n({__name:"index",setup(e){const n=T(),r=s([]),v=async()=>{const e=await x.invoke(b.selectNovelList);r.value=e.reverse().map(((e,a)=>({no:a+1,...e})))},f=s();return C((()=>{v()})),(e,s)=>{const m=o("n-icon"),g=o("n-progress");return a(),t("div",K,[i(d(c),{reverse:"",style:{width:"100%","margin-bottom":"8px"}},{default:u((()=>[i(d(p),{circle:"",type:"primary",onClick:s[0]||(s[0]=e=>{f.value.show()})},{icon:u((()=>[i(m,null,{default:u((()=>[i(d(N))])),_:1})])),_:1})])),_:1}),i(d(j),{class:"list-container","item-size":42,items:r.value,"padding-top":"4","padding-bottom":"4"},{default:u((({item:e})=>[(a(),t("div",{class:"item-wrapper",key:e.id},[l("div",{class:"item",onClick:a=>(e=>{n.push({name:"readDetail",query:{data:JSON.stringify(e)}})})(e)},[l("div",A,[l("span",null,h(e.no)+". ",1),l("span",null,h(e.name),1)]),l("div",null,[i(g,{style:{width:"180px"},type:"line",percentage:e.progress,height:12,"show-indicator":!1,"border-radius":4,"fill-border-radius":0},null,8,["percentage"])])],8,M)]))])),_:1},8,["items"]),i(G,{ref_key:"editModalRef",ref:f,onSuccess:s[1]||(s[1]=e=>v())},null,512)])}}},[["__scopeId","data-v-7f2ea46a"]]);export{F as default}; 2 | -------------------------------------------------------------------------------- /public/dist/assets/index-a13539ee.js: -------------------------------------------------------------------------------- 1 | import{d as a,u as s,r as e,o as t,a as r,c as d,b as o,e as c,F as n,i as u,f as i,_ as p}from"./index-e19c5bd5.js";import{_ as l,W as v}from"./WebCard-c39805ae.js";const y={class:"app-container"},_={class:"wrap"},b=p(a({__name:"index",setup(a){const p=s(),b=a=>{a&&p.push({path:"/web",query:{url:a,category:"study"}})},m=e([]);return t((()=>{(async()=>{m.value=await u.invoke(i.selectWebList,{category:"study"})})()})),(a,s)=>(r(),d("div",y,[o(l,{onSearch:b}),c("div",_,[o(v,{data:m.value},null,8,["data"])]),o(n)]))}}),[["__scopeId","data-v-e032b68d"]]);export{b as default}; 2 | -------------------------------------------------------------------------------- /public/dist/assets/index-b00bd2c4.css: -------------------------------------------------------------------------------- 1 | .preview[data-v-59bdcb08]{white-space:pre-wrap;background:#eee;overflow:auto;height:60px}.list-container[data-v-7f2ea46a]{height:340px;background:#eee}.item-wrapper[data-v-7f2ea46a]{height:42px;display:flex;align-items:center;padding:6px 14px}.item-wrapper .item[data-v-7f2ea46a]{cursor:pointer;flex:1;padding:8px 14px;background:#f5f7f9;border-radius:4px;border:1px solid #eee;display:flex;align-items:center;justify-content:space-between}.item-wrapper .item .title[data-v-7f2ea46a]{font-weight:700} 2 | -------------------------------------------------------------------------------- /public/dist/assets/index-c580b1b6.js: -------------------------------------------------------------------------------- 1 | import{d as e,a,c as l,e as t,r as s,p as u,q as o,w as r,b as n,g as i,s as d,k as v,v as p,x as c,N as f,y as h,z as w,A as y,B as m,i as g,f as _,H as b,I as k,o as x,J as T,K as z,L as q}from"./index-e19c5bd5.js";import{_ as L}from"./lodash-022d070e.js";const M={xmlns:"http://www.w3.org/2000/svg","xmlns:xlink":"http://www.w3.org/1999/xlink",viewBox:"0 0 512 512"},W=[t("path",{d:"M394 480a16 16 0 0 1-9.39-3L256 383.76L127.39 477a16 16 0 0 1-24.55-18.08L153 310.35L23 221.2a16 16 0 0 1 9-29.2h160.38l48.4-148.95a16 16 0 0 1 30.44 0l48.4 149H480a16 16 0 0 1 9.05 29.2L359 310.35l50.13 148.53A16 16 0 0 1 394 480z",fill:"currentColor"},null,-1)],S=e({name:"Star",render:function(e,t){return a(),l("svg",M,W)}}),U={__name:"EditModal",props:{data:{type:Object,default:()=>({})}},setup(e,{expose:l}){const t=s(!1),b=s({name:"",url:"",showType:"phone",category:"entertainment"}),k=s(),x={name:{required:!0,message:"请输入站点名称",trigger:"blur"},showType:{required:!0,message:"请选择展示尺寸",trigger:"change"}};return l({show:e=>{t.value=!0,b.value=e}}),(e,l)=>{const s=u("NButton");return a(),o(i(m),{to:"body",show:t.value,"onUpdate:show":l[4]||(l[4]=e=>t.value=e)},{default:r((()=>[n(i(y),{style:{width:"350px"}},{footer:r((()=>[n(i(d),{justify:"center"},{default:r((()=>[n(s,{type:"primary",onClick:l[3]||(l[3]=e=>{var a;null==(a=k.value)||a.validate((e=>{e||(g.invoke(_.addWeb,L.cloneDeep(b.value)),t.value=!1)}))})},{default:r((()=>l[7]||(l[7]=[v("收 藏")]))),_:1})])),_:1})])),default:r((()=>[n(i(p),{ref_key:"formRef",ref:k,model:b.value,"label-placement":"left","label-width":"auto",rules:x},{default:r((()=>[n(i(c),{label:"站点名称",path:"name"},{default:r((()=>[n(i(f),{size:"small",value:b.value.name,"onUpdate:value":l[0]||(l[0]=e=>b.value.name=e),placeholder:"请输入站点名称"},null,8,["value"])])),_:1}),n(i(c),{label:"站点网站",path:"url"},{default:r((()=>[n(i(f),{size:"small",value:b.value.url,"onUpdate:value":l[1]||(l[1]=e=>b.value.url=e),disabled:"",placeholder:"站点网站"},null,8,["value"])])),_:1}),n(i(c),{label:"类型",path:"showType"},{default:r((()=>[n(i(h),{value:b.value.showType,"onUpdate:value":l[2]||(l[2]=e=>b.value.showType=e),size:"small"},{default:r((()=>[n(i(d),null,{default:r((()=>[n(i(w),{value:"pc"},{default:r((()=>l[5]||(l[5]=[v(" 电脑 ")]))),_:1}),n(i(w),{value:"phone"},{default:r((()=>l[6]||(l[6]=[v(" 手机 ")]))),_:1})])),_:1})])),_:1},8,["value"])])),_:1})])),_:1},8,["model"])])),_:1})])),_:1},8,["show"])}}},j={style:{height:"100%"}},B={class:"web-container"},C={class:"web-content"},A={__name:"index",setup(e){const d={pc:"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0",phone:"Mozilla/5.0 (iPhone; CPU iPhone OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Mobile/15E148 Safari/604.1"},v=b(),p=s(),c=s(""),f=s({name:"",url:"",category:"entertainment"});k((()=>v.query.url),((e="")=>{c.value="",setTimeout((()=>{c.value=e,f.value.url=e}),500)}),{immediate:!0});const h=s([]),w=s();return x((()=>{var e;(async()=>{h.value=await g.invoke(_.selectWebList,{category:"study"})})(),console.log("route.query",v.query),f.value=v.query,e=v.query.showType,g.invoke(_.setWindowSizeByType,{type:e})})),(e,s)=>{const v=u("webview");return a(),l("div",j,[n(T,null,{"right-action":r((()=>[n(i(q),{size:"22",onClick:s[0]||(s[0]=e=>{w.value.show(f.value)})},{default:r((()=>[n(i(S))])),_:1})])),_:1}),t("div",B,[t("div",C,[c.value?(a(),o(v,{key:0,ref_key:"webviewRef",ref:p,allowfullscreen:"",src:c.value,plugins:!0,style:{width:"100%",height:"100%"},useragent:d[f.value.showType]},null,8,["src","useragent"])):z("",!0)])]),n(U,{data:f.value,ref_key:"editModalRef",ref:w},null,8,["data"])])}}};export{A as default}; 2 | -------------------------------------------------------------------------------- /public/dist/assets/index-c73cc8f4.js: -------------------------------------------------------------------------------- 1 | import{r as l,a,c as e,b as t,w as s,g as u,D as d,v as f,x as n,E as b,k as _,s as i,G as o,F as r}from"./index-e19c5bd5.js";const c={class:"app-container"},p={__name:"index",setup:p=>(l(!1),(l,p)=>(a(),e("div",c,[t(u(o),{type:"line",animated:"","default-value":"basic"},{default:s((()=>[t(u(d),{name:"basic",tab:"基础配置"},{default:s((()=>[t(u(f),null,{default:s((()=>[t(u(n),{label:"秒关快捷键","label-style":{fontWeight:"bold"}},{default:s((()=>[t(u(b),null,{default:s((()=>p[0]||(p[0]=[_("Ctrl + Q")]))),_:1})])),_:1}),t(u(n),{label:"显示/隐藏快捷键","label-style":{fontWeight:"bold"}},{default:s((()=>[t(u(b),null,{default:s((()=>p[1]||(p[1]=[_("Ctrl + Tab")]))),_:1})])),_:1}),t(u(n),{label:"退出专注模式快捷键","label-style":{fontWeight:"bold"}},{default:s((()=>[t(u(b),null,{default:s((()=>p[2]||(p[2]=[_("Esc")]))),_:1})])),_:1}),t(u(n),{label:"透明度快捷键","label-style":{fontWeight:"bold"}},{default:s((()=>[t(u(i),null,{default:s((()=>[t(u(b),null,{default:s((()=>p[3]||(p[3]=[_("长按降低:Ctrl + 1")]))),_:1}),t(u(b),null,{default:s((()=>p[4]||(p[4]=[_("长按增加:Ctrl + 2")]))),_:1})])),_:1})])),_:1})])),_:1})])),_:1})])),_:1}),t(r)])))};export{p as default}; 2 | -------------------------------------------------------------------------------- /public/dist/assets/lodash-022d070e.js: -------------------------------------------------------------------------------- 1 | var n="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function t(n){return n&&n.__esModule&&Object.prototype.hasOwnProperty.call(n,"default")?n.default:n}var r,e,u={exports:{}}; 2 | /** 3 | * @license 4 | * Lodash 5 | * Copyright OpenJS Foundation and other contributors 6 | * Released under MIT license 7 | * Based on Underscore.js 1.8.3 8 | * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 9 | */r=u,e=u.exports,function(){var t,u="Expected a function",i="__lodash_hash_undefined__",o="__lodash_placeholder__",f=16,a=32,c=64,l=128,s=256,h=1/0,p=9007199254740991,v=NaN,_=4294967295,g=[["ary",l],["bind",1],["bindKey",2],["curry",8],["curryRight",f],["flip",512],["partial",a],["partialRight",c],["rearg",s]],y="[object Arguments]",d="[object Array]",b="[object Boolean]",w="[object Date]",m="[object Error]",x="[object Function]",j="[object GeneratorFunction]",A="[object Map]",k="[object Number]",O="[object Object]",I="[object Promise]",R="[object RegExp]",z="[object Set]",E="[object String]",S="[object Symbol]",W="[object WeakMap]",L="[object ArrayBuffer]",C="[object DataView]",T="[object Float32Array]",U="[object Float64Array]",B="[object Int8Array]",$="[object Int16Array]",D="[object Int32Array]",M="[object Uint8Array]",F="[object Uint8ClampedArray]",N="[object Uint16Array]",P="[object Uint32Array]",q=/\b__p \+= '';/g,Z=/\b(__p \+=) '' \+/g,K=/(__e\(.*?\)|\b__t\)) \+\n'';/g,V=/&(?:amp|lt|gt|quot|#39);/g,G=/[&<>"']/g,H=RegExp(V.source),J=RegExp(G.source),Y=/<%-([\s\S]+?)%>/g,Q=/<%([\s\S]+?)%>/g,X=/<%=([\s\S]+?)%>/g,nn=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,tn=/^\w*$/,rn=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,en=/[\\^$.*+?()[\]{}|]/g,un=RegExp(en.source),on=/^\s+/,fn=/\s/,an=/\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/,cn=/\{\n\/\* \[wrapped with (.+)\] \*/,ln=/,? & /,sn=/[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g,hn=/[()=,{}\[\]\/\s]/,pn=/\\(\\)?/g,vn=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g,_n=/\w*$/,gn=/^[-+]0x[0-9a-f]+$/i,yn=/^0b[01]+$/i,dn=/^\[object .+?Constructor\]$/,bn=/^0o[0-7]+$/i,wn=/^(?:0|[1-9]\d*)$/,mn=/[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g,xn=/($^)/,jn=/['\n\r\u2028\u2029\\]/g,An="\\ud800-\\udfff",kn="\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff",On="\\u2700-\\u27bf",In="a-z\\xdf-\\xf6\\xf8-\\xff",Rn="A-Z\\xc0-\\xd6\\xd8-\\xde",zn="\\ufe0e\\ufe0f",En="\\xac\\xb1\\xd7\\xf7\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf\\u2000-\\u206f \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000",Sn="['’]",Wn="["+An+"]",Ln="["+En+"]",Cn="["+kn+"]",Tn="\\d+",Un="["+On+"]",Bn="["+In+"]",$n="[^"+An+En+Tn+On+In+Rn+"]",Dn="\\ud83c[\\udffb-\\udfff]",Mn="[^"+An+"]",Fn="(?:\\ud83c[\\udde6-\\uddff]){2}",Nn="[\\ud800-\\udbff][\\udc00-\\udfff]",Pn="["+Rn+"]",qn="\\u200d",Zn="(?:"+Bn+"|"+$n+")",Kn="(?:"+Pn+"|"+$n+")",Vn="(?:['’](?:d|ll|m|re|s|t|ve))?",Gn="(?:['’](?:D|LL|M|RE|S|T|VE))?",Hn="(?:"+Cn+"|"+Dn+")?",Jn="["+zn+"]?",Yn=Jn+Hn+"(?:"+qn+"(?:"+[Mn,Fn,Nn].join("|")+")"+Jn+Hn+")*",Qn="(?:"+[Un,Fn,Nn].join("|")+")"+Yn,Xn="(?:"+[Mn+Cn+"?",Cn,Fn,Nn,Wn].join("|")+")",nt=RegExp(Sn,"g"),tt=RegExp(Cn,"g"),rt=RegExp(Dn+"(?="+Dn+")|"+Xn+Yn,"g"),et=RegExp([Pn+"?"+Bn+"+"+Vn+"(?="+[Ln,Pn,"$"].join("|")+")",Kn+"+"+Gn+"(?="+[Ln,Pn+Zn,"$"].join("|")+")",Pn+"?"+Zn+"+"+Vn,Pn+"+"+Gn,"\\d*(?:1ST|2ND|3RD|(?![123])\\dTH)(?=\\b|[a-z_])","\\d*(?:1st|2nd|3rd|(?![123])\\dth)(?=\\b|[A-Z_])",Tn,Qn].join("|"),"g"),ut=RegExp("["+qn+An+kn+zn+"]"),it=/[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/,ot=["Array","Buffer","DataView","Date","Error","Float32Array","Float64Array","Function","Int8Array","Int16Array","Int32Array","Map","Math","Object","Promise","RegExp","Set","String","Symbol","TypeError","Uint8Array","Uint8ClampedArray","Uint16Array","Uint32Array","WeakMap","_","clearTimeout","isFinite","parseInt","setTimeout"],ft=-1,at={};at[T]=at[U]=at[B]=at[$]=at[D]=at[M]=at[F]=at[N]=at[P]=!0,at[y]=at[d]=at[L]=at[b]=at[C]=at[w]=at[m]=at[x]=at[A]=at[k]=at[O]=at[R]=at[z]=at[E]=at[W]=!1;var ct={};ct[y]=ct[d]=ct[L]=ct[C]=ct[b]=ct[w]=ct[T]=ct[U]=ct[B]=ct[$]=ct[D]=ct[A]=ct[k]=ct[O]=ct[R]=ct[z]=ct[E]=ct[S]=ct[M]=ct[F]=ct[N]=ct[P]=!0,ct[m]=ct[x]=ct[W]=!1;var lt={"\\":"\\","'":"'","\n":"n","\r":"r","\u2028":"u2028","\u2029":"u2029"},st=parseFloat,ht=parseInt,pt="object"==typeof n&&n&&n.Object===Object&&n,vt="object"==typeof self&&self&&self.Object===Object&&self,_t=pt||vt||Function("return this")(),gt=e&&!e.nodeType&&e,yt=gt&&r&&!r.nodeType&&r,dt=yt&&yt.exports===gt,bt=dt&&pt.process,wt=function(){try{var n=yt&&yt.require&&yt.require("util").types;return n||bt&&bt.binding&&bt.binding("util")}catch(t){}}(),mt=wt&&wt.isArrayBuffer,xt=wt&&wt.isDate,jt=wt&&wt.isMap,At=wt&&wt.isRegExp,kt=wt&&wt.isSet,Ot=wt&&wt.isTypedArray;function It(n,t,r){switch(r.length){case 0:return n.call(t);case 1:return n.call(t,r[0]);case 2:return n.call(t,r[0],r[1]);case 3:return n.call(t,r[0],r[1],r[2])}return n.apply(t,r)}function Rt(n,t,r,e){for(var u=-1,i=null==n?0:n.length;++u-1}function Ct(n,t,r){for(var e=-1,u=null==n?0:n.length;++e-1;);return r}function er(n,t){for(var r=n.length;r--&&Pt(t,n[r],0)>-1;);return r}var ur=Gt({"À":"A","Á":"A","Â":"A","Ã":"A","Ä":"A","Å":"A","à":"a","á":"a","â":"a","ã":"a","ä":"a","å":"a","Ç":"C","ç":"c","Ð":"D","ð":"d","È":"E","É":"E","Ê":"E","Ë":"E","è":"e","é":"e","ê":"e","ë":"e","Ì":"I","Í":"I","Î":"I","Ï":"I","ì":"i","í":"i","î":"i","ï":"i","Ñ":"N","ñ":"n","Ò":"O","Ó":"O","Ô":"O","Õ":"O","Ö":"O","Ø":"O","ò":"o","ó":"o","ô":"o","õ":"o","ö":"o","ø":"o","Ù":"U","Ú":"U","Û":"U","Ü":"U","ù":"u","ú":"u","û":"u","ü":"u","Ý":"Y","ý":"y","ÿ":"y","Æ":"Ae","æ":"ae","Þ":"Th","þ":"th","ß":"ss","Ā":"A","Ă":"A","Ą":"A","ā":"a","ă":"a","ą":"a","Ć":"C","Ĉ":"C","Ċ":"C","Č":"C","ć":"c","ĉ":"c","ċ":"c","č":"c","Ď":"D","Đ":"D","ď":"d","đ":"d","Ē":"E","Ĕ":"E","Ė":"E","Ę":"E","Ě":"E","ē":"e","ĕ":"e","ė":"e","ę":"e","ě":"e","Ĝ":"G","Ğ":"G","Ġ":"G","Ģ":"G","ĝ":"g","ğ":"g","ġ":"g","ģ":"g","Ĥ":"H","Ħ":"H","ĥ":"h","ħ":"h","Ĩ":"I","Ī":"I","Ĭ":"I","Į":"I","İ":"I","ĩ":"i","ī":"i","ĭ":"i","į":"i","ı":"i","Ĵ":"J","ĵ":"j","Ķ":"K","ķ":"k","ĸ":"k","Ĺ":"L","Ļ":"L","Ľ":"L","Ŀ":"L","Ł":"L","ĺ":"l","ļ":"l","ľ":"l","ŀ":"l","ł":"l","Ń":"N","Ņ":"N","Ň":"N","Ŋ":"N","ń":"n","ņ":"n","ň":"n","ŋ":"n","Ō":"O","Ŏ":"O","Ő":"O","ō":"o","ŏ":"o","ő":"o","Ŕ":"R","Ŗ":"R","Ř":"R","ŕ":"r","ŗ":"r","ř":"r","Ś":"S","Ŝ":"S","Ş":"S","Š":"S","ś":"s","ŝ":"s","ş":"s","š":"s","Ţ":"T","Ť":"T","Ŧ":"T","ţ":"t","ť":"t","ŧ":"t","Ũ":"U","Ū":"U","Ŭ":"U","Ů":"U","Ű":"U","Ų":"U","ũ":"u","ū":"u","ŭ":"u","ů":"u","ű":"u","ų":"u","Ŵ":"W","ŵ":"w","Ŷ":"Y","ŷ":"y","Ÿ":"Y","Ź":"Z","Ż":"Z","Ž":"Z","ź":"z","ż":"z","ž":"z","IJ":"IJ","ij":"ij","Œ":"Oe","œ":"oe","ʼn":"'n","ſ":"s"}),ir=Gt({"&":"&","<":"<",">":">",'"':""","'":"'"});function or(n){return"\\"+lt[n]}function fr(n){return ut.test(n)}function ar(n){var t=-1,r=Array(n.size);return n.forEach((function(n,e){r[++t]=[e,n]})),r}function cr(n,t){return function(r){return n(t(r))}}function lr(n,t){for(var r=-1,e=n.length,u=0,i=[];++r",""":'"',"'":"'"}),gr=function n(r){var e,fn=(r=null==r?_t:gr.defaults(_t.Object(),r,gr.pick(_t,ot))).Array,An=r.Date,kn=r.Error,On=r.Function,In=r.Math,Rn=r.Object,zn=r.RegExp,En=r.String,Sn=r.TypeError,Wn=fn.prototype,Ln=On.prototype,Cn=Rn.prototype,Tn=r["__core-js_shared__"],Un=Ln.toString,Bn=Cn.hasOwnProperty,$n=0,Dn=(e=/[^.]+$/.exec(Tn&&Tn.keys&&Tn.keys.IE_PROTO||""))?"Symbol(src)_1."+e:"",Mn=Cn.toString,Fn=Un.call(Rn),Nn=_t._,Pn=zn("^"+Un.call(Bn).replace(en,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),qn=dt?r.Buffer:t,Zn=r.Symbol,Kn=r.Uint8Array,Vn=qn?qn.allocUnsafe:t,Gn=cr(Rn.getPrototypeOf,Rn),Hn=Rn.create,Jn=Cn.propertyIsEnumerable,Yn=Wn.splice,Qn=Zn?Zn.isConcatSpreadable:t,Xn=Zn?Zn.iterator:t,rt=Zn?Zn.toStringTag:t,ut=function(){try{var n=si(Rn,"defineProperty");return n({},"",{}),n}catch(t){}}(),lt=r.clearTimeout!==_t.clearTimeout&&r.clearTimeout,pt=An&&An.now!==_t.Date.now&&An.now,vt=r.setTimeout!==_t.setTimeout&&r.setTimeout,gt=In.ceil,yt=In.floor,bt=Rn.getOwnPropertySymbols,wt=qn?qn.isBuffer:t,Mt=r.isFinite,Gt=Wn.join,yr=cr(Rn.keys,Rn),dr=In.max,br=In.min,wr=An.now,mr=r.parseInt,xr=In.random,jr=Wn.reverse,Ar=si(r,"DataView"),kr=si(r,"Map"),Or=si(r,"Promise"),Ir=si(r,"Set"),Rr=si(r,"WeakMap"),zr=si(Rn,"create"),Er=Rr&&new Rr,Sr={},Wr=Mi(Ar),Lr=Mi(kr),Cr=Mi(Or),Tr=Mi(Ir),Ur=Mi(Rr),Br=Zn?Zn.prototype:t,$r=Br?Br.valueOf:t,Dr=Br?Br.toString:t;function Mr(n){if(uf(n)&&!Vo(n)&&!(n instanceof qr)){if(n instanceof Pr)return n;if(Bn.call(n,"__wrapped__"))return Fi(n)}return new Pr(n)}var Fr=function(){function n(){}return function(r){if(!ef(r))return{};if(Hn)return Hn(r);n.prototype=r;var e=new n;return n.prototype=t,e}}();function Nr(){}function Pr(n,r){this.__wrapped__=n,this.__actions__=[],this.__chain__=!!r,this.__index__=0,this.__values__=t}function qr(n){this.__wrapped__=n,this.__actions__=[],this.__dir__=1,this.__filtered__=!1,this.__iteratees__=[],this.__takeCount__=_,this.__views__=[]}function Zr(n){var t=-1,r=null==n?0:n.length;for(this.clear();++t=r?n:r)),n}function ae(n,r,e,u,i,o){var f,a=1&r,c=2&r,l=4&r;if(e&&(f=i?e(n,u,i,o):e(n)),f!==t)return f;if(!ef(n))return n;var s=Vo(n);if(s){if(f=function(n){var t=n.length,r=new n.constructor(t);return t&&"string"==typeof n[0]&&Bn.call(n,"index")&&(r.index=n.index,r.input=n.input),r}(n),!a)return zu(n,f)}else{var h=vi(n),p=h==x||h==j;if(Yo(n))return ju(n,a);if(h==O||h==y||p&&!i){if(f=c||p?{}:gi(n),!a)return c?function(n,t){return Eu(n,pi(n),t)}(n,function(n,t){return n&&Eu(t,Uf(t),n)}(f,n)):function(n,t){return Eu(n,hi(n),t)}(n,ue(f,n))}else{if(!ct[h])return i?n:{};f=function(n,t,r){var e,u=n.constructor;switch(t){case L:return Au(n);case b:case w:return new u(+n);case C:return function(n,t){var r=t?Au(n.buffer):n.buffer;return new n.constructor(r,n.byteOffset,n.byteLength)}(n,r);case T:case U:case B:case $:case D:case M:case F:case N:case P:return ku(n,r);case A:return new u;case k:case E:return new u(n);case R:return function(n){var t=new n.constructor(n.source,_n.exec(n));return t.lastIndex=n.lastIndex,t}(n);case z:return new u;case S:return e=n,$r?Rn($r.call(e)):{}}}(n,h,a)}}o||(o=new Hr);var v=o.get(n);if(v)return v;o.set(n,f),lf(n)?n.forEach((function(t){f.add(ae(t,r,e,t,n,o))})):of(n)&&n.forEach((function(t,u){f.set(u,ae(t,r,e,u,n,o))}));var _=s?t:(l?c?ui:ei:c?Uf:Tf)(n);return zt(_||n,(function(t,u){_&&(t=n[u=t]),te(f,u,ae(t,r,e,u,n,o))})),f}function ce(n,r,e){var u=e.length;if(null==n)return!u;for(n=Rn(n);u--;){var i=e[u],o=r[i],f=n[i];if(f===t&&!(i in n)||!o(f))return!1}return!0}function le(n,r,e){if("function"!=typeof n)throw new Sn(u);return Ei((function(){n.apply(t,e)}),r)}function se(n,t,r,e){var u=-1,i=Lt,o=!0,f=n.length,a=[],c=t.length;if(!f)return a;r&&(t=Tt(t,Xt(r))),e?(i=Ct,o=!1):t.length>=200&&(i=tr,o=!1,t=new Gr(t));n:for(;++u-1},Kr.prototype.set=function(n,t){var r=this.__data__,e=re(r,n);return e<0?(++this.size,r.push([n,t])):r[e][1]=t,this},Vr.prototype.clear=function(){this.size=0,this.__data__={hash:new Zr,map:new(kr||Kr),string:new Zr}},Vr.prototype.delete=function(n){var t=ci(this,n).delete(n);return this.size-=t?1:0,t},Vr.prototype.get=function(n){return ci(this,n).get(n)},Vr.prototype.has=function(n){return ci(this,n).has(n)},Vr.prototype.set=function(n,t){var r=ci(this,n),e=r.size;return r.set(n,t),this.size+=r.size==e?0:1,this},Gr.prototype.add=Gr.prototype.push=function(n){return this.__data__.set(n,i),this},Gr.prototype.has=function(n){return this.__data__.has(n)},Hr.prototype.clear=function(){this.__data__=new Kr,this.size=0},Hr.prototype.delete=function(n){var t=this.__data__,r=t.delete(n);return this.size=t.size,r},Hr.prototype.get=function(n){return this.__data__.get(n)},Hr.prototype.has=function(n){return this.__data__.has(n)},Hr.prototype.set=function(n,t){var r=this.__data__;if(r instanceof Kr){var e=r.__data__;if(!kr||e.length<199)return e.push([n,t]),this.size=++r.size,this;r=this.__data__=new Vr(e)}return r.set(n,t),this.size=r.size,this};var he=Lu(we),pe=Lu(me,!0);function ve(n,t){var r=!0;return he(n,(function(n,e,u){return r=!!t(n,e,u)})),r}function _e(n,r,e){for(var u=-1,i=n.length;++u0&&r(f)?t>1?ye(f,t-1,r,e,u):Ut(u,f):e||(u[u.length]=f)}return u}var de=Cu(),be=Cu(!0);function we(n,t){return n&&de(n,t,Tf)}function me(n,t){return n&&be(n,t,Tf)}function xe(n,t){return Wt(t,(function(t){return nf(n[t])}))}function je(n,r){for(var e=0,u=(r=bu(r,n)).length;null!=n&&et}function Ie(n,t){return null!=n&&Bn.call(n,t)}function Re(n,t){return null!=n&&t in Rn(n)}function ze(n,r,e){for(var u=e?Ct:Lt,i=n[0].length,o=n.length,f=o,a=fn(o),c=1/0,l=[];f--;){var s=n[f];f&&r&&(s=Tt(s,Xt(r))),c=br(s.length,c),a[f]=!e&&(r||i>=120&&s.length>=120)?new Gr(f&&s):t}s=n[0];var h=-1,p=a[0];n:for(;++h=f?a:a*("desc"==r[e]?-1:1)}return n.index-t.index}(n,t,r)}))}function Ze(n,t,r){for(var e=-1,u=t.length,i={};++e-1;)f!==n&&Yn.call(f,a,1),Yn.call(n,a,1);return n}function Ve(n,t){for(var r=n?t.length:0,e=r-1;r--;){var u=t[r];if(r==e||u!==i){var i=u;di(u)?Yn.call(n,u,1):su(n,u)}}return n}function Ge(n,t){return n+yt(xr()*(t-n+1))}function He(n,t){var r="";if(!n||t<1||t>p)return r;do{t%2&&(r+=n),(t=yt(t/2))&&(n+=n)}while(t);return r}function Je(n,t){return Si(Oi(n,t,fa),n+"")}function Ye(n){return Yr(qf(n))}function Qe(n,t){var r=qf(n);return Ci(r,fe(t,0,r.length))}function Xe(n,r,e,u){if(!ef(n))return n;for(var i=-1,o=(r=bu(r,n)).length,f=o-1,a=n;null!=a&&++iu?0:u+t),(r=r>u?u:r)<0&&(r+=u),u=t>r?0:r-t>>>0,t>>>=0;for(var i=fn(u);++e>>1,o=n[i];null!==o&&!hf(o)&&(r?o<=t:o=200){var c=t?null:Hu(n);if(c)return sr(c);o=!1,u=tr,a=new Gr}else a=t?[]:f;n:for(;++e=u?n:eu(n,r,e)}var xu=lt||function(n){return _t.clearTimeout(n)};function ju(n,t){if(t)return n.slice();var r=n.length,e=Vn?Vn(r):new n.constructor(r);return n.copy(e),e}function Au(n){var t=new n.constructor(n.byteLength);return new Kn(t).set(new Kn(n)),t}function ku(n,t){var r=t?Au(n.buffer):n.buffer;return new n.constructor(r,n.byteOffset,n.length)}function Ou(n,r){if(n!==r){var e=n!==t,u=null===n,i=n==n,o=hf(n),f=r!==t,a=null===r,c=r==r,l=hf(r);if(!a&&!l&&!o&&n>r||o&&f&&c&&!a&&!l||u&&f&&c||!e&&c||!i)return 1;if(!u&&!o&&!l&&n1?e[i-1]:t,f=i>2?e[2]:t;for(o=n.length>3&&"function"==typeof o?(i--,o):t,f&&bi(e[0],e[1],f)&&(o=i<3?t:o,i=1),r=Rn(r);++u-1?i[o?r[f]:f]:t}}function Du(n){return ri((function(r){var e=r.length,i=e,o=Pr.prototype.thru;for(n&&r.reverse();i--;){var f=r[i];if("function"!=typeof f)throw new Sn(u);if(o&&!a&&"wrapper"==oi(f))var a=new Pr([],!0)}for(i=a?i:e;++i1&&b.reverse(),h&&ca))return!1;var l=o.get(n),s=o.get(r);if(l&&s)return l==r&&s==n;var h=-1,p=!0,v=2&e?new Gr:t;for(o.set(n,r),o.set(r,n);++h-1&&n%1==0&&n1?"& ":"")+t[e],t=t.join(r>2?", ":" "),n.replace(an,"{\n/* [wrapped with "+t+"] */\n")}(e,function(n,t){return zt(g,(function(r){var e="_."+r[0];t&r[1]&&!Lt(n,e)&&n.push(e)})),n.sort()}(function(n){var t=n.match(cn);return t?t[1].split(ln):[]}(e),r)))}function Li(n){var r=0,e=0;return function(){var u=wr(),i=16-(u-e);if(e=u,i>0){if(++r>=800)return arguments[0]}else r=0;return n.apply(t,arguments)}}function Ci(n,r){var e=-1,u=n.length,i=u-1;for(r=r===t?u:r;++e1?n[r-1]:t;return e="function"==typeof e?(n.pop(),e):t,fo(n,e)}));function vo(n){var t=Mr(n);return t.__chain__=!0,t}function _o(n,t){return t(n)}var go=ri((function(n){var r=n.length,e=r?n[0]:0,u=this.__wrapped__,i=function(t){return oe(t,n)};return!(r>1||this.__actions__.length)&&u instanceof qr&&di(e)?((u=u.slice(e,+e+(r?1:0))).__actions__.push({func:_o,args:[i],thisArg:t}),new Pr(u,this.__chain__).thru((function(n){return r&&!n.length&&n.push(t),n}))):this.thru(i)})),yo=Su((function(n,t,r){Bn.call(n,r)?++n[r]:ie(n,r,1)})),bo=$u(Zi),wo=$u(Ki);function mo(n,t){return(Vo(n)?zt:he)(n,ai(t,3))}function xo(n,t){return(Vo(n)?Et:pe)(n,ai(t,3))}var jo=Su((function(n,t,r){Bn.call(n,r)?n[r].push(t):ie(n,r,[t])})),Ao=Je((function(n,t,r){var e=-1,u="function"==typeof t,i=Ho(n)?fn(n.length):[];return he(n,(function(n){i[++e]=u?It(t,n,r):Ee(n,t,r)})),i})),ko=Su((function(n,t,r){ie(n,r,t)}));function Oo(n,t){return(Vo(n)?Tt:De)(n,ai(t,3))}var Io=Su((function(n,t,r){n[r?0:1].push(t)}),(function(){return[[],[]]})),Ro=Je((function(n,t){if(null==n)return[];var r=t.length;return r>1&&bi(n,t[0],t[1])?t=[]:r>2&&bi(t[0],t[1],t[2])&&(t=[t[0]]),qe(n,ye(t,1),[])})),zo=pt||function(){return _t.Date.now()};function Eo(n,r,e){return r=e?t:r,r=n&&null==r?n.length:r,Yu(n,l,t,t,t,t,r)}function So(n,r){var e;if("function"!=typeof r)throw new Sn(u);return n=df(n),function(){return--n>0&&(e=r.apply(this,arguments)),n<=1&&(r=t),e}}var Wo=Je((function(n,t,r){var e=1;if(r.length){var u=lr(r,fi(Wo));e|=a}return Yu(n,e,t,r,u)})),Lo=Je((function(n,t,r){var e=3;if(r.length){var u=lr(r,fi(Lo));e|=a}return Yu(t,e,n,r,u)}));function Co(n,r,e){var i,o,f,a,c,l,s=0,h=!1,p=!1,v=!0;if("function"!=typeof n)throw new Sn(u);function _(r){var e=i,u=o;return i=o=t,s=r,a=n.apply(u,e)}function g(n){var e=n-l;return l===t||e>=r||e<0||p&&n-s>=f}function y(){var n=zo();if(g(n))return d(n);c=Ei(y,function(n){var t=r-(n-l);return p?br(t,f-(n-s)):t}(n))}function d(n){return c=t,v&&i?_(n):(i=o=t,a)}function b(){var n=zo(),e=g(n);if(i=arguments,o=this,l=n,e){if(c===t)return function(n){return s=n,c=Ei(y,r),h?_(n):a}(l);if(p)return xu(c),c=Ei(y,r),_(l)}return c===t&&(c=Ei(y,r)),a}return r=wf(r)||0,ef(e)&&(h=!!e.leading,f=(p="maxWait"in e)?dr(wf(e.maxWait)||0,r):f,v="trailing"in e?!!e.trailing:v),b.cancel=function(){c!==t&&xu(c),s=0,i=l=o=c=t},b.flush=function(){return c===t?a:d(zo())},b}var To=Je((function(n,t){return le(n,1,t)})),Uo=Je((function(n,t,r){return le(n,wf(t)||0,r)}));function Bo(n,t){if("function"!=typeof n||null!=t&&"function"!=typeof t)throw new Sn(u);var r=function(){var e=arguments,u=t?t.apply(this,e):e[0],i=r.cache;if(i.has(u))return i.get(u);var o=n.apply(this,e);return r.cache=i.set(u,o)||i,o};return r.cache=new(Bo.Cache||Vr),r}function $o(n){if("function"!=typeof n)throw new Sn(u);return function(){var t=arguments;switch(t.length){case 0:return!n.call(this);case 1:return!n.call(this,t[0]);case 2:return!n.call(this,t[0],t[1]);case 3:return!n.call(this,t[0],t[1],t[2])}return!n.apply(this,t)}}Bo.Cache=Vr;var Do=wu((function(n,t){var r=(t=1==t.length&&Vo(t[0])?Tt(t[0],Xt(ai())):Tt(ye(t,1),Xt(ai()))).length;return Je((function(e){for(var u=-1,i=br(e.length,r);++u=t})),Ko=Se(function(){return arguments}())?Se:function(n){return uf(n)&&Bn.call(n,"callee")&&!Jn.call(n,"callee")},Vo=fn.isArray,Go=mt?Xt(mt):function(n){return uf(n)&&ke(n)==L};function Ho(n){return null!=n&&rf(n.length)&&!nf(n)}function Jo(n){return uf(n)&&Ho(n)}var Yo=wt||wa,Qo=xt?Xt(xt):function(n){return uf(n)&&ke(n)==w};function Xo(n){if(!uf(n))return!1;var t=ke(n);return t==m||"[object DOMException]"==t||"string"==typeof n.message&&"string"==typeof n.name&&!af(n)}function nf(n){if(!ef(n))return!1;var t=ke(n);return t==x||t==j||"[object AsyncFunction]"==t||"[object Proxy]"==t}function tf(n){return"number"==typeof n&&n==df(n)}function rf(n){return"number"==typeof n&&n>-1&&n%1==0&&n<=p}function ef(n){var t=typeof n;return null!=n&&("object"==t||"function"==t)}function uf(n){return null!=n&&"object"==typeof n}var of=jt?Xt(jt):function(n){return uf(n)&&vi(n)==A};function ff(n){return"number"==typeof n||uf(n)&&ke(n)==k}function af(n){if(!uf(n)||ke(n)!=O)return!1;var t=Gn(n);if(null===t)return!0;var r=Bn.call(t,"constructor")&&t.constructor;return"function"==typeof r&&r instanceof r&&Un.call(r)==Fn}var cf=At?Xt(At):function(n){return uf(n)&&ke(n)==R},lf=kt?Xt(kt):function(n){return uf(n)&&vi(n)==z};function sf(n){return"string"==typeof n||!Vo(n)&&uf(n)&&ke(n)==E}function hf(n){return"symbol"==typeof n||uf(n)&&ke(n)==S}var pf=Ot?Xt(Ot):function(n){return uf(n)&&rf(n.length)&&!!at[ke(n)]},vf=Ku($e),_f=Ku((function(n,t){return n<=t}));function gf(n){if(!n)return[];if(Ho(n))return sf(n)?pr(n):zu(n);if(Xn&&n[Xn])return function(n){for(var t,r=[];!(t=n.next()).done;)r.push(t.value);return r}(n[Xn]());var t=vi(n);return(t==A?ar:t==z?sr:qf)(n)}function yf(n){return n?(n=wf(n))===h||n===-1/0?17976931348623157e292*(n<0?-1:1):n==n?n:0:0===n?n:0}function df(n){var t=yf(n),r=t%1;return t==t?r?t-r:t:0}function bf(n){return n?fe(df(n),0,_):0}function wf(n){if("number"==typeof n)return n;if(hf(n))return v;if(ef(n)){var t="function"==typeof n.valueOf?n.valueOf():n;n=ef(t)?t+"":t}if("string"!=typeof n)return 0===n?n:+n;n=Qt(n);var r=yn.test(n);return r||bn.test(n)?ht(n.slice(2),r?2:8):gn.test(n)?v:+n}function mf(n){return Eu(n,Uf(n))}function xf(n){return null==n?"":cu(n)}var jf=Wu((function(n,t){if(ji(t)||Ho(t))Eu(t,Tf(t),n);else for(var r in t)Bn.call(t,r)&&te(n,r,t[r])})),Af=Wu((function(n,t){Eu(t,Uf(t),n)})),kf=Wu((function(n,t,r,e){Eu(t,Uf(t),n,e)})),Of=Wu((function(n,t,r,e){Eu(t,Tf(t),n,e)})),If=ri(oe),Rf=Je((function(n,r){n=Rn(n);var e=-1,u=r.length,i=u>2?r[2]:t;for(i&&bi(r[0],r[1],i)&&(u=1);++e1),t})),Eu(n,ui(n),r),e&&(r=ae(r,7,ni));for(var u=t.length;u--;)su(r,t[u]);return r})),Mf=ri((function(n,t){return null==n?{}:function(n,t){return Ze(n,t,(function(t,r){return Sf(n,r)}))}(n,t)}));function Ff(n,t){if(null==n)return{};var r=Tt(ui(n),(function(n){return[n]}));return t=ai(t),Ze(n,r,(function(n,r){return t(n,r[0])}))}var Nf=Ju(Tf),Pf=Ju(Uf);function qf(n){return null==n?[]:nr(n,Tf(n))}var Zf=Uu((function(n,t,r){return t=t.toLowerCase(),n+(r?Kf(t):t)}));function Kf(n){return na(xf(n).toLowerCase())}function Vf(n){return(n=xf(n))&&n.replace(mn,ur).replace(tt,"")}var Gf=Uu((function(n,t,r){return n+(r?"-":"")+t.toLowerCase()})),Hf=Uu((function(n,t,r){return n+(r?" ":"")+t.toLowerCase()})),Jf=Tu("toLowerCase"),Yf=Uu((function(n,t,r){return n+(r?"_":"")+t.toLowerCase()})),Qf=Uu((function(n,t,r){return n+(r?" ":"")+na(t)})),Xf=Uu((function(n,t,r){return n+(r?" ":"")+t.toUpperCase()})),na=Tu("toUpperCase");function ta(n,r,e){return n=xf(n),(r=e?t:r)===t?function(n){return it.test(n)}(n)?function(n){return n.match(et)||[]}(n):function(n){return n.match(sn)||[]}(n):n.match(r)||[]}var ra=Je((function(n,r){try{return It(n,t,r)}catch(e){return Xo(e)?e:new kn(e)}})),ea=ri((function(n,t){return zt(t,(function(t){t=Di(t),ie(n,t,Wo(n[t],n))})),n}));function ua(n){return function(){return n}}var ia=Du(),oa=Du(!0);function fa(n){return n}function aa(n){return Te("function"==typeof n?n:ae(n,1))}var ca=Je((function(n,t){return function(r){return Ee(r,n,t)}})),la=Je((function(n,t){return function(r){return Ee(n,r,t)}}));function sa(n,t,r){var e=Tf(t),u=xe(t,e);null!=r||ef(t)&&(u.length||!e.length)||(r=t,t=n,n=this,u=xe(t,Tf(t)));var i=!(ef(r)&&"chain"in r&&!r.chain),o=nf(n);return zt(u,(function(r){var e=t[r];n[r]=e,o&&(n.prototype[r]=function(){var t=this.__chain__;if(i||t){var r=n(this.__wrapped__);return(r.__actions__=zu(this.__actions__)).push({func:e,args:arguments,thisArg:n}),r.__chain__=t,r}return e.apply(n,Ut([this.value()],arguments))})})),n}function ha(){}var pa=Pu(Tt),va=Pu(St),_a=Pu(Dt);function ga(n){return wi(n)?Vt(Di(n)):function(n){return function(t){return je(t,n)}}(n)}var ya=Zu(),da=Zu(!0);function ba(){return[]}function wa(){return!1}var ma,xa=Nu((function(n,t){return n+t}),0),ja=Gu("ceil"),Aa=Nu((function(n,t){return n/t}),1),ka=Gu("floor"),Oa=Nu((function(n,t){return n*t}),1),Ia=Gu("round"),Ra=Nu((function(n,t){return n-t}),0);return Mr.after=function(n,t){if("function"!=typeof t)throw new Sn(u);return n=df(n),function(){if(--n<1)return t.apply(this,arguments)}},Mr.ary=Eo,Mr.assign=jf,Mr.assignIn=Af,Mr.assignInWith=kf,Mr.assignWith=Of,Mr.at=If,Mr.before=So,Mr.bind=Wo,Mr.bindAll=ea,Mr.bindKey=Lo,Mr.castArray=function(){if(!arguments.length)return[];var n=arguments[0];return Vo(n)?n:[n]},Mr.chain=vo,Mr.chunk=function(n,r,e){r=(e?bi(n,r,e):r===t)?1:dr(df(r),0);var u=null==n?0:n.length;if(!u||r<1)return[];for(var i=0,o=0,f=fn(gt(u/r));ii?0:i+e),(u=u===t||u>i?i:df(u))<0&&(u+=i),u=e>u?0:bf(u);e>>0)?(n=xf(n))&&("string"==typeof r||null!=r&&!cf(r))&&!(r=cu(r))&&fr(n)?mu(pr(n),0,e):n.split(r,e):[]},Mr.spread=function(n,t){if("function"!=typeof n)throw new Sn(u);return t=null==t?0:dr(df(t),0),Je((function(r){var e=r[t],u=mu(r,0,t);return e&&Ut(u,e),It(n,this,u)}))},Mr.tail=function(n){var t=null==n?0:n.length;return t?eu(n,1,t):[]},Mr.take=function(n,r,e){return n&&n.length?eu(n,0,(r=e||r===t?1:df(r))<0?0:r):[]},Mr.takeRight=function(n,r,e){var u=null==n?0:n.length;return u?eu(n,(r=u-(r=e||r===t?1:df(r)))<0?0:r,u):[]},Mr.takeRightWhile=function(n,t){return n&&n.length?pu(n,ai(t,3),!1,!0):[]},Mr.takeWhile=function(n,t){return n&&n.length?pu(n,ai(t,3)):[]},Mr.tap=function(n,t){return t(n),n},Mr.throttle=function(n,t,r){var e=!0,i=!0;if("function"!=typeof n)throw new Sn(u);return ef(r)&&(e="leading"in r?!!r.leading:e,i="trailing"in r?!!r.trailing:i),Co(n,t,{leading:e,maxWait:t,trailing:i})},Mr.thru=_o,Mr.toArray=gf,Mr.toPairs=Nf,Mr.toPairsIn=Pf,Mr.toPath=function(n){return Vo(n)?Tt(n,Di):hf(n)?[n]:zu($i(xf(n)))},Mr.toPlainObject=mf,Mr.transform=function(n,t,r){var e=Vo(n),u=e||Yo(n)||pf(n);if(t=ai(t,4),null==r){var i=n&&n.constructor;r=u?e?new i:[]:ef(n)&&nf(i)?Fr(Gn(n)):{}}return(u?zt:we)(n,(function(n,e,u){return t(r,n,e,u)})),r},Mr.unary=function(n){return Eo(n,1)},Mr.union=eo,Mr.unionBy=uo,Mr.unionWith=io,Mr.uniq=function(n){return n&&n.length?lu(n):[]},Mr.uniqBy=function(n,t){return n&&n.length?lu(n,ai(t,2)):[]},Mr.uniqWith=function(n,r){return r="function"==typeof r?r:t,n&&n.length?lu(n,t,r):[]},Mr.unset=function(n,t){return null==n||su(n,t)},Mr.unzip=oo,Mr.unzipWith=fo,Mr.update=function(n,t,r){return null==n?n:hu(n,t,du(r))},Mr.updateWith=function(n,r,e,u){return u="function"==typeof u?u:t,null==n?n:hu(n,r,du(e),u)},Mr.values=qf,Mr.valuesIn=function(n){return null==n?[]:nr(n,Uf(n))},Mr.without=ao,Mr.words=ta,Mr.wrap=function(n,t){return Mo(du(t),n)},Mr.xor=co,Mr.xorBy=lo,Mr.xorWith=so,Mr.zip=ho,Mr.zipObject=function(n,t){return gu(n||[],t||[],te)},Mr.zipObjectDeep=function(n,t){return gu(n||[],t||[],Xe)},Mr.zipWith=po,Mr.entries=Nf,Mr.entriesIn=Pf,Mr.extend=Af,Mr.extendWith=kf,sa(Mr,Mr),Mr.add=xa,Mr.attempt=ra,Mr.camelCase=Zf,Mr.capitalize=Kf,Mr.ceil=ja,Mr.clamp=function(n,r,e){return e===t&&(e=r,r=t),e!==t&&(e=(e=wf(e))==e?e:0),r!==t&&(r=(r=wf(r))==r?r:0),fe(wf(n),r,e)},Mr.clone=function(n){return ae(n,4)},Mr.cloneDeep=function(n){return ae(n,5)},Mr.cloneDeepWith=function(n,r){return ae(n,5,r="function"==typeof r?r:t)},Mr.cloneWith=function(n,r){return ae(n,4,r="function"==typeof r?r:t)},Mr.conformsTo=function(n,t){return null==t||ce(n,t,Tf(t))},Mr.deburr=Vf,Mr.defaultTo=function(n,t){return null==n||n!=n?t:n},Mr.divide=Aa,Mr.endsWith=function(n,r,e){n=xf(n),r=cu(r);var u=n.length,i=e=e===t?u:fe(df(e),0,u);return(e-=r.length)>=0&&n.slice(e,i)==r},Mr.eq=Po,Mr.escape=function(n){return(n=xf(n))&&J.test(n)?n.replace(G,ir):n},Mr.escapeRegExp=function(n){return(n=xf(n))&&un.test(n)?n.replace(en,"\\$&"):n},Mr.every=function(n,r,e){var u=Vo(n)?St:ve;return e&&bi(n,r,e)&&(r=t),u(n,ai(r,3))},Mr.find=bo,Mr.findIndex=Zi,Mr.findKey=function(n,t){return Ft(n,ai(t,3),we)},Mr.findLast=wo,Mr.findLastIndex=Ki,Mr.findLastKey=function(n,t){return Ft(n,ai(t,3),me)},Mr.floor=ka,Mr.forEach=mo,Mr.forEachRight=xo,Mr.forIn=function(n,t){return null==n?n:de(n,ai(t,3),Uf)},Mr.forInRight=function(n,t){return null==n?n:be(n,ai(t,3),Uf)},Mr.forOwn=function(n,t){return n&&we(n,ai(t,3))},Mr.forOwnRight=function(n,t){return n&&me(n,ai(t,3))},Mr.get=Ef,Mr.gt=qo,Mr.gte=Zo,Mr.has=function(n,t){return null!=n&&_i(n,t,Ie)},Mr.hasIn=Sf,Mr.head=Gi,Mr.identity=fa,Mr.includes=function(n,t,r,e){n=Ho(n)?n:qf(n),r=r&&!e?df(r):0;var u=n.length;return r<0&&(r=dr(u+r,0)),sf(n)?r<=u&&n.indexOf(t,r)>-1:!!u&&Pt(n,t,r)>-1},Mr.indexOf=function(n,t,r){var e=null==n?0:n.length;if(!e)return-1;var u=null==r?0:df(r);return u<0&&(u=dr(e+u,0)),Pt(n,t,u)},Mr.inRange=function(n,r,e){return r=yf(r),e===t?(e=r,r=0):e=yf(e),function(n,t,r){return n>=br(t,r)&&n=-9007199254740991&&n<=p},Mr.isSet=lf,Mr.isString=sf,Mr.isSymbol=hf,Mr.isTypedArray=pf,Mr.isUndefined=function(n){return n===t},Mr.isWeakMap=function(n){return uf(n)&&vi(n)==W},Mr.isWeakSet=function(n){return uf(n)&&"[object WeakSet]"==ke(n)},Mr.join=function(n,t){return null==n?"":Gt.call(n,t)},Mr.kebabCase=Gf,Mr.last=Qi,Mr.lastIndexOf=function(n,r,e){var u=null==n?0:n.length;if(!u)return-1;var i=u;return e!==t&&(i=(i=df(e))<0?dr(u+i,0):br(i,u-1)),r==r?function(n,t,r){for(var e=r+1;e--;)if(n[e]===t)return e;return e}(n,r,i):Nt(n,Zt,i,!0)},Mr.lowerCase=Hf,Mr.lowerFirst=Jf,Mr.lt=vf,Mr.lte=_f,Mr.max=function(n){return n&&n.length?_e(n,fa,Oe):t},Mr.maxBy=function(n,r){return n&&n.length?_e(n,ai(r,2),Oe):t},Mr.mean=function(n){return Kt(n,fa)},Mr.meanBy=function(n,t){return Kt(n,ai(t,2))},Mr.min=function(n){return n&&n.length?_e(n,fa,$e):t},Mr.minBy=function(n,r){return n&&n.length?_e(n,ai(r,2),$e):t},Mr.stubArray=ba,Mr.stubFalse=wa,Mr.stubObject=function(){return{}},Mr.stubString=function(){return""},Mr.stubTrue=function(){return!0},Mr.multiply=Oa,Mr.nth=function(n,r){return n&&n.length?Pe(n,df(r)):t},Mr.noConflict=function(){return _t._===this&&(_t._=Nn),this},Mr.noop=ha,Mr.now=zo,Mr.pad=function(n,t,r){n=xf(n);var e=(t=df(t))?hr(n):0;if(!t||e>=t)return n;var u=(t-e)/2;return qu(yt(u),r)+n+qu(gt(u),r)},Mr.padEnd=function(n,t,r){n=xf(n);var e=(t=df(t))?hr(n):0;return t&&er){var u=n;n=r,r=u}if(e||n%1||r%1){var i=xr();return br(n+i*(r-n+st("1e-"+((i+"").length-1))),r)}return Ge(n,r)},Mr.reduce=function(n,t,r){var e=Vo(n)?Bt:Ht,u=arguments.length<3;return e(n,ai(t,4),r,u,he)},Mr.reduceRight=function(n,t,r){var e=Vo(n)?$t:Ht,u=arguments.length<3;return e(n,ai(t,4),r,u,pe)},Mr.repeat=function(n,r,e){return r=(e?bi(n,r,e):r===t)?1:df(r),He(xf(n),r)},Mr.replace=function(){var n=arguments,t=xf(n[0]);return n.length<3?t:t.replace(n[1],n[2])},Mr.result=function(n,r,e){var u=-1,i=(r=bu(r,n)).length;for(i||(i=1,n=t);++up)return[];var r=_,e=br(n,_);t=ai(t),n-=_;for(var u=Yt(e,t);++r=o)return n;var a=e-hr(u);if(a<1)return u;var c=f?mu(f,0,a).join(""):n.slice(0,a);if(i===t)return c+u;if(f&&(a+=c.length-a),cf(i)){if(n.slice(a).search(i)){var l,s=c;for(i.global||(i=zn(i.source,xf(_n.exec(i))+"g")),i.lastIndex=0;l=i.exec(s);)var h=l.index;c=c.slice(0,h===t?a:h)}}else if(n.indexOf(cu(i),a)!=a){var p=c.lastIndexOf(i);p>-1&&(c=c.slice(0,p))}return c+u},Mr.unescape=function(n){return(n=xf(n))&&H.test(n)?n.replace(V,_r):n},Mr.uniqueId=function(n){var t=++$n;return xf(n)+t},Mr.upperCase=Xf,Mr.upperFirst=na,Mr.each=mo,Mr.eachRight=xo,Mr.first=Gi,sa(Mr,(ma={},we(Mr,(function(n,t){Bn.call(Mr.prototype,t)||(ma[t]=n)})),ma),{chain:!1}),Mr.VERSION="4.17.21",zt(["bind","bindKey","curry","curryRight","partial","partialRight"],(function(n){Mr[n].placeholder=Mr})),zt(["drop","take"],(function(n,r){qr.prototype[n]=function(e){e=e===t?1:dr(df(e),0);var u=this.__filtered__&&!r?new qr(this):this.clone();return u.__filtered__?u.__takeCount__=br(e,u.__takeCount__):u.__views__.push({size:br(e,_),type:n+(u.__dir__<0?"Right":"")}),u},qr.prototype[n+"Right"]=function(t){return this.reverse()[n](t).reverse()}})),zt(["filter","map","takeWhile"],(function(n,t){var r=t+1,e=1==r||3==r;qr.prototype[n]=function(n){var t=this.clone();return t.__iteratees__.push({iteratee:ai(n,3),type:r}),t.__filtered__=t.__filtered__||e,t}})),zt(["head","last"],(function(n,t){var r="take"+(t?"Right":"");qr.prototype[n]=function(){return this[r](1).value()[0]}})),zt(["initial","tail"],(function(n,t){var r="drop"+(t?"":"Right");qr.prototype[n]=function(){return this.__filtered__?new qr(this):this[r](1)}})),qr.prototype.compact=function(){return this.filter(fa)},qr.prototype.find=function(n){return this.filter(n).head()},qr.prototype.findLast=function(n){return this.reverse().find(n)},qr.prototype.invokeMap=Je((function(n,t){return"function"==typeof n?new qr(this):this.map((function(r){return Ee(r,n,t)}))})),qr.prototype.reject=function(n){return this.filter($o(ai(n)))},qr.prototype.slice=function(n,r){n=df(n);var e=this;return e.__filtered__&&(n>0||r<0)?new qr(e):(n<0?e=e.takeRight(-n):n&&(e=e.drop(n)),r!==t&&(e=(r=df(r))<0?e.dropRight(-r):e.take(r-n)),e)},qr.prototype.takeRightWhile=function(n){return this.reverse().takeWhile(n).reverse()},qr.prototype.toArray=function(){return this.take(_)},we(qr.prototype,(function(n,r){var e=/^(?:filter|find|map|reject)|While$/.test(r),u=/^(?:head|last)$/.test(r),i=Mr[u?"take"+("last"==r?"Right":""):r],o=u||/^find/.test(r);i&&(Mr.prototype[r]=function(){var r=this.__wrapped__,f=u?[1]:arguments,a=r instanceof qr,c=f[0],l=a||Vo(r),s=function(n){var t=i.apply(Mr,Ut([n],f));return u&&h?t[0]:t};l&&e&&"function"==typeof c&&1!=c.length&&(a=l=!1);var h=this.__chain__,p=!!this.__actions__.length,v=o&&!h,_=a&&!p;if(!o&&l){r=_?r:new qr(this);var g=n.apply(r,f);return g.__actions__.push({func:_o,args:[s],thisArg:t}),new Pr(g,h)}return v&&_?n.apply(this,f):(g=this.thru(s),v?u?g.value()[0]:g.value():g)})})),zt(["pop","push","shift","sort","splice","unshift"],(function(n){var t=Wn[n],r=/^(?:push|sort|unshift)$/.test(n)?"tap":"thru",e=/^(?:pop|shift)$/.test(n);Mr.prototype[n]=function(){var n=arguments;if(e&&!this.__chain__){var u=this.value();return t.apply(Vo(u)?u:[],n)}return this[r]((function(r){return t.apply(Vo(r)?r:[],n)}))}})),we(qr.prototype,(function(n,t){var r=Mr[t];if(r){var e=r.name+"";Bn.call(Sr,e)||(Sr[e]=[]),Sr[e].push({name:t,func:r})}})),Sr[Mu(t,2).name]=[{name:"wrapper",func:t}],qr.prototype.clone=function(){var n=new qr(this.__wrapped__);return n.__actions__=zu(this.__actions__),n.__dir__=this.__dir__,n.__filtered__=this.__filtered__,n.__iteratees__=zu(this.__iteratees__),n.__takeCount__=this.__takeCount__,n.__views__=zu(this.__views__),n},qr.prototype.reverse=function(){if(this.__filtered__){var n=new qr(this);n.__dir__=-1,n.__filtered__=!0}else(n=this.clone()).__dir__*=-1;return n},qr.prototype.value=function(){var n=this.__wrapped__.value(),t=this.__dir__,r=Vo(n),e=t<0,u=r?n.length:0,i=function(n,t,r){for(var e=-1,u=r.length;++e=this.__values__.length;return{done:n,value:n?t:this.__values__[this.__index__++]}},Mr.prototype.plant=function(n){for(var r,e=this;e instanceof Nr;){var u=Fi(e);u.__index__=0,u.__values__=t,r?i.__wrapped__=u:r=u;var i=u;e=e.__wrapped__}return i.__wrapped__=n,r},Mr.prototype.reverse=function(){var n=this.__wrapped__;if(n instanceof qr){var r=n;return this.__actions__.length&&(r=new qr(this)),(r=r.reverse()).__actions__.push({func:_o,args:[ro],thisArg:t}),new Pr(r,this.__chain__)}return this.thru(ro)},Mr.prototype.toJSON=Mr.prototype.valueOf=Mr.prototype.value=function(){return vu(this.__wrapped__,this.__actions__)},Mr.prototype.first=Mr.prototype.head,Xn&&(Mr.prototype[Xn]=function(){return this}),Mr}();yt?((yt.exports=gr)._=gr,gt._=gr):_t._=gr}.call(n);const i=t(u.exports);export{i as _}; 10 | -------------------------------------------------------------------------------- /public/dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 87 | 88 | 89 | 90 | 91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /public/html/loading.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 83 | 84 | 85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 | 94 | 95 | -------------------------------------------------------------------------------- /public/html/view_example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 15 | 16 | 17 | 18 |
19 | 这是一个html页面 20 |
21 | 22 | -------------------------------------------------------------------------------- /public/images/app-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /public/images/example/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lazycce/ToReduce/a889b034c44924350cc043b06d81ffc75a394db3/public/images/example/img.png -------------------------------------------------------------------------------- /public/images/example/img2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lazycce/ToReduce/a889b034c44924350cc043b06d81ffc75a394db3/public/images/example/img2.png -------------------------------------------------------------------------------- /public/images/example/img3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lazycce/ToReduce/a889b034c44924350cc043b06d81ffc75a394db3/public/images/example/img3.png -------------------------------------------------------------------------------- /public/images/example/img4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lazycce/ToReduce/a889b034c44924350cc043b06d81ffc75a394db3/public/images/example/img4.png -------------------------------------------------------------------------------- /public/images/example/img5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lazycce/ToReduce/a889b034c44924350cc043b06d81ffc75a394db3/public/images/example/img5.png -------------------------------------------------------------------------------- /public/images/example/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lazycce/ToReduce/a889b034c44924350cc043b06d81ffc75a394db3/public/images/example/logo.png -------------------------------------------------------------------------------- /public/images/tray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lazycce/ToReduce/a889b034c44924350cc043b06d81ffc75a394db3/public/images/tray.png -------------------------------------------------------------------------------- /public/ssl/localhost+1.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDpWYqPkljVtDNp 3 | JVwlcOxNVihQPf4T3Q/tuIt5znV5ImBmlWg+OyuG47Y5e+qPMjFCSX4ebTAtBMbY 4 | m6AJihkKy0iKj1mVS9TPCzqcHFnUliCIqJMKFsJTWygNGgogjXhlxFaJgCZO6Gw6 5 | ocWp6nw1gMhrMIxqT2MQIQX16SD1IH/F4JMoaYuifnR+OOgbS3yUKHDTFApkZAWn 6 | dS4GpbT39rW9cmbJrGHCvl8bsm8MXMdXars10A++wjjmHbtZu8beFv+zKbDw4iAu 7 | ls8p/lAoIvQy8MkLLM5b392CLMaK0517+qM3VdEZ0ph+32m9IEoj5QV8xgaPdnyP 8 | 8/8a2bU/AgMBAAECggEBAIzFZ8GVF+JUA2+7CgvMQ8Gj6E4AF/cDtUhDvGCPHG8n 9 | PeCk4W4pY+jMFnI3PxmDvhOvIlZYqGeAKjUiLTmUBedtGyX7tJ9MT+VXcNQchlSo 10 | /Jd0mr/LWw/OPispOlLJBYjfGRV6KaIQtLnqPcRzoNrmBgIkF5FKswhX47CmIyu8 11 | eEXmDsqXzWmYwdOHLNDshLOrCgFgRnqK7HjxvNqb9k4qv3V9WxlJYITK7L4eJNcZ 12 | XYvGl+QV1+n153phyYnHcbohzvE44Hv8e4hiY9uYNdWckNBNFLpk9vEmUjewCRT1 13 | kL30woifUhZCalXBIfKEHNZg23teOMHNZTqcS+ER64ECgYEA7BAWVWS/8b9h1AmP 14 | SYhJgudb+ck1ItuvEDjonKuUCTBYm0iQI7cgAimasIBybRO8zwKstvWGDnUJJEkB 15 | Oh7AXF6CbKuP7navTBAaF3AXsNfDgQuEwtEg78iqiQzTyp6FOvvOT2aOtzi86Ju1 16 | zwKu+DLV3ETeokHWrQmV1uZnbZMCgYEA/Q7LdEEvZxz8OqOjtj8iKLWAv0WvnHTO 17 | Zjqn/BbXi2PyI8d5ntIUPEgm/MRpvXTa465Uu8Orujfq51Z/oFbyvhhEOWT/sVn/ 18 | fzzbH4xXb3bLJ3D+LILQzsm4d8yrV66Re1ehFVxRPIy5RTpssED7bOns9ds4lsHT 19 | W9p7ibYRBSUCgYAMpJftnuXA1tUwfAqWj5wQTL/aUvJrmYR4w/OBYJcfHt3AA1Tk 20 | 9MvcEcpdJaP7P5FfLO9/JQs2/wGsVdSg/kCjMdSeaVneFbExy7L6CmDacdPgt3M2 21 | 0+iFryOjD3LQaUkNbasRCZcfLQTBGIXWPniMhnx5vZ6G5ivPPLIvvktPzQKBgQCd 22 | s/yi5ISwE+Y0fQpnZwzYpdQoXztDm5+NIfzSI0IMgirClWt7yJwHvUdeuuDSyuIm 23 | hdwUb6qzkGl55fP/bnA0e1b5FbIrSlTpbHl6PbG3qyaL2+TqxFNwq1Gkhw44xHex 24 | kDi44SFXRLOpKvHVHYoSo+2igg3QFdasJYpblfUhaQKBgF0l9PpMbDLdPlI33IQz 25 | bEzw0ig8R8nHocJzOkK/BdLI8WiItYGgq4mcZGDWsztNg17QQGTEFrH7H9B8DKAJ 26 | p75jz5O83arjMECqAiXlSGOWtq6NhbgyJcQJxvvvN8wObVFoVkLoEbqE1TkDqZfI 27 | CqiusA5zgG89vzP9xFhW2ia2 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /public/ssl/localhost+1.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEXzCCAsegAwIBAgIRAOLUY4uS9d2yXx0vd6qql30wDQYJKoZIhvcNAQELBQAw 3 | gZExHjAcBgNVBAoTFW1rY2VydCBkZXZlbG9wbWVudCBDQTEzMDEGA1UECwwqQklM 4 | SUJJTElcZ2Fvc2h1YWl4aW5nQENOMjEwMTAyMjc0ICjljaHor7opMTowOAYDVQQD 5 | DDFta2NlcnQgQklMSUJJTElcZ2Fvc2h1YWl4aW5nQENOMjEwMTAyMjc0ICjljaHo 6 | r7opMB4XDTIyMDcyNzA4NDcyOFoXDTI0MTAyNzA4NDcyOFowXjEnMCUGA1UEChMe 7 | bWtjZXJ0IGRldmVsb3BtZW50IGNlcnRpZmljYXRlMTMwMQYDVQQLDCpCSUxJQklM 8 | SVxnYW9zaHVhaXhpbmdAQ04yMTAxMDIyNzQgKOWNoeivuikwggEiMA0GCSqGSIb3 9 | DQEBAQUAA4IBDwAwggEKAoIBAQDpWYqPkljVtDNpJVwlcOxNVihQPf4T3Q/tuIt5 10 | znV5ImBmlWg+OyuG47Y5e+qPMjFCSX4ebTAtBMbYm6AJihkKy0iKj1mVS9TPCzqc 11 | HFnUliCIqJMKFsJTWygNGgogjXhlxFaJgCZO6Gw6ocWp6nw1gMhrMIxqT2MQIQX1 12 | 6SD1IH/F4JMoaYuifnR+OOgbS3yUKHDTFApkZAWndS4GpbT39rW9cmbJrGHCvl8b 13 | sm8MXMdXars10A++wjjmHbtZu8beFv+zKbDw4iAuls8p/lAoIvQy8MkLLM5b392C 14 | LMaK0517+qM3VdEZ0ph+32m9IEoj5QV8xgaPdnyP8/8a2bU/AgMBAAGjZDBiMA4G 15 | A1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAfBgNVHSMEGDAWgBQn 16 | B1E5Js/cFhxBwpZL59aoK/skLjAaBgNVHREEEzARgglsb2NhbGhvc3SHBH8AAAEw 17 | DQYJKoZIhvcNAQELBQADggGBAIaUncQj2XN2rNn6sE0MuaWboFqwpkydyei6FvtN 18 | c/TY9RWW3QRYICcO721l/2jBiWplQt/ZYaJ+IWN+C+3JSAz9IYsM/nMgxHL2azLQ 19 | zHKnASEjxptW9+mlsgVk2LTrBfbc197ikLu80M/0jQYaIBeoEOaMlhBjno139nTO 20 | evNheyFKvAhggOseD00I9VBZkKDBxvqr6PHnGjyAU43C1/HkNjglIbQjAZdBmXlX 21 | HuelQ97glfhzyApvmczPrc8IAqPhtYn2nJ5P6Ea35LEc3D7uVExywcjDFcSwMJCb 22 | TXqouzM/U8pO+DGeuvgwkYrBGlA7iEE+ZQgxCBatOXwG95THtFlfW+H0ILHB2tcX 23 | P+Kztwd+4ipPciJz+1NK7z7erwfxHO5hmXJskH9YWi6YJsIw5g1iYs0pJJ/4p7Bd 24 | 8qSGEhri/+iijcC76q+1N0xhJxQrDDlC0pKp6oAYFDGKirzwmlAf/eJBy0ORWjCj 25 | yk+d9T622yzcXa5fw3HBZh1o6A== 26 | -----END CERTIFICATE----- 27 | --------------------------------------------------------------------------------