+
+ setSettings(v=>({...v,point_size:Number(event.target.value)}))}/>
+ setSettings(v=>({...v,accumulation_seconds:Number(event.target.value)}))}/>
+
+ setSettings(v=>({...v,show_points:value}))}/>setSettings(v=>({...v,show_trajectory:value}))}/>setSettings(v=>({...v,show_grid:value}))}/>
+ ;
+}
diff --git a/packages/sensor-ui/src/K1LiveView.tsx b/packages/sensor-ui/src/K1LiveView.tsx
new file mode 100644
index 0000000..f78a2c8
--- /dev/null
+++ b/packages/sensor-ui/src/K1LiveView.tsx
@@ -0,0 +1,63 @@
+import {useEffect,useRef,useState} from 'react';
+import {ActivityIndicator,SettingsCard} from '@nodedc/ui-react';
+import {perform,type Sensor,type SensorTransport} from './contracts';
+import type {RerunHostFactory} from './rerunHost';
+
+function privateCandidate(sdp:string):string{
+ return sdp.split('\r\n').filter(line=>{
+ if(!line.startsWith('a=candidate:'))return true;
+ const fields=line.split(' '),ip=fields[4]??'',parts=ip.split('.').map(Number);
+ return fields[7]==='host'&&(ip.endsWith('.local')||(parts.length===4&&parts.every(v=>Number.isInteger(v)&&v>=0&&v<=255)&&
+ (parts[0]===10||parts[0]===127||(parts[0]===192&&parts[1]===168)||(parts[0]===172&&parts[1]>=16&&parts[1]<=31)||(parts[0]===100&&parts[1]>=64&&parts[1]<=127))));
+ }).join('\r\n');
+}
+
+export function K1LiveView({device,transport,createRerunHost}:{device:Sensor;transport:SensorTransport;createRerunHost:RerunHostFactory}){
+ const spatial=useRef