🚀 Initial sveltekit app

This commit is contained in:
Rune Harlyk
2024-03-28 01:04:52 +01:00
committed by Rune Harlyk
parent 23806e366b
commit 0acbb4c83a
85 changed files with 6096 additions and 0 deletions
+15
View File
@@ -0,0 +1,15 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { onMount } from 'svelte';
onMount(() => {
setTimeout(() => {
goto('/');
}, 3000);
});
</script>
<div class="flex justify-center items-center w-full h-full">
<h1 class="text-4xl">404 - Page not found</h1>
<p>You will be redirected to the home page in 3 seconds</p>
</div>
+34
View File
@@ -0,0 +1,34 @@
<script lang="ts">
import { page } from '$app/stores';
import TopBar from '$lib/components/TopBar.svelte';
import Menu from './menu.svelte';
import '../app.css';
let menuOpen = false;
</script>
<TopBar />
<svelte:head>
<title>{$page.data.title}</title>
</svelte:head>
<div class="drawer lg:drawer-open">
<input id="main-menu" type="checkbox" class="drawer-toggle" bind:checked={menuOpen} />
<div class="drawer-content flex flex-col">
<!-- Status bar content here -->
<!-- <Statusbar /> -->
<!-- Main page content here -->
<slot />
</div>
<!-- Side Navigation -->
<div class="drawer-side z-30 shadow-lg">
<label for="main-menu" class="drawer-overlay" />
<Menu
on:menuClicked={() => {
menuOpen = false;
}}
/>
</div>
</div>
+50
View File
@@ -0,0 +1,50 @@
import { jointNames, model } from '$lib/stores';
import { loadModelAsync } from '$lib/utilities/model-utilities';
import type { Result } from '$lib/utilities/result';
export const prerender = true;
export const ssr = false;
const registerFetchIntercept = async () => {
if (typeof WebSocket === 'undefined') return;
const { fetch: originalFetch } = window;
const fileService = (await import('$lib/services/file-service')).default;
window.fetch = async (...args) => {
const [resource, config] = args;
let file: Result<Uint8Array | undefined, string>;
file = await fileService.getFile(resource.toString());
return file.isOk() ? new Response(file.inner) : originalFetch(resource, config);
};
};
const setup = async () => {
if (typeof WebSocket === 'undefined') return;
const outControllerData = (await import('$lib/stores/model-store')).outControllerData;
const mode = (await import('$lib/stores/model-store')).mode;
const socketLocation = (await import('$lib/utilities/location-utilities')).socketLocation;
const socketService = (await import('$lib/services/socket-service')).default;
socketService.connect(socketLocation);
socketService.addPublisher(outControllerData);
socketService.addPublisher(mode, 'mode');
await registerFetchIntercept();
const modelRes = await loadModelAsync('/spot_micro.urdf.xacro');
if (modelRes.isOk()) {
const [urdf, JOINT_NAME] = modelRes.inner;
jointNames.set(JOINT_NAME);
model.set(urdf);
} else {
console.error(modelRes.inner, { exception: modelRes.exception });
}
};
export const load = async () => {
await setup();
// const result = await fetch('/rest/features');
const item = {}; //await result.json();
return {
features: item,
title: 'spot-micro-controller',
github: 'runeharlyk/spotmicro'
};
};
+2
View File
@@ -0,0 +1,2 @@
<h1>Welcome to SvelteKit</h1>
<p>Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to read the documentation</p>
@@ -0,0 +1,8 @@
<script lang="ts">
import Controls from '$lib/components/Controls.svelte';
</script>
<div class="flex justify-center items-center w-full h-full">
<slot/>
<Controls />
</div>
+5
View File
@@ -0,0 +1,5 @@
<script>
import Model from "$lib/components/Views/Model.svelte";
</script>
<Model />
+3
View File
@@ -0,0 +1,3 @@
export const load = async () => {
return { title: 'Spot Micro' };
};
@@ -0,0 +1,19 @@
<script lang="ts">
import { onDestroy } from 'svelte';
import { location } from '$lib/utilities';
let videoStream = `//${location}/api/stream`;
onDestroy(() => {
videoStream = '#';
});
</script>
<div class="w-full h-full">
<img
src={videoStream}
class="absolute object-cover blur-3xl w-full h-full -z-10"
alt="Live stream is down"
/>
<img src={videoStream} class="object-contain w-full h-full" alt="Live stream is down" />
</div>
+266
View File
@@ -0,0 +1,266 @@
<script lang="ts">
// import logo from '$lib/assets/logo.png';
import Github from '~icons/tabler/brand-github';
import Discord from '~icons/tabler/brand-discord';
import Users from '~icons/tabler/users';
import Settings from '~icons/tabler/settings';
import Health from '~icons/tabler/stethoscope';
import Update from '~icons/tabler/refresh-alert';
import WiFi from '~icons/tabler/wifi';
import Router from '~icons/tabler/router';
import AP from '~icons/tabler/access-point';
import Remote from '~icons/tabler/network';
import Control from '~icons/tabler/adjustments';
import Avatar from '~icons/tabler/user-circle';
import Logout from '~icons/tabler/logout';
import Copyright from '~icons/tabler/copyright';
import MQTT from '~icons/tabler/topology-star-3';
import NTP from '~icons/tabler/clock-check';
import Metrics from '~icons/tabler/report-analytics';
import { page } from '$app/stores';
import { onMount } from 'svelte';
// import { user } from '$lib/stores/user';
import { createEventDispatcher } from 'svelte';
const appName = 'ESP32 SvelteKit';
const copyright = '2023 theelims';
const github = { href: 'https://github.com/' + $page.data.github, active: true };
const discord = { href: '.', active: false };
type menuItem = {
title: string;
icon: object;
href?: string;
feature: boolean;
active?: boolean;
submenu?: subMenuItem[];
};
type subMenuItem = {
title: string;
icon: object;
href: string;
feature: boolean;
active: boolean;
};
let menuItems = [
{
title: 'Demo App',
icon: Control,
href: '/demo',
feature: true,
active: false
},
{
title: 'Connections',
icon: Remote,
feature: $page.data.features.mqtt || $page.data.features.ntp,
submenu: [
{
title: 'MQTT',
icon: MQTT,
href: '/connections/mqtt',
feature: $page.data.features.mqtt,
active: false
},
{
title: 'NTP',
icon: NTP,
href: '/connections/ntp',
feature: $page.data.features.ntp,
active: false
}
]
},
{
title: 'WiFi',
icon: WiFi,
feature: true,
submenu: [
{
title: 'WiFi Station',
icon: Router,
href: '/wifi/sta',
feature: true,
active: false
},
{
title: 'Access Point',
icon: AP,
href: '/wifi/ap',
feature: true,
active: false
}
]
},
{
title: 'Users',
icon: Users,
href: '/user',
feature: $page.data.features.security, //&& $user.admin,
active: false
},
{
title: 'System',
icon: Settings,
feature: true,
submenu: [
{
title: 'System Status',
icon: Health,
href: '/system/status',
feature: true,
active: false
},
{
title: 'System Metrics',
icon: Metrics,
href: '/system/metrics',
feature: $page.data.features.analytics,
active: false
},
{
title: 'Firmware Update',
icon: Update,
href: '/system/update',
feature:
($page.data.features.ota ||
$page.data.features.upload_firmware ||
$page.data.features.download_firmware) &&
(!$page.data.features.security),// || $user.admin),
active: false
}
]
}
];
const dispatch = createEventDispatcher();
function setActiveMenuItem(menuItems: menuItem[], targetTitle: string) {
for (let i = 0; i < menuItems.length; i++) {
const menuItem = menuItems[i];
// Clear any previous set active flags
menuItem.active = false;
// Check if the current menu item's title matches the target title
if (menuItem.title === targetTitle) {
menuItem.active = true; // Set the active property to true
dispatch('menuClicked');
}
// Check if the current menu item has a submenu
if (menuItem.submenu && menuItem.submenu.length > 0) {
// Recursively call the function for each submenu item
setActiveMenuItem(menuItem.submenu, targetTitle);
}
}
if (targetTitle == '') {
dispatch('menuClicked');
}
menuItems = menuItems;
}
onMount(() => {
setActiveMenuItem(menuItems, $page.data.title);
menuItems = menuItems;
});
</script>
<div class="bg-base-200 text-base-content flex h-full w-80 flex-col p-4">
<!-- Sidebar content here -->
<a
href="/"
class="rounded-box mb-4 flex items-center hover:scale-[1.02] active:scale-[0.98]"
on:click={() => setActiveMenuItem(menuItems, '')}
>
<!-- <img src={logo} alt="Logo" class="h-12 w-12" /> -->
<h1 class="px-4 text-2xl font-bold">{appName}</h1>
</a>
<ul class="menu rounded-box menu-vertical flex-nowrap overflow-y-auto">
{#each menuItems as menuItem, i (menuItem.title)}
{#if menuItem.feature}
{#if menuItem.submenu}
<li>
<details>
<summary class="text-lg font-bold"
><svelte:component this={menuItem.icon} class="h-6 w-6" />{menuItem.title}</summary
>
<ul>
{#each menuItem.submenu as subMenuItem}
{#if subMenuItem.feature}
<li class="hover-bordered">
<a
href={subMenuItem.href}
class="text-ml font-bold {subMenuItem.active ? 'bg-base-100' : ''}"
on:click={() => {
setActiveMenuItem(menuItems, subMenuItem.title);
menuItems = menuItems;
}}
><svelte:component
this={subMenuItem.icon}
class="h-5 w-5"
/>{subMenuItem.title}</a
>
</li>
{/if}
{/each}
</ul>
</details>
</li>
{:else}
<li class="hover-bordered">
<a
href={menuItem.href}
class="text-lg font-bold {menuItem.active ? 'bg-base-100' : ''}"
on:click={() => {
setActiveMenuItem(menuItems, menuItem.title);
menuItems = menuItems;
}}><svelte:component this={menuItem.icon} class="h-6 w-6" />{menuItem.title}</a
>
</li>
{/if}
{/if}
{/each}
</ul>
<div class="flex-col" />
<div class="flex-grow" />
{#if $page.data.features.security}
<div class="flex items-center">
<Avatar class="h-8 w-8" />
<span class="flex-grow px-4 text-xl font-bold">$user.username</span>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
class="btn btn-ghost"
on:click={() => {
// user.invalidate();
}}
>
<Logout class="h-8 w-8 rotate-180" />
</div>
</div>
{/if}
<div class="divider my-0" />
<div class="flex items-center">
{#if github.active}
<a href={github.href} class="btn btn-ghost" target="_blank" rel="noopener noreferrer"
><Github class="h-5 w-5" /></a
>
{/if}
{#if discord.active}
<a href={discord.href} class="btn btn-ghost" target="_blank" rel="noopener noreferrer"
><Discord class="h-5 w-5" /></a
>
{/if}
<div class="inline-flex flex-grow items-center justify-end text-sm">
<Copyright class="h-4 w-4" /><span class="px-2">{copyright}</span>
</div>
</div>
</div>
+56
View File
@@ -0,0 +1,56 @@
<!-- <script lang="ts">
import Info from '$lib/components/settings/Info.svelte';
import Log from '$lib/components/settings/Log.svelte';
import Configuration from '$lib/components/settings/Configuration.svelte';
import Calibration from '$lib/components/settings/Calibration.svelte';
export const page = '';
const menu = [
{
title: 'Calibration',
path: '/calibration',
// icon: AdjustmentsVertical,
component: Calibration
},
{
title: 'System info',
path: '/info',
// icon: InformationCircle,
component: Info
},
{
title: 'Log',
path: '/log',
// icon: BookOpen,
component: Log
},
{
title: 'Settings',
path: '/settings',
// icon: Cog6Tooth,
component: Configuration
}
];
</script> -->
<h1 class="text-2xl font-bold">Settings</h1>
<div class="pt-14 flex h-full">
<nav class="w-1/6 flex flex-col">
<!-- {#each menu as link} -->
<!-- <Link to={'/settings' + link.path}> -->
<div class="px-4 py-2 flex gap-2 items-center">
<!-- <Icon src={link.icon} size="24" />{link.title} -->
</div>
<!-- </Link> -->
<!-- {/each} -->
</nav>
<main class="w-full h-full">
<!-- <Router> -->
<!-- {#each menu as link} -->
<!-- <Route path={link.path} component={link.component}></Route> -->
<!-- {/each} -->
<!-- </Router> -->
</main>
</div>
View File
+1
View File
@@ -0,0 +1 @@
<div>LOGS</div>
@@ -0,0 +1 @@
<div>metrics</div>