🚀 Initial sveltekit app
This commit is contained in:
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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'
|
||||
};
|
||||
};
|
||||
@@ -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>
|
||||
@@ -0,0 +1,5 @@
|
||||
<script>
|
||||
import Model from "$lib/components/Views/Model.svelte";
|
||||
</script>
|
||||
|
||||
<Model />
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -0,0 +1 @@
|
||||
<div>LOGS</div>
|
||||
@@ -0,0 +1 @@
|
||||
<div>metrics</div>
|
||||
Reference in New Issue
Block a user