From 1d8e3c4dc94a86f3912d913942f2d9504732cac3 Mon Sep 17 00:00:00 2001 From: CM Date: Fri, 1 Nov 2024 16:25:21 +0000 Subject: [PATCH] New features Updated packages Added a model benchmarking page Added a model perplexity calculation page Added new language: Taiwanese --- app/background.js | 141 ++++++++++- app/background.js.map | 2 +- app/preload.js | 30 ++- app/preload.js.map | 2 +- package-lock.json | 247 +++++++----------- package.json | 8 +- src/background.js | 141 ++++++++++- src/components/BenchmarkUI.jsx | 289 +++++++++++++++++++++ src/components/Home.jsx | 137 +++++++--- src/components/PageHeader.jsx | 13 +- src/components/PerplexityUI.jsx | 380 ++++++++++++++++++++++++++++ src/data/locales/da/Benchmark.json | 18 ++ src/data/locales/da/Home.json | 42 +-- src/data/locales/da/PageHeader.json | 3 + src/data/locales/da/Perplexity.json | 24 ++ src/data/locales/de/Benchmark.json | 18 ++ src/data/locales/de/Home.json | 42 +-- src/data/locales/de/PageHeader.json | 3 + src/data/locales/de/Perplexity.json | 24 ++ src/data/locales/en/Benchmark.json | 18 ++ src/data/locales/en/Home.json | 12 +- src/data/locales/en/PageHeader.json | 3 + src/data/locales/en/Perplexity.json | 24 ++ src/data/locales/es/Benchmark.json | 18 ++ src/data/locales/es/Home.json | 42 +-- src/data/locales/es/PageHeader.json | 3 + src/data/locales/es/Perplexity.json | 24 ++ src/data/locales/fr/Benchmark.json | 18 ++ src/data/locales/fr/Home.json | 42 +-- src/data/locales/fr/PageHeader.json | 5 +- src/data/locales/fr/Perplexity.json | 24 ++ src/data/locales/it/Benchmark.json | 18 ++ src/data/locales/it/Home.json | 42 +-- src/data/locales/it/PageHeader.json | 3 + src/data/locales/it/Perplexity.json | 24 ++ src/data/locales/ja/Benchmark.json | 18 ++ src/data/locales/ja/Home.json | 42 +-- src/data/locales/ja/PageHeader.json | 3 + src/data/locales/ja/Perplexity.json | 24 ++ src/data/locales/ko/Benchmark.json | 18 ++ src/data/locales/ko/Home.json | 42 +-- src/data/locales/ko/PageHeader.json | 3 + src/data/locales/ko/Perplexity.json | 24 ++ src/data/locales/pt/Benchmark.json | 18 ++ src/data/locales/pt/Home.json | 42 +-- src/data/locales/pt/PageHeader.json | 3 + src/data/locales/pt/Perplexity.json | 24 ++ src/data/locales/th/Benchmark.json | 18 ++ src/data/locales/th/Home.json | 42 +-- src/data/locales/th/PageHeader.json | 3 + src/data/locales/th/Perplexity.json | 24 ++ src/data/locales/tw/Benchmark.json | 18 ++ src/data/locales/tw/Home.json | 22 ++ src/data/locales/tw/PageHeader.json | 21 ++ src/data/locales/tw/Perplexity.json | 24 ++ src/lib/i18n.js | 6 +- src/pages/benchmark.astro | 26 ++ src/pages/index.astro | 2 +- src/pages/perplexity.astro | 26 ++ src/preload.js | 30 ++- 60 files changed, 1987 insertions(+), 420 deletions(-) create mode 100644 src/components/BenchmarkUI.jsx create mode 100644 src/components/PerplexityUI.jsx create mode 100644 src/data/locales/da/Benchmark.json create mode 100644 src/data/locales/da/Perplexity.json create mode 100644 src/data/locales/de/Benchmark.json create mode 100644 src/data/locales/de/Perplexity.json create mode 100644 src/data/locales/en/Benchmark.json create mode 100644 src/data/locales/en/Perplexity.json create mode 100644 src/data/locales/es/Benchmark.json create mode 100644 src/data/locales/es/Perplexity.json create mode 100644 src/data/locales/fr/Benchmark.json create mode 100644 src/data/locales/fr/Perplexity.json create mode 100644 src/data/locales/it/Benchmark.json create mode 100644 src/data/locales/it/Perplexity.json create mode 100644 src/data/locales/ja/Benchmark.json create mode 100644 src/data/locales/ja/Perplexity.json create mode 100644 src/data/locales/ko/Benchmark.json create mode 100644 src/data/locales/ko/Perplexity.json create mode 100644 src/data/locales/pt/Benchmark.json create mode 100644 src/data/locales/pt/Perplexity.json create mode 100644 src/data/locales/th/Benchmark.json create mode 100644 src/data/locales/th/Perplexity.json create mode 100644 src/data/locales/tw/Benchmark.json create mode 100644 src/data/locales/tw/Home.json create mode 100644 src/data/locales/tw/PageHeader.json create mode 100644 src/data/locales/tw/Perplexity.json create mode 100644 src/pages/benchmark.astro create mode 100644 src/pages/perplexity.astro diff --git a/app/background.js b/app/background.js index 2cd6e7b..1d26ef7 100644 --- a/app/background.js +++ b/app/background.js @@ -229,12 +229,14 @@ __webpack_require__.r(__webpack_exports__); let mainWindow = null; let tray = null; let inferenceProcess = null; +let benchmarkProcess = null; +let perplexityProcess = null; function runInference(args) { let mainPath = path__WEBPACK_IMPORTED_MODULE_1___default().join(electron__WEBPACK_IMPORTED_MODULE_7__.app.getAppPath(), 'bin', 'Release', 'llama-cli.exe'); if (!fs__WEBPACK_IMPORTED_MODULE_4___default().existsSync(mainPath)) { - mainPath = path__WEBPACK_IMPORTED_MODULE_1___default().join(electron__WEBPACK_IMPORTED_MODULE_7__.app.getAppPath(), 'bin', 'llama-cli'); + return; } const command = [ @@ -262,7 +264,7 @@ function runInference(args) { }); } -function signalHandler() { +function terminateInference() { if (inferenceProcess) { console.log('Terminating inference process...'); try { @@ -282,6 +284,113 @@ function signalHandler() { inferenceProcess = null; } +function runBenchmark(args) { + let benchPath = path__WEBPACK_IMPORTED_MODULE_1___default().join(electron__WEBPACK_IMPORTED_MODULE_7__.app.getAppPath(), 'bin', 'Release', 'llama-bench.exe'); + + if (!fs__WEBPACK_IMPORTED_MODULE_4___default().existsSync(benchPath)) { + console.error('Benchmark binary not found, please build first.'); + return; + } + + const command = [ + `"${benchPath}"`, + '-m', args.model, + '-n', args.n_token, + '-ngl', '0', + '-b', '1', + '-t', args.threads, + '-p', args.n_prompt, + '-r', '5' + ]; + + benchmarkProcess = (0,child_process__WEBPACK_IMPORTED_MODULE_0__.spawn)(command[0], command.slice(1), { shell: true }); + + benchmarkProcess.stdout.on('data', (data) => { + const log = data.toString(); + mainWindow.webContents.send('benchmarkLog', log); + }); + + benchmarkProcess.on('close', (code) => { + mainWindow.webContents.send('benchmarkComplete'); + benchmarkProcess = null; + }); +} + +function terminateBenchmark() { + if (benchmarkProcess) { + console.log('Terminating benchmark process...'); + try { + benchmarkProcess.kill('SIGKILL'); // Use SIGKILL to forcefully terminate the process + benchmarkProcess.stdout.removeAllListeners('data'); + benchmarkProcess.stderr.removeAllListeners('data'); + benchmarkProcess = null; + console.log('Benchmark process terminated.'); + } catch (error) { + console.error('Failed to terminate benchmark process:', error); + } + } else { + console.log('No benchmark process to terminate.'); + } + + mainWindow.webContents.send('benchmarkComplete'); + benchmarkProcess = null; +} + +function runPerplexity(args) { + let perplexityPath = path__WEBPACK_IMPORTED_MODULE_1___default().join(electron__WEBPACK_IMPORTED_MODULE_7__.app.getAppPath(), 'bin', 'Release', 'llama-perplexity.exe'); + + if (!fs__WEBPACK_IMPORTED_MODULE_4___default().existsSync(perplexityPath)) { + console.error('Perplexity binary not found, please build first.'); + return; + } + + const command = [ + `"${perplexityPath}"`, + '--model', `"${args.model}"`, + '--prompt', `"${args.prompt}"`, + '--threads', args.threads, + '--ctx-size', args.ctx_size, + '--perplexity', + '--ppl-stride', args.ppl_stride + ]; + + perplexityProcess = (0,child_process__WEBPACK_IMPORTED_MODULE_0__.spawn)(command[0], command.slice(1), { shell: true }); + + perplexityProcess.stderr.on('data', (data) => { + const log = data.toString(); + mainWindow.webContents.send('perplexityLog', log); + }); + + perplexityProcess.on('error', (error) => { + console.error('Perplexity process error:', error); + }); + + perplexityProcess.on('close', (code) => { + mainWindow.webContents.send('perplexityComplete'); + perplexityProcess = null; + }); +} + +function terminatePerplexity() { + if (perplexityProcess) { + console.log('Terminating perplexity process...'); + try { + perplexityProcess.kill('SIGKILL'); // Use SIGKILL to forcefully terminate the process + perplexityProcess.stdout.removeAllListeners('data'); + perplexityProcess.stderr.removeAllListeners('data'); + perplexityProcess = null; + console.log('Perplexity process terminated.'); + } catch (error) { + console.error('Failed to terminate perplexity process:', error); + } + } else { + console.log('No perplexity process to terminate.'); + } + + mainWindow.webContents.send('perplexityComplete'); + perplexityProcess = null; +} + const createWindow = async () => { mainWindow = new electron__WEBPACK_IMPORTED_MODULE_7__.BrowserWindow({ minWidth: 480, @@ -373,7 +482,7 @@ const createWindow = async () => { }); electron__WEBPACK_IMPORTED_MODULE_7__.ipcMain.on("stopInference", (event) => { - signalHandler(); + terminateInference(); }); electron__WEBPACK_IMPORTED_MODULE_7__.ipcMain.handle('openFileDialog', async () => { @@ -384,6 +493,26 @@ const createWindow = async () => { return result.filePaths; }); + electron__WEBPACK_IMPORTED_MODULE_7__.ipcMain.handle('getMaxThreads', async () => { + return os__WEBPACK_IMPORTED_MODULE_2___default().cpus().length; + }); + + electron__WEBPACK_IMPORTED_MODULE_7__.ipcMain.on("runBenchmark", (event, arg) => { + runBenchmark(arg); + }); + + electron__WEBPACK_IMPORTED_MODULE_7__.ipcMain.on("stopBenchmark", (event) => { + terminateBenchmark(); + }); + + electron__WEBPACK_IMPORTED_MODULE_7__.ipcMain.on("runPerplexity", (event, arg) => { + runPerplexity(arg); + }); + + electron__WEBPACK_IMPORTED_MODULE_7__.ipcMain.on("stopPerplexity", (event) => { + terminatePerplexity(); + }); + tray.on("click", () => { mainWindow?.setAlwaysOnTop(true); mainWindow?.show(); @@ -417,11 +546,13 @@ if (currentOS === "win32" || currentOS === "linux") { }); electron__WEBPACK_IMPORTED_MODULE_7__.app.on('before-quit', (event) => { - signalHandler(); + terminateInference(); + terminateBenchmark(); }); electron__WEBPACK_IMPORTED_MODULE_7__.app.on('will-quit', (event) => { - signalHandler(); + terminateInference(); + terminateBenchmark(); }); electron__WEBPACK_IMPORTED_MODULE_7__.app.on("window-all-closed", () => { diff --git a/app/background.js.map b/app/background.js.map index 40f3419..6d63bfb 100644 --- a/app/background.js.map +++ b/app/background.js.map @@ -1 +1 @@ -{"version":3,"file":"background.js","mappings":";;;;;;;;;;;;;;;;AAAmC;AACnC;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW;AACX,YAAY,iCAAiC;AAC7C,YAAY;AACZ;AACA;AACA;AACA,iBAAiB,0CAAI;AACrB,IAAI,0CAAI;AACR;;;;;;;;;;;ACvBA;;;;;;;;;;ACAA;;;;;;;;;;ACAA;;;;;;;;;;ACAA;;;;;;;;;;ACAA;;;;;;;;;;ACAA;;;;;;;;;;ACAA;;;;;;;;;;ACAA;;;;;;UCAA;UACA;;UAEA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;;UAEA;UACA;;UAEA;UACA;UACA;;;;;WCtBA;WACA;WACA;WACA;WACA;WACA,iCAAiC,WAAW;WAC5C;WACA;;;;;WCPA;WACA;WACA;WACA;WACA,yCAAyC,wCAAwC;WACjF;WACA;WACA;;;;;WCPA;;;;;WCAA;WACA;WACA;WACA,uDAAuD,iBAAiB;WACxE;WACA,gDAAgD,aAAa;WAC7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACNsC;AACd;AACJ;AACU;AACV;AACE;AACQ;AAC9B;AACkF;AAClF;AAC+D;AAC/D;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,gDAAS,CAAC,yCAAG;AAC9B;AACA,OAAO,oDAAa;AACpB,eAAe,gDAAS,CAAC,yCAAG;AAC5B;AACA;AACA;AACA,QAAQ,SAAS;AACjB;AACA;AACA;AACA,cAAc,YAAY;AAC1B;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB,oDAAK,iCAAiC,aAAa;AACxE;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA,wCAAwC;AACxC;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA,IAAI;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,mBAAmB,mDAAa;AAChC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,eAAe,gDAAS;AACxB,KAAK;AACL;AACA,GAAG;AACH;AACA,qBAAqB,8CAAO;AAC5B;AACA;AACA,MAAM,IAAsC;AAC5C;AACA,IAAI,KAAK,EAEN;AACH;AACA,iBAAiB,wDAAc;AAC/B;AACA;AACA,GAAG;AACH;AACA,EAAE,4EAAmB;AACrB;AACA;AACA;AACA;AACA,aAAa;AACb,GAAG;AACH;AACA,aAAa,0CAAI,CAAC,gDAAS;AAC3B,sBAAsB,0CAAI;AAC1B;AACA;AACA;AACA;AACA,OAAO;AACP,KAAK;AACL;AACA;AACA;AACA;AACA,QAAQ,yCAAG;AACX,OAAO;AACP,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAE,6CAAO;AACT;AACA,4BAA4B,gDAAO;AACnC;AACA;AACA;AACA,uCAAuC,gDAAO;AAC9C;AACA,OAAO;AACP;AACA;AACA,QAAQ,2CAAK;AACb,QAAQ;AACR,kEAAkE,OAAO;AACzE;AACA,MAAM;AACN,2CAA2C,YAAY;AACvD;AACA,GAAG;AACH;AACA,EAAE,6CAAO;AACT;AACA,GAAG;AACH;AACA,EAAE,6CAAO;AACT;AACA,GAAG;AACH;AACA,EAAE,6CAAO;AACT,yBAAyB,4CAAM;AAC/B;AACA,oBAAoB,0CAA0C;AAC9D,KAAK;AACL;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA,kBAAkB,kDAAW;AAC7B;AACA;AACA,qBAAqB,yCAAG;AACxB;AACA;AACA,IAAI,yCAAG;AACP;AACA;AACA,EAAE,yCAAG;AACL;AACA,GAAG;AACH,EAAE;AACF,EAAE,yCAAG;AACL;AACA,GAAG;AACH;AACA,EAAE,yCAAG;AACL;AACA,GAAG;AACH;AACA,EAAE,yCAAG;AACL;AACA,GAAG;AACH;AACA,EAAE,yCAAG;AACL,QAAQ,yDAAgB;AACxB,MAAM,yCAAG;AACT;AACA,GAAG;AACH;AACA,EAAE,yCAAG;AACL;AACA;AACA;AACA,GAAG;AACH,C","sources":["webpack://electron-bitnet/./src/lib/applicationMenu.js","webpack://electron-bitnet/external commonjs \"electron\"","webpack://electron-bitnet/external commonjs \"express\"","webpack://electron-bitnet/external node-commonjs \"child_process\"","webpack://electron-bitnet/external node-commonjs \"fs\"","webpack://electron-bitnet/external node-commonjs \"os\"","webpack://electron-bitnet/external node-commonjs \"path\"","webpack://electron-bitnet/external node-commonjs \"process\"","webpack://electron-bitnet/external node-commonjs \"url\"","webpack://electron-bitnet/webpack/bootstrap","webpack://electron-bitnet/webpack/runtime/compat get default export","webpack://electron-bitnet/webpack/runtime/define property getters","webpack://electron-bitnet/webpack/runtime/hasOwnProperty shorthand","webpack://electron-bitnet/webpack/runtime/make namespace object","webpack://electron-bitnet/./src/background.js"],"sourcesContent":["import {app, Menu} from 'electron';\r\n\r\n/**\r\n * For configuring the electron window menu\r\n */\r\nexport function initApplicationMenu(mainWindow) {\r\n const template = [\r\n {\r\n label: 'View',\r\n submenu: [\r\n {\r\n label: 'Send to tray',\r\n click() {\r\n mainWindow.minimize();\r\n }\r\n },\r\n { label: 'Reload', role: 'reload' },\r\n { label: 'Dev tools', role: 'toggleDevTools' }\r\n ]\r\n }\r\n ];\r\n const menu = Menu.buildFromTemplate(template);\r\n Menu.setApplicationMenu(menu);\r\n}\r\n","module.exports = require(\"electron\");","module.exports = require(\"express\");","module.exports = require(\"child_process\");","module.exports = require(\"fs\");","module.exports = require(\"os\");","module.exports = require(\"path\");","module.exports = require(\"process\");","module.exports = require(\"url\");","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import { spawn } from 'child_process';\r\nimport path from 'path';\r\nimport os from 'os';\r\nimport process from 'process';\r\nimport fs from 'fs';\r\nimport url from \"url\";\r\nimport express from \"express\";\r\n\r\nimport { app, BrowserWindow, Menu, Tray, ipcMain, shell, dialog } from \"electron\";\r\n\r\nimport { initApplicationMenu } from \"./lib/applicationMenu.js\";\r\n\r\nlet mainWindow = null;\r\nlet tray = null;\r\nlet inferenceProcess = null;\r\n\r\nfunction runInference(args) {\r\n let mainPath = path.join(app.getAppPath(), 'bin', 'Release', 'llama-cli.exe');\r\n\r\n if (!fs.existsSync(mainPath)) {\r\n mainPath = path.join(app.getAppPath(), 'bin', 'llama-cli');\r\n }\r\n\r\n const command = [\r\n `\"${mainPath}\"`,\r\n '-m', args.model,\r\n '-n', args.n_predict,\r\n '-t', args.threads,\r\n '-p', `\"${args.prompt}\"`,\r\n '-ngl', '0',\r\n '-c', args.ctx_size,\r\n '--temp', args.temperature,\r\n '-b', '1'\r\n ];\r\n\r\n inferenceProcess = spawn(command[0], command.slice(1), { shell: true });\r\n\r\n inferenceProcess.stdout.on('data', (data) => {\r\n const chars = data.toString();\r\n mainWindow.webContents.send('aiResponse', chars);\r\n });\r\n\r\n inferenceProcess.on('close', (code) => {\r\n mainWindow.webContents.send('aiComplete');\r\n inferenceProcess = null;\r\n });\r\n}\r\n\r\nfunction signalHandler() {\r\n if (inferenceProcess) {\r\n console.log('Terminating inference process...');\r\n try {\r\n inferenceProcess.kill('SIGKILL'); // Use SIGKILL to forcefully terminate the process\r\n inferenceProcess.stdout.removeAllListeners('data');\r\n inferenceProcess.stderr.removeAllListeners('data');\r\n inferenceProcess = null;\r\n console.log('Inference process terminated.');\r\n } catch (error) {\r\n console.error('Failed to terminate inference process:', error);\r\n }\r\n } else {\r\n console.log('No inference process to terminate.');\r\n }\r\n\r\n mainWindow.webContents.send('aiComplete');\r\n inferenceProcess = null;\r\n}\r\n\r\nconst createWindow = async () => {\r\n mainWindow = new BrowserWindow({\r\n minWidth: 480,\r\n minHeight: 695,\r\n maximizable: true,\r\n useContentSize: true,\r\n autoHideMenuBar: true,\r\n webPreferences: {\r\n nodeIntegration: false,\r\n contextIsolation: true,\r\n sandbox: true,\r\n preload: path.join(__dirname, \"preload.js\"),\r\n },\r\n icon: __dirname + \"/img/taskbar.png\",\r\n });\r\n\r\n const expressApp = express();\r\n\r\n let astroDistPath;\r\n if (process.env.NODE_ENV === \"development\") {\r\n astroDistPath = \"astroDist\";\r\n } else {\r\n astroDistPath = path.join(process.resourcesPath, \"astroDist\");\r\n }\r\n\r\n expressApp.use(express.static(astroDistPath));\r\n expressApp.listen(8080, () => {\r\n console.log(\"Express server listening on port 8080\");\r\n });\r\n\r\n initApplicationMenu(mainWindow);\r\n\r\n mainWindow.loadURL(\"http://localhost:8080/index.html\");\r\n\r\n mainWindow.webContents.setWindowOpenHandler(() => {\r\n return { action: \"deny\" };\r\n });\r\n\r\n tray = new Tray(path.join(__dirname, \"img\", \"tray.png\"));\r\n const contextMenu = Menu.buildFromTemplate([\r\n {\r\n label: \"Show App\",\r\n click: function () {\r\n mainWindow?.show();\r\n },\r\n },\r\n {\r\n label: \"Quit\",\r\n click: function () {\r\n tray = null;\r\n app.quit();\r\n },\r\n },\r\n ]);\r\n\r\n tray.setToolTip(\"Electron BitNet\");\r\n\r\n tray.on(\"right-click\", (event, bounds) => {\r\n tray?.popUpContextMenu(contextMenu);\r\n });\r\n\r\n const safeDomains = [\r\n \"https://github.com\",\r\n \"https://react.dev/\",\r\n \"https://astro.build/\",\r\n \"https://www.electronjs.org/\"\r\n ];\r\n\r\n ipcMain.on(\"openURL\", (event, arg) => {\r\n try {\r\n const parsedUrl = new url.URL(arg);\r\n const domain = parsedUrl.hostname;\r\n\r\n const isSafeDomain = safeDomains.some((safeDomain) => {\r\n const safeDomainHostname = new url.URL(safeDomain).hostname;\r\n return safeDomainHostname === domain;\r\n });\r\n\r\n if (isSafeDomain) {\r\n shell.openExternal(arg);\r\n } else {\r\n console.error(`Rejected opening URL with unsafe domain: ${domain}`);\r\n }\r\n } catch (err) {\r\n console.error(`Failed to open URL: ${err.message}`);\r\n }\r\n });\r\n\r\n ipcMain.on(\"runInference\", (event, arg) => {\r\n runInference(arg);\r\n });\r\n\r\n ipcMain.on(\"stopInference\", (event) => {\r\n signalHandler();\r\n });\r\n\r\n ipcMain.handle('openFileDialog', async () => {\r\n const result = await dialog.showOpenDialog({\r\n properties: ['openFile'],\r\n filters: [{ name: 'GGUF Files', extensions: ['gguf'] }]\r\n });\r\n return result.filePaths;\r\n });\r\n\r\n tray.on(\"click\", () => {\r\n mainWindow?.setAlwaysOnTop(true);\r\n mainWindow?.show();\r\n mainWindow?.focus();\r\n mainWindow?.setAlwaysOnTop(false);\r\n });\r\n\r\n tray.on(\"balloon-click\", () => {\r\n mainWindow?.setAlwaysOnTop(true);\r\n mainWindow?.show();\r\n mainWindow?.focus();\r\n mainWindow?.setAlwaysOnTop(false);\r\n });\r\n};\r\n\r\nconst currentOS = os.platform();\r\nif (currentOS === \"win32\" || currentOS === \"linux\") {\r\n // windows + linux setup phase\r\n const gotTheLock = app.requestSingleInstanceLock();\r\n\r\n if (!gotTheLock) {\r\n app.quit();\r\n }\r\n\r\n app.whenReady().then(() => {\r\n createWindow();\r\n });\r\n} else {\r\n app.whenReady().then(() => {\r\n createWindow();\r\n });\r\n\r\n app.on('before-quit', (event) => {\r\n signalHandler();\r\n });\r\n \r\n app.on('will-quit', (event) => {\r\n signalHandler();\r\n });\r\n\r\n app.on(\"window-all-closed\", () => {\r\n if (process.platform !== \"darwin\") {\r\n app.quit();\r\n }\r\n });\r\n\r\n app.on(\"activate\", () => {\r\n if (mainWindow === null) {\r\n createWindow();\r\n }\r\n });\r\n}"],"names":[],"sourceRoot":""} \ No newline at end of file +{"version":3,"file":"background.js","mappings":";;;;;;;;;;;;;;;;AAAmC;AACnC;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW;AACX,YAAY,iCAAiC;AAC7C,YAAY;AACZ;AACA;AACA;AACA,iBAAiB,0CAAI;AACrB,IAAI,0CAAI;AACR;;;;;;;;;;;ACvBA;;;;;;;;;;ACAA;;;;;;;;;;ACAA;;;;;;;;;;ACAA;;;;;;;;;;ACAA;;;;;;;;;;ACAA;;;;;;;;;;ACAA;;;;;;;;;;ACAA;;;;;;UCAA;UACA;;UAEA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;;UAEA;UACA;;UAEA;UACA;UACA;;;;;WCtBA;WACA;WACA;WACA;WACA;WACA,iCAAiC,WAAW;WAC5C;WACA;;;;;WCPA;WACA;WACA;WACA;WACA,yCAAyC,wCAAwC;WACjF;WACA;WACA;;;;;WCPA;;;;;WCAA;WACA;WACA;WACA,uDAAuD,iBAAiB;WACxE;WACA,gDAAgD,aAAa;WAC7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACNsC;AACd;AACJ;AACU;AACV;AACE;AACQ;AAC9B;AACkF;AAClF;AAC+D;AAC/D;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,gDAAS,CAAC,yCAAG;AAC9B;AACA,OAAO,oDAAa;AACpB;AACA;AACA;AACA;AACA,QAAQ,SAAS;AACjB;AACA;AACA;AACA,cAAc,YAAY;AAC1B;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB,oDAAK,iCAAiC,aAAa;AACxE;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA,wCAAwC;AACxC;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA,IAAI;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,kBAAkB,gDAAS,CAAC,yCAAG;AAC/B;AACA,OAAO,oDAAa;AACpB;AACA;AACA;AACA;AACA;AACA,QAAQ,UAAU;AAClB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB,oDAAK,iCAAiC,aAAa;AACxE;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA,wCAAwC;AACxC;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA,IAAI;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,uBAAuB,gDAAS,CAAC,yCAAG;AACpC;AACA,OAAO,oDAAa;AACpB;AACA;AACA;AACA;AACA;AACA,QAAQ,eAAe;AACvB,mBAAmB,WAAW;AAC9B,oBAAoB,YAAY;AAChC;AACA;AACA;AACA;AACA;AACA;AACA,sBAAsB,oDAAK,iCAAiC,aAAa;AACzE;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA,yCAAyC;AACzC;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA,IAAI;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,mBAAmB,mDAAa;AAChC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,eAAe,gDAAS;AACxB,KAAK;AACL;AACA,GAAG;AACH;AACA,qBAAqB,8CAAO;AAC5B;AACA;AACA,MAAM,IAAsC;AAC5C;AACA,IAAI,KAAK,EAEN;AACH;AACA,iBAAiB,wDAAc;AAC/B;AACA;AACA,GAAG;AACH;AACA,EAAE,4EAAmB;AACrB;AACA;AACA;AACA;AACA,aAAa;AACb,GAAG;AACH;AACA,aAAa,0CAAI,CAAC,gDAAS;AAC3B,sBAAsB,0CAAI;AAC1B;AACA;AACA;AACA;AACA,OAAO;AACP,KAAK;AACL;AACA;AACA;AACA;AACA,QAAQ,yCAAG;AACX,OAAO;AACP,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAE,6CAAO;AACT;AACA,4BAA4B,gDAAO;AACnC;AACA;AACA;AACA,uCAAuC,gDAAO;AAC9C;AACA,OAAO;AACP;AACA;AACA,QAAQ,2CAAK;AACb,QAAQ;AACR,kEAAkE,OAAO;AACzE;AACA,MAAM;AACN,2CAA2C,YAAY;AACvD;AACA,GAAG;AACH;AACA,EAAE,6CAAO;AACT;AACA,GAAG;AACH;AACA,EAAE,6CAAO;AACT;AACA,GAAG;AACH;AACA,EAAE,6CAAO;AACT,yBAAyB,4CAAM;AAC/B;AACA,oBAAoB,0CAA0C;AAC9D,KAAK;AACL;AACA,GAAG;AACH;AACA,EAAE,6CAAO;AACT,WAAW,8CAAO;AAClB,GAAG;AACH;AACA,EAAE,6CAAO;AACT;AACA,GAAG;AACH;AACA,EAAE,6CAAO;AACT;AACA,GAAG;AACH;AACA,EAAE,6CAAO;AACT;AACA,GAAG;AACH;AACA,EAAE,6CAAO;AACT;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA,kBAAkB,kDAAW;AAC7B;AACA;AACA,qBAAqB,yCAAG;AACxB;AACA;AACA,IAAI,yCAAG;AACP;AACA;AACA,EAAE,yCAAG;AACL;AACA,GAAG;AACH,EAAE;AACF,EAAE,yCAAG;AACL;AACA,GAAG;AACH;AACA,EAAE,yCAAG;AACL;AACA;AACA,GAAG;AACH;AACA,EAAE,yCAAG;AACL;AACA;AACA,GAAG;AACH;AACA,EAAE,yCAAG;AACL,QAAQ,yDAAgB;AACxB,MAAM,yCAAG;AACT;AACA,GAAG;AACH;AACA,EAAE,yCAAG;AACL;AACA;AACA;AACA,GAAG;AACH,C","sources":["webpack://electron-bitnet/./src/lib/applicationMenu.js","webpack://electron-bitnet/external commonjs \"electron\"","webpack://electron-bitnet/external commonjs \"express\"","webpack://electron-bitnet/external node-commonjs \"child_process\"","webpack://electron-bitnet/external node-commonjs \"fs\"","webpack://electron-bitnet/external node-commonjs \"os\"","webpack://electron-bitnet/external node-commonjs \"path\"","webpack://electron-bitnet/external node-commonjs \"process\"","webpack://electron-bitnet/external node-commonjs \"url\"","webpack://electron-bitnet/webpack/bootstrap","webpack://electron-bitnet/webpack/runtime/compat get default export","webpack://electron-bitnet/webpack/runtime/define property getters","webpack://electron-bitnet/webpack/runtime/hasOwnProperty shorthand","webpack://electron-bitnet/webpack/runtime/make namespace object","webpack://electron-bitnet/./src/background.js"],"sourcesContent":["import {app, Menu} from 'electron';\r\n\r\n/**\r\n * For configuring the electron window menu\r\n */\r\nexport function initApplicationMenu(mainWindow) {\r\n const template = [\r\n {\r\n label: 'View',\r\n submenu: [\r\n {\r\n label: 'Send to tray',\r\n click() {\r\n mainWindow.minimize();\r\n }\r\n },\r\n { label: 'Reload', role: 'reload' },\r\n { label: 'Dev tools', role: 'toggleDevTools' }\r\n ]\r\n }\r\n ];\r\n const menu = Menu.buildFromTemplate(template);\r\n Menu.setApplicationMenu(menu);\r\n}\r\n","module.exports = require(\"electron\");","module.exports = require(\"express\");","module.exports = require(\"child_process\");","module.exports = require(\"fs\");","module.exports = require(\"os\");","module.exports = require(\"path\");","module.exports = require(\"process\");","module.exports = require(\"url\");","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import { spawn } from 'child_process';\r\nimport path from 'path';\r\nimport os from 'os';\r\nimport process from 'process';\r\nimport fs from 'fs';\r\nimport url from \"url\";\r\nimport express from \"express\";\r\n\r\nimport { app, BrowserWindow, Menu, Tray, ipcMain, shell, dialog } from \"electron\";\r\n\r\nimport { initApplicationMenu } from \"./lib/applicationMenu.js\";\r\n\r\nlet mainWindow = null;\r\nlet tray = null;\r\nlet inferenceProcess = null;\r\nlet benchmarkProcess = null;\r\nlet perplexityProcess = null;\r\n\r\nfunction runInference(args) {\r\n let mainPath = path.join(app.getAppPath(), 'bin', 'Release', 'llama-cli.exe');\r\n\r\n if (!fs.existsSync(mainPath)) {\r\n return;\r\n }\r\n\r\n const command = [\r\n `\"${mainPath}\"`,\r\n '-m', args.model,\r\n '-n', args.n_predict,\r\n '-t', args.threads,\r\n '-p', `\"${args.prompt}\"`,\r\n '-ngl', '0',\r\n '-c', args.ctx_size,\r\n '--temp', args.temperature,\r\n '-b', '1'\r\n ];\r\n\r\n inferenceProcess = spawn(command[0], command.slice(1), { shell: true });\r\n\r\n inferenceProcess.stdout.on('data', (data) => {\r\n const chars = data.toString();\r\n mainWindow.webContents.send('aiResponse', chars);\r\n });\r\n\r\n inferenceProcess.on('close', (code) => {\r\n mainWindow.webContents.send('aiComplete');\r\n inferenceProcess = null;\r\n });\r\n}\r\n\r\nfunction terminateInference() {\r\n if (inferenceProcess) {\r\n console.log('Terminating inference process...');\r\n try {\r\n inferenceProcess.kill('SIGKILL'); // Use SIGKILL to forcefully terminate the process\r\n inferenceProcess.stdout.removeAllListeners('data');\r\n inferenceProcess.stderr.removeAllListeners('data');\r\n inferenceProcess = null;\r\n console.log('Inference process terminated.');\r\n } catch (error) {\r\n console.error('Failed to terminate inference process:', error);\r\n }\r\n } else {\r\n console.log('No inference process to terminate.');\r\n }\r\n\r\n mainWindow.webContents.send('aiComplete');\r\n inferenceProcess = null;\r\n}\r\n\r\nfunction runBenchmark(args) {\r\n let benchPath = path.join(app.getAppPath(), 'bin', 'Release', 'llama-bench.exe');\r\n\r\n if (!fs.existsSync(benchPath)) {\r\n console.error('Benchmark binary not found, please build first.');\r\n return;\r\n }\r\n\r\n const command = [\r\n `\"${benchPath}\"`,\r\n '-m', args.model,\r\n '-n', args.n_token,\r\n '-ngl', '0',\r\n '-b', '1',\r\n '-t', args.threads,\r\n '-p', args.n_prompt,\r\n '-r', '5'\r\n ];\r\n\r\n benchmarkProcess = spawn(command[0], command.slice(1), { shell: true });\r\n\r\n benchmarkProcess.stdout.on('data', (data) => {\r\n const log = data.toString();\r\n mainWindow.webContents.send('benchmarkLog', log);\r\n });\r\n\r\n benchmarkProcess.on('close', (code) => {\r\n mainWindow.webContents.send('benchmarkComplete');\r\n benchmarkProcess = null;\r\n });\r\n}\r\n\r\nfunction terminateBenchmark() {\r\n if (benchmarkProcess) {\r\n console.log('Terminating benchmark process...');\r\n try {\r\n benchmarkProcess.kill('SIGKILL'); // Use SIGKILL to forcefully terminate the process\r\n benchmarkProcess.stdout.removeAllListeners('data');\r\n benchmarkProcess.stderr.removeAllListeners('data');\r\n benchmarkProcess = null;\r\n console.log('Benchmark process terminated.');\r\n } catch (error) {\r\n console.error('Failed to terminate benchmark process:', error);\r\n }\r\n } else {\r\n console.log('No benchmark process to terminate.');\r\n }\r\n\r\n mainWindow.webContents.send('benchmarkComplete');\r\n benchmarkProcess = null;\r\n}\r\n\r\nfunction runPerplexity(args) {\r\n let perplexityPath = path.join(app.getAppPath(), 'bin', 'Release', 'llama-perplexity.exe');\r\n\r\n if (!fs.existsSync(perplexityPath)) {\r\n console.error('Perplexity binary not found, please build first.');\r\n return;\r\n }\r\n\r\n const command = [\r\n `\"${perplexityPath}\"`,\r\n '--model', `\"${args.model}\"`,\r\n '--prompt', `\"${args.prompt}\"`,\r\n '--threads', args.threads,\r\n '--ctx-size', args.ctx_size,\r\n '--perplexity',\r\n '--ppl-stride', args.ppl_stride\r\n ];\r\n\r\n perplexityProcess = spawn(command[0], command.slice(1), { shell: true });\r\n\r\n perplexityProcess.stderr.on('data', (data) => {\r\n const log = data.toString();\r\n mainWindow.webContents.send('perplexityLog', log);\r\n });\r\n\r\n perplexityProcess.on('error', (error) => {\r\n console.error('Perplexity process error:', error);\r\n });\r\n\r\n perplexityProcess.on('close', (code) => {\r\n mainWindow.webContents.send('perplexityComplete');\r\n perplexityProcess = null;\r\n });\r\n}\r\n\r\nfunction terminatePerplexity() {\r\n if (perplexityProcess) {\r\n console.log('Terminating perplexity process...');\r\n try {\r\n perplexityProcess.kill('SIGKILL'); // Use SIGKILL to forcefully terminate the process\r\n perplexityProcess.stdout.removeAllListeners('data');\r\n perplexityProcess.stderr.removeAllListeners('data');\r\n perplexityProcess = null;\r\n console.log('Perplexity process terminated.');\r\n } catch (error) {\r\n console.error('Failed to terminate perplexity process:', error);\r\n }\r\n } else {\r\n console.log('No perplexity process to terminate.');\r\n }\r\n\r\n mainWindow.webContents.send('perplexityComplete');\r\n perplexityProcess = null;\r\n}\r\n\r\nconst createWindow = async () => {\r\n mainWindow = new BrowserWindow({\r\n minWidth: 480,\r\n minHeight: 695,\r\n maximizable: true,\r\n useContentSize: true,\r\n autoHideMenuBar: true,\r\n webPreferences: {\r\n nodeIntegration: false,\r\n contextIsolation: true,\r\n sandbox: true,\r\n preload: path.join(__dirname, \"preload.js\"),\r\n },\r\n icon: __dirname + \"/img/taskbar.png\",\r\n });\r\n\r\n const expressApp = express();\r\n\r\n let astroDistPath;\r\n if (process.env.NODE_ENV === \"development\") {\r\n astroDistPath = \"astroDist\";\r\n } else {\r\n astroDistPath = path.join(process.resourcesPath, \"astroDist\");\r\n }\r\n\r\n expressApp.use(express.static(astroDistPath));\r\n expressApp.listen(8080, () => {\r\n console.log(\"Express server listening on port 8080\");\r\n });\r\n\r\n initApplicationMenu(mainWindow);\r\n\r\n mainWindow.loadURL(\"http://localhost:8080/index.html\");\r\n\r\n mainWindow.webContents.setWindowOpenHandler(() => {\r\n return { action: \"deny\" };\r\n });\r\n\r\n tray = new Tray(path.join(__dirname, \"img\", \"tray.png\"));\r\n const contextMenu = Menu.buildFromTemplate([\r\n {\r\n label: \"Show App\",\r\n click: function () {\r\n mainWindow?.show();\r\n },\r\n },\r\n {\r\n label: \"Quit\",\r\n click: function () {\r\n tray = null;\r\n app.quit();\r\n },\r\n },\r\n ]);\r\n\r\n tray.setToolTip(\"Electron BitNet\");\r\n\r\n tray.on(\"right-click\", (event, bounds) => {\r\n tray?.popUpContextMenu(contextMenu);\r\n });\r\n\r\n const safeDomains = [\r\n \"https://github.com\",\r\n \"https://react.dev/\",\r\n \"https://astro.build/\",\r\n \"https://www.electronjs.org/\"\r\n ];\r\n\r\n ipcMain.on(\"openURL\", (event, arg) => {\r\n try {\r\n const parsedUrl = new url.URL(arg);\r\n const domain = parsedUrl.hostname;\r\n\r\n const isSafeDomain = safeDomains.some((safeDomain) => {\r\n const safeDomainHostname = new url.URL(safeDomain).hostname;\r\n return safeDomainHostname === domain;\r\n });\r\n\r\n if (isSafeDomain) {\r\n shell.openExternal(arg);\r\n } else {\r\n console.error(`Rejected opening URL with unsafe domain: ${domain}`);\r\n }\r\n } catch (err) {\r\n console.error(`Failed to open URL: ${err.message}`);\r\n }\r\n });\r\n\r\n ipcMain.on(\"runInference\", (event, arg) => {\r\n runInference(arg);\r\n });\r\n\r\n ipcMain.on(\"stopInference\", (event) => {\r\n terminateInference();\r\n });\r\n\r\n ipcMain.handle('openFileDialog', async () => {\r\n const result = await dialog.showOpenDialog({\r\n properties: ['openFile'],\r\n filters: [{ name: 'GGUF Files', extensions: ['gguf'] }]\r\n });\r\n return result.filePaths;\r\n });\r\n\r\n ipcMain.handle('getMaxThreads', async () => {\r\n return os.cpus().length;\r\n });\r\n\r\n ipcMain.on(\"runBenchmark\", (event, arg) => {\r\n runBenchmark(arg);\r\n });\r\n\r\n ipcMain.on(\"stopBenchmark\", (event) => {\r\n terminateBenchmark();\r\n });\r\n\r\n ipcMain.on(\"runPerplexity\", (event, arg) => {\r\n runPerplexity(arg);\r\n });\r\n \r\n ipcMain.on(\"stopPerplexity\", (event) => {\r\n terminatePerplexity();\r\n });\r\n\r\n tray.on(\"click\", () => {\r\n mainWindow?.setAlwaysOnTop(true);\r\n mainWindow?.show();\r\n mainWindow?.focus();\r\n mainWindow?.setAlwaysOnTop(false);\r\n });\r\n\r\n tray.on(\"balloon-click\", () => {\r\n mainWindow?.setAlwaysOnTop(true);\r\n mainWindow?.show();\r\n mainWindow?.focus();\r\n mainWindow?.setAlwaysOnTop(false);\r\n });\r\n};\r\n\r\nconst currentOS = os.platform();\r\nif (currentOS === \"win32\" || currentOS === \"linux\") {\r\n // windows + linux setup phase\r\n const gotTheLock = app.requestSingleInstanceLock();\r\n\r\n if (!gotTheLock) {\r\n app.quit();\r\n }\r\n\r\n app.whenReady().then(() => {\r\n createWindow();\r\n });\r\n} else {\r\n app.whenReady().then(() => {\r\n createWindow();\r\n });\r\n\r\n app.on('before-quit', (event) => {\r\n terminateInference();\r\n terminateBenchmark();\r\n });\r\n \r\n app.on('will-quit', (event) => {\r\n terminateInference();\r\n terminateBenchmark();\r\n });\r\n\r\n app.on(\"window-all-closed\", () => {\r\n if (process.platform !== \"darwin\") {\r\n app.quit();\r\n }\r\n });\r\n\r\n app.on(\"activate\", () => {\r\n if (mainWindow === null) {\r\n createWindow();\r\n }\r\n });\r\n}"],"names":[],"sourceRoot":""} \ No newline at end of file diff --git a/app/preload.js b/app/preload.js index 2bf5517..7242946 100644 --- a/app/preload.js +++ b/app/preload.js @@ -93,6 +93,9 @@ __webpack_require__.r(__webpack_exports__); electron__WEBPACK_IMPORTED_MODULE_0__.contextBridge.exposeInMainWorld("electron", { openURL: async (target) => electron__WEBPACK_IMPORTED_MODULE_0__.ipcRenderer.send("openURL", target), + openFileDialog: async () => electron__WEBPACK_IMPORTED_MODULE_0__.ipcRenderer.invoke("openFileDialog"), + getMaxThreads: async () => electron__WEBPACK_IMPORTED_MODULE_0__.ipcRenderer.invoke("getMaxThreads"), + // onAiResponse: (func) => { electron__WEBPACK_IMPORTED_MODULE_0__.ipcRenderer.on("aiResponse", (event, data) => { func(data); @@ -110,7 +113,32 @@ electron__WEBPACK_IMPORTED_MODULE_0__.contextBridge.exposeInMainWorld("electron" }, runInference: async (args) => electron__WEBPACK_IMPORTED_MODULE_0__.ipcRenderer.send("runInference", args), stopInference: async (args) => electron__WEBPACK_IMPORTED_MODULE_0__.ipcRenderer.send("stopInference", args), - openFileDialog: async () => electron__WEBPACK_IMPORTED_MODULE_0__.ipcRenderer.invoke("openFileDialog"), + // + onBenchmarkLog: (func) => { + electron__WEBPACK_IMPORTED_MODULE_0__.ipcRenderer.on("benchmarkLog", (event, data) => { + func(data); + }); + }, + onBenchmarkComplete: (func) => { + electron__WEBPACK_IMPORTED_MODULE_0__.ipcRenderer.on("benchmarkComplete", (event) => { + func(); + }); + }, + runBenchmark: async (args) => electron__WEBPACK_IMPORTED_MODULE_0__.ipcRenderer.send("runBenchmark", args), + stopBenchmark: async (args) => electron__WEBPACK_IMPORTED_MODULE_0__.ipcRenderer.send("stopBenchmark", args), + // + onPerplexityLog: (func) => { + electron__WEBPACK_IMPORTED_MODULE_0__.ipcRenderer.on("perplexityLog", (event, data) => { + func(data); + }); + }, + onPerplexityComplete: (func) => { + electron__WEBPACK_IMPORTED_MODULE_0__.ipcRenderer.on("perplexityComplete", (event) => { + func(); + }); + }, + runPerplexity: async (args) => electron__WEBPACK_IMPORTED_MODULE_0__.ipcRenderer.send("runPerplexity", args), + stopPerplexity: async (args) => electron__WEBPACK_IMPORTED_MODULE_0__.ipcRenderer.send("stopPerplexity", args), }); })(); diff --git a/app/preload.js.map b/app/preload.js.map index fdb0711..38a526b 100644 --- a/app/preload.js.map +++ b/app/preload.js.map @@ -1 +1 @@ -{"version":3,"file":"preload.js","mappings":";;;;;;;;;;AAAA;;;;;;UCAA;UACA;;UAEA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;;UAEA;UACA;;UAEA;UACA;UACA;;;;;WCtBA;WACA;WACA;WACA;WACA;WACA,iCAAiC,WAAW;WAC5C;WACA;;;;;WCPA;WACA;WACA;WACA;WACA,yCAAyC,wCAAwC;WACjF;WACA;WACA;;;;;WCPA;;;;;WCAA;WACA;WACA;WACA,uDAAuD,iBAAiB;WACxE;WACA,gDAAgD,aAAa;WAC7D;;;;;;;;;;;;;ACNsD;AACtD;AACA,mDAAa;AACb,6BAA6B,iDAAW;AACxC;AACA,IAAI,iDAAW;AACf;AACA,KAAK;AACL,GAAG;AACH;AACA,IAAI,iDAAW;AACf;AACA,KAAK;AACL,GAAG;AACH;AACA,IAAI,iDAAW;AACf;AACA,KAAK;AACL,GAAG;AACH,gCAAgC,iDAAW;AAC3C,iCAAiC,iDAAW;AAC5C,8BAA8B,iDAAW;AACzC,CAAC","sources":["webpack://electron-bitnet/external node-commonjs \"electron\"","webpack://electron-bitnet/webpack/bootstrap","webpack://electron-bitnet/webpack/runtime/compat get default export","webpack://electron-bitnet/webpack/runtime/define property getters","webpack://electron-bitnet/webpack/runtime/hasOwnProperty shorthand","webpack://electron-bitnet/webpack/runtime/make namespace object","webpack://electron-bitnet/./src/preload.js"],"sourcesContent":["module.exports = require(\"electron\");","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import { ipcRenderer, contextBridge } from \"electron\";\r\n\r\ncontextBridge.exposeInMainWorld(\"electron\", {\r\n openURL: async (target) => ipcRenderer.send(\"openURL\", target),\r\n onAiResponse: (func) => {\r\n ipcRenderer.on(\"aiResponse\", (event, data) => {\r\n func(data);\r\n });\r\n },\r\n onAiError: (func) => {\r\n ipcRenderer.on(\"aiError\", (event) => {\r\n func();\r\n });\r\n },\r\n onAiComplete: (func) => {\r\n ipcRenderer.on(\"aiComplete\", (event) => {\r\n func();\r\n });\r\n },\r\n runInference: async (args) => ipcRenderer.send(\"runInference\", args),\r\n stopInference: async (args) => ipcRenderer.send(\"stopInference\", args),\r\n openFileDialog: async () => ipcRenderer.invoke(\"openFileDialog\"),\r\n});\r\n"],"names":[],"sourceRoot":""} \ No newline at end of file +{"version":3,"file":"preload.js","mappings":";;;;;;;;;;AAAA;;;;;;UCAA;UACA;;UAEA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;;UAEA;UACA;;UAEA;UACA;UACA;;;;;WCtBA;WACA;WACA;WACA;WACA;WACA,iCAAiC,WAAW;WAC5C;WACA;;;;;WCPA;WACA;WACA;WACA;WACA,yCAAyC,wCAAwC;WACjF;WACA;WACA;;;;;WCPA;;;;;WCAA;WACA;WACA;WACA,uDAAuD,iBAAiB;WACxE;WACA,gDAAgD,aAAa;WAC7D;;;;;;;;;;;;;ACNsD;AACtD;AACA,mDAAa;AACb,6BAA6B,iDAAW;AACxC,8BAA8B,iDAAW;AACzC,6BAA6B,iDAAW;AACxC;AACA;AACA,IAAI,iDAAW;AACf;AACA,KAAK;AACL,GAAG;AACH;AACA,IAAI,iDAAW;AACf;AACA,KAAK;AACL,GAAG;AACH;AACA,IAAI,iDAAW;AACf;AACA,KAAK;AACL,GAAG;AACH,gCAAgC,iDAAW;AAC3C,iCAAiC,iDAAW;AAC5C;AACA;AACA,IAAI,iDAAW;AACf;AACA,KAAK;AACL,GAAG;AACH;AACA,IAAI,iDAAW;AACf;AACA,KAAK;AACL,GAAG;AACH,gCAAgC,iDAAW;AAC3C,iCAAiC,iDAAW;AAC5C;AACA;AACA,IAAI,iDAAW;AACf;AACA,KAAK;AACL,GAAG;AACH;AACA,IAAI,iDAAW;AACf;AACA,KAAK;AACL,GAAG;AACH,iCAAiC,iDAAW;AAC5C,kCAAkC,iDAAW;AAC7C,CAAC","sources":["webpack://electron-bitnet/external node-commonjs \"electron\"","webpack://electron-bitnet/webpack/bootstrap","webpack://electron-bitnet/webpack/runtime/compat get default export","webpack://electron-bitnet/webpack/runtime/define property getters","webpack://electron-bitnet/webpack/runtime/hasOwnProperty shorthand","webpack://electron-bitnet/webpack/runtime/make namespace object","webpack://electron-bitnet/./src/preload.js"],"sourcesContent":["module.exports = require(\"electron\");","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import { ipcRenderer, contextBridge } from \"electron\";\r\n\r\ncontextBridge.exposeInMainWorld(\"electron\", {\r\n openURL: async (target) => ipcRenderer.send(\"openURL\", target),\r\n openFileDialog: async () => ipcRenderer.invoke(\"openFileDialog\"),\r\n getMaxThreads: async () => ipcRenderer.invoke(\"getMaxThreads\"),\r\n //\r\n onAiResponse: (func) => {\r\n ipcRenderer.on(\"aiResponse\", (event, data) => {\r\n func(data);\r\n });\r\n },\r\n onAiError: (func) => {\r\n ipcRenderer.on(\"aiError\", (event) => {\r\n func();\r\n });\r\n },\r\n onAiComplete: (func) => {\r\n ipcRenderer.on(\"aiComplete\", (event) => {\r\n func();\r\n });\r\n },\r\n runInference: async (args) => ipcRenderer.send(\"runInference\", args),\r\n stopInference: async (args) => ipcRenderer.send(\"stopInference\", args),\r\n //\r\n onBenchmarkLog: (func) => {\r\n ipcRenderer.on(\"benchmarkLog\", (event, data) => {\r\n func(data);\r\n });\r\n },\r\n onBenchmarkComplete: (func) => {\r\n ipcRenderer.on(\"benchmarkComplete\", (event) => {\r\n func();\r\n });\r\n },\r\n runBenchmark: async (args) => ipcRenderer.send(\"runBenchmark\", args),\r\n stopBenchmark: async (args) => ipcRenderer.send(\"stopBenchmark\", args),\r\n //\r\n onPerplexityLog: (func) => {\r\n ipcRenderer.on(\"perplexityLog\", (event, data) => {\r\n func(data);\r\n });\r\n },\r\n onPerplexityComplete: (func) => {\r\n ipcRenderer.on(\"perplexityComplete\", (event) => {\r\n func();\r\n });\r\n },\r\n runPerplexity: async (args) => ipcRenderer.send(\"runPerplexity\", args),\r\n stopPerplexity: async (args) => ipcRenderer.send(\"stopPerplexity\", args),\r\n});\r\n"],"names":[],"sourceRoot":""} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 101eaa5..f3d82c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,7 +26,7 @@ "@radix-ui/react-dialog": "^1.1.2", "@radix-ui/react-dropdown-menu": "^2.1.2", "@radix-ui/react-hover-card": "^1.1.2", - "@radix-ui/react-icons": "^1.3.0", + "@radix-ui/react-icons": "^1.3.1", "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-menubar": "^1.1.2", "@radix-ui/react-navigation-menu": "^1.2.1", @@ -46,7 +46,7 @@ "@radix-ui/react-tooltip": "^1.1.3", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", - "astro": "^4.16.7", + "astro": "^4.16.8", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "cmdk": "^1.0.0", @@ -79,8 +79,8 @@ "@babel/preset-env": "^7.25.3", "@soda/friendly-errors-webpack-plugin": "^1.8.1", "babel-loader": "^9.1.3", - "electron": "^32.1.2", - "electron-builder": "^25.1.7", + "electron": "^33.0.2", + "electron-builder": "^25.1.8", "webpack": "^5.93.0", "webpack-cli": "^5.1.4", "webpack-merge": "^6.0.1", @@ -228,12 +228,13 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.25.9.tgz", - "integrity": "sha512-z88xeGxnzehn2sqZ8UdGQEvYErF1odv2CftxInpSYJt6uHuPe9YjahKZITGs3l5LeI9d2ROG+obuDAoSlqbNfQ==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", "license": "MIT", "dependencies": { - "@babel/highlight": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", "picocolors": "^1.0.0" }, "engines": { @@ -250,21 +251,21 @@ } }, "node_modules/@babel/core": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.9.tgz", - "integrity": "sha512-WYvQviPw+Qyib0v92AwNIrdLISTp7RfDkM7bPqBvpbnhY4wq8HvHBZREVdYDXk98C8BkOIVnHAY3yvj7AVISxQ==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", + "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.25.9", - "@babel/generator": "^7.25.9", + "@babel/code-frame": "^7.26.0", + "@babel/generator": "^7.26.0", "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-module-transforms": "^7.25.9", - "@babel/helpers": "^7.25.9", - "@babel/parser": "^7.25.9", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.0", + "@babel/parser": "^7.26.0", "@babel/template": "^7.25.9", "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9", + "@babel/types": "^7.26.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -289,12 +290,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.9.tgz", - "integrity": "sha512-omlUGkr5EaoIJrhLf9CJ0TvjBRpd9+AXRG//0GEQ9THSo8wPiTlbpy1/Ow8ZTrbXpjd9FHXfbFQx32I04ht0FA==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", + "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", "license": "MIT", "dependencies": { - "@babel/types": "^7.25.9", + "@babel/parser": "^7.26.2", + "@babel/types": "^7.26.0", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -459,13 +461,12 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.9.tgz", - "integrity": "sha512-TvLZY/F3+GvdRYFZFyxMvnsKi+4oJdgZzU3BoGN9Uc2d9C6zfNwJcKKhjqLAhK8i46mv93jsO74fDh3ih6rpHA==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-simple-access": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9", "@babel/traverse": "^7.25.9" }, @@ -538,6 +539,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.9.tgz", "integrity": "sha512-c6WHXuiaRsJTyHYLJV75t9IqsmTbItYfdj99PnzYGQZkYKvan5/2jKJ7gu31J3/BJ/A18grImSPModuyG/Eo0Q==", + "dev": true, "license": "MIT", "dependencies": { "@babel/traverse": "^7.25.9", @@ -604,40 +606,25 @@ } }, "node_modules/@babel/helpers": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.9.tgz", - "integrity": "sha512-oKWp3+usOJSzDZOucZUAMayhPz/xVjzymyDzUN8dk0Wd3RWMlGLXi07UCQ/CgQVb8LvXx3XBajJH4XGgkt7H7g==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", + "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", "license": "MIT", "dependencies": { "@babel/template": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.9.tgz", - "integrity": "sha512-llL88JShoCsth8fF8R4SJnIn+WLvR6ccFxu1H3FlMhDontdcmZWf2HgIZ7AIqV3Xcck1idlohrN4EUBQz6klbw==", - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "@babel/types": "^7.26.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.9.tgz", - "integrity": "sha512-aI3jjAAO1fh7vY/pBGsn1i9LDbRP43+asrRlkPuTXW5yHXtd1NgTEMudbBoDDxrf1daEEfPJqR+JBMakzrR4Dg==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", + "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", "license": "MIT", "dependencies": { - "@babel/types": "^7.25.9" + "@babel/types": "^7.26.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -1914,9 +1901,9 @@ } }, "node_modules/@babel/types": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.9.tgz", - "integrity": "sha512-OwS2CM5KocvQ/k7dFJa8i5bNGJP0hXWfVCfDkqRFP1IreH1JDC7wG6eCYCi0+McbfT8OR/kNqsI0UU0xP9H6PQ==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.25.9", @@ -4121,12 +4108,12 @@ } }, "node_modules/@radix-ui/react-icons": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-icons/-/react-icons-1.3.0.tgz", - "integrity": "sha512-jQxj/0LKgp+j9BiTXz3O3sgs26RNet2iLWmsPyRz2SIcR4q/4SbazXfnYwbAr+vLYKSfc7qxzyGQA1HLlYiuNw==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-icons/-/react-icons-1.3.1.tgz", + "integrity": "sha512-QvYompk0X+8Yjlo/Fv4McrzxohDdM5GgLHyQcPpcsPvlOSXCGFjdbuyGL5dzRbg0GpknAjQJJZzdiRK7iWVuFQ==", "license": "MIT", "peerDependencies": { - "react": "^16.x || ^17.x || ^18.x" + "react": "^16.x || ^17.x || ^18.x || ^19.x" } }, "node_modules/@radix-ui/react-id": { @@ -5277,44 +5264,44 @@ ] }, "node_modules/@shikijs/core": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.22.1.tgz", - "integrity": "sha512-bqAhT/Ri5ixV4oYsvJNH8UJjpjbINWlWyXY6tBTsP4OmD6XnFv43nRJ+lTdxd2rmG5pgam/x+zGR6kLRXrpEKA==", + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.22.2.tgz", + "integrity": "sha512-bvIQcd8BEeR1yFvOYv6HDiyta2FFVePbzeowf5pPS1avczrPK+cjmaxxh0nx5QzbON7+Sv0sQfQVciO7bN72sg==", "license": "MIT", "dependencies": { - "@shikijs/engine-javascript": "1.22.1", - "@shikijs/engine-oniguruma": "1.22.1", - "@shikijs/types": "1.22.1", + "@shikijs/engine-javascript": "1.22.2", + "@shikijs/engine-oniguruma": "1.22.2", + "@shikijs/types": "1.22.2", "@shikijs/vscode-textmate": "^9.3.0", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.3" } }, "node_modules/@shikijs/engine-javascript": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-1.22.1.tgz", - "integrity": "sha512-540pyoy0LWe4jj2BVbgELwOFu1uFvRI7lg4hdsExrSXA9x7gqfzZ/Nnh4RfX86aDAgJ647gx4TCmRwACbnQSvw==", + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-1.22.2.tgz", + "integrity": "sha512-iOvql09ql6m+3d1vtvP8fLCVCK7BQD1pJFmHIECsujB0V32BJ0Ab6hxk1ewVSMFA58FI0pR2Had9BKZdyQrxTw==", "license": "MIT", "dependencies": { - "@shikijs/types": "1.22.1", + "@shikijs/types": "1.22.2", "@shikijs/vscode-textmate": "^9.3.0", "oniguruma-to-js": "0.4.3" } }, "node_modules/@shikijs/engine-oniguruma": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-1.22.1.tgz", - "integrity": "sha512-L+1Vmd+a2kk8HtogUFymQS6BjUfJnzcWoUp1BUgxoDiklbKSMvrsMuLZGevTOP1m0rEjgnC5MsDmsr8lX1lC+Q==", + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-1.22.2.tgz", + "integrity": "sha512-GIZPAGzQOy56mGvWMoZRPggn0dTlBf1gutV5TdceLCZlFNqWmuc7u+CzD0Gd9vQUTgLbrt0KLzz6FNprqYAxlA==", "license": "MIT", "dependencies": { - "@shikijs/types": "1.22.1", + "@shikijs/types": "1.22.2", "@shikijs/vscode-textmate": "^9.3.0" } }, "node_modules/@shikijs/types": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-1.22.1.tgz", - "integrity": "sha512-+45f8mu/Hxqs6Kyhfm98Nld5n7Q7lwhjU8UtdQwrOPs7BnM4VAb929O3IQ2ce+4D7SlNFlZGd8CnKRSnwbQreQ==", + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-1.22.2.tgz", + "integrity": "sha512-NCWDa6LGZqTuzjsGfXOBWfjS/fDIbDdmVDug+7ykVe1IKT4c1gakrvlfFYp5NhAXH/lyqLM8wsAPo5wNy73Feg==", "license": "MIT", "dependencies": { "@shikijs/vscode-textmate": "^9.3.0", @@ -6072,9 +6059,9 @@ } }, "node_modules/acorn": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.13.0.tgz", - "integrity": "sha512-8zSiw54Oxrdym50NlZ9sUusyO1Z1ZchgRLWRaK6c86XJFClyCgFKetdowBg5bKxyp/u+CDBJG4Mpp0m3HLZl9w==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -6264,18 +6251,6 @@ "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", @@ -6638,23 +6613,23 @@ } }, "node_modules/astro": { - "version": "4.16.7", - "resolved": "https://registry.npmjs.org/astro/-/astro-4.16.7.tgz", - "integrity": "sha512-nON+8MUEkWTFwXbS4zsQIq4t0Fs42eulM4x236AL+qNnWfqNAOOqAnFxO1dxfJ1q+XopIBbbT9Mtev+0zH47PQ==", + "version": "4.16.8", + "resolved": "https://registry.npmjs.org/astro/-/astro-4.16.8.tgz", + "integrity": "sha512-BRWFP0UQ8gkOr90KQW7oooedtgCk/j91pyv1WQUmgZwMUZk/v0HJRiddAZgvGCECOnmZFc9ZqRZnBsAMUgApNQ==", "license": "MIT", "dependencies": { "@astrojs/compiler": "^2.10.3", "@astrojs/internal-helpers": "0.4.1", "@astrojs/markdown-remark": "5.3.0", "@astrojs/telemetry": "3.1.0", - "@babel/core": "^7.25.8", - "@babel/plugin-transform-react-jsx": "^7.25.7", - "@babel/types": "^7.25.8", + "@babel/core": "^7.26.0", + "@babel/plugin-transform-react-jsx": "^7.25.9", + "@babel/types": "^7.26.0", "@oslojs/encoding": "^1.1.0", - "@rollup/pluginutils": "^5.1.2", + "@rollup/pluginutils": "^5.1.3", "@types/babel__core": "^7.20.5", "@types/cookie": "^0.6.0", - "acorn": "^8.13.0", + "acorn": "^8.14.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "boxen": "8.0.1", @@ -6692,18 +6667,18 @@ "prompts": "^2.4.2", "rehype": "^13.0.2", "semver": "^7.6.3", - "shiki": "^1.22.0", + "shiki": "^1.22.2", "tinyexec": "^0.3.1", "tsconfck": "^3.1.4", "unist-util-visit": "^5.0.0", "vfile": "^6.0.3", - "vite": "^5.4.9", + "vite": "^5.4.10", "vitefu": "^1.0.3", "which-pm": "^3.0.0", "xxhash-wasm": "^1.0.2", "yargs-parser": "^21.1.1", "zod": "^3.23.8", - "zod-to-json-schema": "^3.23.3", + "zod-to-json-schema": "^3.23.5", "zod-to-ts": "^1.2.0" }, "bin": { @@ -7484,20 +7459,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/character-entities": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", @@ -8284,20 +8245,12 @@ "node": ">=12.5.0" } }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, "node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "license": "MIT" + "license": "MIT", + "optional": true }, "node_modules/color-string": { "version": "1.9.1", @@ -9192,9 +9145,9 @@ } }, "node_modules/electron": { - "version": "32.2.2", - "resolved": "https://registry.npmjs.org/electron/-/electron-32.2.2.tgz", - "integrity": "sha512-c7TRE42JcgEmJ4elJyCdKk/2os0UX7YMkRDeXBkxFEoM34iX1/2x+c5T9PgeroKz8FEG7omRU5TvjulqVtXvdw==", + "version": "33.0.2", + "resolved": "https://registry.npmjs.org/electron/-/electron-33.0.2.tgz", + "integrity": "sha512-C2WksfP0COsMHbYXSJG68j6S3TjuGDrw/YT42B526yXalIlNQZ2GeAYKryg6AEMkIp3p8TUfDRD0+HyiyCt/nw==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -9786,15 +9739,6 @@ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "license": "MIT" }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -10828,15 +10772,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/has-property-descriptors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", @@ -15994,15 +15929,15 @@ } }, "node_modules/shiki": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.22.1.tgz", - "integrity": "sha512-PbJ6XxrWLMwB2rm3qdjIHNm3zq4SfFnOx0B3rEoi4AN8AUngsdyZ1tRe5slMPtn6jQkbUURLNZPpLR7Do3k78g==", + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.22.2.tgz", + "integrity": "sha512-3IZau0NdGKXhH2bBlUk4w1IHNxPh6A5B2sUpyY+8utLu2j/h1QpFkAaUA1bAMxOWWGtTWcAh531vnS4NJKS/lA==", "license": "MIT", "dependencies": { - "@shikijs/core": "1.22.1", - "@shikijs/engine-javascript": "1.22.1", - "@shikijs/engine-oniguruma": "1.22.1", - "@shikijs/types": "1.22.1", + "@shikijs/core": "1.22.2", + "@shikijs/engine-javascript": "1.22.2", + "@shikijs/engine-oniguruma": "1.22.2", + "@shikijs/types": "1.22.2", "@shikijs/vscode-textmate": "^9.3.0", "@types/hast": "^3.0.4" } @@ -16475,18 +16410,6 @@ "node": ">= 8.0" } }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", diff --git a/package.json b/package.json index a38a0d8..85b3fe1 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "@radix-ui/react-dialog": "^1.1.2", "@radix-ui/react-dropdown-menu": "^2.1.2", "@radix-ui/react-hover-card": "^1.1.2", - "@radix-ui/react-icons": "^1.3.0", + "@radix-ui/react-icons": "^1.3.1", "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-menubar": "^1.1.2", "@radix-ui/react-navigation-menu": "^1.2.1", @@ -84,7 +84,7 @@ "@radix-ui/react-tooltip": "^1.1.3", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", - "astro": "^4.16.7", + "astro": "^4.16.8", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "cmdk": "^1.0.0", @@ -117,8 +117,8 @@ "@babel/preset-env": "^7.25.3", "@soda/friendly-errors-webpack-plugin": "^1.8.1", "babel-loader": "^9.1.3", - "electron": "^32.1.2", - "electron-builder": "^25.1.7", + "electron": "^33.0.2", + "electron-builder": "^25.1.8", "webpack": "^5.93.0", "webpack-cli": "^5.1.4", "webpack-merge": "^6.0.1", diff --git a/src/background.js b/src/background.js index 512ec9a..0fbff59 100644 --- a/src/background.js +++ b/src/background.js @@ -13,12 +13,14 @@ import { initApplicationMenu } from "./lib/applicationMenu.js"; let mainWindow = null; let tray = null; let inferenceProcess = null; +let benchmarkProcess = null; +let perplexityProcess = null; function runInference(args) { let mainPath = path.join(app.getAppPath(), 'bin', 'Release', 'llama-cli.exe'); if (!fs.existsSync(mainPath)) { - mainPath = path.join(app.getAppPath(), 'bin', 'llama-cli'); + return; } const command = [ @@ -46,7 +48,7 @@ function runInference(args) { }); } -function signalHandler() { +function terminateInference() { if (inferenceProcess) { console.log('Terminating inference process...'); try { @@ -66,6 +68,113 @@ function signalHandler() { inferenceProcess = null; } +function runBenchmark(args) { + let benchPath = path.join(app.getAppPath(), 'bin', 'Release', 'llama-bench.exe'); + + if (!fs.existsSync(benchPath)) { + console.error('Benchmark binary not found, please build first.'); + return; + } + + const command = [ + `"${benchPath}"`, + '-m', args.model, + '-n', args.n_token, + '-ngl', '0', + '-b', '1', + '-t', args.threads, + '-p', args.n_prompt, + '-r', '5' + ]; + + benchmarkProcess = spawn(command[0], command.slice(1), { shell: true }); + + benchmarkProcess.stdout.on('data', (data) => { + const log = data.toString(); + mainWindow.webContents.send('benchmarkLog', log); + }); + + benchmarkProcess.on('close', (code) => { + mainWindow.webContents.send('benchmarkComplete'); + benchmarkProcess = null; + }); +} + +function terminateBenchmark() { + if (benchmarkProcess) { + console.log('Terminating benchmark process...'); + try { + benchmarkProcess.kill('SIGKILL'); // Use SIGKILL to forcefully terminate the process + benchmarkProcess.stdout.removeAllListeners('data'); + benchmarkProcess.stderr.removeAllListeners('data'); + benchmarkProcess = null; + console.log('Benchmark process terminated.'); + } catch (error) { + console.error('Failed to terminate benchmark process:', error); + } + } else { + console.log('No benchmark process to terminate.'); + } + + mainWindow.webContents.send('benchmarkComplete'); + benchmarkProcess = null; +} + +function runPerplexity(args) { + let perplexityPath = path.join(app.getAppPath(), 'bin', 'Release', 'llama-perplexity.exe'); + + if (!fs.existsSync(perplexityPath)) { + console.error('Perplexity binary not found, please build first.'); + return; + } + + const command = [ + `"${perplexityPath}"`, + '--model', `"${args.model}"`, + '--prompt', `"${args.prompt}"`, + '--threads', args.threads, + '--ctx-size', args.ctx_size, + '--perplexity', + '--ppl-stride', args.ppl_stride + ]; + + perplexityProcess = spawn(command[0], command.slice(1), { shell: true }); + + perplexityProcess.stderr.on('data', (data) => { + const log = data.toString(); + mainWindow.webContents.send('perplexityLog', log); + }); + + perplexityProcess.on('error', (error) => { + console.error('Perplexity process error:', error); + }); + + perplexityProcess.on('close', (code) => { + mainWindow.webContents.send('perplexityComplete'); + perplexityProcess = null; + }); +} + +function terminatePerplexity() { + if (perplexityProcess) { + console.log('Terminating perplexity process...'); + try { + perplexityProcess.kill('SIGKILL'); // Use SIGKILL to forcefully terminate the process + perplexityProcess.stdout.removeAllListeners('data'); + perplexityProcess.stderr.removeAllListeners('data'); + perplexityProcess = null; + console.log('Perplexity process terminated.'); + } catch (error) { + console.error('Failed to terminate perplexity process:', error); + } + } else { + console.log('No perplexity process to terminate.'); + } + + mainWindow.webContents.send('perplexityComplete'); + perplexityProcess = null; +} + const createWindow = async () => { mainWindow = new BrowserWindow({ minWidth: 480, @@ -159,7 +268,7 @@ const createWindow = async () => { }); ipcMain.on("stopInference", (event) => { - signalHandler(); + terminateInference(); }); ipcMain.handle('openFileDialog', async () => { @@ -170,6 +279,26 @@ const createWindow = async () => { return result.filePaths; }); + ipcMain.handle('getMaxThreads', async () => { + return os.cpus().length; + }); + + ipcMain.on("runBenchmark", (event, arg) => { + runBenchmark(arg); + }); + + ipcMain.on("stopBenchmark", (event) => { + terminateBenchmark(); + }); + + ipcMain.on("runPerplexity", (event, arg) => { + runPerplexity(arg); + }); + + ipcMain.on("stopPerplexity", (event) => { + terminatePerplexity(); + }); + tray.on("click", () => { mainWindow?.setAlwaysOnTop(true); mainWindow?.show(); @@ -203,11 +332,13 @@ if (currentOS === "win32" || currentOS === "linux") { }); app.on('before-quit', (event) => { - signalHandler(); + terminateInference(); + terminateBenchmark(); }); app.on('will-quit', (event) => { - signalHandler(); + terminateInference(); + terminateBenchmark(); }); app.on("window-all-closed", () => { diff --git a/src/components/BenchmarkUI.jsx b/src/components/BenchmarkUI.jsx new file mode 100644 index 0000000..5875304 --- /dev/null +++ b/src/components/BenchmarkUI.jsx @@ -0,0 +1,289 @@ +import React, { useState, useEffect, useMemo } from "react"; +import { UploadIcon, ReloadIcon } from "@radix-ui/react-icons"; +import { useTranslation } from "react-i18next"; +import { i18n as i18nInstance, locale } from "@/lib/i18n.js"; + +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/ui/card"; + +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Textarea } from "@/components/ui/textarea"; + +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select" + +import ExternalLink from "@/components/ExternalLink.jsx"; +import HoverInfo from "@/components/HoverInfo.jsx"; + +export default function BenchmarkUI(properties) { + const { t, i18n } = useTranslation(locale.get(), { i18n: i18nInstance }); + + const [tokenQuantity, setTokenQuantity] = useState(1); + const [model, setModel] = useState(""); // proven compatible: ggml-model-i2_s.gguf + const [threads, setThreads] = useState(2); + const [promptLength, setPromptLength] = useState(1); + + const [runningBenchmark, setRunningBenchmark] = useState(false); + const [benchmarkLog, setBenchmarkLog] = useState(""); + + const [maxThreads, setMaxThreads] = useState(2); + useEffect(() => { + if (!window.electron) { + return; + } + async function getMaxThreads() { + const _maxThreads = await window.electron.getMaxThreads(); + setMaxThreads(_maxThreads); + } + getMaxThreads(); + }, []); + + const handleFileSelect = async () => { + const filePaths = await window.electron.openFileDialog(); + if (filePaths.length > 0) { + setModel(filePaths[0]); + } + }; + + useEffect(() => { + if (!window.electron) { + return; + } + + window.electron.onBenchmarkLog((log) => { + setBenchmarkLog((prevBenchmarkLog) => prevBenchmarkLog + log); + }); + + window.electron.onBenchmarkComplete(() => { + setRunningBenchmark(false); + }); + }, []); + + const [parsedBenchmarkData, setParsedBenchmarkData] = useState([]); + const [timer, setTimer] = useState(null); + + const parseBenchmarkData = () => { + const lines = benchmarkLog.trim().split('\n'); + const headers = lines[0]?.split('|').map(header => header.trim()).filter(header => header); + const dataLines = lines.slice(2); // Skip the header and dashes rows + + return dataLines.map(line => { + const values = line.split('|').map(value => value.trim()).filter(value => value); + const obj = {}; + headers.forEach((header, index) => { + obj[header] = values[index]; + }); + return obj; + }).filter(obj => { + // Filter out empty objects and irrelevant entries + return headers.every(header => obj[header]) && !obj['model']?.includes('build:'); + }); + }; + + useEffect(() => { + if (timer) { + clearTimeout(timer); + } + + const newTimer = setTimeout(() => { + setParsedBenchmarkData(parseBenchmarkData()); + }, 750); + + setTimer(newTimer); + + return () => { + clearTimeout(newTimer); + }; + }, [benchmarkLog]); + + return ( +
+ + + {t("Benchmark:title")} + + {t("Benchmark:description")} + + + +
+
+ {t("Benchmark:commandOptions")} + + { + const value = e.currentTarget.value; + const regex = /^\d*$/; // Only allows positive whole numbers + if (regex.test(value)) { + setTokenQuantity(value); + } + }} + /> + +
+
+ +
+ +
+ + + + { + const value = e.currentTarget.value; + const regex = /^\d*$/; // Only allows positive whole numbers + if (regex.test(value)) { + setPromptLength(value); + } + }} + /> +
+ { + model && runningBenchmark + ? + : null + } + { + model && + tokenQuantity && tokenQuantity > 0 && + promptLength && promptLength > 0 && + !runningBenchmark + ? + : null + } + { + !model || !tokenQuantity || !promptLength + ? + : null + } + { + runningBenchmark + ? + : + } +
+ +
+
+ {t("Benchmark:log")} +