feat(ui): migrate routes and app shell to controller-driven state
This commit is contained in:
@@ -1,8 +1,107 @@
|
||||
<script lang="ts">
|
||||
import CameraScreen from '$lib/sim/screens/CameraScreen.svelte';
|
||||
// @ts-nocheck
|
||||
import AppChrome from '$lib/sim/ui/AppChrome.svelte';
|
||||
import { appController } from '$lib/app/controller';
|
||||
import { appState } from '$lib/app/store';
|
||||
|
||||
let cameraVideoElement: HTMLVideoElement | null = null;
|
||||
|
||||
$effect(() => {
|
||||
appController.setCameraVideoElement(cameraVideoElement);
|
||||
});
|
||||
</script>
|
||||
|
||||
<AppChrome pageKey="camera">
|
||||
<CameraScreen />
|
||||
<section id="screen-home-camera" class="flex flex-col gap-6 max-w-7xl mx-auto h-full min-h-0">
|
||||
<div class="flex-1 min-h-0 flex flex-col gap-6">
|
||||
<div class="flex-[3] min-h-[260px] glass-card rounded-3xl overflow-hidden relative flex flex-col border border-white/10">
|
||||
<div id="cameraPreview" class="flex-1 bg-black relative flex items-center justify-center {$appState.isMotionActive ? 'bg-red-900/20' : ''}">
|
||||
<video
|
||||
id="cameraVideo"
|
||||
class="absolute inset-0 w-full h-full object-cover {$appState.cameraPreviewReady ? '' : 'hidden'}"
|
||||
autoplay
|
||||
playsinline
|
||||
muted
|
||||
bind:this={cameraVideoElement}
|
||||
></video>
|
||||
<div
|
||||
class="absolute top-4 left-4 z-20 flex items-center gap-2 px-3 py-1.5 rounded-full bg-black/50 backdrop-blur border border-white/10"
|
||||
>
|
||||
<span class="w-2.5 h-2.5 bg-red-500 rounded-full shadow-[0_0_8px_rgba(239,68,68,0.8)] animate-pulse"></span>
|
||||
<span class="text-xs text-white font-medium tracking-wide">REC</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
id="cameraOfflineOverlay"
|
||||
class="absolute inset-0 bg-[#0a0a0c]/80 backdrop-blur-sm z-10 flex flex-col items-center justify-center gap-4 {$appState.socketConnected ? 'hidden' : ''}"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-16 w-16 text-gray-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728A9 9 0 015.636 5.636m12.728 12.728L5.636 5.636"
|
||||
/>
|
||||
</svg>
|
||||
<p class="text-gray-400 font-medium tracking-wide">Camera Offline</p>
|
||||
<button
|
||||
id="cameraGoOnlineBtn"
|
||||
class="btn btn-outline btn-success rounded-xl border-green-500/50 text-green-400 hover:bg-green-500/10 hover:border-green-400"
|
||||
onclick={() => appController.goOnline()}
|
||||
>
|
||||
Go Online
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-[2] min-h-0 grid grid-cols-1 xl:grid-cols-[2fr_1fr] gap-6">
|
||||
<div class="glass-card p-6 rounded-3xl border border-white/5 flex flex-col min-h-[220px] overflow-hidden">
|
||||
<h3 class="text-xs font-bold text-gray-500 uppercase tracking-wider mb-4 shrink-0">Logs</h3>
|
||||
<div
|
||||
id="cameraLogs"
|
||||
class="flex-1 min-h-0 bg-black/40 rounded-xl p-4 text-xs font-mono text-gray-400 overflow-y-auto border border-white/5"
|
||||
>
|
||||
{#if $appState.activityLog.length === 0}
|
||||
<div class="text-gray-600 italic">Awaiting connection...</div>
|
||||
{:else}
|
||||
{#each $appState.activityLog as log (log.id)}
|
||||
<div>[{new Date(log.createdAt).toLocaleTimeString()}] {log.type}: {log.message}</div>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="glass-card p-6 rounded-3xl border border-white/5 space-y-4 min-h-[220px]">
|
||||
<h3 class="text-xs font-bold text-gray-500 uppercase tracking-wider">Actions / Options</h3>
|
||||
<div class="space-y-3">
|
||||
{#if !$appState.isMotionActive}
|
||||
<button
|
||||
id="startMotionBtn"
|
||||
class="btn w-full justify-start h-14 rounded-xl bg-white/5 border-white/5 hover:bg-red-500/10 hover:border-red-500/30 hover:text-red-400 transition-all font-medium group"
|
||||
onclick={() => appController.startMotion()}
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-400 group-hover:text-red-400 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
|
||||
</svg>
|
||||
Simulate Motion Event
|
||||
</button>
|
||||
{:else}
|
||||
<button
|
||||
id="endMotionBtn"
|
||||
class="btn w-full justify-start h-14 rounded-xl bg-white/5 border-white/5 hover:bg-white/10 font-medium disabled:opacity-30"
|
||||
onclick={() => appController.endMotion()}
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-400 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 10a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1h-4a1 1 0 01-1-1v-4z" />
|
||||
</svg>
|
||||
Stop Recording
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</AppChrome>
|
||||
|
||||
Reference in New Issue
Block a user