refac
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -92,4 +92,4 @@ typings/ | ||||
| out/ | ||||
| 
 | ||||
| 
 | ||||
| resources/* | ||||
| resources/python.tar.gz | ||||
| @ -10,7 +10,8 @@ import { FuseV1Options, FuseVersion } from "@electron/fuses"; | ||||
| const config: ForgeConfig = { | ||||
|   packagerConfig: { | ||||
|     asar: true, | ||||
|     icon: "src/assets/icon.png", | ||||
|     icon: "public/assets/icon.png", | ||||
|     extraResource: ["public/assets", "resources"], | ||||
|   }, | ||||
|   rebuildConfig: {}, | ||||
|   makers: [ | ||||
|  | ||||
| @ -47,8 +47,5 @@ | ||||
|     "electron-squirrel-startup": "^1.0.1", | ||||
|     "tar": "^7.4.3", | ||||
|     "update-electron-app": "^3.1.0" | ||||
|   }, | ||||
|   "extraResources": [ | ||||
|     "resources/py311.tar.gz" | ||||
|   ] | ||||
|   } | ||||
| } | ||||
|  | ||||
							
								
								
									
										
											BIN
										
									
								
								public/assets/fonts/Archivo-Variable.ttf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								public/assets/fonts/InstrumentSerif-Regular.ttf
									
									
									
									
									
										Normal file
									
								
							
							
						
						| Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB | 
| Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/assets/images/adam.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.3 MiB | 
							
								
								
									
										
											BIN
										
									
								
								public/assets/images/earth.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 782 KiB | 
| Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/assets/images/galaxy.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 2.8 MiB | 
							
								
								
									
										
											BIN
										
									
								
								public/assets/images/space.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 432 KiB | 
| Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB | 
| Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/assets/tray.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 5.3 KiB | 
							
								
								
									
										173
									
								
								src/main.ts
									
									
									
									
									
								
							
							
						
						| @ -1,57 +1,134 @@ | ||||
| import { app, BrowserWindow } from "electron"; | ||||
| import { | ||||
|   app, | ||||
|   protocol, | ||||
|   nativeImage, | ||||
|   Tray, | ||||
|   Menu, | ||||
|   BrowserWindow, | ||||
|   ipcMain, | ||||
| } from "electron"; | ||||
| import path from "path"; | ||||
| import started from "electron-squirrel-startup"; | ||||
| 
 | ||||
| // Handle creating/removing shortcuts on Windows when installing/uninstalling.
 | ||||
| if (started) { | ||||
|   app.quit(); | ||||
| } | ||||
| 
 | ||||
| const createWindow = () => { | ||||
|   // Create the browser window.
 | ||||
|   const mainWindow = new BrowserWindow({ | ||||
|     width: 800, | ||||
|     height: 600, | ||||
|     icon: "src/assets/icon.png", | ||||
|     webPreferences: { | ||||
|       preload: path.join(__dirname, "preload.js"), | ||||
|     }, | ||||
| // Restrict app to a single instance
 | ||||
| const gotTheLock = app.requestSingleInstanceLock(); | ||||
| if (!gotTheLock) { | ||||
|   app.quit(); // Quit if another instance is already running
 | ||||
| } else { | ||||
|   // Handle second-instance logic
 | ||||
|   app.on("second-instance", (event, argv, workingDirectory) => { | ||||
|     // This event happens if a second instance is launched
 | ||||
|     if (mainWindow) { | ||||
|       if (mainWindow.isMinimized()) mainWindow.restore(); // Restore if minimized
 | ||||
|       mainWindow.show(); // Show existing window
 | ||||
|       mainWindow.focus(); // Focus the existing window
 | ||||
|     } | ||||
|   }); | ||||
| 
 | ||||
|   // 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(); | ||||
| }; | ||||
| 
 | ||||
| // 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.on("ready", 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") { | ||||
|   // Handle creating/removing shortcuts on Windows during installation/uninstallation
 | ||||
|   if (started) { | ||||
|     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(); | ||||
|   } | ||||
| }); | ||||
|   app.setAboutPanelOptions({ | ||||
|     applicationName: "Open WebUI", | ||||
|     iconPath: path.join(__dirname, "assets/icon.png"), | ||||
|     applicationVersion: app.getVersion(), | ||||
|     version: app.getVersion(), | ||||
|     website: "https://openwebui.com", | ||||
|     copyright: `© ${new Date().getFullYear()} Open WebUI (Timothy Jaeryang Baek)`, | ||||
|   }); | ||||
| 
 | ||||
| // 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.
 | ||||
|   // Main application logic
 | ||||
|   let mainWindow: BrowserWindow | null = null; | ||||
|   let tray: Tray | null = null; | ||||
| 
 | ||||
|   const onReady = () => { | ||||
|     console.log(process.resourcesPath); | ||||
|     mainWindow = new BrowserWindow({ | ||||
|       width: 800, | ||||
|       height: 600, | ||||
|       icon: path.join(__dirname, "assets/icon.png"), | ||||
|       webPreferences: { | ||||
|         preload: path.join(__dirname, "preload.js"), | ||||
|       }, | ||||
|     }); | ||||
| 
 | ||||
|     mainWindow.setIcon(path.join(__dirname, "assets/icon.png")); | ||||
| 
 | ||||
|     // Load index.html or dev server URL
 | ||||
|     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`) | ||||
|       ); | ||||
|     } | ||||
| 
 | ||||
|     // Create a system tray icon
 | ||||
|     const image = nativeImage.createFromPath( | ||||
|       path.join(__dirname, "assets/tray.png") | ||||
|     ); | ||||
|     tray = new Tray(image.resize({ width: 16, height: 16 })); | ||||
| 
 | ||||
|     const trayMenu = Menu.buildFromTemplate([ | ||||
|       { | ||||
|         label: "Show Application", | ||||
|         click: () => { | ||||
|           mainWindow.show(); // Show the main window when clicked
 | ||||
|         }, | ||||
|       }, | ||||
|       { | ||||
|         label: "Quit", | ||||
|         click: () => { | ||||
|           app.isQuiting = true; // Mark as quitting
 | ||||
|           app.quit(); // Quit the application
 | ||||
|         }, | ||||
|       }, | ||||
|     ]); | ||||
| 
 | ||||
|     tray.setToolTip("Open WebUI"); | ||||
|     tray.setContextMenu(trayMenu); | ||||
| 
 | ||||
|     // Handle the close event
 | ||||
|     mainWindow.on("close", (event) => { | ||||
|       if (!app.isQuiting) { | ||||
|         event.preventDefault(); // Prevent the default close behavior
 | ||||
|         mainWindow.hide(); // Hide the window instead of closing it
 | ||||
|       } | ||||
|     }); | ||||
|   }; | ||||
| 
 | ||||
|   ipcMain.on("load-webui", (event, arg) => { | ||||
|     console.log(arg); // prints "ping"
 | ||||
|     mainWindow.loadURL("http://localhost:8080"); | ||||
| 
 | ||||
|     mainWindow.webContents.once("did-finish-load", () => { | ||||
|       mainWindow.webContents.send("main:data", { | ||||
|         type: "ping", // This is the same type you're listening for in the renderer
 | ||||
|       }); | ||||
|     }); | ||||
| 
 | ||||
|     ipcMain.on("send-ping", (event) => { | ||||
|       console.log("Received PING from renderer process"); | ||||
|       mainWindow.webContents.send("ping-reply", "PONG from Main Process!"); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   // Quit when all windows are closed, except on macOS
 | ||||
|   app.on("window-all-closed", () => { | ||||
|     if (process.platform !== "darwin") { | ||||
|       app.quit(); | ||||
|     } | ||||
|   }); | ||||
| 
 | ||||
|   app.on("activate", () => { | ||||
|     if (BrowserWindow.getAllWindows().length === 0) { | ||||
|       onReady(); | ||||
|     } else { | ||||
|       mainWindow?.show(); | ||||
|     } | ||||
|   }); | ||||
| 
 | ||||
|   app.on("ready", onReady); | ||||
| } | ||||
|  | ||||
| @ -1,2 +1,26 @@ | ||||
| // See the Electron documentation for details on how to use preload scripts:
 | ||||
| // https://www.electronjs.org/docs/latest/tutorial/process-model#preload-scripts
 | ||||
| import { ipcRenderer, contextBridge } from "electron"; | ||||
| 
 | ||||
| window.addEventListener("DOMContentLoaded", () => { | ||||
|   // Listen for messages from the main process
 | ||||
|   ipcRenderer.on("main:data", (event, data) => { | ||||
|     // Forward the message to the renderer using window.postMessage
 | ||||
|     window.postMessage( | ||||
|       { | ||||
|         type: `electron:${data.type}`, | ||||
|         data: data, | ||||
|       }, | ||||
|       window.location.origin | ||||
|     ); | ||||
|   }); | ||||
| }); | ||||
| 
 | ||||
| contextBridge.exposeInMainWorld("electronAPI", { | ||||
|   sendPing: () => { | ||||
|     console.log("Sending PING to main process..."); | ||||
|     ipcRenderer.send("send-ping"); // Send the ping back to the main process
 | ||||
|   }, | ||||
| 
 | ||||
|   loadWebUI: (arg) => { | ||||
|     ipcRenderer.send("load-webui", arg); | ||||
|   }, | ||||
| }); | ||||
|  | ||||
| @ -1,12 +1,9 @@ | ||||
| <script lang="ts"> | ||||
|   import { onMount } from "svelte"; | ||||
| 
 | ||||
|   import Onboarding from "./lib/components/Onboarding.svelte"; | ||||
|   onMount(() => { | ||||
|     console.log("Mounted"); | ||||
|   }); | ||||
|   import Main from "./lib/components/Main.svelte"; | ||||
|   onMount(() => {}); | ||||
| </script> | ||||
| 
 | ||||
| <main class="w-screen h-screen bg-gray-900"> | ||||
|   <Onboarding /> | ||||
|   <Main /> | ||||
| </main> | ||||
|  | ||||
| @ -1,8 +1,29 @@ | ||||
| @import "tailwindcss"; | ||||
| 
 | ||||
| @font-face { | ||||
|   font-family: "Archivo"; | ||||
|   src: url("/assets/fonts/Archivo-Variable.ttf"); | ||||
|   font-display: swap; | ||||
| } | ||||
| 
 | ||||
| @font-face { | ||||
|   font-family: "InstrumentSerif"; | ||||
|   src: url("/assets/fonts/InstrumentSerif-Regular.ttf"); | ||||
|   font-display: swap; | ||||
| } | ||||
| 
 | ||||
| .font-secondary { | ||||
|   font-family: "InstrumentSerif", sans-serif; | ||||
| } | ||||
| 
 | ||||
| html { | ||||
|   font-family: "Archivo"; | ||||
| } | ||||
| 
 | ||||
| @theme { | ||||
|   --color-*: initial; | ||||
| 
 | ||||
|   --color-white: #fff; | ||||
|   --color-gray-50: #f9f9f9; | ||||
|   --color-gray-100: #ececec; | ||||
|   --color-gray-200: #e3e3e3; | ||||
|  | ||||
| @ -1,25 +1,27 @@ | ||||
| <script lang="ts"> | ||||
|   import { onMount } from "svelte"; | ||||
| 
 | ||||
|   import ServerList from "./main/ServerList.svelte"; | ||||
|   import ServerView from "./main/ServerView.svelte"; | ||||
| 
 | ||||
|   onMount(() => { | ||||
|     console.log("Mounted"); | ||||
|   }); | ||||
| </script> | ||||
| 
 | ||||
| <div class="flex flex-row w-full h-full"> | ||||
|   <div class=""> | ||||
|     <ServerList /> | ||||
|   </div> | ||||
| 
 | ||||
|   <div class="my-1.5 mr-1.5 flex-1"> | ||||
|     <div class="w-full h-full rounded-lg border border-gray-850"> | ||||
|       <ServerView | ||||
|         className="rounded-lg w-full h-full" | ||||
|         url="http://localhost:8080" | ||||
|   <div class="m-auto"> | ||||
|     <!-- <div class=" flex justify-center mb-3"> | ||||
|       <img | ||||
|         src="./assets/images/splash.png" | ||||
|         class="size-16 dark:invert" | ||||
|         alt="hero" | ||||
|       /> | ||||
|     </div> | ||||
|     </div> --> | ||||
| 
 | ||||
|     <!-- <div class=" text-2xl text-gray-50 font-secondary">Install Open WebUI</div> --> | ||||
| 
 | ||||
|     <button | ||||
|       class=" text-gray-100 hover:text-white transition font-medium cursor-pointer" | ||||
|       onclick={() => { | ||||
|         console.log("clicked"); | ||||
|         if (window?.electronAPI) { | ||||
|           window.electronAPI.loadWebUI(); | ||||
|         } | ||||
|       }}>Install Open WebUI</button | ||||
|     > | ||||
|   </div> | ||||
| </div> | ||||
|  | ||||
| @ -1,5 +0,0 @@ | ||||
| <script lang="ts"> | ||||
|   import Main from "./Main.svelte"; | ||||
| </script> | ||||
| 
 | ||||
| <Main /> | ||||
| @ -1,32 +0,0 @@ | ||||
| <script lang="ts"> | ||||
|   import Plus from "../icons/Plus.svelte"; | ||||
|   import FaviconImage from "../../assets/images/favicon.png"; | ||||
| </script> | ||||
| 
 | ||||
| <div | ||||
|   class="flex flex-col gap-2.5 w-16 h-full py-3.5 px-2.5 max-h-screen overflow-y-auto bg-gray-900" | ||||
| > | ||||
|   <div class=" w-full flex justify-center"> | ||||
|     <button | ||||
|       class="size-11 flex justify-center self-center bg-gray-800 text-gray-100 rounded-full cursor-pointer" | ||||
|       onclick={() => { | ||||
|         console.log("Clicked"); | ||||
|       }} | ||||
|     > | ||||
|       <img src={FaviconImage} class="w-full h-full" /> | ||||
|     </button> | ||||
|   </div> | ||||
| 
 | ||||
|   <div class=" w-full flex justify-center"> | ||||
|     <button | ||||
|       class="size-11 flex justify-center self-center bg-gray-800 text-gray-100 rounded-full cursor-pointer" | ||||
|       onclick={() => { | ||||
|         console.log("Clicked"); | ||||
|       }} | ||||
|     > | ||||
|       <div class="m-auto"> | ||||
|         <Plus className=" size-5" strokeWidth="2.5" /> | ||||
|       </div> | ||||
|     </button> | ||||
|   </div> | ||||
| </div> | ||||
| @ -1,6 +0,0 @@ | ||||
| <script lang="ts"> | ||||
|   let { className, url } = $props(); | ||||
| </script> | ||||
| 
 | ||||
| <!-- iframe --> | ||||
| <iframe class={className} src={url} frameborder="0"></iframe> | ||||