Merge pull request #1 from couscousdude/camera-feed-overhaul
Camera feed overhaul
This commit is contained in:
commit
13e5790567
15 changed files with 161 additions and 63 deletions
|
@ -17,6 +17,7 @@
|
||||||
"@tauri-apps/cli": "^1.5.10",
|
"@tauri-apps/cli": "^1.5.10",
|
||||||
"@tsconfig/svelte": "^5.0.2",
|
"@tsconfig/svelte": "^5.0.2",
|
||||||
"@types/howler": "^2.2.11",
|
"@types/howler": "^2.2.11",
|
||||||
|
"@types/konami-code-js": "^0.8.3",
|
||||||
"@types/three": "^0.161.2",
|
"@types/three": "^0.161.2",
|
||||||
"autoprefixer": "^10.4.17",
|
"autoprefixer": "^10.4.17",
|
||||||
"postcss": "^8.4.35",
|
"postcss": "^8.4.35",
|
||||||
|
@ -35,6 +36,7 @@
|
||||||
"@threlte/extras": "^8.8.0",
|
"@threlte/extras": "^8.8.0",
|
||||||
"camera-controls": "^2.8.3",
|
"camera-controls": "^2.8.3",
|
||||||
"howler": "^2.2.4",
|
"howler": "^2.2.4",
|
||||||
|
"konami-code-js": "^0.8.1",
|
||||||
"material-icons": "^1.13.12",
|
"material-icons": "^1.13.12",
|
||||||
"material-symbols": "^0.15.0",
|
"material-symbols": "^0.15.0",
|
||||||
"overlayscrollbars-svelte": "^0.5.3",
|
"overlayscrollbars-svelte": "^0.5.3",
|
||||||
|
|
|
@ -23,6 +23,9 @@ dependencies:
|
||||||
howler:
|
howler:
|
||||||
specifier: ^2.2.4
|
specifier: ^2.2.4
|
||||||
version: 2.2.4
|
version: 2.2.4
|
||||||
|
konami-code-js:
|
||||||
|
specifier: ^0.8.1
|
||||||
|
version: 0.8.1
|
||||||
material-icons:
|
material-icons:
|
||||||
specifier: ^1.13.12
|
specifier: ^1.13.12
|
||||||
version: 1.13.12
|
version: 1.13.12
|
||||||
|
@ -61,6 +64,9 @@ devDependencies:
|
||||||
'@types/howler':
|
'@types/howler':
|
||||||
specifier: ^2.2.11
|
specifier: ^2.2.11
|
||||||
version: 2.2.11
|
version: 2.2.11
|
||||||
|
'@types/konami-code-js':
|
||||||
|
specifier: ^0.8.3
|
||||||
|
version: 0.8.3
|
||||||
'@types/three':
|
'@types/three':
|
||||||
specifier: ^0.161.2
|
specifier: ^0.161.2
|
||||||
version: 0.161.2
|
version: 0.161.2
|
||||||
|
@ -724,6 +730,10 @@ packages:
|
||||||
resolution: {integrity: sha512-7aBoUL6RbSIrqKnpEgfa1wSNUBK06mn08siP2QI0zYk7MXfEJAaORc4tohamQYqCqVESoDyRWSdQn2BOKWj2Qw==}
|
resolution: {integrity: sha512-7aBoUL6RbSIrqKnpEgfa1wSNUBK06mn08siP2QI0zYk7MXfEJAaORc4tohamQYqCqVESoDyRWSdQn2BOKWj2Qw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@types/konami-code-js@0.8.3:
|
||||||
|
resolution: {integrity: sha512-TYwIDZ16MuzqHdgv/zhbbE3p/iXY/FotJkMYx1oOzZKJoINezl91stc/U/i5AknH5lrEpXoims+tsCnAwIJGew==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/pug@2.0.10:
|
/@types/pug@2.0.10:
|
||||||
resolution: {integrity: sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==}
|
resolution: {integrity: sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -1312,6 +1322,10 @@ packages:
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/konami-code-js@0.8.1:
|
||||||
|
resolution: {integrity: sha512-bJ0tuWYLYiUueIVTpA0MV4h4Gz1X16uuJggh5TpIWXOQoLv0238SU7Im23z2wYKCCBsOfk5j4HKWB/pqdCgu5Q==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/lilconfig@2.1.0:
|
/lilconfig@2.1.0:
|
||||||
resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==}
|
resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
Before Width: | Height: | Size: 217 KiB After Width: | Height: | Size: 217 KiB |
File diff suppressed because one or more lines are too long
Binary file not shown.
Before Width: | Height: | Size: 453 KiB After Width: | Height: | Size: 217 KiB |
8
client/src/globals.d.ts
vendored
8
client/src/globals.d.ts
vendored
|
@ -1,3 +1,11 @@
|
||||||
|
declare module 'konami-code-js' {
|
||||||
|
export default class KonamiCode {
|
||||||
|
constructor(options: any = {})
|
||||||
|
setCallback: (callback: () => void) => void
|
||||||
|
disable: () => void
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type Gear = 'park' | 'reverse' | 'neutral' | 'low' | 'auto' | 'drive'
|
type Gear = 'park' | 'reverse' | 'neutral' | 'low' | 'auto' | 'drive'
|
||||||
|
|
||||||
type Mode = 'chill' | 'ludicrous' | 'cruise'
|
type Mode = 'chill' | 'ludicrous' | 'cruise'
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import AppContainer from '../AppContainer.svelte'
|
import AppContainer from '../AppContainer.svelte'
|
||||||
import CameraContainer from './CameraContainer.svelte'
|
import CameraContainer from './CameraContainer.svelte'
|
||||||
|
import { settingsStore } from '../../stores/settingsStore'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<AppContainer
|
<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"
|
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">
|
<div class="my-auto">
|
||||||
<CameraContainer cameraUrl="camera_url_here" />
|
<CameraContainer cameraUrl={$settingsStore.frontCameraAddr} />
|
||||||
</div>
|
</div>
|
||||||
<div class="my-auto">
|
<div class="my-auto">
|
||||||
<CameraContainer cameraUrl="camera_url_here" />
|
<CameraContainer cameraUrl={$settingsStore.rearCameraAddr} />
|
||||||
</div>
|
</div>
|
||||||
</AppContainer>
|
</AppContainer>
|
||||||
|
|
|
@ -7,10 +7,19 @@
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export let cameraUrl: string
|
export let cameraUrl: string
|
||||||
|
|
||||||
|
let feed: HTMLImageElement
|
||||||
|
let placeholder: HTMLDivElement
|
||||||
|
|
||||||
|
$: {
|
||||||
|
if (feed && placeholder) {
|
||||||
|
placeholder.style.width = `${feed.clientWidth}px`
|
||||||
|
placeholder.style.height = `${feed.clientHeight}px`
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<!-- svelte-ignore a11y-missing-attribute -->
|
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
<img
|
<img
|
||||||
src={cameraUrl}
|
src={cameraUrl}
|
||||||
|
@ -18,9 +27,11 @@
|
||||||
height="400px"
|
height="400px"
|
||||||
class="rounded-xl shadow-md layer1 z-10"
|
class="rounded-xl shadow-md layer1 z-10"
|
||||||
alt="⠀"
|
alt="⠀"
|
||||||
|
bind:this={feed}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class="w-[400px] h-[400px] rounded-xl shadow-md bg-neutral-300 animate-pulse layer2"
|
class="rounded-xl shadow-md bg-neutral-300 animate-pulse layer2"
|
||||||
|
bind:this={placeholder}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -5,6 +5,9 @@
|
||||||
import SettingsSelector from './SettingsSelector.svelte'
|
import SettingsSelector from './SettingsSelector.svelte'
|
||||||
import SettingsInput from './SettingsInput.svelte'
|
import SettingsInput from './SettingsInput.svelte'
|
||||||
import SettingsToggle from './SettingsToggle.svelte'
|
import SettingsToggle from './SettingsToggle.svelte'
|
||||||
|
import KonamiCode from 'konami-code-js'
|
||||||
|
import { onMount } from 'svelte'
|
||||||
|
import { fly } from 'svelte/transition'
|
||||||
|
|
||||||
settingsStore.subscribe(async value => {
|
settingsStore.subscribe(async value => {
|
||||||
window.localStorage.setItem('settings', JSON.stringify(value))
|
window.localStorage.setItem('settings', JSON.stringify(value))
|
||||||
|
@ -15,43 +18,97 @@
|
||||||
settingsStore.reset()
|
settingsStore.reset()
|
||||||
Notifications.success('Settings reset! Refresh for all changes to apply.')
|
Notifications.success('Settings reset! Refresh for all changes to apply.')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let unlockSecret = false
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
const kc = new KonamiCode()
|
||||||
|
kc.setCallback(() => {
|
||||||
|
kc.disable()
|
||||||
|
unlockSecret = true
|
||||||
|
})
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<AppContainer
|
<AppContainer
|
||||||
class="flex gap-6 bg-blue-200 bg-opacity-25 backdrop-blur-xl media-background rounded-3xl flex-wrap px-10 py-20"
|
class="flex gap-6 bg-blue-200 bg-opacity-25 backdrop-blur-xl media-background rounded-3xl flex-wrap px-10 py-20 transition-all"
|
||||||
>
|
>
|
||||||
<h1 class="text-5xl font-medium text-slate-100 basis-full">Settings</h1>
|
<h1 class="text-5xl font-medium text-slate-100 basis-full">Settings</h1>
|
||||||
<p class="text-slate-300">Hover over setting names to see helpful tooltips</p>
|
<p class="text-slate-300">Hover over setting names to see helpful tooltips</p>
|
||||||
<h2 class="text-2xl font-medium text-slate-200 mt-4 basis-full">General</h2>
|
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<SettingsToggle
|
<h2 class="text-2xl font-medium text-slate-200 mt-4 basis-full">General</h2>
|
||||||
setting="disableAnnoyances"
|
<ul>
|
||||||
tooltip="Disable non-critical popups and audio cues."
|
<li>
|
||||||
>Disable Annoyances</SettingsToggle
|
<SettingsToggle
|
||||||
>
|
setting="disableAnnoyances"
|
||||||
<SettingsToggle
|
tooltip="Disable non-critical popups and audio cues."
|
||||||
setting="goWoke"
|
>Disable Annoyances</SettingsToggle
|
||||||
tooltip="Disables content that could be perceived as offensive for PR and DEI purposes."
|
>
|
||||||
>Go Woke</SettingsToggle
|
</li>
|
||||||
>
|
<li>
|
||||||
<SettingsInput
|
<SettingsSelector
|
||||||
setting="randomWeight"
|
setting="voiceLang"
|
||||||
tooltip="Changes the likelihood of random events occurring (default: 1). Set to a decimal to lower probability and a number >= 1 to increase it."
|
options={['en-US', 'en-RU', 'en-UK']}
|
||||||
width="3rem"
|
tooltip="Selects the language/locale used for Jankboard voice prompts. Does not affect application language (ie. Jankboard itself will always be in English)."
|
||||||
>
|
>Voice Prompt Language</SettingsSelector
|
||||||
RNG Weight
|
>
|
||||||
</SettingsInput>
|
</li>
|
||||||
<SettingsSelector
|
</ul>
|
||||||
setting="voiceLang"
|
|
||||||
options={['en-US', 'en-RU', 'en-UK']}
|
<h2 class="text-2xl font-medium text-slate-200 mt-4 basis-full">
|
||||||
tooltip="Selects the language/locale used for Jankboard voice prompts. Does not affect application language (ie. Jankboard itself will always be in English)."
|
Camera Configuration
|
||||||
>Voice Prompt Language</SettingsSelector
|
</h2>
|
||||||
>
|
<ul>
|
||||||
<SettingsToggle
|
<li>
|
||||||
setting="sentry"
|
<SettingsInput
|
||||||
tooltip="Sentry mode protects the robot and operator from foreign threats."
|
setting="frontCameraAddr"
|
||||||
>Sentry Mode</SettingsToggle
|
tooltip="Set the IP address of the front-facing camera. Input in the format xxx.xxx.xxx.xxx:PORT (eg. 244.178.44.111:8080)"
|
||||||
>
|
width="16rem">Front Camera IP</SettingsInput
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<SettingsInput
|
||||||
|
setting="rearCameraAddr"
|
||||||
|
tooltip="Set the IP address of the rear-facing camera. Input in the format xxx.xxx.xxx.xxx:PORT (eg. 244.178.44.111:8080)"
|
||||||
|
width="16rem">Rear Camera IP</SettingsInput
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<h2 class="text-2xl font-medium text-slate-200 mt-4 basis-full">Fun</h2>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<SettingsToggle
|
||||||
|
setting="sentry"
|
||||||
|
tooltip="Sentry mode protects the robot and operator from foreign threats (doesn't actually do anything besides add extra voice prompts)"
|
||||||
|
>Sentry Mode</SettingsToggle
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<SettingsInput
|
||||||
|
setting="randomWeight"
|
||||||
|
tooltip="Changes the likelihood of random events occurring (default: 1). Set to a decimal to lower probability and a number >= 1 to increase it."
|
||||||
|
width="3rem"
|
||||||
|
>
|
||||||
|
RNG Weight
|
||||||
|
</SettingsInput>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
{#if unlockSecret}
|
||||||
|
<div transition:fly={{ y: -100, duration: 200 }}>
|
||||||
|
<h2 class="text-2xl font-medium text-slate-200 mt-4 basis-full">
|
||||||
|
Secret
|
||||||
|
</h2>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<SettingsToggle
|
||||||
|
setting="goWoke"
|
||||||
|
tooltip="Disables content that could be perceived as offensive for PR and DEI purposes."
|
||||||
|
>Go Woke</SettingsToggle
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
<button
|
<button
|
||||||
class="mt-10 px-4 py-2 bg-amber-600 hover:brightness-75 text-medium rounded-lg w-min"
|
class="mt-10 px-4 py-2 bg-amber-600 hover:brightness-75 text-medium rounded-lg w-min"
|
||||||
on:click={resetSettings}>Reset</button
|
on:click={resetSettings}>Reset</button
|
||||||
|
@ -63,3 +120,10 @@
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</AppContainer>
|
</AppContainer>
|
||||||
|
<div class="w-full h-24" />
|
||||||
|
|
||||||
|
<style lang="postcss">
|
||||||
|
li {
|
||||||
|
@apply my-3;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
await tick()
|
await tick()
|
||||||
// @ts-expect-error
|
|
||||||
settingsStore.update(setting, value)
|
settingsStore.update(setting, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -217,7 +217,7 @@ export const infotainmentBootupSequence = async () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!get(sequenceStore).initializationComplete) {
|
if (!get(sequenceStore).initializationComplete) {
|
||||||
const unsubscribe = sequenceStore.subscribe((data) => {
|
const unsubscribe = sequenceStore.subscribe(data => {
|
||||||
if (data.initializationComplete) {
|
if (data.initializationComplete) {
|
||||||
sequence()
|
sequence()
|
||||||
unsubscribe()
|
unsubscribe()
|
||||||
|
@ -237,7 +237,7 @@ export const infotainmentBootupSequence = async () => {
|
||||||
*/
|
*/
|
||||||
const waitForInfotainmentBootup = (sequence: () => void) => {
|
const waitForInfotainmentBootup = (sequence: () => void) => {
|
||||||
if (!get(sequenceStore).infotainmentStartedFirstTime) {
|
if (!get(sequenceStore).infotainmentStartedFirstTime) {
|
||||||
const unsubscribe = sequenceStore.subscribe((data) => {
|
const unsubscribe = sequenceStore.subscribe(data => {
|
||||||
if (data.infotainmentStartedFirstTime) {
|
if (data.infotainmentStartedFirstTime) {
|
||||||
sequence()
|
sequence()
|
||||||
unsubscribe()
|
unsubscribe()
|
||||||
|
|
|
@ -11,6 +11,8 @@ export interface SettingsStoreData {
|
||||||
randomWeight: number
|
randomWeight: number
|
||||||
voiceLang: SupportedLanguage
|
voiceLang: SupportedLanguage
|
||||||
sentry: boolean
|
sentry: boolean
|
||||||
|
frontCameraAddr: string
|
||||||
|
rearCameraAddr: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaults: SettingsStoreData = {
|
export const defaults: SettingsStoreData = {
|
||||||
|
@ -18,8 +20,10 @@ export const defaults: SettingsStoreData = {
|
||||||
goWoke: true, // go woke (for showing parents or other officials where DEI has taken over), disables "offensive" sequences
|
goWoke: true, // go woke (for showing parents or other officials where DEI has taken over), disables "offensive" sequences
|
||||||
fastStartup: false, // skip the loading splash screen (for development purposes. Setting this from within the app has no effect.)
|
fastStartup: false, // skip the loading splash screen (for development purposes. Setting this from within the app has no effect.)
|
||||||
randomWeight: 1, // the weight of random events (multiplied by the original probability)
|
randomWeight: 1, // the weight of random events (multiplied by the original probability)
|
||||||
voiceLang: 'en-US', // locale-specific voice for alerts
|
voiceLang: 'en-UK', // locale-specific voice for alerts
|
||||||
sentry: true, // protect the robot and operator from foreign threats
|
sentry: false, // protect the robot and operator from foreign threats
|
||||||
|
frontCameraAddr: '',
|
||||||
|
rearCameraAddr: '',
|
||||||
}
|
}
|
||||||
|
|
||||||
const createSequenceStore = () => {
|
const createSequenceStore = () => {
|
||||||
|
@ -30,7 +34,7 @@ const createSequenceStore = () => {
|
||||||
data: keyof SettingsStoreData,
|
data: keyof SettingsStoreData,
|
||||||
newValue: SettingsStoreData[typeof data]
|
newValue: SettingsStoreData[typeof data]
|
||||||
) => {
|
) => {
|
||||||
update((store) => {
|
update(store => {
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
store[data] = newValue
|
store[data] = newValue
|
||||||
return store
|
return store
|
||||||
|
|
|
@ -44,3 +44,4 @@ screen!**
|
||||||
inlined.
|
inlined.
|
||||||
4. Rename this outputted `index.html` to `splashscreen.html`, and then move it
|
4. Rename this outputted `index.html` to `splashscreen.html`, and then move it
|
||||||
into `/client/public`, replacing the existing `splashscreen.html`.
|
into `/client/public`, replacing the existing `splashscreen.html`.
|
||||||
|
Note: the background image will not load in development since it's designed to load `/splash-screen.jpg` from the main app. Update the `splash-screen.jpg` image in `client/public` to change it.
|
||||||
|
|
|
@ -3,13 +3,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
<title>Jankboard 2</title>
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
|
||||||
<link rel="manifest" href="/site.webmanifest" />
|
|
||||||
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5" />
|
|
||||||
<meta name="msapplication-TileColor" content="#da532c" />
|
|
||||||
<meta name="theme-color" content="#ffffff" />
|
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<Loading />
|
<Loading />
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { blur, fade } from 'svelte/transition'
|
import { fly } from 'svelte/transition'
|
||||||
|
|
||||||
import SvelteLogo from './SvelteLogo.svelte'
|
import SvelteLogo from './SvelteLogo.svelte'
|
||||||
import { onMount } from 'svelte'
|
import { onMount } from 'svelte'
|
||||||
|
import { cubicIn } from 'svelte/easing'
|
||||||
|
|
||||||
let loadingStuck = false
|
let loadingStuck = false
|
||||||
|
|
||||||
|
@ -15,24 +16,23 @@
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="absolute w-screen h-screen flex justify-center items-center flex-col overflow-hidden select-none bg"
|
class="absolute w-screen h-screen flex justify-center items-center flex-col overflow-hidden select-none bg"
|
||||||
transition:blur={{ duration: 300, amount: 0.5 }}
|
|
||||||
>
|
>
|
||||||
<div class="max-w-64">
|
<div class="max-w-64">
|
||||||
<SvelteLogo />
|
<SvelteLogo />
|
||||||
</div>
|
</div>
|
||||||
{#if loadingStuck}
|
{#if loadingStuck}
|
||||||
<p
|
<p
|
||||||
class="text-5xl text-slate-300 absolute bottom-20 animate-pulse"
|
class="text-4xl text-slate-300 absolute bottom-20 animate-pulse font-medium"
|
||||||
transition:fade={{ duration: 300 }}
|
in:fly={{ duration: 150, y: 25, easing: cubicIn }}
|
||||||
>
|
>
|
||||||
Loading 3D assets...please wait
|
Loading 3D assets...
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style lang="postcss">
|
<style lang="postcss">
|
||||||
.bg {
|
.bg {
|
||||||
background-image: url('../../assets/wallpaper.jpg');
|
background-image: url('/splash-screen.jpg');
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue