mirror of
				https://github.com/open-webui/assistant
				synced 2025-06-26 18:15:57 +00:00 
			
		
		
		
	init
This commit is contained in:
		
						commit
						f3096270a3
					
				
							
								
								
									
										16
									
								
								.eslintrc.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								.eslintrc.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | |||||||
|  | { | ||||||
|  |   "env": { | ||||||
|  |     "browser": true, | ||||||
|  |     "es6": true, | ||||||
|  |     "node": true | ||||||
|  |   }, | ||||||
|  |   "extends": [ | ||||||
|  |     "eslint:recommended", | ||||||
|  |     "plugin:@typescript-eslint/eslint-recommended", | ||||||
|  |     "plugin:@typescript-eslint/recommended", | ||||||
|  |     "plugin:import/recommended", | ||||||
|  |     "plugin:import/electron", | ||||||
|  |     "plugin:import/typescript" | ||||||
|  |   ], | ||||||
|  |   "parser": "@typescript-eslint/parser" | ||||||
|  | } | ||||||
							
								
								
									
										97
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,97 @@ | |||||||
|  | .vite | ||||||
|  | node_modules | ||||||
|  | out | ||||||
|  | _test | ||||||
|  | 
 | ||||||
|  | # Logs | ||||||
|  | logs | ||||||
|  | *.log | ||||||
|  | npm-debug.log* | ||||||
|  | yarn-debug.log* | ||||||
|  | yarn-error.log* | ||||||
|  | lerna-debug.log* | ||||||
|  | 
 | ||||||
|  | # Diagnostic reports (https://nodejs.org/api/report.html) | ||||||
|  | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json | ||||||
|  | 
 | ||||||
|  | # Runtime data | ||||||
|  | pids | ||||||
|  | *.pid | ||||||
|  | *.seed | ||||||
|  | *.pid.lock | ||||||
|  | .DS_Store | ||||||
|  | 
 | ||||||
|  | # Directory for instrumented libs generated by jscoverage/JSCover | ||||||
|  | lib-cov | ||||||
|  | 
 | ||||||
|  | # Coverage directory used by tools like istanbul | ||||||
|  | coverage | ||||||
|  | *.lcov | ||||||
|  | 
 | ||||||
|  | # nyc test coverage | ||||||
|  | .nyc_output | ||||||
|  | 
 | ||||||
|  | # node-waf configuration | ||||||
|  | .lock-wscript | ||||||
|  | 
 | ||||||
|  | # Compiled binary addons (https://nodejs.org/api/addons.html) | ||||||
|  | build/Release | ||||||
|  | 
 | ||||||
|  | # Dependency directories | ||||||
|  | node_modules/ | ||||||
|  | jspm_packages/ | ||||||
|  | 
 | ||||||
|  | # TypeScript v1 declaration files | ||||||
|  | typings/ | ||||||
|  | 
 | ||||||
|  | # TypeScript cache | ||||||
|  | *.tsbuildinfo | ||||||
|  | 
 | ||||||
|  | # Optional npm cache directory | ||||||
|  | .npm | ||||||
|  | 
 | ||||||
|  | # Optional eslint cache | ||||||
|  | .eslintcache | ||||||
|  | 
 | ||||||
|  | # Optional REPL history | ||||||
|  | .node_repl_history | ||||||
|  | 
 | ||||||
|  | # Output of 'npm pack' | ||||||
|  | *.tgz | ||||||
|  | 
 | ||||||
|  | # Yarn Integrity file | ||||||
|  | .yarn-integrity | ||||||
|  | 
 | ||||||
|  | # dotenv environment variables file | ||||||
|  | .env | ||||||
|  | .env.test | ||||||
|  | 
 | ||||||
|  | # parcel-bundler cache (https://parceljs.org/) | ||||||
|  | .cache | ||||||
|  | 
 | ||||||
|  | # next.js build output | ||||||
|  | .next | ||||||
|  | 
 | ||||||
|  | # nuxt.js build output | ||||||
|  | .nuxt | ||||||
|  | 
 | ||||||
|  | # vuepress build output | ||||||
|  | .vuepress/dist | ||||||
|  | 
 | ||||||
|  | # Serverless directories | ||||||
|  | .serverless/ | ||||||
|  | 
 | ||||||
|  | # FuseBox cache | ||||||
|  | .fusebox/ | ||||||
|  | 
 | ||||||
|  | # DynamoDB Local files | ||||||
|  | .dynamodb/ | ||||||
|  | 
 | ||||||
|  | # Webpack | ||||||
|  | .webpack/ | ||||||
|  | 
 | ||||||
|  | # Vite | ||||||
|  | .vite/ | ||||||
|  | 
 | ||||||
|  | # Electron-Forge | ||||||
|  | out/ | ||||||
							
								
								
									
										57
									
								
								forge.config.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								forge.config.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,57 @@ | |||||||
|  | import type { ForgeConfig } from "@electron-forge/shared-types"; | ||||||
|  | import { MakerSquirrel } from "@electron-forge/maker-squirrel"; | ||||||
|  | import { MakerZIP } from "@electron-forge/maker-zip"; | ||||||
|  | import { MakerDeb } from "@electron-forge/maker-deb"; | ||||||
|  | import { MakerRpm } from "@electron-forge/maker-rpm"; | ||||||
|  | import { VitePlugin } from "@electron-forge/plugin-vite"; | ||||||
|  | import { FusesPlugin } from "@electron-forge/plugin-fuses"; | ||||||
|  | import { FuseV1Options, FuseVersion } from "@electron/fuses"; | ||||||
|  | 
 | ||||||
|  | const config: ForgeConfig = { | ||||||
|  |   packagerConfig: { | ||||||
|  |     asar: true, | ||||||
|  |   }, | ||||||
|  |   rebuildConfig: {}, | ||||||
|  |   makers: [ | ||||||
|  |     new MakerSquirrel({}), | ||||||
|  |     new MakerZIP({}, ["darwin"]), | ||||||
|  |     new MakerRpm({}), | ||||||
|  |     new MakerDeb({}), | ||||||
|  |   ], | ||||||
|  |   plugins: [ | ||||||
|  |     new VitePlugin({ | ||||||
|  |       // `build` can specify multiple entry builds, which can be Main process, Preload scripts, Worker process, etc.
 | ||||||
|  |       // If you are familiar with Vite configuration, it will look really familiar.
 | ||||||
|  |       build: [ | ||||||
|  |         { | ||||||
|  |           // `entry` is just an alias for `build.lib.entry` in the corresponding file of `config`.
 | ||||||
|  |           entry: "src/main.ts", | ||||||
|  |           config: "vite.main.config.ts", | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           entry: "src/preload.ts", | ||||||
|  |           config: "vite.preload.config.ts", | ||||||
|  |         }, | ||||||
|  |       ], | ||||||
|  |       renderer: [ | ||||||
|  |         { | ||||||
|  |           name: "main_window", | ||||||
|  |           config: "vite.renderer.config.ts", | ||||||
|  |         }, | ||||||
|  |       ], | ||||||
|  |     }), | ||||||
|  |     // Fuses are used to enable/disable various Electron functionality
 | ||||||
|  |     // at package time, before code signing the application
 | ||||||
|  |     new FusesPlugin({ | ||||||
|  |       version: FuseVersion.V1, | ||||||
|  |       [FuseV1Options.RunAsNode]: false, | ||||||
|  |       [FuseV1Options.EnableCookieEncryption]: true, | ||||||
|  |       [FuseV1Options.EnableNodeOptionsEnvironmentVariable]: false, | ||||||
|  |       [FuseV1Options.EnableNodeCliInspectArguments]: false, | ||||||
|  |       [FuseV1Options.EnableEmbeddedAsarIntegrityValidation]: true, | ||||||
|  |       [FuseV1Options.OnlyLoadAppFromAsar]: true, | ||||||
|  |     }), | ||||||
|  |   ], | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export default config; | ||||||
							
								
								
									
										31
									
								
								forge.env.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								forge.env.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,31 @@ | |||||||
|  | export {}; // Make this a module
 | ||||||
|  | 
 | ||||||
|  | declare global { | ||||||
|  |   // This allows TypeScript to pick up the magic constants that's auto-generated by Forge's Vite
 | ||||||
|  |   // plugin that tells the Electron app where to look for the Vite-bundled app code (depending on
 | ||||||
|  |   // whether you're running in development or production).
 | ||||||
|  |   const MAIN_WINDOW_VITE_DEV_SERVER_URL: string; | ||||||
|  |   const MAIN_WINDOW_VITE_NAME: string; | ||||||
|  | 
 | ||||||
|  |   namespace NodeJS { | ||||||
|  |     interface Process { | ||||||
|  |       // Used for hot reload after preload scripts.
 | ||||||
|  |       viteDevServers: Record<string, import('vite').ViteDevServer>; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   type VitePluginConfig = ConstructorParameters<typeof import('@electron-forge/plugin-vite').VitePlugin>[0]; | ||||||
|  | 
 | ||||||
|  |   interface VitePluginRuntimeKeys { | ||||||
|  |     VITE_DEV_SERVER_URL: `${string}_VITE_DEV_SERVER_URL`; | ||||||
|  |     VITE_NAME: `${string}_VITE_NAME`; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | declare module 'vite' { | ||||||
|  |   interface ConfigEnv<K extends keyof VitePluginConfig = keyof VitePluginConfig> { | ||||||
|  |     root: string; | ||||||
|  |     forgeConfig: VitePluginConfig; | ||||||
|  |     forgeConfigSelf: VitePluginConfig[K][number]; | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										11
									
								
								index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								index.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | |||||||
|  | <!DOCTYPE html> | ||||||
|  | <html> | ||||||
|  |   <head> | ||||||
|  |     <meta charset="UTF-8" /> | ||||||
|  |     <title>Hello World!</title> | ||||||
|  |   </head> | ||||||
|  |   <body> | ||||||
|  |     <div id="app"></div> | ||||||
|  |     <script type="module" src="/src/renderer.ts"></script> | ||||||
|  |   </body> | ||||||
|  | </html> | ||||||
							
								
								
									
										10735
									
								
								package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										10735
									
								
								package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										49
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,49 @@ | |||||||
|  | { | ||||||
|  |   "name": "open-webui-assistant", | ||||||
|  |   "productName": "open-webui-assistant", | ||||||
|  |   "version": "1.0.0", | ||||||
|  |   "description": "My Electron application description", | ||||||
|  |   "main": ".vite/build/main.js", | ||||||
|  |   "scripts": { | ||||||
|  |     "start": "electron-forge start", | ||||||
|  |     "package": "electron-forge package", | ||||||
|  |     "make": "electron-forge make", | ||||||
|  |     "publish": "electron-forge publish", | ||||||
|  |     "lint": "eslint --ext .ts,.tsx ." | ||||||
|  |   }, | ||||||
|  |   "devDependencies": { | ||||||
|  |     "@electron-forge/cli": "^7.3.0", | ||||||
|  |     "@electron-forge/maker-deb": "^7.3.0", | ||||||
|  |     "@electron-forge/maker-rpm": "^7.3.0", | ||||||
|  |     "@electron-forge/maker-squirrel": "^7.3.0", | ||||||
|  |     "@electron-forge/maker-zip": "^7.3.0", | ||||||
|  |     "@electron-forge/plugin-auto-unpack-natives": "^7.3.0", | ||||||
|  |     "@electron-forge/plugin-fuses": "^7.3.0", | ||||||
|  |     "@electron-forge/plugin-vite": "^7.3.0", | ||||||
|  |     "@electron/fuses": "^1.7.0", | ||||||
|  |     "@typescript-eslint/eslint-plugin": "^5.62.0", | ||||||
|  |     "@typescript-eslint/parser": "^5.62.0", | ||||||
|  |     "@vitejs/plugin-vue": "^5.0.4", | ||||||
|  |     "autoprefixer": "^10.4.18", | ||||||
|  |     "electron": "29.1.1", | ||||||
|  |     "eslint": "^8.57.0", | ||||||
|  |     "eslint-plugin-import": "^2.29.1", | ||||||
|  |     "postcss": "^8.4.35", | ||||||
|  |     "tailwindcss": "^3.4.1", | ||||||
|  |     "ts-node": "^10.9.2", | ||||||
|  |     "typescript": "~4.5.4", | ||||||
|  |     "vite": "^5.1.6" | ||||||
|  |   }, | ||||||
|  |   "keywords": [], | ||||||
|  |   "author": { | ||||||
|  |     "name": "Timothy J. Baek", | ||||||
|  |     "email": "timothyjrbeck@gmail.com" | ||||||
|  |   }, | ||||||
|  |   "license": "MIT", | ||||||
|  |   "dependencies": { | ||||||
|  |     "@electron/rebuild": "^3.6.0", | ||||||
|  |     "@nut-tree/nut-js": "^3.1.2", | ||||||
|  |     "electron-squirrel-startup": "^1.0.0", | ||||||
|  |     "vue": "^3.4.21" | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										6
									
								
								postcss.config.cjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								postcss.config.cjs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | |||||||
|  | module.exports = { | ||||||
|  |   plugins: { | ||||||
|  |     tailwindcss: {}, | ||||||
|  |     autoprefixer: {}, | ||||||
|  |   }, | ||||||
|  | }; | ||||||
							
								
								
									
										74
									
								
								src/App.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								src/App.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,74 @@ | |||||||
|  | <script setup> | ||||||
|  | 
 | ||||||
|  | import { ref, onBeforeMount } from 'vue'; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | const url = ref('http://localhost:3000') | ||||||
|  | const token = ref('your_jwt') | ||||||
|  | 
 | ||||||
|  | const submitHandler = () => { | ||||||
|  |     console.log(url.value, token.value) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     if (url.value.endsWith('/')) { | ||||||
|  |         url.value = url.value.substring(0, url.value.length - 1); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     window.electron.saveConfig({ | ||||||
|  |         url: url.value, | ||||||
|  |         token: token.value | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | onBeforeMount(async () => { | ||||||
|  |     console.log('hi') | ||||||
|  |     const res = await window.electron.loadConfig() | ||||||
|  | 
 | ||||||
|  |     if (res) { | ||||||
|  |         url.value = res.url | ||||||
|  |         token.value = res.token | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | }) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <template> | ||||||
|  |     <div class=" h-screen w-screen px-3 flex justify-center"> | ||||||
|  |         <div class=" my-auto w-full flex flex-col gap-2"> | ||||||
|  |             <div class="flex justify-between items-center"> | ||||||
|  |                 <div class=" text-sm font-semibold">Open WebUI Assistant</div> | ||||||
|  | 
 | ||||||
|  |                 <button class="bg-neutral-700 hover:bg-neutral-800 transition text-white text-xs px-3 py-1 rounded-lg" | ||||||
|  |                     @click="submitHandler">Save</button> | ||||||
|  |             </div> | ||||||
|  |             <div class="flex flex-col gap-1.5"> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |                 <input v-model="url" | ||||||
|  |                     class=" w-full bg-gray-100 hover:bg-gray-200 transition rounded-lg py-1 px-2 text-sm outline-none" | ||||||
|  |                     placeholder="Open WebUI URL" /> | ||||||
|  |                 <input v-model="token" | ||||||
|  |                     class=" w-full bg-gray-100 hover:bg-gray-200 transition rounded-lg py-1 px-2 text-sm outline-none" | ||||||
|  |                     placeholder="Open WebUI Token" /> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |             </div> | ||||||
|  |         </div> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     </div> | ||||||
|  | 
 | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | <style> | ||||||
|  | @import './index.css'; | ||||||
|  | </style> | ||||||
							
								
								
									
										3
									
								
								src/index.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/index.css
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | |||||||
|  | @tailwind base; | ||||||
|  | @tailwind components; | ||||||
|  | @tailwind utilities; | ||||||
							
								
								
									
										202
									
								
								src/main.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								src/main.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,202 @@ | |||||||
|  | import { | ||||||
|  |   app, | ||||||
|  |   BrowserWindow, | ||||||
|  |   globalShortcut, | ||||||
|  |   clipboard, | ||||||
|  |   ipcMain, | ||||||
|  | } from "electron"; | ||||||
|  | import path from "path"; | ||||||
|  | 
 | ||||||
|  | import { keyboard, Key } from "@nut-tree/nut-js"; | ||||||
|  | import { splitStream } from "./utils"; | ||||||
|  | 
 | ||||||
|  | keyboard.config.autoDelayMs = 0; | ||||||
|  | 
 | ||||||
|  | let config = { | ||||||
|  |   url: "", | ||||||
|  |   token: "", | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // Handle creating/removing shortcuts on Windows when installing/uninstalling.
 | ||||||
|  | if (require("electron-squirrel-startup")) { | ||||||
|  |   app.quit(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const createWindow = () => { | ||||||
|  |   // Create the browser window.
 | ||||||
|  |   const mainWindow = new BrowserWindow({ | ||||||
|  |     width: 300, | ||||||
|  |     height: 150, | ||||||
|  |     webPreferences: { | ||||||
|  |       preload: path.join(__dirname, "preload.js"), | ||||||
|  |     }, | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   // and load the index.html of the app.
 | ||||||
|  |   if (MAIN_WINDOW_VITE_DEV_SERVER_URL) { | ||||||
|  |     mainWindow.loadURL(MAIN_WINDOW_VITE_DEV_SERVER_URL); | ||||||
|  |   } else { | ||||||
|  |     mainWindow.loadFile( | ||||||
|  |       path.join(__dirname, `../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`) | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Open the DevTools.
 | ||||||
|  |   mainWindow.webContents.openDevTools(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const updateConfig = async (_config) => { | ||||||
|  |   config = { ...config, ..._config }; | ||||||
|  |   return config; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const typeWord = async (word: string) => { | ||||||
|  |   for (let i = 0; i < word.length; i++) { | ||||||
|  |     if (word[i] === "\n") { | ||||||
|  |       await keyboard.type(Key.Return); | ||||||
|  |     } else { | ||||||
|  |       await keyboard.type(word[i]); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const generateResponse = async (prompt: string) => { | ||||||
|  |   console.log("generateResponse"); | ||||||
|  |   const res = await fetch(`${config.url}/ollama/api/chat`, { | ||||||
|  |     method: "POST", | ||||||
|  |     headers: { | ||||||
|  |       Accept: "application/json", | ||||||
|  |       "Content-Type": "application/json", | ||||||
|  |       Authorization: `Bearer ${config.token}`, | ||||||
|  |     }, | ||||||
|  |     body: JSON.stringify({ | ||||||
|  |       model: "mistral:latest", | ||||||
|  |       messages: [ | ||||||
|  |         { | ||||||
|  |           role: "user", | ||||||
|  |           content: prompt, | ||||||
|  |         }, | ||||||
|  |       ], | ||||||
|  |       stream: true, | ||||||
|  |     }), | ||||||
|  |   }).catch((err) => { | ||||||
|  |     console.log(err); | ||||||
|  |     return null; | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   if (res && res.ok) { | ||||||
|  |     const reader = res.body | ||||||
|  |       .pipeThrough(new TextDecoderStream()) | ||||||
|  |       .pipeThrough(splitStream("\n")) | ||||||
|  |       .getReader(); | ||||||
|  | 
 | ||||||
|  |     while (true) { | ||||||
|  |       const { value, done } = await reader.read(); | ||||||
|  |       if (done) { | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       try { | ||||||
|  |         let lines = value.split("\n"); | ||||||
|  | 
 | ||||||
|  |         for (const line of lines) { | ||||||
|  |           if (line !== "") { | ||||||
|  |             console.log(line); | ||||||
|  |             let data = JSON.parse(line); | ||||||
|  | 
 | ||||||
|  |             if ("detail" in data) { | ||||||
|  |               throw data; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if ("id" in data) { | ||||||
|  |               console.log(data); | ||||||
|  |             } else { | ||||||
|  |               if (data.done == false) { | ||||||
|  |                 await typeWord(data.message.content); | ||||||
|  |               } | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } catch (error) { | ||||||
|  |         console.log(error); | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | function sleep(ms) { | ||||||
|  |   return new Promise((resolve) => { | ||||||
|  |     setTimeout(resolve, ms); | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const shortcutHandler = async () => { | ||||||
|  |   console.log("shortcutHandler"); | ||||||
|  |   keyboard.config.autoDelayMs = 10; | ||||||
|  | 
 | ||||||
|  |   let i = 0; | ||||||
|  |   while (i !== 5) { | ||||||
|  |     if (process.platform !== "darwin") { | ||||||
|  |       await keyboard.type(Key.LeftControl, Key.C); | ||||||
|  |     } else { | ||||||
|  |       await keyboard.type(Key.LeftSuper, Key.C); | ||||||
|  |     } | ||||||
|  |     i++; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   await sleep(100); | ||||||
|  | 
 | ||||||
|  |   const prompt = await clipboard.readText(); | ||||||
|  |   console.log(prompt); | ||||||
|  | 
 | ||||||
|  |   if (config.url !== "" && config.token !== "") { | ||||||
|  |     keyboard.config.autoDelayMs = 0; | ||||||
|  | 
 | ||||||
|  |     await generateResponse(prompt); | ||||||
|  |   } else { | ||||||
|  |     keyboard.config.autoDelayMs = 100; | ||||||
|  | 
 | ||||||
|  |     console.log(config); | ||||||
|  |     await typeWord("Open WebUI URL/Token Required!"); | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // This method will be called when Electron has finished
 | ||||||
|  | // initialization and is ready to create browser windows.
 | ||||||
|  | // Some APIs can only be used after this event occurs.
 | ||||||
|  | app | ||||||
|  |   .whenReady() | ||||||
|  |   .then(() => { | ||||||
|  |     ipcMain.handle("load-config", (event, arg) => { | ||||||
|  |       return config; | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     ipcMain.on("save-config", (event, data) => { | ||||||
|  |       console.log(data); | ||||||
|  |       updateConfig(data); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     globalShortcut.register("Alt+CommandOrControl+O", shortcutHandler); | ||||||
|  |   }) | ||||||
|  |   .then(createWindow); | ||||||
|  | 
 | ||||||
|  | // Quit when all windows are closed, except on macOS. There, it's common
 | ||||||
|  | // for applications and their menu bar to stay active until the user quits
 | ||||||
|  | // explicitly with Cmd + Q.
 | ||||||
|  | app.on("window-all-closed", () => { | ||||||
|  |   if (process.platform !== "darwin") { | ||||||
|  |     app.quit(); | ||||||
|  |   } | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | app.on("activate", () => { | ||||||
|  |   // On OS X it's common to re-create a window in the app when the
 | ||||||
|  |   // dock icon is clicked and there are no other windows open.
 | ||||||
|  |   if (BrowserWindow.getAllWindows().length === 0) { | ||||||
|  |     createWindow(); | ||||||
|  |   } | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | // In this file you can include the rest of your app's specific main process
 | ||||||
|  | // code. You can also put them in separate files and import them here.
 | ||||||
							
								
								
									
										13
									
								
								src/preload.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/preload.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | |||||||
|  | // See the Electron documentation for details on how to use preload scripts:
 | ||||||
|  | // https://www.electronjs.org/docs/latest/tutorial/process-model#preload-scripts
 | ||||||
|  | 
 | ||||||
|  | const { contextBridge, ipcRenderer } = require("electron"); | ||||||
|  | 
 | ||||||
|  | contextBridge.exposeInMainWorld("electron", { | ||||||
|  |   on(event, callback) { | ||||||
|  |     ipcRenderer.on(event, callback); | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   loadConfig: () => ipcRenderer.invoke("load-config"), | ||||||
|  |   saveConfig: (data) => ipcRenderer.send("save-config", data), | ||||||
|  | }); | ||||||
							
								
								
									
										4
									
								
								src/renderer.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								src/renderer.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | |||||||
|  | import { createApp } from "vue"; | ||||||
|  | import App from "./App.vue"; | ||||||
|  | 
 | ||||||
|  | createApp(App).mount("#app"); | ||||||
							
								
								
									
										14
									
								
								src/utils/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/utils/index.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | |||||||
|  | export const splitStream = (splitOn) => { | ||||||
|  |   let buffer = ""; | ||||||
|  |   return new TransformStream({ | ||||||
|  |     transform(chunk, controller) { | ||||||
|  |       buffer += chunk; | ||||||
|  |       const parts = buffer.split(splitOn); | ||||||
|  |       parts.slice(0, -1).forEach((part) => controller.enqueue(part)); | ||||||
|  |       buffer = parts[parts.length - 1]; | ||||||
|  |     }, | ||||||
|  |     flush(controller) { | ||||||
|  |       if (buffer) controller.enqueue(buffer); | ||||||
|  |     }, | ||||||
|  |   }); | ||||||
|  | }; | ||||||
							
								
								
									
										8
									
								
								tailwind.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								tailwind.config.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | |||||||
|  | /** @type {import('tailwindcss').Config} */ | ||||||
|  | module.exports = { | ||||||
|  |   content: ["./src/**/*.{html,js,vue}"], | ||||||
|  |   theme: { | ||||||
|  |     extend: {}, | ||||||
|  |   }, | ||||||
|  |   plugins: [], | ||||||
|  | }; | ||||||
							
								
								
									
										15
									
								
								tsconfig.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								tsconfig.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | |||||||
|  | { | ||||||
|  |   "compilerOptions": { | ||||||
|  |     "target": "ESNext", | ||||||
|  |     "module": "commonjs", | ||||||
|  |     "allowJs": true, | ||||||
|  |     "skipLibCheck": true, | ||||||
|  |     "esModuleInterop": true, | ||||||
|  |     "noImplicitAny": true, | ||||||
|  |     "sourceMap": true, | ||||||
|  |     "baseUrl": ".", | ||||||
|  |     "outDir": "dist", | ||||||
|  |     "moduleResolution": "node", | ||||||
|  |     "resolveJsonModule": true | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										93
									
								
								vite.base.config.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								vite.base.config.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,93 @@ | |||||||
|  | import { builtinModules } from 'node:module'; | ||||||
|  | import type { AddressInfo } from 'node:net'; | ||||||
|  | import type { ConfigEnv, Plugin, UserConfig } from 'vite'; | ||||||
|  | import pkg from './package.json'; | ||||||
|  | 
 | ||||||
|  | export const builtins = ['electron', ...builtinModules.map((m) => [m, `node:${m}`]).flat()]; | ||||||
|  | 
 | ||||||
|  | export const external = [...builtins, ...Object.keys('dependencies' in pkg ? (pkg.dependencies as Record<string, unknown>) : {})]; | ||||||
|  | 
 | ||||||
|  | export function getBuildConfig(env: ConfigEnv<'build'>): UserConfig { | ||||||
|  |   const { root, mode, command } = env; | ||||||
|  | 
 | ||||||
|  |   return { | ||||||
|  |     root, | ||||||
|  |     mode, | ||||||
|  |     build: { | ||||||
|  |       // Prevent multiple builds from interfering with each other.
 | ||||||
|  |       emptyOutDir: false, | ||||||
|  |       // 🚧 Multiple builds may conflict.
 | ||||||
|  |       outDir: '.vite/build', | ||||||
|  |       watch: command === 'serve' ? {} : null, | ||||||
|  |       minify: command === 'build', | ||||||
|  |     }, | ||||||
|  |     clearScreen: false, | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function getDefineKeys(names: string[]) { | ||||||
|  |   const define: { [name: string]: VitePluginRuntimeKeys } = {}; | ||||||
|  | 
 | ||||||
|  |   return names.reduce((acc, name) => { | ||||||
|  |     const NAME = name.toUpperCase(); | ||||||
|  |     const keys: VitePluginRuntimeKeys = { | ||||||
|  |       VITE_DEV_SERVER_URL: `${NAME}_VITE_DEV_SERVER_URL`, | ||||||
|  |       VITE_NAME: `${NAME}_VITE_NAME`, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     return { ...acc, [name]: keys }; | ||||||
|  |   }, define); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function getBuildDefine(env: ConfigEnv<'build'>) { | ||||||
|  |   const { command, forgeConfig } = env; | ||||||
|  |   const names = forgeConfig.renderer.filter(({ name }) => name != null).map(({ name }) => name!); | ||||||
|  |   const defineKeys = getDefineKeys(names); | ||||||
|  |   const define = Object.entries(defineKeys).reduce((acc, [name, keys]) => { | ||||||
|  |     const { VITE_DEV_SERVER_URL, VITE_NAME } = keys; | ||||||
|  |     const def = { | ||||||
|  |       [VITE_DEV_SERVER_URL]: command === 'serve' ? JSON.stringify(process.env[VITE_DEV_SERVER_URL]) : undefined, | ||||||
|  |       [VITE_NAME]: JSON.stringify(name), | ||||||
|  |     }; | ||||||
|  |     return { ...acc, ...def }; | ||||||
|  |   }, {} as Record<string, any>); | ||||||
|  | 
 | ||||||
|  |   return define; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function pluginExposeRenderer(name: string): Plugin { | ||||||
|  |   const { VITE_DEV_SERVER_URL } = getDefineKeys([name])[name]; | ||||||
|  | 
 | ||||||
|  |   return { | ||||||
|  |     name: '@electron-forge/plugin-vite:expose-renderer', | ||||||
|  |     configureServer(server) { | ||||||
|  |       process.viteDevServers ??= {}; | ||||||
|  |       // Expose server for preload scripts hot reload.
 | ||||||
|  |       process.viteDevServers[name] = server; | ||||||
|  | 
 | ||||||
|  |       server.httpServer?.once('listening', () => { | ||||||
|  |         const addressInfo = server.httpServer!.address() as AddressInfo; | ||||||
|  |         // Expose env constant for main process use.
 | ||||||
|  |         process.env[VITE_DEV_SERVER_URL] = `http://localhost:${addressInfo?.port}`; | ||||||
|  |       }); | ||||||
|  |     }, | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function pluginHotRestart(command: 'reload' | 'restart'): Plugin { | ||||||
|  |   return { | ||||||
|  |     name: '@electron-forge/plugin-vite:hot-restart', | ||||||
|  |     closeBundle() { | ||||||
|  |       if (command === 'reload') { | ||||||
|  |         for (const server of Object.values(process.viteDevServers)) { | ||||||
|  |           // Preload scripts hot reload.
 | ||||||
|  |           server.ws.send({ type: 'full-reload' }); | ||||||
|  |         } | ||||||
|  |       } else { | ||||||
|  |         // Main process hot restart.
 | ||||||
|  |         // https://github.com/electron/forge/blob/v7.2.0/packages/api/core/src/api/start.ts#L216-L223
 | ||||||
|  |         process.stdin.emit('data', 'rs'); | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |   }; | ||||||
|  | } | ||||||
							
								
								
									
										30
									
								
								vite.main.config.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								vite.main.config.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | |||||||
|  | import type { ConfigEnv, UserConfig } from 'vite'; | ||||||
|  | import { defineConfig, mergeConfig } from 'vite'; | ||||||
|  | import { getBuildConfig, getBuildDefine, external, pluginHotRestart } from './vite.base.config'; | ||||||
|  | 
 | ||||||
|  | // https://vitejs.dev/config
 | ||||||
|  | export default defineConfig((env) => { | ||||||
|  |   const forgeEnv = env as ConfigEnv<'build'>; | ||||||
|  |   const { forgeConfigSelf } = forgeEnv; | ||||||
|  |   const define = getBuildDefine(forgeEnv); | ||||||
|  |   const config: UserConfig = { | ||||||
|  |     build: { | ||||||
|  |       lib: { | ||||||
|  |         entry: forgeConfigSelf.entry!, | ||||||
|  |         fileName: () => '[name].js', | ||||||
|  |         formats: ['cjs'], | ||||||
|  |       }, | ||||||
|  |       rollupOptions: { | ||||||
|  |         external, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     plugins: [pluginHotRestart('restart')], | ||||||
|  |     define, | ||||||
|  |     resolve: { | ||||||
|  |       // Load the Node.js entry.
 | ||||||
|  |       mainFields: ['module', 'jsnext:main', 'jsnext'], | ||||||
|  |     }, | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   return mergeConfig(getBuildConfig(forgeEnv), config); | ||||||
|  | }); | ||||||
							
								
								
									
										29
									
								
								vite.preload.config.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								vite.preload.config.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | |||||||
|  | import type { ConfigEnv, UserConfig } from 'vite'; | ||||||
|  | import { defineConfig, mergeConfig } from 'vite'; | ||||||
|  | import { getBuildConfig, external, pluginHotRestart } from './vite.base.config'; | ||||||
|  | 
 | ||||||
|  | // https://vitejs.dev/config
 | ||||||
|  | export default defineConfig((env) => { | ||||||
|  |   const forgeEnv = env as ConfigEnv<'build'>; | ||||||
|  |   const { forgeConfigSelf } = forgeEnv; | ||||||
|  |   const config: UserConfig = { | ||||||
|  |     build: { | ||||||
|  |       rollupOptions: { | ||||||
|  |         external, | ||||||
|  |         // Preload scripts may contain Web assets, so use the `build.rollupOptions.input` instead `build.lib.entry`.
 | ||||||
|  |         input: forgeConfigSelf.entry!, | ||||||
|  |         output: { | ||||||
|  |           format: 'cjs', | ||||||
|  |           // It should not be split chunks.
 | ||||||
|  |           inlineDynamicImports: true, | ||||||
|  |           entryFileNames: '[name].js', | ||||||
|  |           chunkFileNames: '[name].js', | ||||||
|  |           assetFileNames: '[name].[ext]', | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     plugins: [pluginHotRestart('reload')], | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   return mergeConfig(getBuildConfig(forgeEnv), config); | ||||||
|  | }); | ||||||
							
								
								
									
										25
									
								
								vite.renderer.config.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								vite.renderer.config.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,25 @@ | |||||||
|  | import type { ConfigEnv, UserConfig } from "vite"; | ||||||
|  | import { defineConfig } from "vite"; | ||||||
|  | import vue from "@vitejs/plugin-vue"; | ||||||
|  | import { pluginExposeRenderer } from "./vite.base.config"; | ||||||
|  | 
 | ||||||
|  | // https://vitejs.dev/config
 | ||||||
|  | export default defineConfig((env) => { | ||||||
|  |   const forgeEnv = env as ConfigEnv<"renderer">; | ||||||
|  |   const { root, mode, forgeConfigSelf } = forgeEnv; | ||||||
|  |   const name = forgeConfigSelf.name ?? ""; | ||||||
|  | 
 | ||||||
|  |   return { | ||||||
|  |     root, | ||||||
|  |     mode, | ||||||
|  |     base: "./", | ||||||
|  |     build: { | ||||||
|  |       outDir: `.vite/renderer/${name}`, | ||||||
|  |     }, | ||||||
|  |     plugins: [pluginExposeRenderer(name), vue()], | ||||||
|  |     resolve: { | ||||||
|  |       preserveSymlinks: true, | ||||||
|  |     }, | ||||||
|  |     clearScreen: false, | ||||||
|  |   } as UserConfig; | ||||||
|  | }); | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user