refac: styling

This commit is contained in:
Timothy Jaeryang Baek 2025-01-11 21:55:31 -08:00
parent 37cdf72959
commit 37e1b6bd1d
11 changed files with 204 additions and 105 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 MiB

View File

@ -8,6 +8,7 @@ import {
MenuItem,
BrowserWindow,
globalShortcut,
Notification,
ipcMain
} from 'electron';
import path from 'path';
@ -104,17 +105,27 @@ if (!gotTheLock) {
// Wait for the renderer to finish loading
mainWindow.webContents.once('did-finish-load', async () => {
console.log('Renderer finished loading');
});
// Check installation and start the server
if (validateInstallation()) {
if (await validateInstallation()) {
mainWindow.webContents.send('main:data', {
type: 'install:status',
data: true
});
try {
SERVER_URL = await startServer();
mainWindow.loadURL(SERVER_URL);
} catch (error) {
console.error('Failed to start server:', error);
}
} else {
mainWindow.webContents.send('main:data', {
type: 'install:status',
data: false
});
}
});
globalShortcut.register('Alt+CommandOrControl+O', () => {
mainWindow?.show();
@ -134,6 +145,12 @@ if (!gotTheLock) {
click: () => {
loadDefaultView();
}
},
{
label: 'Reset',
click: () => {
removePackage();
}
}
]
});
@ -178,6 +195,10 @@ if (!gotTheLock) {
installPackage();
});
ipcMain.handle('install:status', async (event) => {
return await validateInstallation();
});
ipcMain.handle('remove', async (event) => {
console.log('Resetting package...');
removePackage();
@ -199,6 +220,15 @@ if (!gotTheLock) {
return SERVER_URL;
});
ipcMain.handle('notification', async (event, { title, body }) => {
console.log('Received notification:', title, body);
const notification = new Notification({
title: title,
body: body
});
notification.show();
});
ipcMain.handle('load-webui', async (event, arg) => {
console.log(arg); // prints "ping"
mainWindow.loadURL('http://localhost:8080');

View File

@ -1,4 +1,4 @@
import { ipcRenderer, contextBridge, desktopCapturer } from 'electron';
import { ipcRenderer, contextBridge } from 'electron';
const isLocalSource = () => {
// Check if the execution environment is local
@ -41,6 +41,10 @@ contextBridge.exposeInMainWorld('electronAPI', {
await ipcRenderer.invoke('install');
},
getInstallStatus: async () => {
return await ipcRenderer.invoke('install:status');
},
removePackage: async () => {
if (!isLocalSource()) {
throw new Error('Access restricted: This operation is only allowed in a local environment.');
@ -67,5 +71,9 @@ contextBridge.exposeInMainWorld('electronAPI', {
getServerUrl: async () => {
return await ipcRenderer.invoke('server:url');
},
notification: async (title: string, body: string) => {
await ipcRenderer.invoke('notification', { title, body });
}
});

View File

@ -1,6 +1,6 @@
<script lang="ts">
import { onMount } from 'svelte';
import { serverUrl } from './lib/stores.ts';
import { installStatus } from './lib/stores';
import Main from './lib/components/Main.svelte';
@ -18,10 +18,10 @@
// Perform actions based on the `type` or the `data`
switch (event.data.type) {
case 'electron:server:url':
console.log('Setting server URL:', event.data.data);
// Set the server URL
serverUrl.set(event.data.data);
case 'electron:install:status':
console.log('Install status:', event.data.data);
installStatus.set(event.data.data);
break;
default:
@ -30,9 +30,8 @@
}
});
if (!$serverUrl) {
const url = await window.electronAPI.getServerUrl();
serverUrl.set(url);
if (window.electronAPI) {
installStatus.set(await window.electronAPI.getInstallStatus());
}
});
</script>

View File

@ -16,6 +16,23 @@
font-family: 'InstrumentSerif', sans-serif;
}
.font-system {
font-family:
system-ui,
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
Roboto,
'Helvetica Neue',
Arial,
'Noto Sans',
sans-serif,
'Apple Color Emoji',
'Segoe UI Emoji',
'Segoe UI Symbol',
'Noto Color Emoji';
}
html {
font-family: 'Archivo';
}

View File

@ -1,64 +0,0 @@
<script lang="ts">
import Tooltip from './common/Tooltip.svelte';
import Plus from './icons/Plus.svelte';
let selected = 'home';
</script>
<div class="min-w-18 bg-black flex gap-2.5 flex-col pt-9.5">
<div class="flex justify-center relative">
{#if selected === 'home'}
<div class="absolute top-0 left-0 flex h-full">
<div class="my-auto rounded-r-lg w-1 h-8 bg-white"></div>
</div>
{/if}
<Tooltip content="Home" placement="right">
<button
class=" cursor-pointer bg-gray-850 {selected === 'home'
? 'rounded-2xl'
: 'rounded-full'}"
onclick={() => {
selected = 'home';
}}
>
<img
src="./assets/images/splash.png"
class="size-11 dark:invert p-1"
alt="logo"
draggable="false"
/>
</button>
</Tooltip>
</div>
<!-- <div class="border-t border-gray-900 mx-3"></div> -->
<!-- <div class="flex justify-center relative group">
{#if selected === ""}
<div class="absolute top-0 left-0 flex h-full">
<div class="my-auto rounded-r-lg w-1 h-8 bg-white"></div>
</div>
{/if}
<button
class=" cursor-pointer bg-transparent"
onclick={() => {
selected = "";
}}
>
<img
src="./assets/images/adam.jpg"
class="size-11 {selected === '' ? 'rounded-2xl' : 'rounded-full'}"
alt="logo"
draggable="false"
/>
</button>
</div> -->
<!-- <div class="flex justify-center relative group text-gray-400">
<button class=" cursor-pointer p-2" onclick={() => {}}>
<Plus className="size-5" strokeWidth="2" />
</button>
</div> -->
</div>

View File

@ -1,31 +1,82 @@
<script lang="ts">
import { onMount } from 'svelte';
import { serverUrl } from '../stores.ts';
import { installStatus } from '../stores';
import Spinner from './common/Spinner.svelte';
import ListView from './ListView.svelte';
import ArrowRightCircle from './icons/ArrowRightCircle.svelte';
const continueHandler = async () => {
if (window?.electronAPI) {
window.electronAPI.installPackage();
}
};
</script>
<div class="flex flex-row w-full h-full relative dark:text-gray-100">
{#if $installStatus === null}
<div class="flex flex-row w-full h-full relative dark:text-gray-100">
<div class="absolute top-0 left-0 w-full h-7 bg-transparent draggable"></div>
<ListView />
<div class="flex-1 w-full flex justify-center">
{#if !$serverUrl}
<div class="flex-1 w-full flex justify-center relative">
<div class="m-auto">
<Spinner className="size-5" />
</div>
{:else}
<iframe
src={$serverUrl}
class="w-full h-full"
allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; display-capture; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; navigation-override; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-script; sync-xhr; usb; screen-wake-lock; web-share; unoptimized-images; unsized-media; xr-spatial-tracking"
/>
{/if}
</div>
</div>
</div>
{:else if $installStatus === false}
<div class="flex flex-row w-full h-full relative dark:text-gray-100">
<div class="absolute top-0 left-0 w-full h-7 bg-transparent draggable"></div>
<div class="fixed right-0 m-10 z-50">
<div class="flex space-x-2">
<div class=" self-center">
<img
crossorigin="anonymous"
src="./assets/images/splash.png"
class=" w-6 rounded-full dark:invert"
alt="logo"
/>
</div>
</div>
</div>
<div
class="image w-full h-full absolute top-0 left-0 bg-cover bg-center transition-opacity duration-1000"
style="opacity: 1; background-image: url('./assets/images/green.jpg')"
></div>
<div
class="w-full h-full absolute top-0 left-0 bg-gradient-to-t from-20% from-black to-transparent"
></div>
<div class="w-full h-full absolute top-0 left-0 backdrop-blur-sm bg-black/50"></div>
<div class="flex-1 w-full flex justify-center relative">
<div class="m-auto flex flex-col justify-center text-center max-w-md">
<div class=" font-medium text-5xl text-center mb-4 font-secondary">Open WebUI</div>
<div class=" text-sm text-center mb-3">To install Open WebUI, click Continue.</div>
</div>
<div class="absolute bottom-0 pb-10">
<div class="flex justify-center mt-8">
<div class="flex flex-col justify-center items-center">
<button
class="relative z-20 flex p-1 rounded-full bg-white/5 hover:bg-white/10 transition font-medium text-sm cursor-pointer"
on:click={() => {
continueHandler();
}}
>
<ArrowRightCircle className="size-6" />
</button>
<div class="mt-1.5 font-primary text-base font-medium">
{`Continue`}
</div>
</div>
</div>
</div>
</div>
</div>
{/if}
<style>
.draggable {

View File

@ -0,0 +1,39 @@
<script lang="ts">
import { onMount } from 'svelte';
export let imageUrls = [
'./assets/images/adam.jpg',
'./assets/images/galaxy.jpg',
'./assets/images/earth.jpg',
'./assets/images/space.jpg'
];
export let duration = 5000;
let selectedImageIdx = 0;
onMount(() => {
setInterval(() => {
selectedImageIdx = (selectedImageIdx + 1) % (imageUrls.length - 1);
}, duration);
});
</script>
{#each imageUrls as imageUrl, idx (idx)}
<div
class="image w-full h-full absolute top-0 left-0 bg-cover bg-center transition-opacity duration-1000"
style="opacity: {selectedImageIdx === idx ? 1 : 0}; background-image: url('{imageUrl}')"
></div>
{/each}
<style>
.image {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-size: cover;
background-position: center; /* Center the background images */
transition: opacity 1s ease-in-out; /* Smooth fade effect */
opacity: 0; /* Make images initially not visible */
}
</style>

View File

@ -0,0 +1,19 @@
<script lang="ts">
export let className = 'size-4';
export let strokeWidth = '1.5';
</script>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width={strokeWidth}
stroke="currentColor"
class={className}
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="m12.75 15 3-3m0 0-3-3m3 3h-7.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
/>
</svg>

View File

@ -1,3 +1,3 @@
import { writable } from 'svelte/store';
export const serverUrl = writable(null);
export const installStatus = writable(null);

View File

@ -269,11 +269,12 @@ export async function installPackage(installationPath?: string) {
}
export async function removePackage(installationPath?: string) {
await stopAllServers();
installationPath = installationPath || getBundledPythonInstallationPath();
// remove the python env entirely
if (fs.existsSync(installationPath)) {
fs.rmdirSync(installationPath, { recursive: true });
fs.rmSync(installationPath, { recursive: true });
}
}
@ -293,6 +294,7 @@ export async function validateInstallation(installationPath?: string): Promise<b
if (!fs.existsSync(pythonPath)) {
return false;
}
try {
const checkCommand =
process.platform === 'win32'
@ -315,7 +317,7 @@ const serverPIDs: Set<number> = new Set();
export async function startServer(installationPath?: string, port?: number): Promise<string> {
installationPath = path.normalize(installationPath || getBundledPythonInstallationPath());
if (!validateInstallation(installationPath)) {
if (!(await validateInstallation(installationPath))) {
console.error('Failed to validate installation');
return;
}
@ -373,9 +375,7 @@ export async function startServer(installationPath?: string, port?: number): Pro
serverCrashed = true;
if (!detectedURL) {
reject(
new Error(
`Process exited unexpectedly with code ${code}. No server URL detected.`
)
new Error(`Process exited unexpectedly with code ${code}. No server URL detected.`)
);
}
});