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>
|
||||