fix: git lfs is back.

This commit is contained in:
Youwen Wu 2024-02-24 01:38:00 -08:00
parent 78f9eb0cbf
commit 23163b947d
87 changed files with 322 additions and 61 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

BIN
app/.DS_Store vendored Normal file

Binary file not shown.

BIN
app/static/.DS_Store vendored Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
app/static/sfx/.DS_Store vendored Normal file

Binary file not shown.

BIN
app/static/voices/.DS_Store vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
app/static/voices/rus/.DS_Store vendored Normal file

Binary file not shown.

View file

@ -9,14 +9,17 @@
"version": "0.0.0",
"dependencies": {
"@fontsource/roboto": "^5.0.8",
"howler": "^2.2.4",
"material-icons": "^1.13.12",
"material-symbols": "^0.15.0",
"socket.io-client": "^4.7.4"
"socket.io-client": "^4.7.4",
"svelte-french-toast": "^1.2.0"
},
"devDependencies": {
"@sveltejs/adapter-static": "^3.0.1",
"@sveltejs/vite-plugin-svelte": "^3.0.2",
"@tsconfig/svelte": "^5.0.2",
"@types/howler": "^2.2.11",
"autoprefixer": "^10.4.17",
"postcss": "^8.4.35",
"svelte": "^4.2.11",
@ -43,7 +46,6 @@
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz",
"integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==",
"dev": true,
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.0",
"@jridgewell/trace-mapping": "^0.3.9"
@ -446,7 +448,6 @@
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
"integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
"dev": true,
"dependencies": {
"@jridgewell/set-array": "^1.0.1",
"@jridgewell/sourcemap-codec": "^1.4.10",
@ -460,7 +461,6 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
"dev": true,
"engines": {
"node": ">=6.0.0"
}
@ -469,7 +469,6 @@
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
"dev": true,
"engines": {
"node": ">=6.0.0"
}
@ -477,14 +476,12 @@
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.4.15",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
"dev": true
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.22",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz",
"integrity": "sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==",
"dev": true,
"dependencies": {
"@jridgewell/resolve-uri": "^3.1.0",
"@jridgewell/sourcemap-codec": "^1.4.14"
@ -813,7 +810,12 @@
"node_modules/@types/estree": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw=="
},
"node_modules/@types/howler": {
"version": "2.2.11",
"resolved": "https://registry.npmjs.org/@types/howler/-/howler-2.2.11.tgz",
"integrity": "sha512-7aBoUL6RbSIrqKnpEgfa1wSNUBK06mn08siP2QI0zYk7MXfEJAaORc4tohamQYqCqVESoDyRWSdQn2BOKWj2Qw==",
"dev": true
},
"node_modules/@types/pug": {
@ -826,7 +828,6 @@
"version": "8.11.3",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
"dev": true,
"bin": {
"acorn": "bin/acorn"
},
@ -887,7 +888,6 @@
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
"integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
"dev": true,
"dependencies": {
"dequal": "^2.0.3"
}
@ -933,7 +933,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.0.0.tgz",
"integrity": "sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw==",
"dev": true,
"dependencies": {
"dequal": "^2.0.3"
}
@ -1082,7 +1081,6 @@
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz",
"integrity": "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==",
"dev": true,
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.15",
"@types/estree": "^1.0.1",
@ -1152,7 +1150,6 @@
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
"integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
"dev": true,
"dependencies": {
"mdn-data": "2.0.30",
"source-map-js": "^1.0.1"
@ -1202,7 +1199,6 @@
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
"integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
"dev": true,
"engines": {
"node": ">=6"
}
@ -1337,7 +1333,6 @@
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
"dev": true,
"dependencies": {
"@types/estree": "^1.0.0"
}
@ -1501,6 +1496,11 @@
"node": ">= 0.4"
}
},
"node_modules/howler": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/howler/-/howler-2.2.4.tgz",
"integrity": "sha512-iARIBPgcQrwtEr+tALF+rapJ8qSc+Set2GJQl7xT1MQzWaVkFebdJhR3alVlSiUf5U7nAANKuj3aWpwerocD5w=="
},
"node_modules/import-fresh": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
@ -1611,7 +1611,6 @@
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz",
"integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==",
"dev": true,
"dependencies": {
"@types/estree": "*"
}
@ -1676,8 +1675,7 @@
"node_modules/locate-character": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz",
"integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==",
"dev": true
"integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="
},
"node_modules/lru-cache": {
"version": "10.2.0",
@ -1692,7 +1690,6 @@
"version": "0.30.7",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.7.tgz",
"integrity": "sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==",
"dev": true,
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.15"
},
@ -1713,8 +1710,7 @@
"node_modules/mdn-data": {
"version": "2.0.30",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
"integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
"dev": true
"integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA=="
},
"node_modules/merge2": {
"version": "1.4.1",
@ -1949,7 +1945,6 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz",
"integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==",
"dev": true,
"dependencies": {
"@types/estree": "^1.0.0",
"estree-walker": "^3.0.0",
@ -2409,7 +2404,6 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
@ -2606,7 +2600,6 @@
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.11.tgz",
"integrity": "sha512-YIQk3J4X89wOLhjsqIW8tqY3JHPuBdtdOIkASP2PZeAMcSW9RsIjQzMesCrxOF3gdWYC0mKknlKF7OqmLM+Zqg==",
"dev": true,
"dependencies": {
"@ampproject/remapping": "^2.2.1",
"@jridgewell/sourcemap-codec": "^1.4.15",
@ -2649,6 +2642,17 @@
"svelte": "^3.55.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0"
}
},
"node_modules/svelte-french-toast": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/svelte-french-toast/-/svelte-french-toast-1.2.0.tgz",
"integrity": "sha512-5PW+6RFX3xQPbR44CngYAP1Sd9oCq9P2FOox4FZffzJuZI2mHOB7q5gJBVnOiLF5y3moVGZ7u2bYt7+yPAgcEQ==",
"dependencies": {
"svelte-writable-derived": "^3.1.0"
},
"peerDependencies": {
"svelte": "^3.57.0 || ^4.0.0"
}
},
"node_modules/svelte-hmr": {
"version": "0.15.3",
"resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.15.3.tgz",
@ -2724,6 +2728,17 @@
}
}
},
"node_modules/svelte-writable-derived": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/svelte-writable-derived/-/svelte-writable-derived-3.1.0.tgz",
"integrity": "sha512-cTvaVFNIJ036vSDIyPxJYivKC7ZLtcFOPm1Iq6qWBDo1fOHzfk6ZSbwaKrxhjgy52Rbl5IHzRcWgos6Zqn9/rg==",
"funding": {
"url": "https://ko-fi.com/pixievoltno1"
},
"peerDependencies": {
"svelte": "^3.2.1 || ^4.0.0-next.1"
}
},
"node_modules/tailwindcss": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz",

View file

@ -13,6 +13,7 @@
"@sveltejs/adapter-static": "^3.0.1",
"@sveltejs/vite-plugin-svelte": "^3.0.2",
"@tsconfig/svelte": "^5.0.2",
"@types/howler": "^2.2.11",
"autoprefixer": "^10.4.17",
"postcss": "^8.4.35",
"svelte": "^4.2.11",
@ -24,8 +25,10 @@
},
"dependencies": {
"@fontsource/roboto": "^5.0.8",
"howler": "^2.2.4",
"material-icons": "^1.13.12",
"material-symbols": "^0.15.0",
"socket.io-client": "^4.7.4"
"socket.io-client": "^4.7.4",
"svelte-french-toast": "^1.2.0"
}
}

View file

@ -7,6 +7,8 @@
import { appList } from './lib/Apps/appList'
import { initializeTelemetry } from './lib/utils/initializeTelemetry'
import { onMount } from 'svelte'
import { Toaster } from 'svelte-french-toast'
import { initializationSequence } from './lib/Sequences/sequences'
let activeApp: App = 'camera'
let topics: TelemetryTopics = {
@ -27,6 +29,7 @@
onMount(() => {
initializeTelemetry(topics, 200)
initializationSequence()
})
</script>
@ -45,6 +48,9 @@
<AppBar bind:activeApp {appList} />
</div>
</div>
<!-- toast service -->
<Toaster />
</main>
<style lang="postcss">

View file

@ -71,3 +71,16 @@ interface TelemetryTopics {
strings: string[]
booleans: string[]
}
type ToastType = 'info' | 'success' | 'error'
type ToastData = {
message: string
type: ToastType
timeout?: number
}
declare module '@zerodevx/svelte-toast' {
const value: any
export = value
}

View file

@ -0,0 +1,28 @@
<!--
@component
A utility component that wraps your app in a container and adds transitions
@param useContainer - Whether or not to use the default app container. Defaults to true.
-->
<script lang="ts">
import { fade } from 'svelte/transition'
export let useContainer: boolean = true
</script>
<div
in:fade={{ duration: 150, delay: 150 }}
out:fade={{ duration: 150 }}
class:app-container={useContainer}
{...$$restProps}
>
<slot />
</div>
<style lang="postcss">
.app-container {
@apply pb-20;
}
</style>

View file

@ -1,12 +1,10 @@
<script lang="ts">
import AppContainer from '../AppContainer.svelte'
import CameraContainer from './CameraContainer.svelte'
import { fade } from 'svelte/transition'
</script>
<div
in:fade={{ duration: 150, delay: 150 }}
out:fade={{ duration: 150 }}
class="flex gap-4 w-full py-40 px-10 backdrop-blur-lg justify-center h-full rounded-3xl shadow-md bg-slate-300 bg-opacity-30"
<AppContainer
class="px-10 py-20 flex gap-4 w-full backdrop-blur-lg justify-center h-full rounded-3xl shadow-md bg-slate-300 bg-opacity-30"
>
<div class="my-auto">
<CameraContainer cameraUrl="camera_url_here" />
@ -14,4 +12,4 @@
<div class="my-auto">
<CameraContainer cameraUrl="camera_url_here" />
</div>
</div>
</AppContainer>

View file

@ -6,17 +6,17 @@
<script lang="ts">
import Song from './Song.svelte'
import { songList } from '../../Dashboard/MediaPlayer/songList'
import { fade } from 'svelte/transition'
import AppContainer from '../AppContainer.svelte'
</script>
<div
class="flex gap-4 w-full py-10 px-10 bg-blue-200 bg-opacity-25 backdrop-blur-xl h-full media-background rounded-3xl flex-wrap"
in:fade={{ duration: 150, delay: 150 }}
out:fade={{ duration: 150 }}
<AppContainer
class="flex gap-4 bg-blue-200 bg-opacity-25 backdrop-blur-xl media-background rounded-3xl flex-wrap px-10 py-20"
>
<h2 class="text-8xl font-bold basis-full text-slate-200">Music</h2>
<h2 class="h-full w-full text-8xl font-bold basis-full text-slate-200">
Music
</h2>
<div class="basis-full h-2" />
{#each Object.entries(songList) as [slug, song]}
<Song {song} {slug} />
{/each}
</div>
</AppContainer>

View file

@ -30,7 +30,7 @@
</script>
<div
class="flex gap-1 flex-col rounded-lg p-4 bg-slate-800 backdrop-blur-xl shadow-md w-60 flex-grow basis-1/5"
class="flex gap-1 flex-col rounded-lg p-4 bg-slate-800 backdrop-blur-xl bg-opacity-80 shadow-md w-60 flex-grow basis-1/5"
>
<img src={coverImg} alt="album cover" class="shadow-md rounded-lg w-full" />
<p class="mt-2 text-2xl font-medium">{title}</p>

View file

@ -0,0 +1,22 @@
<script lang="ts">
import { Notifications } from '../../Notifications/notifications'
import AppContainer from '../AppContainer.svelte'
const handleClick = () => {
Notifications.error('Jankboard initialized', {
src: '/static/voices/en/jankboard-initialized.wav',
})
Notifications.playAudio('static/voices/en/jankboard-initialized.wav')
}
</script>
<AppContainer
class="flex gap-4 bg-blue-200 bg-opacity-25 backdrop-blur-xl media-background rounded-3xl flex-wrap px-10 py-20"
>
<button
class="px-4 py-2 bg-blue-500 rounded-md hover:brightness-75"
on:click={handleClick}
>
Test Toast
</button>
</AppContainer>

View file

@ -1 +0,0 @@
export const toastLibrary = {}

View file

@ -1,5 +1,6 @@
import Camera from './Camera/Camera.svelte'
import MusicBrowser from './MusicBrowser/MusicBrowser.svelte'
import Settings from './Settings/Settings.svelte'
export const appList = {
'camera': {
@ -12,4 +13,9 @@ export const appList = {
component: MusicBrowser,
icon: '/static/app-icons/media-player.png',
},
'settings': {
name: 'Settings',
component: Settings,
icon: '/static/app-icons/settings.webp',
},
}

View file

@ -10,6 +10,7 @@
<script lang="ts">
import { getAcceleration, getDirection } from '../utils/helpers'
import { mpss2knps } from '../utils/unitConversions'
import { fade } from 'svelte/transition'
export let accx: number
export let accy: number
@ -19,20 +20,36 @@
$: placeholder = accx === -999 && accy === -999
</script>
<div class="flex flex-col gap-2 text-center transition-all">
<div class="flex flex-col gap-2 text-center">
<p class="text-xl font-medium">
{#if !placeholder}
Heading {getDirection(orientation)} ({orientation.toFixed(2)}°)
<span
in:fade={{ duration: 150, delay: 150 }}
out:fade={{ duration: 150 }}
class="text-lg font-medium"
>Heading {getDirection(orientation)} ({orientation.toFixed(2)}°)</span
>
{:else}
<span class="placeholder">--------------------------------</span>
<span
class="placeholder"
in:fade={{ duration: 150, delay: 150 }}
out:fade={{ duration: 150 }}>--------------------------------</span
>
{/if}
</p>
<p class="text-lg font-medium">
{#if !placeholder}
{getAcceleration(accResolved)} ({mpss2knps(accResolved).toFixed(2)}
<span in:fade={{ duration: 150, delay: 150 }} out:fade={{ duration: 150 }}
>{getAcceleration(accResolved)} ({mpss2knps(accResolved).toFixed(
2
)}</span
>
kn/s)
{:else}
<span class="placeholder"
<span
class="placeholder"
in:fade={{ duration: 150, delay: 150 }}
out:fade={{ duration: 150 }}
>-----------------------------------------------------</span
>
{/if}

View file

@ -7,22 +7,28 @@
-->
<script lang="ts">
export let speedLimit: number = 5.0
$: placeholder = speedLimit === -999
</script>
<div
class="bg-white p-[0.15rem] text-black rounded-xl shadow-md shadow-neutral-500"
>
<div
class="px-3 py-1 border-black rounded-xl border-2 flex flex-col text-center gap-1"
class:speed-limit-placeholder={speedLimit === -999}
class="px-3 py-1 border-black rounded-xl border-2 flex flex-col text-center gap-1 transition-all"
>
<div class="text-lg font-medium">SPEED<br />LIMIT</div>
<div class="text-2xl font-bold">{speedLimit}</div>
<div
class="text-2xl font-bold transition"
class:speed-limit-placeholder={placeholder}
>
{speedLimit}
</div>
</div>
</div>
<style lang="postcss">
.speed-limit-placeholder {
@apply animate-pulse bg-neutral-300 text-neutral-300;
@apply text-neutral-200 bg-neutral-200 animate-pulse rounded-lg;
}
</style>

View file

@ -17,11 +17,15 @@
</script>
<div class="flex flex-col gap-4">
<div class="text-6xl" class:placeholder class:width-limit={placeholder}>
<div
class="text-6xl transition"
class:placeholder
class:width-limit={placeholder}
>
{placeholder ? '-----' : formatted}
</div>
<div
class="text-2xl font-medium"
class="text-2xl font-medium transition"
class:placeholder={speed === Math.hypot(-999, -999)}
>
MPH

View file

@ -7,11 +7,12 @@
</script>
<span class="flex gap-1">
<div class="text-lg font-medium" class:placeholder>
<div class="text-lg font-medium transition" class:placeholder>
{formatted} V
</div>
<span class="material-symbols-outlined battery-icon" class:placeholder
>battery_horiz_075</span
<span
class="material-symbols-outlined battery-icon transition"
class:placeholder>battery_horiz_075</span
>
</span>

View file

@ -2,7 +2,7 @@
export let selectedGear: Gear | '-999'
</script>
<div class="flex justify-center w-full">
<div class="flex justify-center w-full transition">
<div
class="flex flex-row gap-2 text-neutral-400 text-xl font-bold"
class:placeholder={selectedGear === '-999'}

View file

@ -6,6 +6,8 @@
Displays the drive mode
-->
<script lang="ts">
import { fade } from 'svelte/transition'
export let selectedMode: Mode | '-999'
let modeText = ''
@ -27,5 +29,5 @@
</script>
<div class="font-medium text-xl">
{modeText}
<span>{modeText}</span>
</div>

View file

@ -0,0 +1,10 @@
<script lang="ts">
export let size = '1.5rem'
</script>
<span
class="material-symbols-outlined text-slate-600"
style="font-size: {size}"
>
info
</span>

View file

@ -0,0 +1,93 @@
import { toast } from 'svelte-french-toast'
import type { ToastOptions } from 'svelte-french-toast'
import InfoIcon from './InfoIcon.svelte'
import { Howl } from 'howler'
interface NotificationOptions extends ToastOptions {
withAudio?: boolean
src?: string
}
// get colors from https://tailwindcss.com/docs/customizing-colors
export class Notifications {
private static readonly defaultDuration = 3000
public static success(message: string, options?: NotificationOptions) {
if (options?.withAudio && !options.src)
throw new Error('No audio source provided')
const sendToast = (duration: number) => {
toast.success(message, {
style:
'padding: 25px; font-size: 1.5rem; background-color: #15803d; color: #fafafa;',
duration,
...options,
})
}
if (options?.withAudio && options?.src) {
let sound: Howl
sound = new Howl({
src: [options.src],
preload: true,
autoplay: true,
onload: () => sendToast(sound.duration() * 1000),
})
} else {
sendToast(this.defaultDuration)
}
}
public static error(message: string, options?: NotificationOptions) {
if (options?.withAudio && !options.src)
throw new Error('No audio source provided')
const sendToast = (duration: number) => {
toast.error(message, {
style:
'padding: 25px; font-size: 1.5rem; background-color: #dc2626; color: #fafafa;',
duration,
...options,
})
}
if (options?.withAudio && options?.src) {
let sound: Howl
sound = new Howl({
src: [options.src],
preload: true,
autoplay: true,
onload: () => sendToast(sound.duration() * 1000),
})
} else {
sendToast(this.defaultDuration)
}
}
public static info(message: string, options?: any) {
const sendToast = (duration: number) => {
toast(message, {
style: 'padding: 25px; font-size: 1.5rem;',
icon: InfoIcon,
duration,
...options,
})
}
if (options?.withAudio && options?.src) {
let sound: Howl
sound = new Howl({
src: [options.src],
preload: true,
autoplay: true,
onload: () => sendToast(sound.duration() * 1000),
})
} else {
sendToast(this.defaultDuration)
}
}
public static playAudio(src: string) {
new Howl({
src: [src],
preload: true,
autoplay: true,
})
}
}

View file

@ -0,0 +1,28 @@
/*
define various sequences to play out in this file
for example, we can define an initialization sequence that
plays out some series of notifications, and call it whenever we need it,
or a sequence to change the screen color and play some audio queues for a crash
*/
import { Notifications } from '../Notifications/notifications'
import getVoicePath from '../utils/getVoicePath'
export const initializationSequence = () => {
Notifications.info('Jankboard initialized!', {
withAudio: true,
src: getVoicePath('jankboard-initialized', 'en'),
})
setTimeout(() => {
Notifications.success('LittenOS is online.', {
withAudio: true,
src: getVoicePath('littenos-is-online', 'en'),
})
setTimeout(() => {
Notifications.error('Breaching Monte Vista codebase.', {
withAudio: true,
src: getVoicePath('breaching-monte-vista', 'en'),
})
}, 3000)
}, 3000)
}

View file

@ -0,0 +1,11 @@
/**
* Retrieves the voice audio path for the given audio file.
*
* @param audio - the id of the voice audio file requested (see the readout)
* @param lang - the language of the audio
* @return the path of the audio file
*/
type SupportedLanguage = 'en' | 'rus'
export default function getVoicePath(audio: string, lang: SupportedLanguage) {
return `/static/voices/${lang}/${audio}.wav`
}

View file

@ -81,10 +81,9 @@ export const resolveAcceleration = (
* @return The cardinal direction based on the input angle
*/
export const getDirection = (angle: number): CardinalDirection => {
if (angle < 0 || angle > 360)
if (angle < 22.5 || angle > 337.5) {
return 'North'
}
if (angle < 22.5 || angle > 337.5) {
return 'North'
}
if (angle > 22.5 && angle < 67.5) {
return 'Northeast'
}