feat(ui): migrate routes and app shell to controller-driven state

This commit is contained in:
2026-03-02 14:15:00 +00:00
parent 13e77294be
commit 531e77d900
8 changed files with 660 additions and 30 deletions

View File

@@ -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>