From e1c8cf3257cb33457aa882c548f5195ecc0f9854 Mon Sep 17 00:00:00 2001
From: Adam Malczewski <github@tradam.dev>
Date: Sat, 6 Jun 2026 22:08:16 +0900
Subject: Slice 1: surface system + WS transport + composition root

Pure-core feature libraries assembled at the composition root:
- core/protocol: pure reducer over surface catalog/spec/error messages
- features/surface-host: generic field-kind interpreter (toggle/progress/
  selector/stat/button) + pure plan logic; no surface-id special-casing
- adapters/ws: injected WebSocket client (effects at the edge)
- app: composition root store (Svelte 5 runes over the pure reducer),
  host-relative surface WS URL resolution (resolveWsUrl), root App.svelte

Verified green: svelte-check 0/0, vitest 84 passed, biome clean, vite build ok.
---
 src/features/surface-host/ui/SurfaceView.svelte | 33 +++++++++++++++++++++++++
 1 file changed, 33 insertions(+)
 create mode 100644 src/features/surface-host/ui/SurfaceView.svelte

(limited to 'src/features/surface-host/ui/SurfaceView.svelte')

diff --git a/src/features/surface-host/ui/SurfaceView.svelte b/src/features/surface-host/ui/SurfaceView.svelte
new file mode 100644
index 0000000..4207913
--- /dev/null
+++ b/src/features/surface-host/ui/SurfaceView.svelte
@@ -0,0 +1,33 @@
+<script lang="ts">
+	import type { InvokeMessage, SurfaceSpec } from "@dispatch/ui-contract";
+	import { planSurface } from "../logic/plan";
+	import Button from "./Button.svelte";
+	import Progress from "./Progress.svelte";
+	import Selector from "./Selector.svelte";
+	import Stat from "./Stat.svelte";
+	import Toggle from "./Toggle.svelte";
+
+	let {
+		spec,
+		onInvoke,
+	}: { spec: SurfaceSpec; onInvoke: (msg: InvokeMessage) => void } = $props();
+
+	const plan = $derived(planSurface(spec));
+</script>
+
+<article>
+	<h2>{spec.title}</h2>
+	{#each plan.fields as field (field)}
+		{#if field.kind === "toggle"}
+			<Toggle {field} surfaceId={spec.id} {onInvoke} />
+		{:else if field.kind === "progress"}
+			<Progress {field} />
+		{:else if field.kind === "selector"}
+			<Selector {field} surfaceId={spec.id} {onInvoke} />
+		{:else if field.kind === "stat"}
+			<Stat {field} />
+		{:else if field.kind === "button"}
+			<Button {field} surfaceId={spec.id} {onInvoke} />
+		{/if}
+	{/each}
+</article>
-- 
cgit v1.2.3

