@@ -360,8 +377,7 @@ export function ConfigPage() {
{activeFile.path}
- {isZh ? "可写" : "Writable"}: {String(activeFile.writable)} · {isZh ? "存在" : "Exists"}:{" "}
- {String(activeFile.exists)}
+ {writableHint(activeFile)} · {isZh ? "存在" : "Exists"}: {String(activeFile.exists)}
diff --git a/web/spa/src/shared/api.ts b/web/spa/src/shared/api.ts
index 44ccc48..9aaf07f 100644
--- a/web/spa/src/shared/api.ts
+++ b/web/spa/src/shared/api.ts
@@ -279,6 +279,7 @@ export type LabPublicSettings = {
camera_width: number;
camera_height: number;
camera_fps: number;
+ shared_preview_fps?: number;
solver_fov_deg: number;
solver_max_image_side: number;
solver_large_scale_bg_downsample?: number;
diff --git a/web/static/analysis-lab/assets/analysis-D_aCLSNL.js b/web/static/analysis-lab/assets/analysis-D_aCLSNL.js
new file mode 100644
index 0000000..7584981
--- /dev/null
+++ b/web/static/analysis-lab/assets/analysis-D_aCLSNL.js
@@ -0,0 +1,51 @@
+var Os=Object.defineProperty;var $a=e=>{throw TypeError(e)};var Ps=(e,s,n)=>s in e?Os(e,s,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[s]=n;var F=(e,s,n)=>Ps(e,typeof s!="symbol"?s+"":s,n),Ta=(e,s,n)=>s.has(e)||$a("Cannot "+n);var f=(e,s,n)=>(Ta(e,s,"read from private field"),n?n.call(e):s.get(e)),ae=(e,s,n)=>s.has(e)?$a("Cannot add the same private member more than once"):s instanceof WeakSet?s.add(e):s.set(e,n),De=(e,s,n,r)=>(Ta(e,s,"write to private field"),r?r.call(e,n):s.set(e,n),n);import{j as t,r as l,a as $s,R as Ts}from"./client-D1ZVDB-N.js";import{c as J,u as $e,T as es,I as As}from"./index-C78KOEFu.js";import{R as Us}from"./refresh-cw-gseCLMRP.js";/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const Fe=J("ChevronDown",[["path",{d:"m6 9 6 6 6-6",key:"qrunsl"}]]);/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const Bs=J("Database",[["ellipse",{cx:"12",cy:"5",rx:"9",ry:"3",key:"msslwz"}],["path",{d:"M3 5V19A9 3 0 0 0 21 19V5",key:"1wlel7"}],["path",{d:"M3 12A9 3 0 0 0 21 12",key:"mv7ke4"}]]);/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const Aa=J("Grid3x3",[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",key:"afitv7"}],["path",{d:"M3 9h18",key:"1pudct"}],["path",{d:"M3 15h18",key:"5xshup"}],["path",{d:"M9 3v18",key:"fh3hqa"}],["path",{d:"M15 3v18",key:"14nvp0"}]]);/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const Hs=J("History",[["path",{d:"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",key:"1357e3"}],["path",{d:"M3 3v5h5",key:"1xhq8a"}],["path",{d:"M12 7v5l4 2",key:"1fdv2h"}]]);/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const zs=J("House",[["path",{d:"M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8",key:"5wwlr5"}],["path",{d:"M3 10a2 2 0 0 1 .709-1.528l7-5.999a2 2 0 0 1 2.582 0l7 5.999A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z",key:"1d0kgt"}]]);/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const at=J("LoaderCircle",[["path",{d:"M21 12a9 9 0 1 1-6.219-8.56",key:"13zald"}]]);/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const Ua=J("RotateCcw",[["path",{d:"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",key:"1357e3"}],["path",{d:"M3 3v5h5",key:"1xhq8a"}]]);/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const Ws=J("Upload",[["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["polyline",{points:"17 8 12 3 7 8",key:"t8dd8p"}],["line",{x1:"12",x2:"12",y1:"3",y2:"15",key:"widbto"}]]);/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const Ba=J("ZoomIn",[["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}],["line",{x1:"21",x2:"16.65",y1:"21",y2:"16.65",key:"13gj7c"}],["line",{x1:"11",x2:"11",y1:"8",y2:"14",key:"1vmskp"}],["line",{x1:"8",x2:"14",y1:"11",y2:"11",key:"durymu"}]]);/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const Ha=J("ZoomOut",[["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}],["line",{x1:"21",x2:"16.65",y1:"21",y2:"16.65",key:"13gj7c"}],["line",{x1:"8",x2:"14",y1:"11",y2:"11",key:"durymu"}]]),_="/api/dev";async function V(e){if((e.headers.get("content-type")||"").includes("application/json"))return e.json();const n=await e.text();throw new Error(n||`HTTP ${e.status}`)}async function Vs(){const e=await fetch(`${_}/analysis/uploads`);if(!e.ok)throw new Error(await e.text());return e.json()}async function Gs(e,s){const n=new URLSearchParams;s!=null&&s.deleteExperiments&&n.set("delete_experiments","true");const r=n.toString(),o=await fetch(`${_}/analysis/uploads/${encodeURIComponent(e)}${r?`?${r}`:""}`,{method:"DELETE"});if(!o.ok)throw new Error(await o.text());return await o.json().catch(()=>({}))}async function Ut(e,s="analysis_upload"){const n=new FormData;n.append("file",e),n.append("source",s);const r=await fetch(`${_}/analysis/upload`,{method:"POST",body:n}),o=await V(r);if(!r.ok)throw new Error(String(o.detail||r.status));return o}async function Ks(e){const s=await fetch(`${_}/analysis/uploads/import_from_debug`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({filename:e})}),n=await V(s);if(!s.ok)throw new Error(String(n.detail||s.status));return n}async function za(e){const s=await fetch(`${_}/analysis/uploads/replace_video`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)}),n=await V(s);if(!s.ok)throw new Error(String(n.detail||s.status));return n}async function qs(e,s){const n=await fetch(`${_}/analysis/solve/image`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({input_name:e,...s})}),r=await V(n);if(!n.ok)throw new Error(String(r.detail||n.status));return r}async function Js(e,s){const n=await fetch(`${_}/analysis/solve/batch`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({input_name:e,runs:s})}),r=await V(n);if(!n.ok)throw new Error(String(r.detail||n.status));return r}async function Bt(e){const s=await fetch(`${_}/analysis/presets?scope=${e}`);if(!s.ok)throw new Error(await s.text());return s.json()}async function Zs(e,s){const n=await fetch(`${_}/analysis/presets`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:e,params:s})}),r=await V(n);if(!n.ok)throw new Error(String(r.detail||n.status));return r}async function Ht(e){const s=await fetch(`${_}/analysis/experiments`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)}),n=await V(s);if(!s.ok)throw new Error(String(n.detail||s.status));return n}async function Ys(e){const s=await fetch(`${_}/analysis/experiments/${encodeURIComponent(e)}`,{method:"DELETE"});if(!s.ok)throw new Error(await s.text())}async function zt(e,s,n=30){const r=new URLSearchParams({page:String(s),page_size:String(n)});e&&r.set("q",e);const o=await fetch(`${_}/analysis/experiments?${r}`);if(!o.ok)throw new Error(await o.text());return o.json()}async function Xs(){const e=await fetch(`${_}/debug/files`);if(!e.ok)throw new Error(await e.text());return e.json()}function Qs(e){return`${_}/debug/files/${encodeURIComponent(e)}`}async function Wa(e){const s=await fetch(`${_}/debug/files/${encodeURIComponent(e)}/info`),n=await V(s);if(!s.ok)throw new Error(String(n.detail||s.status));return n}async function en(e){const s=await fetch(`${_}/analysis/uploads/${encodeURIComponent(e)}/info`),n=await V(s);if(!s.ok)throw new Error(String(n.detail||s.status));return n}function Wt(e){return`${_}/analysis/uploads/file?filename=${encodeURIComponent(e)}`}async function Va(e){const s=await fetch(`${_}/analysis/experiments/export?format=${e}`);if(!s.ok)throw new Error(await s.text());return s.text()}async function tn(e){const s=await fetch(`${_}/analysis/uploads/${encodeURIComponent(e)}/experiment_count`);if(!s.ok)throw new Error(await s.text());return s.json()}async function an(){const e=await fetch(`${_}/analysis/settings`);if(!e.ok)throw new Error(await e.text());return e.json()}async function sn(e){const s=await fetch(`${_}/analysis/solve/frame`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)}),n=await V(s);if(!s.ok)throw new Error(String(n.detail||s.status));return n}async function nn(e,s){const n=new FormData;n.append("file",e,"frame.jpg"),n.append("payload",JSON.stringify(s));const r=await fetch(`${_}/analysis/solve/frame_upload`,{method:"POST",body:n}),o=await V(r);if(!r.ok)throw new Error(String(o.detail||r.status));return o}function rn(e){return`${_}/analysis/experiments/${encodeURIComponent(e)}/asset`}async function ln(){const e=await fetch("/api/dev/system/info");if(!e.ok)throw new Error(await e.text());return e.json()}function ts(e,s,n,r,o){var g,x,h,N;if(!r)return;if(e.clearRect(0,0,s,n),o.all&&Array.isArray(r.stars_all_centroids)){e.fillStyle="rgba(156, 163, 175, 0.85)";for(const m of r.stars_all_centroids)e.beginPath(),e.arc(m.x,m.y,2.4,0,Math.PI*2),e.fill()}if(o.rejected&&Array.isArray(r.stars_rejected_centroids)){e.strokeStyle="rgba(248, 113, 113, 0.95)",e.lineWidth=1.6;for(const m of r.stars_rejected_centroids)e.beginPath(),e.moveTo(m.x-4,m.y-4),e.lineTo(m.x+4,m.y+4),e.moveTo(m.x+4,m.y-4),e.lineTo(m.x-4,m.y+4),e.stroke()}if(o.pattern&&Array.isArray(r.stars_pattern)){e.strokeStyle="rgba(251, 146, 60, 0.95)",e.lineWidth=2;for(const m of r.stars_pattern)e.beginPath(),e.arc(m.x,m.y,6,0,Math.PI*2),e.stroke()}if(o.matched&&Array.isArray(r.stars_matched)){e.strokeStyle="rgba(34, 197, 94, 0.95)",e.fillStyle="rgba(34, 197, 94, 0.95)",e.lineWidth=2,e.font="11px system-ui, sans-serif";for(const m of r.stars_matched)e.beginPath(),e.arc(m.x,m.y,7,0,Math.PI*2),e.stroke(),m.mag!=null&&e.fillText(`m${Number(m.mag).toFixed(1)}`,m.x+4,m.y-4)}const u=r.overlay_ext;if(Array.isArray(u==null?void 0:u.labels_topn)&&u.labels_topn.length>0){e.fillStyle="rgba(96, 165, 250, 0.95)",e.font="12px system-ui, sans-serif";for(const m of u.labels_topn){if(typeof(m==null?void 0:m.x)!="number"||typeof(m==null?void 0:m.y)!="number")continue;const C=typeof m.mag=="number"?` m${m.mag.toFixed(1)}`:"",v=`${m.name??"Star"}${C}`;e.fillText(v,m.x+8,m.y+14)}}const c=u==null?void 0:u.polar_guide;if(c&&typeof((g=c.frame_center)==null?void 0:g.x)=="number"&&typeof((x=c.frame_center)==null?void 0:x.y)=="number"&&typeof((h=c.target)==null?void 0:h.x)=="number"&&typeof((N=c.target)==null?void 0:N.y)=="number"){const m=c.frame_center.x,C=c.frame_center.y,v=c.target.x,I=c.target.y;e.strokeStyle="rgba(244, 63, 94, 0.95)",e.fillStyle="rgba(244, 63, 94, 0.95)",e.lineWidth=2,e.beginPath(),e.moveTo(m,C),e.lineTo(v,I),e.stroke(),e.beginPath(),e.arc(v,I,6,0,Math.PI*2),e.stroke();const R=typeof c.angular_sep_deg=="number"?c.angular_sep_deg.toFixed(2):"-";e.font="12px system-ui, sans-serif",e.fillText(`Polar ${R}deg`,v+10,I-6)}}function Ga(e,s,n,r){if(!n)return;const o=s.naturalWidth||1,u=s.naturalHeight||1;e.width=o,e.height=u,e.style.width=`${s.clientWidth}px`,e.style.height=`${s.clientHeight}px`;const c=e.getContext("2d");c&&ts(c,o,u,n,r)}function on(e,s,n,r){if(!n)return;const o=s.videoWidth||1,u=s.videoHeight||1;if(o<2||u<2)return;e.width=o,e.height=u,e.style.width=`${s.clientWidth}px`,e.style.height=`${s.clientHeight}px`;const c=e.getContext("2d");c&&ts(c,o,u,n,r)}var j;(function(e){e.LOAD="LOAD",e.EXEC="EXEC",e.FFPROBE="FFPROBE",e.WRITE_FILE="WRITE_FILE",e.READ_FILE="READ_FILE",e.DELETE_FILE="DELETE_FILE",e.RENAME="RENAME",e.CREATE_DIR="CREATE_DIR",e.LIST_DIR="LIST_DIR",e.DELETE_DIR="DELETE_DIR",e.ERROR="ERROR",e.DOWNLOAD="DOWNLOAD",e.PROGRESS="PROGRESS",e.LOG="LOG",e.MOUNT="MOUNT",e.UNMOUNT="UNMOUNT"})(j||(j={}));const cn=(()=>{let e=0;return()=>e++})(),dn=new Error("ffmpeg is not loaded, call `await ffmpeg.load()` first"),un=new Error("called FFmpeg.terminate()");var A,se,X,de,ue,lt,P;class mn{constructor(){ae(this,A,null);ae(this,se,{});ae(this,X,{});ae(this,de,[]);ae(this,ue,[]);F(this,"loaded",!1);ae(this,lt,()=>{f(this,A)&&(f(this,A).onmessage=({data:{id:s,type:n,data:r}})=>{switch(n){case j.LOAD:this.loaded=!0,f(this,se)[s](r);break;case j.MOUNT:case j.UNMOUNT:case j.EXEC:case j.FFPROBE:case j.WRITE_FILE:case j.READ_FILE:case j.DELETE_FILE:case j.RENAME:case j.CREATE_DIR:case j.LIST_DIR:case j.DELETE_DIR:f(this,se)[s](r);break;case j.LOG:f(this,de).forEach(o=>o(r));break;case j.PROGRESS:f(this,ue).forEach(o=>o(r));break;case j.ERROR:f(this,X)[s](r);break}delete f(this,se)[s],delete f(this,X)[s]})});ae(this,P,({type:s,data:n},r=[],o)=>f(this,A)?new Promise((u,c)=>{const g=cn();f(this,A)&&f(this,A).postMessage({id:g,type:s,data:n},r),f(this,se)[g]=u,f(this,X)[g]=c,o==null||o.addEventListener("abort",()=>{c(new DOMException(`Message # ${g} was aborted`,"AbortError"))},{once:!0})}):Promise.reject(dn));F(this,"load",({classWorkerURL:s,...n}={},{signal:r}={})=>(f(this,A)||(De(this,A,s?new Worker(new URL(s,import.meta.url),{type:"module"}):new Worker(new URL("/static/analysis-lab/assets/worker-BAOIWoxA.js",import.meta.url),{type:"module"})),f(this,lt).call(this)),f(this,P).call(this,{type:j.LOAD,data:n},void 0,r)));F(this,"exec",(s,n=-1,{signal:r}={})=>f(this,P).call(this,{type:j.EXEC,data:{args:s,timeout:n}},void 0,r));F(this,"ffprobe",(s,n=-1,{signal:r}={})=>f(this,P).call(this,{type:j.FFPROBE,data:{args:s,timeout:n}},void 0,r));F(this,"terminate",()=>{const s=Object.keys(f(this,X));for(const n of s)f(this,X)[n](un),delete f(this,X)[n],delete f(this,se)[n];f(this,A)&&(f(this,A).terminate(),De(this,A,null),this.loaded=!1)});F(this,"writeFile",(s,n,{signal:r}={})=>{const o=[];return n instanceof Uint8Array&&o.push(n.buffer),f(this,P).call(this,{type:j.WRITE_FILE,data:{path:s,data:n}},o,r)});F(this,"mount",(s,n,r)=>{const o=[];return f(this,P).call(this,{type:j.MOUNT,data:{fsType:s,options:n,mountPoint:r}},o)});F(this,"unmount",s=>{const n=[];return f(this,P).call(this,{type:j.UNMOUNT,data:{mountPoint:s}},n)});F(this,"readFile",(s,n="binary",{signal:r}={})=>f(this,P).call(this,{type:j.READ_FILE,data:{path:s,encoding:n}},void 0,r));F(this,"deleteFile",(s,{signal:n}={})=>f(this,P).call(this,{type:j.DELETE_FILE,data:{path:s}},void 0,n));F(this,"rename",(s,n,{signal:r}={})=>f(this,P).call(this,{type:j.RENAME,data:{oldPath:s,newPath:n}},void 0,r));F(this,"createDir",(s,{signal:n}={})=>f(this,P).call(this,{type:j.CREATE_DIR,data:{path:s}},void 0,n));F(this,"listDir",(s,{signal:n}={})=>f(this,P).call(this,{type:j.LIST_DIR,data:{path:s}},void 0,n));F(this,"deleteDir",(s,{signal:n}={})=>f(this,P).call(this,{type:j.DELETE_DIR,data:{path:s}},void 0,n))}on(s,n){s==="log"?f(this,de).push(n):s==="progress"&&f(this,ue).push(n)}off(s,n){s==="log"?De(this,de,f(this,de).filter(r=>r!==n)):s==="progress"&&De(this,ue,f(this,ue).filter(r=>r!==n))}}A=new WeakMap,se=new WeakMap,X=new WeakMap,de=new WeakMap,ue=new WeakMap,lt=new WeakMap,P=new WeakMap;var Ka;(function(e){e.MEMFS="MEMFS",e.NODEFS="NODEFS",e.NODERAWFS="NODERAWFS",e.IDBFS="IDBFS",e.WORKERFS="WORKERFS",e.PROXYFS="PROXYFS"})(Ka||(Ka={}));const xn=new Error("failed to get response body reader"),pn=new Error("failed to complete download"),fn="Content-Length",hn=e=>new Promise((s,n)=>{const r=new FileReader;r.onload=()=>{const{result:o}=r;o instanceof ArrayBuffer?s(new Uint8Array(o)):s(new Uint8Array)},r.onerror=o=>{var u,c;n(Error(`File could not be read! Code=${((c=(u=o==null?void 0:o.target)==null?void 0:u.error)==null?void 0:c.code)||-1}`))},r.readAsArrayBuffer(e)}),bn=async e=>{let s;if(typeof e=="string")/data:_data\/([a-zA-Z]*);base64,([^"]*)/.test(e)?s=atob(e.split(",")[1]).split("").map(n=>n.charCodeAt(0)):s=await(await fetch(e)).arrayBuffer();else if(e instanceof URL)s=await(await fetch(e)).arrayBuffer();else if(e instanceof File||e instanceof Blob)s=await hn(e);else return new Uint8Array;return new Uint8Array(s)},vn=async(e,s)=>{var o;const n=await fetch(e);let r;try{const u=parseInt(n.headers.get(fn)||"-1"),c=(o=n.body)==null?void 0:o.getReader();if(!c)throw xn;const g=[];let x=0;for(;;){const{done:m,value:C}=await c.read(),v=C?C.length:0;if(m){if(u!=-1&&u!==x)throw pn;s&&s({url:e,total:u,received:x,delta:v,done:m});break}g.push(C),x+=v,s&&s({url:e,total:u,received:x,delta:v,done:m})}const h=new Uint8Array(x);let N=0;for(const m of g)h.set(m,N),N+=m.length;r=h.buffer}catch(u){console.log("failed to send download progress event: ",u),r=await n.arrayBuffer()}return r},qa=async(e,s,n=!1,r)=>{const o=n?await vn(e,r):await(await fetch(e)).arrayBuffer(),u=new Blob([o],{type:s});return URL.createObjectURL(u)};let Vt=null,Oe=null,O;async function gn(){if(Vt)return Vt;if(Oe)return Oe;Oe=(async()=>{const e=new mn;e.on("progress",({progress:o})=>{O==null||O(o,"transcoding")}),O==null||O(.01,"loading_ffmpeg");const s="https://unpkg.com/@ffmpeg/core@0.12.6/dist/esm",n=await qa(`${s}/ffmpeg-core.js`,"text/javascript");O==null||O(.04,"loading_ffmpeg");const r=await qa(`${s}/ffmpeg-core.wasm`,"application/wasm");return O==null||O(.07,"loading_ffmpeg"),await e.load({coreURL:n,wasmURL:r}),Vt=e,e})();try{return await Oe}finally{Oe=null}}async function yn(e){const s=URL.createObjectURL(e);try{return await new Promise(r=>{const o=document.createElement("video");o.preload="metadata",o.onloadedmetadata=()=>{const u=Number(o.duration);r(Number.isFinite(u)&&u>0?u:null)},o.onerror=()=>r(null),o.src=s})}finally{URL.revokeObjectURL(s)}}async function Ja(e,s){const n=O;O=s;try{const r=await gn(),o="input.avi",u="output.mp4";s==null||s(.1,"writing_input"),await r.writeFile(o,await bn(e)),await r.exec(["-i",o,"-c:v","libx264","-preset","veryfast","-crf","24","-pix_fmt","yuv420p","-movflags","+faststart","-an",u]);const c=await r.readFile(u);s==null||s(.98,"packing_output");const g=new Blob([c],{type:"video/mp4"}),x=new File([g],e.name.replace(/\.avi$/i,".mp4"),{type:"video/mp4"});await r.deleteFile(o),await r.deleteFile(u);const h=await yn(x);return s==null||s(1,"done"),{file:x,duration_s:h}}finally{O=n}}function Jt(e){return e==null||Number.isNaN(e)?"—":e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/(1024*1024)).toFixed(2)} MB`}function Pe(e,s){if(!e)return"—";try{const n=new Date(e);return new Intl.DateTimeFormat(s==="en"?"en-GB":"zh-CN",{dateStyle:"short",timeStyle:"medium"}).format(n)}catch{return e}}function ce(e){return e==null?null:typeof e=="string"&&e.trim()?e.trim():typeof e=="number"&&!Number.isNaN(e)?String(e):null}function wn(e){return typeof e!="number"||e<=0?null:e>=1e6?`${(e/1e6).toFixed(2)} s`:e>=1e3?`${(e/1e3).toFixed(0)} ms`:`${e} µs`}function jn(e,s){if(!e)return[];const n=[],r=wn(e.exposure_us);r&&n.push({key:"meta.exposure",value:r});const o=ce(e.analogue_gain),u=ce(e.digital_gain);if(o||u){const C=[o?`A ${o}`:"",u?`D ${u}`:""].filter(Boolean);n.push({key:"meta.gain",value:C.join(" · ")})}const c=ce(e.fps);c&&n.push({key:"meta.fps",value:c});const g=ce(e.sensor);g&&n.push({key:"meta.sensor",value:g});const x=ce(e.color_mode);x&&n.push({key:"meta.colorMode",value:x});const h=ce(e.resolution);h&&n.push({key:"meta.outputResolution",value:h});const N=ce(e.modified);N&&n.push({key:"meta.fileTime",value:Pe(N,s)});const m=e.size;return typeof m=="number"&&m>0&&n.push({key:"meta.fileSize",value:Jt(m)}),n}function as(e){const s=n=>typeof n=="number"&&!Number.isNaN(n)?n:null;return e?{tSolveMs:s(e.t_solve_ms),tExtractMs:s(e.t_extract_ms),tPreprocessMs:s(e.t_preprocess_ms),tOpenDecodeMs:s(e.t_open_decode_ms),tBackendTotalMs:s(e.t_backend_total_ms),raDeg:s(e.ra_deg),decDeg:s(e.dec_deg),matches:s(e.matches),rmseArcsec:s(e.rmse_arcsec),prob:s(e.prob),status:typeof e.status=="string"?e.status:null}:{tSolveMs:null,tExtractMs:null,tPreprocessMs:null,tOpenDecodeMs:null,tBackendTotalMs:null,raDeg:null,decDeg:null,matches:null,rmseArcsec:null,prob:null,status:null}}function it(e){return e==null?"—":`${e.toFixed(4)}°`}function ss(e){if(e==null||typeof e!="number"||Number.isNaN(e)||e<0)return null;if(e>=1)return 0;if(e===0)return 100;const s=(1-e)*100;if(s<=100-1e-12)return Math.min(100,Math.max(0,s));const n=-Math.log10(Math.max(e,Number.MIN_VALUE)),r=14,u=Math.min(1,Math.max(0,(n-r)/(50-r)));return Math.min(100,Math.max(0,70+30*u))}function Nn(e){const s=ss(e);return s==null?"—":`${s.toFixed(1)}%`}function _n(e){if(e==null)return"—";if(typeof e=="number"&&!Number.isNaN(e)){const s=Math.abs(e);return s>0&&s<.001?e.toExponential(4):s>=0&&s<=1?`${(e*100).toFixed(4)}%`:String(e)}return String(e)}function ge(e,s){const n=s==null?void 0:s.tetra,r=n?n.Prob??n.prob:void 0,o=Nn(e),u=r!=null?_n(r):null;return{line:o,rawLine:u}}function st({result:e,t:s,roundTripMs:n}){const r=as(e??void 0);return e?t.jsxs("div",{className:"mt-2 space-y-2 text-[10px]",children:[t.jsxs("div",{className:"grid grid-cols-2 gap-2",children:[r.tBackendTotalMs!=null&&t.jsxs("div",{children:[t.jsx("div",{className:"text-on-surface-variant",children:s("lab.metric.backendTotalMs")}),t.jsxs("div",{className:"font-semibold tabular-nums text-on-surface",children:[r.tBackendTotalMs.toFixed(0)," ms"]})]}),r.tSolveMs!=null&&t.jsxs("div",{children:[t.jsx("div",{className:"text-on-surface-variant",children:s("lab.metric.solveComputeMs")}),t.jsxs("div",{className:"font-semibold tabular-nums text-on-surface",children:[r.tSolveMs.toFixed(0)," ms"]})]}),r.tOpenDecodeMs!=null&&t.jsxs("div",{children:[t.jsx("div",{className:"text-on-surface-variant",children:s("lab.metric.openDecodeMs")}),t.jsxs("div",{className:"font-semibold tabular-nums text-on-surface",children:[r.tOpenDecodeMs.toFixed(0)," ms"]})]}),r.tPreprocessMs!=null&&t.jsxs("div",{children:[t.jsx("div",{className:"text-on-surface-variant",children:s("lab.metric.preprocessMs")}),t.jsxs("div",{className:"font-semibold tabular-nums text-on-surface",children:[r.tPreprocessMs.toFixed(0)," ms"]})]}),r.tExtractMs!=null&&t.jsxs("div",{children:[t.jsx("div",{className:"text-on-surface-variant",children:s("lab.metric.extractMs")}),t.jsxs("div",{className:"font-semibold tabular-nums text-on-surface",children:[r.tExtractMs.toFixed(0)," ms"]})]}),n!=null&&t.jsxs("div",{children:[t.jsx("div",{className:"text-on-surface-variant",children:s("lab.metric.solveRoundTripMs")}),t.jsxs("div",{className:"font-semibold tabular-nums text-on-surface",children:[n.toFixed(0)," ms"]})]}),r.matches!=null&&t.jsxs("div",{children:[t.jsx("div",{className:"text-on-surface-variant",children:s("lab.metric.matches")}),t.jsx("div",{className:"font-semibold tabular-nums text-on-surface",children:r.matches})]}),r.rmseArcsec!=null&&t.jsxs("div",{children:[t.jsx("div",{className:"text-on-surface-variant",children:s("lab.metric.rmse")}),t.jsxs("div",{className:"font-semibold tabular-nums text-on-surface",children:[r.rmseArcsec.toFixed(2),"″"]})]}),r.prob!=null&&t.jsxs("div",{className:"col-span-2",children:[t.jsx("div",{className:"text-on-surface-variant",children:s("lab.metric.prob")}),t.jsx("div",{className:"font-semibold text-on-surface",children:ge(r.prob,e).line}),t.jsx("p",{className:"mt-0.5 text-[9px] leading-snug text-on-surface-variant/90",children:s("lab.metric.probHelp")}),ge(r.prob,e).rawLine&&t.jsxs("div",{className:"mt-1 text-[9px] text-on-surface-variant",children:[s("lab.metric.probRaw"),": ",ge(r.prob,e).rawLine,t.jsx("p",{className:"mt-0.5 text-[8px] leading-snug opacity-90",children:s("lab.metric.probRawHelp")})]})]})]}),e.centroid_quality!=null&&typeof e.centroid_quality=="object"&&t.jsxs("div",{className:"rounded border border-outline-variant/25 bg-surface-container-high/40 p-2",children:[t.jsxs("div",{className:"text-[9px] font-medium text-on-surface-variant",children:[s("lab.centroidQualityTitle"),typeof e.centroid_quality.level=="number"?` · L${e.centroid_quality.level}`:""]}),Array.isArray(e.centroid_quality.hints)&&e.centroid_quality.hints.length>0?t.jsx("ul",{className:"mt-1 list-inside list-disc text-[9px] leading-snug text-on-surface-variant",children:e.centroid_quality.hints.map((o,u)=>t.jsx("li",{children:o},u))}):null,(()=>{const o=e.centroid_quality.metrics;if(!o)return null;const u=o.input_count,c=o.output_count,g=o.removed_dense,x=o.removed_line;return typeof u!="number"||typeof c!="number"||typeof g!="number"||typeof x!="number"?null:t.jsx("p",{className:"mt-1 font-mono text-[9px] text-on-surface-variant/95",children:s("lab.centroidQualityMetrics",{in:u,out:c,dense:g,line:x})})})()]}),t.jsxs("div",{children:[t.jsx("div",{className:"text-on-surface-variant",children:s("lab.metric.radec")}),t.jsxs("div",{className:"font-mono text-[9px] text-on-surface",children:["α ",it(r.raDeg)," · δ ",it(r.decDeg)]})]}),r.status&&t.jsxs("div",{className:"rounded bg-surface-container-high px-2 py-1 text-[9px] font-mono text-on-surface",children:[s("lab.metric.status"),": ",r.status]})]}):t.jsx("p",{className:"mt-2 text-[10px] text-on-surface-variant",children:"—"})}function W({label:e,helpKey:s,value:n,onChange:r,type:o="number",step:u}){const{t:c}=$e(),g=s?c(s):void 0;return t.jsxs("label",{className:"block",children:[t.jsx("span",{className:"text-[10px] font-medium text-on-surface-variant",children:e}),g&&t.jsx("p",{className:"mb-1 mt-0.5 text-[9px] leading-snug text-on-surface-variant/85",children:g}),t.jsx("input",{type:o,step:u,className:"w-full rounded bg-surface-container-highest px-2 py-1",value:n===""?"":n,onChange:x=>{const h=x.target.value;r(h===""?void 0:Number(h))}})]})}function Sn({view:e,setView:s}){const{t:n,locale:r,setLocale:o}=$e();return t.jsxs("header",{className:"flex h-12 shrink-0 items-center justify-between border-b border-outline-variant/20 px-4",children:[t.jsxs("div",{className:"flex items-center gap-6",children:[t.jsx("span",{className:"font-headline text-sm font-bold tracking-wide text-on-surface",children:n("app.title")}),t.jsxs("nav",{className:"flex flex-wrap gap-3 text-xs",children:[t.jsx("button",{type:"button",className:e==="lab_image"?"text-primary":"text-on-surface-variant",onClick:()=>s("lab_image"),children:n("nav.labImage")}),t.jsx("button",{type:"button",className:e==="lab_video"?"text-primary":"text-on-surface-variant",onClick:()=>s("lab_video"),children:n("nav.labVideo")}),t.jsx("button",{type:"button",className:e==="pool"?"text-primary":"text-on-surface-variant",onClick:()=>s("pool"),children:n("nav.pool")}),t.jsx("button",{type:"button",className:e==="history"?"text-primary":"text-on-surface-variant",onClick:()=>s("history"),children:n("nav.history")})]})]}),t.jsxs("div",{className:"flex items-center gap-2",children:[t.jsxs("div",{className:"mr-2 flex gap-1 text-[10px]",children:[t.jsx("button",{type:"button",className:`rounded px-2 py-0.5 ${r==="zh"?"bg-primary-container text-on-primary-container":"text-on-surface-variant"}`,onClick:()=>o("zh"),children:n("lang.zh")}),t.jsx("button",{type:"button",className:`rounded px-2 py-0.5 ${r==="en"?"bg-primary-container text-on-primary-container":"text-on-surface-variant"}`,onClick:()=>o("en"),children:n("lang.en")})]}),t.jsxs("a",{className:"flex items-center gap-1 rounded border border-outline-variant/30 px-2 py-1 text-xs text-on-surface-variant hover:bg-surface-container",href:"/debug",children:[t.jsx(zs,{className:"h-3.5 w-3.5"})," ",n("nav.systemAdmin")]})]})]})}function kn({historyQ:e,setHistoryQ:s,historyPage:n,setHistoryPage:r,historyData:o,historyExpandId:u,setHistoryExpandId:c,historyTotalPages:g,onDeleteExperiment:x,onSearch:h}){var C;const{t:N,locale:m}=$e();return t.jsxs("main",{className:"og-scrollbar flex-1 overflow-auto p-4",children:[t.jsx("p",{className:"mb-4 rounded-lg border border-outline-variant/25 bg-surface-container-lowest/90 p-3 text-xs leading-relaxed text-on-surface-variant",children:N("history.intro")}),t.jsxs("div",{className:"mb-4 flex flex-wrap items-center gap-4",children:[t.jsxs("h2",{className:"flex items-center gap-2 text-lg font-semibold",children:[t.jsx(Hs,{className:"h-5 w-5"})," ",N("history.title")]}),t.jsx("input",{className:"rounded bg-surface-container-highest px-2 py-1 text-xs",placeholder:N("history.search"),value:e,onChange:v=>s(v.target.value)}),t.jsx("button",{type:"button",className:"rounded bg-surface-container px-2 py-1 text-xs",onClick:h,children:N("history.searchBtn")}),t.jsx("button",{type:"button",className:"rounded bg-primary-container/50 px-2 py-1 text-xs",onClick:()=>Va("json").then(v=>{const I=new Blob([v],{type:"application/json"}),R=document.createElement("a");R.href=URL.createObjectURL(I),R.download="experiments.json",R.click()}),children:N("history.exportJson")}),t.jsx("button",{type:"button",className:"rounded bg-primary-container/50 px-2 py-1 text-xs",onClick:()=>Va("csv").then(v=>{const I=new Blob([v],{type:"text/csv"}),R=document.createElement("a");R.href=URL.createObjectURL(I),R.download="experiments.csv",R.click()}),children:N("history.exportCsv")})]}),t.jsxs("div",{className:"flex flex-wrap items-center gap-3 text-xs text-on-surface-variant",children:[t.jsx("span",{children:N("history.total",{n:(o==null?void 0:o.total)??0})}),t.jsx("button",{type:"button",className:"rounded border border-outline-variant/30 px-2 py-0.5 disabled:opacity-40",disabled:n<=1,onClick:()=>r(v=>Math.max(1,v-1)),children:N("history.prev")}),t.jsxs("span",{children:[n," / ",g]}),t.jsx("button",{type:"button",className:"rounded border border-outline-variant/30 px-2 py-0.5 disabled:opacity-40",disabled:n>=g,onClick:()=>r(v=>v+1),children:N("history.next")})]}),t.jsx("ul",{className:"mt-2 space-y-2",children:(C=o==null?void 0:o.items)==null?void 0:C.map(v=>{const I=String(v.id??""),R=v.metrics,B=u===I;return t.jsxs("li",{className:"rounded border border-outline-variant/20 p-2 text-[11px]",children:[t.jsxs("div",{className:"flex flex-wrap items-start justify-between gap-2 font-mono",children:[t.jsxs("div",{children:[t.jsxs("div",{className:"text-on-surface",children:[Pe(String(v.created_at??""),m)," —"," ",String(v.input_name)," — ",String(v.preset_label)]}),t.jsxs("div",{className:"mt-1 text-on-surface-variant",children:[N("history.preset"),": ",String(v.preset_label)," · ",N("history.metrics"),":"," ","matches=",String((R==null?void 0:R.matches)??"—")," rmse=",String((R==null?void 0:R.rmse_arcsec)??"—")]})]}),t.jsxs("div",{className:"flex shrink-0 gap-1",children:[t.jsx("button",{type:"button",className:"rounded bg-surface-container px-2 py-0.5 text-[10px]",onClick:()=>c(B?null:I),children:N(B?"history.collapse":"history.detail")}),t.jsx("button",{type:"button",className:"rounded px-2 py-0.5 text-[10px] text-error hover:bg-error-container/20",title:N("history.delete"),onClick:()=>void x(I),children:N("history.delete")})]})]}),B&&t.jsxs("div",{className:"mt-2 space-y-2",children:[v.asset_snapshot_relpath?t.jsx("img",{src:rn(I),alt:"",className:"max-h-48 max-w-full rounded border border-outline-variant/20 object-contain"}):null,t.jsx("pre",{className:"og-scrollbar max-h-64 overflow-auto rounded bg-surface-container p-2 text-[10px]",children:JSON.stringify(v.result_json,null,2)})]})]},I)})})]})}function En({uploads:e,onDelete:s}){const{t:n,locale:r}=$e();return t.jsxs("main",{className:"og-scrollbar flex-1 overflow-auto p-4",children:[t.jsxs("h2",{className:"mb-4 flex items-center gap-2 text-lg font-semibold",children:[t.jsx(Bs,{className:"h-5 w-5"})," ",n("pool.title")]}),t.jsxs("table",{className:"w-full text-left text-xs",children:[t.jsx("thead",{children:t.jsxs("tr",{className:"border-b border-outline-variant/30 text-on-surface-variant",children:[t.jsx("th",{className:"py-2",children:n("pool.col.name")}),t.jsx("th",{className:"py-2",children:n("pool.col.source")}),t.jsx("th",{className:"py-2",children:n("pool.col.size")}),t.jsx("th",{className:"py-2",children:n("pool.col.time")}),t.jsx("th",{className:"w-16 py-2 text-center",children:n("pool.delete")})]})}),t.jsx("tbody",{children:e.map(o=>t.jsxs("tr",{className:"border-b border-outline-variant/10",children:[t.jsx("td",{className:"py-2 font-mono",children:o.filename}),t.jsx("td",{className:"py-2",children:o.source??n("common.placeholder")}),t.jsx("td",{className:"py-2",children:Jt(o.size)}),t.jsx("td",{className:"py-2",children:Pe(o.modified_at,r)}),t.jsx("td",{className:"py-2 text-center",children:t.jsx("button",{type:"button",className:"inline-flex rounded p-1 text-on-surface-variant hover:bg-error-container/30 hover:text-error",title:n("pool.delete"),"aria-label":n("pool.delete"),onClick:()=>void s(o.filename),children:t.jsx(es,{className:"h-3.5 w-3.5"})})})]},o.filename))})]})]})}function Gt(e){return e?{matches:e.matches,rmse_arcsec:e.rmse_arcsec,status:e.status,prob:e.prob,t_solve_ms:e.t_solve_ms}:{}}function Rn(e){var n,r;if(!e)return null;const s=e.solve_overlay;return(n=s==null?void 0:s.stars_matched)!=null&&n.length?s.stars_matched.length:(r=s==null?void 0:s.stars_all_centroids)!=null&&r.length?s.stars_all_centroids.length:typeof e.matches=="number"?e.matches:null}function Za(e){return/\.(jpe?g|png|webp|bmp|gif|fits?)$/i.test(e)}function ve(e){return/\.(mp4|mov|webm|mkv|avi)$/i.test(e)}function nt(e,s=16,n=14){return e.length<=s+n+1?e:`${e.slice(0,s)}...${e.slice(-n)}`}const Ya=10,rt=30,Kt=6,qt=()=>({hint_ra_deg:45,hint_dec_deg:80,fov_estimate:11,fov_max_error:void 0,solve_timeout_ms:1500,solve_profile:"balanced",max_image_side:1280,large_scale_bg_subtract:!1,detail_level:"summary",centroid_rejection_level:3,centroid:{sigma:2.5,max_area:400,min_area:5,filtsize:25,binary_open:!0,max_axis_ratio:void 0}});function Xa(e,s,n,r){const o=typeof r=="number"&&r>0?` ${e("lab.gateNextMs",{ms:r})}`:"",u=String(n??"").toLowerCase();return s==="SKIPPED_BUSY"?u.includes("recording")?`${e("lab.gate.skipRecording")}${o}`:u.includes("previous")?`${e("lab.gate.skipBusyInFlight")}${o}`:`${e("lab.gate.skipBusyFallback",{detail:String(n??"")})}${o}`:s==="SKIPPED_INTERVAL"?`${e("lab.gate.skipInterval")}${o}`:`${s??"?"}${n?`: ${String(n)}`:""}${o}`}async function Qa(){try{const e=await fetch("/api/dev/debug/camera/stream/status",{cache:"no-store",credentials:"same-origin"});if(!e.ok)return"fail";const s=await e.json();if(s.success===!1)return"fail";const n=Number(s.max_clients??0),r=Number(s.active_clients??0);return n>0&&r>=n?"busy":"ok"}catch{return"fail"}}function Mn(){var Ma,Ca,Ia,La,Da,Fa,Oa;const{t:e,locale:s}=$e(),[n,r]=l.useState("lab_image"),[o,u]=l.useState([]),[c,g]=l.useState(null),[x,h]=l.useState(qt),[N,m]=l.useState([]),[C,v]=l.useState([]),[I,R]=l.useState(new Set),[B,U]=l.useState(!1),[ot,y]=l.useState(null),[S,ye]=l.useState(null),[T,we]=l.useState(null),[Te,Zt]=l.useState([]),[ns,Yt]=l.useState([]),Xt=l.useRef(null),Qt=l.useRef(null),[je,rs]=l.useState(""),[Ae,ea]=l.useState(1),[Ue,ct]=l.useState(null),[ta,aa]=l.useState(null),[dt,sa]=l.useState(""),[G,is]=l.useState({matched:!0,pattern:!0,all:!0,rejected:!0}),[Be,na]=l.useState([]),[H,ra]=l.useState(null),[Ne,_e]=l.useState(1),[He,ia]=l.useState(!1),[me,ut]=l.useState(1),[la,ls]=l.useState({w:0,h:0}),[xe,os]=l.useState({w:0,h:0}),[oa,ca]=l.useState({w:0,h:0}),[k,Se]=l.useState("file"),[da,ze]=l.useState(()=>Date.now()),[mt,K]=l.useState("unknown"),[ke,xt]=l.useState(null),[Ee,ua]=l.useState(!1),[pe,ma]=l.useState(!1),[xa,cs]=l.useState(!1),[q,pt]=l.useState(!1),[ds,ft]=l.useState(null),[ht,bt]=l.useState(null),[vt,We]=l.useState(null),[gt,pa]=l.useState(!1),[us,yt]=l.useState({}),[fa,wt]=l.useState(!1),[jt,Z]=l.useState(null),[ms,Re]=l.useState(null),[Nt,Me]=l.useState(null),[_t,ha]=l.useState(null),[ba,va]=l.useState(!1),[ga,St]=l.useState(null),[xs,Q]=l.useState(null),[ya,wa]=l.useState(!1),[kt,ne]=l.useState(null),[ps,z]=l.useState(null),[Et,Rt]=l.useState(null),Mt=l.useRef(null),Ce=l.useRef(null),Ie=l.useRef(null),Ct=l.useRef(0),re=l.useRef(null),ie=l.useRef(null),It=l.useRef(!1),Lt=l.useRef(!1),Dt=l.useRef(!1),Ft=l.useRef(!1),fe=l.useRef(null),fs=l.useCallback(async()=>{const a=Ie.current;if(!a||a.naturalWidth<2)return null;const i=document.createElement("canvas");i.width=a.naturalWidth,i.height=a.naturalHeight;const d=i.getContext("2d");if(!d)return null;try{d.drawImage(a,0,0)}catch{return null}return await new Promise(p=>{i.toBlob(w=>p(w?URL.createObjectURL(w):null),"image/jpeg",.92)})},[]),[Ve,hs]=l.useState(null),[L,ja]=l.useState(null),Ge=l.useCallback((a,i)=>{const d=`${Date.now()}-${Math.random().toString(36).slice(2,9)}`;Yt(p=>[...p,{id:d,text:a,variant:i}]),window.setTimeout(()=>{Yt(p=>p.filter(w=>w.id!==d))},4800)},[]);l.useEffect(()=>{Xt.current=S},[S]),l.useEffect(()=>{Qt.current=T},[T]),l.useEffect(()=>{Dt.current=pe},[pe]),l.useEffect(()=>{Ft.current=Ee},[Ee]),l.useEffect(()=>{ot&&(Ge(ot,"error"),y(null))},[ot,Ge]),l.useEffect(()=>{Nt&&(Ge(Nt,"warn"),Me(null))},[Nt,Ge]);const Ke=l.useCallback(()=>{const a=Xt.current,i=Qt.current;if(!a&&!i)return;const d=`${Date.now()}-${Math.random().toString(36).slice(2,9)}`;Zt(p=>[{id:d,at:Date.now(),single:a??void 0,batch:i??void 0},...p].slice(0,Ya))},[]),qe=()=>{ie.current!=null&&(window.clearTimeout(ie.current),ie.current=null)},Je=()=>{re.current!=null&&(window.clearTimeout(re.current),re.current=null)},Ot=(L==null?void 0:L.star_analysis_min_interval_ms)??2e3,Pt=(L==null?void 0:L.star_analysis_max_interval_ms)??12e3,$t=l.useMemo(()=>{const a=(L==null?void 0:L.star_analysis_target_fps)??.6666666666666666,i=Math.min(Math.max(a,.2),5);return Math.round(1e3/i)},[L]),le=l.useMemo(()=>Math.min(Math.max(_t??$t,Ot),Pt),[$t,_t,Pt,Ot]),ee=l.useCallback(async()=>{const[a,i,d]=await Promise.all([Vs(),Bt("official"),Bt("user")]);u(a.files),m(i.presets),v(d.presets)},[]),Tt=l.useCallback(()=>{Xs().then(a=>na(a.files)).catch(()=>na([]))},[]);l.useEffect(()=>{an().then(a=>{ja(a);const i=Math.round(1e3/Math.min(Math.max(a.star_analysis_target_fps,.2),5)),d=Math.min(Math.max(i,a.star_analysis_min_interval_ms),a.star_analysis_max_interval_ms);ha(d);const p=a.centroid_rejection_level_default;typeof p=="number"&&p>=1&&p<=5&&h(w=>({...w,centroid_rejection_level:p}))}).catch(()=>ja(null))},[]),l.useEffect(()=>{ee().catch(a=>y(String(a)))},[ee]),l.useEffect(()=>{Tt()},[Tt]),l.useEffect(()=>{n==="history"&&zt(je,Ae,rt).then(ct).catch(a=>y(String(a)))},[n,je,Ae]),l.useEffect(()=>{if(!c){We(null);return}let a=!1;return pa(!0),(async()=>{try{const i=await en(c);if(a)return;let d={...i};try{d={...await Wa(c),...i}}catch{}We(d)}catch{try{const i=await Wa(c);a||We(i)}catch{a||We(null)}}finally{a||pa(!1)}})(),()=>{a=!0}},[c]),l.useEffect(()=>{ye(null),we(null),yt({}),wt(!1),Z(null),Re(null),Se("file"),xt(null),Zt([])},[c]);const Y=l.useMemo(()=>{const a=S==null?void 0:S.result;if(!a)return null;const i=a.solve_overlay||null;if(!i)return null;const d=a.overlay_ext||null;return{...i,overlay_ext:d||void 0}},[S]),te=l.useMemo(()=>(S==null?void 0:S.result)??null,[S]),Na=l.useMemo(()=>Rn(te),[te]),At=l.useMemo(()=>n==="lab_image"?la:n==="lab_video"?k==="file"?xe:oa:{w:0,h:0},[n,k,la,xe,oa]),Ze=c?Wt(c):"";l.useEffect(()=>{if(n!=="lab_image")return;const a=Mt.current,i=fe.current;if(!a||!i||!Y||!c)return;const d=()=>Ga(i,a,Y,G);a.complete?d():a.onload=d},[Y,c,S,G,n]),l.useEffect(()=>{if(n!=="lab_video"||k!=="file")return;const a=Ce.current,i=fe.current;if(!a||!i||!Y||!c)return;const d=()=>on(i,a,Y,G);return a.addEventListener("loadeddata",d),a.addEventListener("seeked",d),a.readyState>=2&&d(),()=>{a.removeEventListener("loadeddata",d),a.removeEventListener("seeked",d)}},[Y,G,n,k,c,S]),l.useEffect(()=>{if(n!=="lab_video"||k!=="camera")return;const a=Ie.current,i=fe.current;if(!a||!i||!Y)return;const d=()=>Ga(i,a,Y,G);a.complete?d():a.onload=d},[Y,G,n,k,S,da,q]),l.useEffect(()=>{if(n!=="lab_video"||k!=="camera"){K("unknown");return}if(q){K("ok");return}let a=!1;return K("unknown"),(async()=>{try{const i=await Qa();if(a)return;if(i==="fail"){K("fail");return}if(i==="busy"){K("busy");return}K("ok"),Ct.current=0,ze(Date.now())}catch{a||K("fail")}})(),()=>{a=!0}},[n,k,q]),l.useEffect(()=>{const a=Mt.current;if(!a)return;const i=()=>ls({w:a.naturalWidth||0,h:a.naturalHeight||0});return i(),a.addEventListener("load",i),()=>a.removeEventListener("load",i)},[c,Ze]),l.useEffect(()=>{const a=Ce.current;if(!a)return;const i=()=>os({w:a.videoWidth||0,h:a.videoHeight||0});return a.addEventListener("loadedmetadata",i),a.addEventListener("loadeddata",i),i(),()=>{a.removeEventListener("loadedmetadata",i),a.removeEventListener("loadeddata",i)}},[c,Ze,n]);const bs=a=>{h({...qt(),...a,centroid:{...qt().centroid,...a.centroid}})},vs=async()=>{if(!c){y(e("err.selectFile"));return}y(null),U(!0);const a=performance.now();try{const i=await qs(c,x);Ke(),ye(i),we(null),Re("file"),Z(performance.now()-a)}catch(i){y(String(i)),Z(null)}finally{U(!1)}},gs=async()=>{if(!c){y(e("err.selectFile"));return}const a=[];for(const d of I){const w=[...N,...C].find($=>$.id===d);w&&a.push({label:w.name,params:structuredClone(w.params)})}if(a.length===0){y(e("err.selectPresets"));return}y(null),U(!0);const i=performance.now();try{const d=await Js(c,a);Ke(),we({results:d.results}),ye(null),yt({}),Re("file"),Z(performance.now()-i)}catch(d){y(String(d)),Z(null)}finally{U(!1)}},Ye=async a=>{if(It.current)return;y(null),It.current=!0;const i=performance.now();try{const d=await sn({source:"camera",overlay_topn_count:3,enable_polar_guide:!0,solve_interval_ms:le,solve_timeout_ms:Math.min(((L==null?void 0:L.solver_timeout_ms)??1500)*.6,1200),...x}),p=d.gate_status,w=d.next_allowed_in_ms,$=d.effective_interval_ms;if(p==="SKIPPED_INTERVAL"||p==="SKIPPED_BUSY"){if(Me(Xa(e,p,d.gate_reason,typeof w=="number"?w:null)),a){Je();const M=Math.max(50,Number(w??le));re.current=window.setTimeout(()=>{re.current=null,Ft.current&&Ye(!0)},M)}return}Me(null),d.result!=null&&(Ke(),ye(d)),we(null),Re("camera"),Se("camera");const E=d.result,D=typeof(E==null?void 0:E.status)=="string"?String(E.status):"";if(xa&&D==="MATCH_FOUND"){pt(!0),ft(d.frame_id!=null?String(d.frame_id):null);const M=await fs();bt(oe=>(oe&&URL.revokeObjectURL(oe),M)),et()}else if(a){Je();const M=Math.max(50,Number(w??$??le));re.current=window.setTimeout(()=>{re.current=null,Ft.current&&Ye(!0)},M)}Z(performance.now()-i)}catch(d){y(String(d)),Z(null)}finally{It.current=!1}},ys=()=>void Ye(!1),Xe=async a=>{if(Lt.current||!c)return;const i=Ce.current;if(!i||i.videoWidth<2||i.videoHeight<2||ke)return;y(null),Lt.current=!0;const d=performance.now();try{const p=document.createElement("canvas");p.width=i.videoWidth,p.height=i.videoHeight;const w=p.getContext("2d");if(!w)throw new Error("无法创建画布上下文 / Cannot create canvas context");w.drawImage(i,0,0,p.width,p.height);const $=await new Promise((be,Fs)=>{p.toBlob(Pa=>Pa?be(Pa):Fs(new Error("帧编码失败 / Frame encode failed")),"image/jpeg",.92)}),E=await nn($,{...x,solve_interval_ms:le,solve_timeout_ms:Math.min(((L==null?void 0:L.solver_timeout_ms)??1500)*.6,1200),overlay_topn_count:3,enable_polar_guide:!0}),D=E.gate_status,M=E.next_allowed_in_ms,oe=E.effective_interval_ms;if(D==="SKIPPED_INTERVAL"||D==="SKIPPED_BUSY"){if(Me(Xa(e,D,E.gate_reason,typeof M=="number"?M:null)),a){qe();const be=Math.max(50,Number(M??le));ie.current=window.setTimeout(()=>{ie.current=null,Dt.current&&Xe(!0)},be)}return}if(Me(null),E.result!=null&&(Ke(),ye(E)),we(null),Re("file"),Z(performance.now()-d),a){qe();const be=Math.max(50,Number(M??oe??le));ie.current=window.setTimeout(()=>{ie.current=null,Dt.current&&Xe(!0)},be)}}catch(p){y(String(p)),Z(null)}finally{Lt.current=!1}},ws=()=>void Xe(!1),js=()=>{pe||!c||(ma(!0),qe(),Xe(!0))},_a=l.useMemo(()=>n!=="lab_video"||k!=="file"||!c||/\.avi$/i.test(c)||ke?!1:xe.w>1&&xe.h>1,[n,k,c,ke,xe.w,xe.h]),Sa=l.useMemo(()=>!!c&&/\.avi$/i.test(c),[c]),Ns=async()=>{if(!(!c||!Sa||ba)){y(null),va(!0),St(0),Q(e("lab.transcode.loading"));try{const a=Wt(c),i=await fetch(a,{cache:"no-store"});if(!i.ok)throw new Error(e("lab.transcode.fetchFailed"));const d=await i.blob(),p=new File([d],c,{type:"video/x-msvideo"}),w=await Ja(p,(E,D)=>{St(Math.max(0,Math.min(1,E))),D==="loading_ffmpeg"?Q(e("lab.transcode.loading")):D==="writing_input"?Q(e("lab.transcode.writingBuffer")):D==="transcoding"?Q(e("lab.transcode.running")):D==="packing_output"&&Q(e("lab.transcode.packaging"))});Q(e("lab.transcode.uploading"));const $=await Ut(w.file,"analysis_transcoded");await za({old_filename:c,new_filename:$.filename,duration_s:w.duration_s,nominal_fps:null,codec_fourcc:"libx264",container:"MP4"}),await ee(),g($.filename),St(1),Q(e("lab.transcode.done"))}catch(a){y(String(a)),Q(e("lab.transcode.failed"))}finally{va(!1)}}},_s=async a=>{z(e("sidebar.flowDownloadingPool")),ne(.19);const i=Wt(a),d=await fetch(i,{cache:"no-store"});if(!d.ok)throw new Error(e("lab.transcode.fetchFailed"));const p=await d.blob(),w=new File([p],a,{type:"video/x-msvideo"}),$=await Ja(w,(D,M)=>{ne(.2+Math.max(0,Math.min(1,D))*.6),M==="loading_ffmpeg"?z(e("sidebar.flowLoadingTranscoder")):M==="writing_input"?z(e("sidebar.flowWritingBuffer")):M==="transcoding"?z(e("sidebar.flowTranscoding")):M==="packing_output"&&z(e("sidebar.flowPackaging"))});z(e("sidebar.flowUploadingMp4")),ne(.86);const E=await Ut($.file,"debug_console_transcoded");return z(e("sidebar.flowReplacing")),ne(.94),await za({old_filename:a,new_filename:E.filename,duration_s:$.duration_s,nominal_fps:null,codec_fourcc:"libx264",container:"MP4"}),E.filename},Ss=async()=>{if(!H||ya)return;const a=/\.avi$/i.test(H);y(null),wa(!0),ne(.02),Rt(null);try{z(e("sidebar.flowImportingDebug"));const i=await Ks(H);ne(a?.18:1);let d=i.filename;a?d=await _s(i.filename):z(e("sidebar.flowNoTranscode")),await ee(),g(d),r(ve(d)?"lab_video":"lab_image"),ne(1),z(e("sidebar.flowDone")),Rt(e("sidebar.flowDoneMsg",{name:nt(d)}))}catch(i){y(String(i)),z(e("sidebar.flowFailed")),Rt(String(i))}finally{wa(!1)}},Qe=()=>{ma(!1),qe()},ks=()=>{Ee||q||(ua(!0),Je(),Ye(!0))},et=()=>{ua(!1),Je()},Es=async()=>{try{const a=Ie.current;a&&(a.onload=null,a.onerror=null,a.src="",a.removeAttribute("src")),await new Promise(i=>window.requestAnimationFrame(()=>i())),await fetch("/api/dev/debug/camera/stop",{method:"POST"}),ze(Date.now()),k==="camera"&&Se("file")}catch{}},Rs=()=>{pt(!1),ft(null),bt(a=>(a&&URL.revokeObjectURL(a),null)),ze(Date.now())},Ms=a=>{R(i=>{const d=new Set(i);return d.has(a)?d.delete(a):d.add(a),d})},ka=async a=>{if(!window.confirm(e("delete.uploadFirst",{name:a})))return;let i=0;try{i=(await tn(a)).count}catch{i=0}if(i>0){if(!window.confirm(e("delete.uploadCascade",{n:i})))return}else if(!window.confirm(e("delete.uploadSecond")))return;U(!0),y(null);try{await Gs(a,{deleteExperiments:i>0}),await ee(),c===a&&g(null)}catch(d){y(String(d))}finally{U(!1)}},Cs=async a=>{if(window.confirm(e("delete.experimentFirst"))&&window.confirm(e("delete.experimentSecond"))){U(!0),y(null);try{await Ys(a),ta===a&&aa(null);const i=await zt(je,Ae,rt);ct(i)}catch(i){y(String(i))}finally{U(!1)}}},tt=a=>ut(Math.min(4,Math.max(.5,a))),Is=Math.max(1,Math.ceil(((Ue==null?void 0:Ue.total)??0)/rt)),he=l.useMemo(()=>n==="lab_image"?Be.filter(a=>a.type==="image"||Za(a.name)):n==="lab_video"?Be.filter(a=>a.type==="video"||ve(a.name)):Be,[Be,n]),Le=Math.max(1,Math.ceil(he.length/Kt)),Ls=l.useMemo(()=>{const a=(Ne-1)*Kt;return he.slice(a,a+Kt)},[he,Ne]);l.useEffect(()=>{_e(1)},[n]),l.useEffect(()=>{_e(a=>Math.min(a,Le))},[Le]),l.useEffect(()=>{H&&!he.some(a=>a.name===H)&&ra(null)},[he,H]);const Ea=l.useMemo(()=>jn(vt,s),[vt,s]),Ds=l.useMemo(()=>n==="lab_image"?o.filter(a=>Za(a.filename)):n==="lab_video"?o.filter(a=>ve(a.filename)):o,[o,n]),b=l.useMemo(()=>as(te),[te]),Ra=l.useMemo(()=>ss(b.prob),[b.prob]);return l.useEffect(()=>{wt(!1)},[S]),l.useEffect(()=>{if(n!=="lab_video")return;let a;const i=()=>{ln().then(hs).catch(()=>{})};return i(),a=setInterval(i,1500),()=>{a&&clearInterval(a)}},[n]),l.useEffect(()=>{(n!=="lab_video"||k!=="camera")&&(et(),pt(!1),ft(null),bt(a=>(a&&URL.revokeObjectURL(a),null))),(n!=="lab_video"||k!=="file")&&Qe()},[n,k]),l.useEffect(()=>{c||Qe()},[c]),l.useEffect(()=>()=>{et(),Qe()},[]),t.jsxs("div",{className:"flex min-h-full flex-col bg-surface text-on-surface",children:[t.jsx(Sn,{view:n,setView:r}),t.jsxs("div",{className:"flex min-h-0 flex-1",children:[t.jsxs("aside",{className:"w-64 shrink-0 border-r border-outline-variant/15 bg-surface-container-lowest p-3 text-xs",children:[t.jsx("div",{className:"mb-3 font-semibold text-on-surface-variant",children:e("sidebar.assets")}),t.jsxs("label",{className:"mb-3 flex cursor-pointer items-center gap-2 rounded bg-primary-container/30 px-2 py-2 text-on-primary-container",children:[t.jsx(Ws,{className:"h-4 w-4"}),t.jsx("span",{children:e("sidebar.upload")}),t.jsx("input",{type:"file",accept:"image/*,video/*",className:"hidden",onChange:async a=>{var d;const i=(d=a.target.files)==null?void 0:d[0];if(i){U(!0),y(null);try{const p=await Ut(i);await ee(),g(p.filename)}catch(p){y(String(p))}finally{U(!1),a.target.value=""}}}})]}),t.jsxs("button",{type:"button",className:"mb-2 flex w-full items-center gap-1 text-left text-on-surface-variant hover:text-on-surface",onClick:()=>{ee().catch(a=>y(String(a))),_e(1),Tt()},children:[t.jsx(Us,{className:"h-3 w-3"})," ",e("sidebar.refresh")]}),t.jsx("div",{className:"og-scrollbar max-h-36 overflow-y-auto border-t border-outline-variant/10 pt-2",children:Ds.map(a=>t.jsxs("div",{className:`mb-1 flex items-center gap-0.5 rounded px-1 ${c===a.filename?"bg-surface-container":""}`,children:[t.jsxs("button",{type:"button",className:`min-w-0 flex-1 truncate rounded px-1 py-1 text-left ${c===a.filename?"text-primary":""}`,title:a.filename,onClick:()=>{g(a.filename),r(ve(a.filename)?"lab_video":"lab_image")},children:[t.jsx("span",{className:"block truncate",children:nt(a.filename)}),t.jsx("span",{className:"block text-[9px] text-on-surface-variant/90",children:ve(a.filename)?e("sidebar.assetTypeVideo"):e("sidebar.assetTypeImage")})]}),t.jsx("button",{type:"button",className:"shrink-0 rounded p-1 text-on-surface-variant hover:bg-surface-container-high hover:text-error",title:e("pool.delete"),"aria-label":e("pool.delete"),onClick:i=>{i.stopPropagation(),ka(a.filename)},children:t.jsx(es,{className:"h-3.5 w-3.5"})})]},a.filename))}),t.jsxs("div",{className:"mt-3 border-t border-outline-variant/10 pt-3",children:[t.jsxs("div",{className:"mb-2 flex items-start justify-between gap-2",children:[t.jsx("div",{className:"min-w-0 font-semibold leading-tight text-on-surface-variant",children:e("sidebar.debugCaptures")}),t.jsx("button",{type:"button",className:"shrink-0 rounded bg-primary px-2 py-1 text-[10px] font-medium text-on-primary disabled:opacity-40",disabled:!H||ya,title:H??void 0,onClick:()=>void Ss(),children:H&&/\.avi$/i.test(H)?e("sidebar.importToPoolWithTranscode"):e("sidebar.importToPoolDirect")})]}),kt!=null&&t.jsxs("div",{className:"mb-2 rounded border border-outline-variant/25 bg-surface-container-low px-2 py-2",children:[t.jsxs("div",{className:"flex items-center justify-between gap-2 text-[10px]",children:[t.jsx("span",{className:"text-on-surface-variant",children:ps||e("sidebar.flowPreparing")}),t.jsxs("span",{className:"font-mono text-on-surface",children:[Math.round((kt||0)*100),"%"]})]}),t.jsx("div",{className:"mt-1.5 h-1.5 w-full overflow-hidden rounded bg-surface-container-highest",children:t.jsx("div",{className:"h-full bg-primary transition-all",style:{width:`${Math.round((kt||0)*100)}%`}})}),Et&&t.jsx("div",{className:"mt-1 text-[10px] text-on-surface-variant",title:Et,children:Et})]}),he.length===0?t.jsx("p",{className:"text-[10px] text-on-surface-variant",children:e("sidebar.debugEmpty")}):t.jsxs(t.Fragment,{children:[t.jsx("div",{className:"og-scrollbar max-h-[min(22rem,55vh)] space-y-1.5 overflow-y-auto pr-0.5",children:Ls.map(a=>t.jsxs("button",{type:"button",className:`flex w-full items-center gap-2 rounded-md border px-2 py-1.5 text-left transition-colors ${H===a.name?"border-primary bg-primary-container/20":"border-outline-variant/25 hover:bg-surface-container"}`,onClick:()=>ra(a.name),children:[a.type==="image"?t.jsx("img",{src:Qs(a.name),alt:"",className:"h-11 w-11 shrink-0 rounded object-cover"}):t.jsx("div",{className:"flex h-11 w-11 shrink-0 items-center justify-center rounded bg-surface-container-high text-[9px] text-on-surface-variant",children:"video"}),t.jsxs("div",{className:"min-w-0 flex-1",children:[t.jsx("div",{className:"truncate font-mono text-[10px] text-on-surface",title:a.name,children:nt(a.name)}),t.jsxs("div",{className:"text-[9px] text-on-surface-variant",children:[a.type==="video"||ve(a.name)?e("sidebar.assetTypeVideo"):e("sidebar.assetTypeImage")," ","· ",Pe(a.modified,s)," · ",Jt(a.size)]})]})]},a.name))}),t.jsxs("div",{className:"mt-2 flex items-center justify-between gap-1 text-[10px] text-on-surface-variant",children:[t.jsx("button",{type:"button",className:"rounded border border-outline-variant/30 px-2 py-0.5 disabled:opacity-40",disabled:Ne<=1,onClick:()=>_e(a=>Math.max(1,a-1)),children:e("history.prev")}),t.jsx("span",{className:"tabular-nums",children:e("sidebar.debugPage",{cur:Ne,total:Le})}),t.jsx("button",{type:"button",className:"rounded border border-outline-variant/30 px-2 py-0.5 disabled:opacity-40",disabled:Ne>=Le,onClick:()=>_e(a=>Math.min(Le,a+1)),children:e("history.next")})]})]})]})]}),(n==="lab_image"||n==="lab_video")&&t.jsxs("div",{className:"flex min-h-0 min-w-0 flex-1",children:[t.jsxs("main",{className:"og-scrollbar min-h-0 min-w-0 flex-1 overflow-y-auto p-4",children:[n==="lab_video"&&t.jsxs("div",{className:"mb-3 max-w-5xl rounded-lg border border-outline-variant/25 bg-surface-container-low/80 p-3 text-[11px] leading-relaxed text-on-surface",children:[t.jsx("p",{className:"text-[10px] text-on-surface-variant",children:e("lab.videoLiveIntro")}),t.jsx("p",{className:"mt-1.5 text-[10px] leading-snug text-on-surface-variant/95",children:e("lab.videoMjpegHint")}),t.jsxs("div",{className:"mt-2 flex flex-wrap gap-2",children:[t.jsx("button",{type:"button",className:`rounded px-3 py-1.5 text-[11px] font-medium ${k==="file"?"bg-primary text-on-primary-container":"border border-outline-variant/40 bg-surface-container text-on-surface"}`,onClick:()=>Se("file"),children:e("lab.previewModeFile")}),t.jsx("button",{type:"button",className:`rounded px-3 py-1.5 text-[11px] font-medium ${k==="camera"?"bg-primary text-on-primary-container":"border border-outline-variant/40 bg-surface-container text-on-surface"}`,onClick:()=>Se("camera"),children:e("lab.previewModeCamera")}),t.jsx("button",{type:"button",className:"rounded border border-outline-variant/40 bg-surface-container px-3 py-1.5 text-[11px] font-medium text-on-surface",onClick:()=>void Es(),children:e("lab.stopCameraPreview")})]})]}),t.jsxs("div",{className:"relative aspect-video w-full max-w-5xl overflow-hidden rounded-lg border border-outline-variant/20 bg-surface-container-lowest",children:[Ra!=null&&t.jsxs("div",{className:"pointer-events-none absolute right-2 top-2 z-20 rounded border border-white/30 bg-black/55 px-2 py-1 text-[10px] font-semibold tabular-nums text-white",children:[e("lab.previewConfidence"),": ",Ra.toFixed(1),"%"]}),n==="lab_video"&&k==="camera"?t.jsx("div",{className:"relative flex h-full min-h-[220px] flex-col items-center justify-center gap-3 bg-black p-2",children:t.jsxs("div",{className:"relative inline-block max-h-[70vh] max-w-full",children:[q&&ht?t.jsx("img",{ref:Ie,src:ht,alt:"",className:"max-h-[70vh] w-full min-h-[120px] object-contain",onLoad:a=>ca({w:a.currentTarget.naturalWidth,h:a.currentTarget.naturalHeight})}):q&&!ht?t.jsxs("div",{className:"flex min-h-[200px] w-full min-w-[280px] flex-col items-center justify-center gap-2 text-[11px] text-on-surface-variant",children:[t.jsx(at,{className:"h-8 w-8 animate-spin text-primary"}),t.jsx("span",{children:e("lab.cameraPreviewLoading")})]}):mt==="busy"?t.jsx("div",{className:"flex min-h-[200px] max-w-md flex-col items-center justify-center gap-2 px-4 text-center text-[11px] text-on-surface-variant",children:t.jsx("span",{className:"text-error",children:e("lab.videoMjpegHint")})}):mt==="fail"?t.jsx("div",{className:"flex min-h-[200px] max-w-md flex-col items-center justify-center gap-2 px-4 text-center text-[11px] text-on-surface-variant",children:t.jsx("span",{className:"text-error",children:e("lab.cameraStreamStatusFail")})}):mt==="unknown"?t.jsxs("div",{className:"flex min-h-[200px] w-full min-w-[280px] flex-col items-center justify-center gap-2 text-[11px] text-on-surface-variant",children:[t.jsx(at,{className:"h-8 w-8 animate-spin text-primary"}),t.jsx("span",{children:e("lab.cameraPreviewLoading")})]}):t.jsx("img",{ref:Ie,src:`/api/dev/debug/camera/stream?t=${da}`,alt:"",className:"max-h-[70vh] w-full min-h-[120px] object-contain",onLoad:a=>ca({w:a.currentTarget.naturalWidth,h:a.currentTarget.naturalHeight}),onError:()=>{(async()=>{const a=await Qa();if(a==="busy"){K("busy");return}if(a==="fail"){K("fail");return}if(Ct.current>=6){K("fail");return}Ct.current+=1,window.setTimeout(()=>ze(Date.now()),400)})()}}),t.jsx("canvas",{ref:fe,className:"pointer-events-none absolute left-0 top-0"})]})}):c?t.jsx("div",{className:"og-scrollbar relative h-full min-h-[200px] overflow-auto",children:n==="lab_video"?t.jsxs(t.Fragment,{children:[t.jsxs("div",{className:"absolute left-2 top-2 z-20 flex flex-wrap gap-1",children:[t.jsxs("button",{type:"button",title:e("lab.grid"),className:`rounded border px-2 py-1 text-[10px] ${He?"border-primary bg-primary/20":"border-white/30 bg-black/40"} text-white`,onClick:()=>ia(a=>!a),children:[t.jsx(Aa,{className:"mr-1 inline h-3 w-3"}),e("lab.grid")]}),t.jsx("button",{type:"button",title:e("lab.zoomOut"),className:"rounded border border-white/30 bg-black/40 px-2 py-1 text-[10px] text-white",onClick:()=>tt(me-.25),children:t.jsx(Ha,{className:"inline h-3 w-3"})}),t.jsx("button",{type:"button",title:e("lab.zoomIn"),className:"rounded border border-white/30 bg-black/40 px-2 py-1 text-[10px] text-white",onClick:()=>tt(me+.25),children:t.jsx(Ba,{className:"inline h-3 w-3"})}),t.jsx("button",{type:"button",title:e("lab.zoomReset"),className:"rounded border border-white/30 bg-black/40 px-2 py-1 text-[10px] text-white",onClick:()=>ut(1),children:t.jsx(Ua,{className:"inline h-3 w-3"})})]}),t.jsxs("div",{className:"flex min-h-[200px] flex-col items-center justify-center gap-2 bg-black py-2",children:[t.jsx("div",{className:"inline-block origin-top-left transition-transform",style:{transform:`scale(${me})`},children:t.jsxs("div",{className:"relative inline-block",children:[He&&t.jsx("div",{className:"pointer-events-none absolute inset-0 z-[1]",style:{backgroundImage:["linear-gradient(to right, rgba(255,255,255,0.12) 1px, transparent 1px)","linear-gradient(to bottom, rgba(255,255,255,0.12) 1px, transparent 1px)"].join(","),backgroundSize:"48px 48px"}}),t.jsx("video",{ref:Ce,src:Ze,loop:!0,playsInline:!0,autoPlay:!0,muted:!0,preload:"metadata",className:"max-h-[70vh] w-full max-w-full object-contain",onError:()=>xt(e("lab.videoPreviewFailed")),onLoadedData:()=>{xt(null);const a=Ce.current;a&&a.play().catch(()=>{})}}),t.jsx("canvas",{ref:fe,className:"pointer-events-none absolute left-0 top-0"})]})}),ke&&t.jsx("div",{className:"rounded border border-error/40 bg-error-container/20 px-3 py-1.5 text-[11px] text-error",children:ke}),Sa&&t.jsxs("div",{className:"max-w-3xl rounded border border-primary/40 bg-primary/10 px-3 py-2 text-[11px] text-on-surface",children:[t.jsx("div",{className:"font-semibold text-primary",children:e("lab.transcode.title")}),t.jsx("div",{className:"mt-1 text-on-surface-variant",children:e("lab.transcode.desc")}),ga!=null&&t.jsxs("div",{className:"mt-2",children:[t.jsx("div",{className:"mb-1 text-[10px] text-on-surface-variant",children:xs||e("lab.transcode.running")}),t.jsx("div",{className:"h-1.5 w-full overflow-hidden rounded bg-surface-container-highest",children:t.jsx("div",{className:"h-full bg-primary transition-all",style:{width:`${Math.round(ga*100)}%`}})})]}),t.jsx("div",{className:"mt-2",children:t.jsx("button",{type:"button",className:"rounded bg-primary px-3 py-1.5 text-[11px] font-medium text-on-primary disabled:opacity-50",disabled:ba,onClick:()=>void Ns(),children:e("lab.transcode.button")})})]})]})]}):t.jsxs(t.Fragment,{children:[t.jsxs("div",{className:"absolute left-2 top-2 z-20 flex flex-wrap gap-1",children:[t.jsxs("button",{type:"button",title:e("lab.grid"),className:`rounded border px-2 py-1 text-[10px] ${He?"border-primary bg-primary/20":"border-white/30 bg-black/40"} text-white`,onClick:()=>ia(a=>!a),children:[t.jsx(Aa,{className:"mr-1 inline h-3 w-3"}),e("lab.grid")]}),t.jsx("button",{type:"button",title:e("lab.zoomOut"),className:"rounded border border-white/30 bg-black/40 px-2 py-1 text-[10px] text-white",onClick:()=>tt(me-.25),children:t.jsx(Ha,{className:"inline h-3 w-3"})}),t.jsx("button",{type:"button",title:e("lab.zoomIn"),className:"rounded border border-white/30 bg-black/40 px-2 py-1 text-[10px] text-white",onClick:()=>tt(me+.25),children:t.jsx(Ba,{className:"inline h-3 w-3"})}),t.jsx("button",{type:"button",title:e("lab.zoomReset"),className:"rounded border border-white/30 bg-black/40 px-2 py-1 text-[10px] text-white",onClick:()=>ut(1),children:t.jsx(Ua,{className:"inline h-3 w-3"})})]}),t.jsx("div",{className:"inline-block origin-top-left transition-transform",style:{transform:`scale(${me})`},children:t.jsxs("div",{className:"relative inline-block",children:[He&&t.jsx("div",{className:"pointer-events-none absolute inset-0 z-[1]",style:{backgroundImage:["linear-gradient(to right, rgba(255,255,255,0.12) 1px, transparent 1px)","linear-gradient(to bottom, rgba(255,255,255,0.12) 1px, transparent 1px)"].join(","),backgroundSize:"48px 48px"}}),t.jsx("img",{ref:Mt,src:Ze,alt:"preview",className:"max-h-[70vh] w-full object-contain"}),t.jsx("canvas",{ref:fe,className:"pointer-events-none absolute left-0 top-0"})]})})]})}):t.jsx("div",{className:"flex h-full items-center justify-center px-4 text-center text-on-surface-variant",children:e(n==="lab_video"?"lab.selectOrUploadVideo":"lab.selectOrUpload")}),B&&t.jsx("div",{className:"absolute inset-0 flex items-center justify-center bg-black/40",children:t.jsx(at,{className:"h-8 w-8 animate-spin text-primary"})})]}),(c&&n==="lab_image"||n==="lab_video"&&(k==="file"&&c||k==="camera"))&&t.jsxs("div",{className:"mt-2 flex flex-wrap items-center gap-3 rounded-lg border border-outline-variant/25 bg-surface-container-lowest/90 px-3 py-2 text-xs text-on-surface",children:[t.jsx("span",{className:"shrink-0 font-medium text-on-surface-variant",children:e("lab.layers")}),t.jsx("div",{className:"flex flex-wrap gap-x-4 gap-y-1",children:["matched","pattern","all","rejected"].map(a=>t.jsxs("label",{className:"flex cursor-pointer items-center gap-1",children:[t.jsx("input",{type:"checkbox",className:"accent-primary",checked:G[a],onChange:i=>is(d=>({...d,[a]:i.target.checked}))}),t.jsx("span",{children:e(a==="matched"?"lab.layer.matched":a==="pattern"?"lab.layer.pattern":a==="all"?"lab.layer.all":"lab.layer.rejected")})]},a))})]}),(c||n==="lab_video"&&k==="camera")&&t.jsxs(t.Fragment,{children:[t.jsxs("div",{className:"mt-2 grid gap-2 sm:grid-cols-2",children:[t.jsxs("details",{open:!0,className:"rounded-lg border border-outline-variant/25 bg-surface-container-lowest/90",children:[t.jsxs("summary",{className:"flex cursor-pointer list-none items-center justify-between gap-2 px-3 py-2 text-xs font-semibold text-on-surface [&::-webkit-details-marker]:hidden",children:[e("lab.solveSection"),t.jsx(Fe,{className:"h-4 w-4 shrink-0 text-on-surface-variant"})]}),t.jsx("div",{className:"border-t border-outline-variant/15 px-3 py-2 text-[10px]",children:te?t.jsxs("div",{className:"space-y-1 text-on-surface",children:[b.tSolveMs!=null&&t.jsxs("div",{className:"space-y-0.5",children:[t.jsxs("div",{className:"flex justify-between gap-2",children:[t.jsx("span",{className:"text-on-surface-variant",children:e("lab.metric.solveComputeMs")}),t.jsxs("span",{className:"font-mono tabular-nums",children:[b.tSolveMs.toFixed(0)," ms"]})]}),t.jsx("p",{className:"text-[8px] leading-snug text-on-surface-variant/90",children:e("lab.metric.solveComputeHelp")})]}),jt!=null&&t.jsxs("div",{className:"space-y-0.5 border-t border-outline-variant/10 pt-1",children:[t.jsxs("div",{className:"flex justify-between gap-2",children:[t.jsx("span",{className:"text-on-surface-variant",children:e("lab.metric.solveRoundTripMs")}),t.jsxs("span",{className:"font-mono tabular-nums",children:[jt.toFixed(0)," ms"]})]}),t.jsx("p",{className:"text-[8px] leading-snug text-on-surface-variant/90",children:e("lab.metric.solveRoundTripHelp")})]}),(b.tBackendTotalMs!=null||b.tOpenDecodeMs!=null||b.tPreprocessMs!=null||b.tExtractMs!=null||b.tSolveMs!=null)&&t.jsxs("div",{className:"space-y-0.5 border-t border-outline-variant/10 pt-1",children:[t.jsxs("div",{className:"flex justify-between gap-2",children:[t.jsx("span",{className:"text-on-surface-variant",children:e("lab.metric.backendTotalMs")}),t.jsx("span",{className:"font-mono tabular-nums",children:b.tBackendTotalMs!=null?`${b.tBackendTotalMs.toFixed(0)} ms`:e("common.placeholder")})]}),t.jsxs("div",{className:"flex flex-wrap gap-x-2 gap-y-0.5 text-[8px] text-on-surface-variant/90",children:[t.jsxs("span",{children:[e("lab.metric.openDecodeMs"),":"," ",b.tOpenDecodeMs!=null?`${b.tOpenDecodeMs.toFixed(0)} ms`:e("common.placeholder")]}),t.jsxs("span",{children:[e("lab.metric.preprocessMs"),":"," ",b.tPreprocessMs!=null?`${b.tPreprocessMs.toFixed(0)} ms`:e("common.placeholder")]}),t.jsxs("span",{children:[e("lab.metric.extractMs"),":"," ",b.tExtractMs!=null?`${b.tExtractMs.toFixed(0)} ms`:e("common.placeholder")]}),t.jsxs("span",{children:[e("lab.metric.solveOnlyMs"),":"," ",b.tSolveMs!=null?`${b.tSolveMs.toFixed(0)} ms`:e("common.placeholder")]})]})]}),(b.raDeg!=null||b.decDeg!=null)&&t.jsxs("div",{className:"leading-tight",children:[t.jsx("span",{className:"text-on-surface-variant",children:e("lab.metric.radec")}),t.jsxs("div",{className:"mt-0.5 font-mono text-[9px]",children:["α ",it(b.raDeg)," · δ"," ",it(b.decDeg)]})]}),t.jsxs("div",{className:"flex flex-wrap gap-x-2 gap-y-0.5 text-[9px]",children:[b.matches!=null&&t.jsxs("span",{children:[t.jsx("span",{className:"text-on-surface-variant",children:e("lab.metric.matches")})," ",t.jsx("span",{className:"font-mono",children:b.matches})]}),b.rmseArcsec!=null&&t.jsxs("span",{children:[t.jsx("span",{className:"text-on-surface-variant",children:e("lab.metric.rmse")})," ",t.jsxs("span",{className:"font-mono",children:[b.rmseArcsec.toFixed(2),"″"]})]}),b.prob!=null&&t.jsxs("span",{className:"inline-flex max-w-full flex-col gap-0.5",children:[t.jsxs("span",{children:[t.jsx("span",{className:"text-on-surface-variant",children:e("lab.metric.prob")})," ",t.jsx("span",{className:"font-mono",children:ge(b.prob,te??void 0).line})]}),t.jsx("span",{className:"text-[8px] leading-snug text-on-surface-variant/90",children:e("lab.metric.probHelp")}),ge(b.prob,te??void 0).rawLine&&t.jsxs(t.Fragment,{children:[t.jsxs("span",{className:"text-[8px] text-on-surface-variant",children:[e("lab.metric.probRaw"),":"," ",ge(b.prob,te??void 0).rawLine]}),t.jsx("span",{className:"text-[8px] leading-snug text-on-surface-variant/90",children:e("lab.metric.probRawHelp")})]})]})]}),b.status&&t.jsxs("div",{className:"border-t border-outline-variant/15 pt-1 text-[9px]",children:[t.jsxs("span",{className:"text-on-surface-variant",children:[e("lab.metric.status")," "]}),t.jsx("span",{className:"font-mono text-secondary",children:b.status})]})]}):t.jsx("p",{className:"text-on-surface-variant",children:e("common.placeholder")})})]}),t.jsxs("details",{open:!0,className:"rounded-lg border border-outline-variant/25 bg-surface-container-lowest/90",children:[t.jsxs("summary",{className:"flex cursor-pointer list-none items-center justify-between gap-2 px-3 py-2 text-xs font-semibold text-on-surface [&::-webkit-details-marker]:hidden",children:[e("lab.imageSection"),t.jsx(Fe,{className:"h-4 w-4 shrink-0 text-on-surface-variant"})]}),t.jsxs("div",{className:"space-y-0.5 border-t border-outline-variant/15 px-3 py-2 text-[10px]",children:[t.jsxs("div",{className:"flex justify-between gap-2",children:[t.jsx("span",{className:"text-on-surface-variant",children:e("lab.resolution")}),t.jsx("span",{className:"font-mono tabular-nums",children:At.w>0?`${At.w}×${At.h}`:e("common.placeholder")})]}),t.jsxs("div",{className:"flex justify-between gap-2",children:[t.jsx("span",{className:"text-on-surface-variant",children:e("lab.starsDetected")}),t.jsx("span",{className:"font-mono tabular-nums",children:Na??e("common.placeholder")})]}),t.jsxs("div",{className:"flex justify-between gap-2",children:[t.jsx("span",{className:"text-on-surface-variant",children:e("lab.fwhm")}),t.jsx("span",{children:e("common.placeholder")})]})]})]})]}),c&&t.jsxs(t.Fragment,{children:[t.jsxs("div",{className:"mt-2 flex flex-wrap gap-4 text-xs text-on-surface-variant",children:[t.jsxs("span",{children:[e("lab.file"),":"," ",t.jsx("span",{className:"font-mono text-on-surface",title:c,children:nt(c,22,18)})]}),((Ma=o.find(a=>a.filename===c))==null?void 0:Ma.source)&&t.jsxs("span",{className:"rounded bg-surface-container-high px-2 py-0.5",children:[e("lab.source"),":"," ",(Ca=o.find(a=>a.filename===c))==null?void 0:Ca.source]})]}),t.jsxs("details",{open:!0,className:"mt-2 rounded-lg border border-outline-variant/25 bg-surface-container-lowest/90 text-xs shadow-sm",children:[t.jsxs("summary",{className:"flex cursor-pointer list-none items-center gap-2 px-3 py-2 font-semibold text-on-surface [&::-webkit-details-marker]:hidden",children:[e("lab.meta.title"),gt&&t.jsx(at,{className:"h-3.5 w-3.5 animate-spin"}),t.jsx(Fe,{className:"ml-auto h-4 w-4 shrink-0 text-on-surface-variant"})]}),t.jsx("div",{className:"border-t border-outline-variant/15 p-3 pt-2",children:Ea.length>0?t.jsx("dl",{className:"grid grid-cols-2 gap-x-4 gap-y-2 sm:grid-cols-3",children:Ea.map(a=>t.jsxs("div",{children:[t.jsx("dt",{className:"text-[10px] text-on-surface-variant",children:e(a.key)}),t.jsx("dd",{className:"text-[11px] font-medium text-on-surface",children:a.value})]},`${a.key}-${a.value}`))}):vt&&!gt?t.jsx("p",{className:"text-[10px] leading-relaxed text-on-surface-variant",children:e("lab.meta.partial")}):gt?null:t.jsx("p",{className:"text-[10px] text-on-surface-variant",children:e("lab.meta.noSidecar")})})]})]})]}),(c||n==="lab_video"&&k==="camera")&&(Te.length>0||S||T)&&t.jsxs(t.Fragment,{children:[Te.length>0&&t.jsxs("section",{className:"mt-3 rounded-lg border border-outline-variant/25 bg-surface-container-lowest/90",children:[t.jsxs("div",{className:"flex h-9 shrink-0 items-center border-b border-outline-variant/15 px-3 text-[10px] uppercase text-on-surface-variant",children:[t.jsx("span",{children:e("results.solveHistoryTitle")}),t.jsxs("span",{className:"ml-2 normal-case text-on-surface-variant/70",children:["(",Te.length,"/",Ya,")"]})]}),t.jsx("div",{className:"og-scrollbar max-h-[min(36vh,18rem)] space-y-2 overflow-y-auto p-3",children:Te.map(a=>{var $;const i=a.batch,d=a.single,p=Pe(new Date(a.at).toISOString(),s),w=i&&i.results.length?e("results.historyBatch",{count:i.results.length}):e("results.historySingle");return t.jsxs("details",{className:"rounded border border-outline-variant/20 bg-surface-container/80 text-xs",children:[t.jsx("summary",{className:"cursor-pointer list-none px-3 py-2 font-medium text-on-surface [&::-webkit-details-marker]:hidden",children:t.jsxs("span",{children:[p," · ",w]})}),t.jsxs("div",{className:"border-t border-outline-variant/15 p-3 pt-2",children:[($=i==null?void 0:i.results)!=null&&$.length?t.jsx("div",{className:"og-scrollbar flex gap-2 overflow-x-auto pb-1",children:i.results.map((E,D)=>{const M=E.result,oe=E.success===!0;return t.jsxs("div",{className:"min-w-[12rem] max-w-xs shrink-0 rounded border border-outline-variant/20 bg-surface-container-lowest p-2",children:[t.jsx("div",{className:"border-b border-outline-variant/10 pb-1 text-[10px] font-semibold text-secondary",children:String(E.label??D)}),oe&&M?t.jsx(st,{result:M,t:e,roundTripMs:null}):t.jsx("div",{className:"mt-1 text-[10px] text-error",children:String(E.error??"—")})]},D)})}):null,d&&d.result?t.jsx("div",{className:"mt-2 rounded border border-outline-variant/15 p-2",children:t.jsx(st,{result:d.result,t:e,roundTripMs:null})}):null,t.jsxs("details",{className:"mt-2",children:[t.jsx("summary",{className:"cursor-pointer text-[10px] text-primary hover:underline",children:e("results.viewRaw")}),t.jsx("pre",{className:"og-scrollbar mt-1 max-h-32 overflow-auto rounded bg-surface-container-highest p-2 text-[9px] text-on-surface-variant",children:JSON.stringify({single:d,batch:i},null,2)})]})]})]},a.id)})})]}),(S||T)&&t.jsxs("section",{className:"mt-3 max-h-[min(50vh,28rem)] rounded-lg border border-outline-variant/25 bg-surface-container-lowest/95",children:[t.jsxs("div",{className:"flex h-9 shrink-0 items-center justify-between border-b border-outline-variant/15 px-3 text-[10px] uppercase text-on-surface-variant",children:[t.jsx("span",{children:e("results.title")}),t.jsxs("div",{className:"flex gap-3",children:[(Ia=T==null?void 0:T.results)!=null&&Ia.length?t.jsx("button",{type:"button",className:"rounded bg-surface-container px-2 py-0.5 normal-case",onClick:async()=>{if(!(!c||!T)){for(const a of T.results){if(!a.success)continue;const i=a.result;await Ht({input_name:c,preset_label:String(a.label),result_json:a,metrics:Gt(i??null),replay:{layers:G,params:x}})}y(null)}},children:e("results.saveBatchAll")}):null,S&&t.jsx("button",{type:"button",className:"rounded bg-surface-container px-2 py-0.5 normal-case",onClick:async()=>{if(!c&&ms!=="camera")return;const a=S.result;await Ht({input_name:c??e("lab.cameraSnapshotName"),preset_label:"manual",result_json:S,metrics:Gt(a??null),replay:{layers:G,params:x}}),y(null)},children:e("results.saveCurrent")})]})]}),t.jsx("div",{className:"og-scrollbar min-h-0 overflow-y-auto p-3",children:t.jsxs("div",{className:"og-scrollbar flex gap-3 overflow-x-auto text-xs",children:[T==null?void 0:T.results.map((a,i)=>{const d=a.result,p=us[i]??!1;return t.jsxs("div",{className:"min-w-[15rem] max-w-sm shrink-0 rounded-lg border border-outline-variant/25 bg-surface-container p-3 shadow-sm",children:[t.jsx("div",{className:"border-b border-outline-variant/15 pb-2 font-semibold text-secondary",children:String(a.label)}),a.success?t.jsxs(t.Fragment,{children:[t.jsx(st,{result:d,t:e,roundTripMs:null}),t.jsx("button",{type:"button",className:"mt-2 text-[10px] text-primary hover:underline",onClick:()=>yt(w=>({...w,[i]:!p})),children:e(p?"results.hideRaw":"results.viewRaw")}),p&&t.jsx("pre",{className:"og-scrollbar mt-1 max-h-40 overflow-auto rounded bg-surface-container-highest p-2 text-[9px] text-on-surface-variant",children:JSON.stringify(a,null,2)})]}):t.jsx("div",{className:"mt-2 text-[10px] text-error",children:String(a.error)}),a.success===!0&&c?t.jsx("button",{type:"button",className:"mt-3 w-full rounded bg-surface-container-high py-1.5 text-[10px] font-medium",onClick:()=>Ht({input_name:c,preset_label:String(a.label),result_json:a,metrics:Gt(d??null),replay:{layers:G,params:x}}).catch(w=>y(String(w))),children:e("results.saveRow")}):null]},i)}),S&&!T&&t.jsxs("div",{className:"min-w-[16rem] max-w-md shrink-0 rounded-lg border border-outline-variant/25 bg-surface-container p-3 shadow-sm",children:[t.jsx("div",{className:"border-b border-outline-variant/15 pb-2 text-[10px] font-semibold uppercase text-on-surface-variant",children:e("results.title")}),t.jsx(st,{result:S.result,t:e,roundTripMs:jt}),t.jsx("button",{type:"button",className:"mt-2 text-[10px] text-primary hover:underline",onClick:()=>wt(a=>!a),children:e(fa?"results.hideRaw":"results.viewRaw")}),fa&&t.jsx("pre",{className:"og-scrollbar mt-1 max-h-48 overflow-auto rounded bg-surface-container-highest p-2 text-[9px] text-on-surface-variant",children:JSON.stringify(S,null,2)})]})]})})]})]})]}),t.jsxs("aside",{className:"flex w-80 shrink-0 flex-col border-l border-outline-variant/15 bg-surface-container-low text-xs min-h-0",children:[n!=="lab_video"&&t.jsxs("div",{className:"shrink-0 space-y-2 border-b border-outline-variant/20 p-4 pb-3",children:[t.jsx("button",{type:"button",className:"w-full rounded bg-primary py-2.5 font-semibold text-on-primary-container",onClick:vs,disabled:B,children:e("btn.solveOne")}),t.jsx("button",{type:"button",className:"w-full rounded bg-secondary/80 py-2.5 font-semibold text-on-secondary",onClick:gs,disabled:B,children:e("btn.solveBatch")})]}),n==="lab_video"&&t.jsx("div",{className:"shrink-0 space-y-2 border-b border-outline-variant/20 p-4 pb-3",children:k==="file"?t.jsxs(t.Fragment,{children:[t.jsx("button",{type:"button",className:"w-full rounded bg-primary py-2.5 font-semibold text-on-primary-container disabled:opacity-40",onClick:()=>js(),disabled:B||!_a||pe,children:e("lab.solveFileStart")}),t.jsx("button",{type:"button",className:"w-full rounded bg-error/80 py-2.5 font-semibold text-on-error disabled:opacity-40",onClick:()=>Qe(),disabled:!pe,children:e("lab.solveFileStop")}),t.jsx("button",{type:"button",className:"w-full rounded bg-secondary/80 py-2.5 font-semibold text-on-secondary disabled:opacity-40",onClick:()=>void ws(),disabled:B||!_a||pe,children:e("lab.solveCurrentFrame")})]}):t.jsxs(t.Fragment,{children:[t.jsx("button",{type:"button",className:"w-full rounded bg-primary py-2.5 font-semibold text-on-primary-container disabled:opacity-40",onClick:()=>ks(),disabled:B||Ee||q,children:e("lab.solveCameraStart")}),t.jsx("button",{type:"button",className:"w-full rounded bg-error/80 py-2.5 font-semibold text-on-error disabled:opacity-40",onClick:()=>et(),disabled:!Ee,children:e("lab.solveCameraStop")}),t.jsx("button",{type:"button",className:"w-full rounded bg-secondary/80 py-2.5 font-semibold text-on-secondary disabled:opacity-40",onClick:()=>void ys(),disabled:B||q,children:e("lab.solveCameraFrame")}),t.jsxs("label",{className:"flex items-center gap-2 rounded border border-outline-variant/25 px-2 py-1.5 text-[11px]",children:[t.jsx("input",{type:"checkbox",checked:xa,onChange:a=>cs(a.target.checked)}),t.jsx("span",{children:"自动保持(解算成功后冻结) / Auto Hold"})]}),q&&t.jsx("button",{type:"button",className:"w-full rounded bg-tertiary/80 py-2.5 font-semibold text-on-tertiary disabled:opacity-40",onClick:()=>Rs(),children:"继续实时 / Resume Live"}),q&&t.jsxs("div",{className:"text-[10px] text-on-surface-variant",children:["已冻结帧 / Frozen frame ",ds??"-"]})]})}),n==="lab_video"&&Ve&&t.jsxs("div",{className:"shrink-0 border-b border-outline-variant/20 px-4 py-2 text-[10px] text-on-surface-variant",children:[t.jsx("div",{className:"font-medium text-on-surface",children:e("lab.systemLoad")}),t.jsxs("div",{children:["CPU ",Ve.cpu_usage,"% · RAM ",Ve.memory_usage,"% ·"," ",Ve.temperature,"°C"]})]}),t.jsxs("div",{className:"og-scrollbar min-h-0 flex-1 overflow-y-auto p-4",children:[t.jsxs("details",{open:!0,className:"rounded-lg border border-outline-variant/20 bg-surface-container-highest/30",children:[t.jsxs("summary",{className:"flex cursor-pointer list-none items-center justify-between gap-2 px-3 py-2 font-semibold text-on-surface-variant [&::-webkit-details-marker]:hidden",children:[e("params.title"),t.jsx(Fe,{className:"h-4 w-4 shrink-0"})]}),t.jsxs("div",{className:"border-t border-outline-variant/15 p-3 pt-2",children:[t.jsx("p",{className:"mb-3 text-[10px] leading-relaxed text-on-surface-variant/90",children:e("params.blockSolveIntro")}),t.jsxs("div",{className:"space-y-2",children:[t.jsxs("label",{className:"block",children:[t.jsx("span",{className:"text-[10px] font-medium text-on-surface-variant",children:e("params.solveProfile")}),t.jsx("p",{className:"mb-1 mt-0.5 text-[9px] leading-snug text-on-surface-variant/85",children:e("params.solveProfileHelp")}),t.jsxs("select",{className:"w-full rounded bg-surface-container-highest px-2 py-1",value:x.solve_profile??"balanced",onChange:a=>h(i=>({...i,solve_profile:a.target.value})),children:[t.jsx("option",{value:"speed",children:e("params.solveProfileSpeed")}),t.jsx("option",{value:"balanced",children:e("params.solveProfileBalanced")}),t.jsx("option",{value:"robust",children:e("params.solveProfileRobust")})]})]}),t.jsx(W,{label:e("params.fov"),helpKey:"params.fovHelp",value:x.fov_estimate??"",onChange:a=>h(i=>({...i,fov_estimate:a}))}),t.jsx(W,{label:e("params.fovErr"),helpKey:"params.fovErrHelp",value:x.fov_max_error??"",onChange:a=>h(i=>({...i,fov_max_error:a}))}),t.jsx(W,{label:e("params.timeout"),helpKey:"params.timeoutHelp",value:x.solve_timeout_ms??"",onChange:a=>h(i=>({...i,solve_timeout_ms:a}))}),t.jsx(W,{label:e("params.solveIntervalMs"),helpKey:"params.solveIntervalMsHelp",value:_t??"",onChange:a=>ha(a==null?$t:Math.round(a))}),t.jsx("p",{className:"text-[9px] leading-snug text-on-surface-variant/80",children:e("params.solveIntervalBound",{min:Ot,max:Pt,effective:le})}),t.jsx("p",{className:"text-[9px] leading-snug text-on-surface-variant/80",children:e("params.solveIntervalIndependent")}),n==="lab_video"&&t.jsxs("label",{className:"mt-2 block",children:[t.jsx("span",{className:"text-[10px] font-medium text-on-surface-variant",children:e("params.centroidRejectionLevel")}),t.jsx("p",{className:"mb-1 mt-0.5 text-[9px] leading-snug text-on-surface-variant/85",children:e("params.centroidRejectionLevelHelp")}),t.jsxs("div",{className:"flex items-center gap-2",children:[t.jsx("span",{className:"w-5 shrink-0 text-center text-[9px] text-on-surface-variant",children:"1"}),t.jsx("input",{type:"range",min:1,max:5,step:1,className:"h-2 flex-1 accent-primary",value:x.centroid_rejection_level??3,onChange:a=>h(i=>({...i,centroid_rejection_level:Number(a.target.value)}))}),t.jsx("span",{className:"w-5 shrink-0 text-center text-[9px] text-on-surface-variant",children:"5"}),t.jsx("span",{className:"ml-1 min-w-[1.25rem] tabular-nums text-[10px] font-semibold text-on-surface",children:x.centroid_rejection_level??3})]}),t.jsx("p",{className:"mt-0.5 text-[9px] text-on-surface-variant/90",children:e("params.centroidRejectionScale")})]}),t.jsx(W,{label:e("params.ra"),helpKey:"params.raHelp",value:x.hint_ra_deg??"",onChange:a=>h(i=>({...i,hint_ra_deg:a}))}),t.jsx(W,{label:e("params.dec"),helpKey:"params.decHelp",value:x.hint_dec_deg??"",onChange:a=>h(i=>({...i,hint_dec_deg:a}))}),t.jsx(W,{label:e("params.maxSide"),helpKey:"params.maxSideHelp",value:x.max_image_side??"",onChange:a=>h(i=>({...i,max_image_side:a}))}),t.jsxs("label",{className:"mt-1.5 flex cursor-pointer items-center gap-2",children:[t.jsx("input",{type:"checkbox",className:"accent-primary",checked:x.detail_level==="full",onChange:a=>h(i=>({...i,detail_level:a.target.checked?"full":"summary"}))}),t.jsx("span",{className:"text-[10px] text-on-surface-variant",children:e("params.detailLevelFull")})]}),t.jsxs("label",{className:"mt-1.5 flex cursor-pointer items-start gap-2",children:[t.jsx("input",{type:"checkbox",className:"accent-primary mt-0.5",checked:!!x.large_scale_bg_subtract,onChange:a=>h(i=>({...i,large_scale_bg_subtract:a.target.checked}))}),t.jsxs("span",{children:[t.jsx("span",{className:"text-[10px] text-on-surface-variant",children:e("params.largeScaleBg")}),t.jsx("p",{className:"mt-0.5 text-[9px] leading-snug text-on-surface-variant/85",children:e("params.largeScaleBgHelp")})]})]})]})]})]}),t.jsxs("details",{open:!0,className:"mt-3 rounded-lg border border-outline-variant/20 bg-surface-container-highest/30",children:[t.jsxs("summary",{className:"flex cursor-pointer list-none items-center justify-between gap-2 px-3 py-2 font-semibold text-on-surface-variant [&::-webkit-details-marker]:hidden",children:[e("params.centroid"),t.jsx(Fe,{className:"h-4 w-4 shrink-0"})]}),t.jsxs("div",{className:"border-t border-outline-variant/15 p-3 pt-2",children:[t.jsx("p",{className:"mb-3 text-[10px] leading-relaxed text-on-surface-variant/90",children:e("params.blockCentroidIntro")}),t.jsxs("div",{className:"space-y-2",children:[t.jsx(W,{label:e("params.sigma"),helpKey:"params.sigmaHelp",step:.1,value:((La=x.centroid)==null?void 0:La.sigma)??"",onChange:a=>h(i=>({...i,centroid:{...i.centroid,sigma:a}}))}),t.jsx(W,{label:e("params.maxArea"),helpKey:"params.maxAreaHelp",value:((Da=x.centroid)==null?void 0:Da.max_area)??"",onChange:a=>h(i=>({...i,centroid:{...i.centroid,max_area:a}}))}),t.jsx(W,{label:e("params.minArea"),helpKey:"params.minAreaHelp",value:((Fa=x.centroid)==null?void 0:Fa.min_area)??"",onChange:a=>h(i=>({...i,centroid:{...i.centroid,min_area:a}}))}),t.jsx(W,{label:e("params.filtsize"),helpKey:"params.filtsizeHelp",step:2,value:((Oa=x.centroid)==null?void 0:Oa.filtsize)??"",onChange:a=>h(i=>({...i,centroid:{...i.centroid,filtsize:a}}))})]})]})]}),t.jsxs("div",{className:"mt-4 rounded-md border border-outline-variant/20 bg-surface-container-highest/40 p-3",children:[t.jsx("div",{className:"mb-2 font-semibold text-on-surface-variant",children:e("sidebar.batchPresets")}),t.jsx("p",{className:"mb-2 text-[10px] leading-snug text-on-surface-variant",children:e("sidebar.batchHint")}),t.jsx("div",{className:"og-scrollbar max-h-36 space-y-1.5 overflow-y-auto",children:[...N,...C].map(a=>t.jsxs("label",{className:"flex cursor-pointer items-center gap-2",children:[t.jsx("input",{type:"checkbox",checked:I.has(a.id),onChange:()=>Ms(a.id)}),t.jsx("span",{className:"truncate",children:a.name})]},a.id))})]}),t.jsxs("div",{className:"mt-6 border-t border-outline-variant/20 pt-4",children:[t.jsx("div",{className:"mb-2 font-semibold",children:e("btn.applyPresets")}),t.jsx("div",{className:"og-scrollbar max-h-24 space-y-1 overflow-y-auto",children:[...N,...C].map(a=>t.jsx("button",{type:"button",className:"block w-full truncate text-left text-primary hover:underline",onClick:()=>bs(a.params),children:a.name},a.id))}),t.jsxs("div",{className:"mt-3 flex gap-1",children:[t.jsx("input",{className:"flex-1 rounded bg-surface-container-highest px-2 py-1",placeholder:e("placeholder.newPreset"),value:dt,onChange:a=>sa(a.target.value)}),t.jsx("button",{type:"button",className:"rounded bg-surface-container-high px-2",onClick:async()=>{if(dt.trim()){U(!0);try{await Zs(dt.trim(),x),sa(""),await ee();const a=await Bt("user");v(a.presets)}catch(a){y(String(a))}finally{U(!1)}}},children:e("btn.savePreset")})]})]})]})]})]}),n==="pool"&&t.jsx(En,{uploads:o,onDelete:a=>void ka(a)}),n==="history"&&t.jsx(kn,{historyQ:je,setHistoryQ:rs,historyPage:Ae,setHistoryPage:ea,historyData:Ue,historyExpandId:ta,setHistoryExpandId:aa,historyTotalPages:Is,onDeleteExperiment:a=>void Cs(a),onSearch:()=>zt(je,1,rt).then(a=>{ea(1),ct(a)})})]}),t.jsx("div",{className:"pointer-events-none fixed bottom-4 right-4 z-[100] flex max-w-sm flex-col gap-2",children:ns.map(a=>t.jsx("div",{className:`pointer-events-auto rounded border px-3 py-2 text-left text-xs shadow-lg ${a.variant==="error"?"border-error/50 bg-error-container/95 text-on-error-container":a.variant==="warn"?"border-amber-500/40 bg-surface-container-highest/95 text-on-surface":"border-outline-variant/40 bg-surface-container-high/95 text-on-surface"}`,children:a.text},a.id))})]})}$s.createRoot(document.getElementById("root")).render(t.jsx(Ts.StrictMode,{children:t.jsx(As,{children:t.jsx(Mn,{})})}));
diff --git a/web/static/analysis-lab/assets/analysis-DwGzxfRb.js b/web/static/analysis-lab/assets/analysis-DwGzxfRb.js
deleted file mode 100644
index c47d4f1..0000000
--- a/web/static/analysis-lab/assets/analysis-DwGzxfRb.js
+++ /dev/null
@@ -1,51 +0,0 @@
-var Os=Object.defineProperty;var $a=e=>{throw TypeError(e)};var Ps=(e,s,n)=>s in e?Os(e,s,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[s]=n;var F=(e,s,n)=>Ps(e,typeof s!="symbol"?s+"":s,n),Ta=(e,s,n)=>s.has(e)||$a("Cannot "+n);var f=(e,s,n)=>(Ta(e,s,"read from private field"),n?n.call(e):s.get(e)),ae=(e,s,n)=>s.has(e)?$a("Cannot add the same private member more than once"):s instanceof WeakSet?s.add(e):s.set(e,n),De=(e,s,n,r)=>(Ta(e,s,"write to private field"),r?r.call(e,n):s.set(e,n),n);import{j as t,r as l,a as $s,R as Ts}from"./client-D1ZVDB-N.js";import{c as J,u as $e,T as es,I as As}from"./index-CutgeBjy.js";import{R as Us}from"./refresh-cw-BkMjDReH.js";/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Fe=J("ChevronDown",[["path",{d:"m6 9 6 6 6-6",key:"qrunsl"}]]);/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Bs=J("Database",[["ellipse",{cx:"12",cy:"5",rx:"9",ry:"3",key:"msslwz"}],["path",{d:"M3 5V19A9 3 0 0 0 21 19V5",key:"1wlel7"}],["path",{d:"M3 12A9 3 0 0 0 21 12",key:"mv7ke4"}]]);/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Aa=J("Grid3x3",[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",key:"afitv7"}],["path",{d:"M3 9h18",key:"1pudct"}],["path",{d:"M3 15h18",key:"5xshup"}],["path",{d:"M9 3v18",key:"fh3hqa"}],["path",{d:"M15 3v18",key:"14nvp0"}]]);/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Hs=J("History",[["path",{d:"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",key:"1357e3"}],["path",{d:"M3 3v5h5",key:"1xhq8a"}],["path",{d:"M12 7v5l4 2",key:"1fdv2h"}]]);/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const zs=J("House",[["path",{d:"M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8",key:"5wwlr5"}],["path",{d:"M3 10a2 2 0 0 1 .709-1.528l7-5.999a2 2 0 0 1 2.582 0l7 5.999A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z",key:"1d0kgt"}]]);/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const at=J("LoaderCircle",[["path",{d:"M21 12a9 9 0 1 1-6.219-8.56",key:"13zald"}]]);/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Ua=J("RotateCcw",[["path",{d:"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",key:"1357e3"}],["path",{d:"M3 3v5h5",key:"1xhq8a"}]]);/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Ws=J("Upload",[["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["polyline",{points:"17 8 12 3 7 8",key:"t8dd8p"}],["line",{x1:"12",x2:"12",y1:"3",y2:"15",key:"widbto"}]]);/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Ba=J("ZoomIn",[["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}],["line",{x1:"21",x2:"16.65",y1:"21",y2:"16.65",key:"13gj7c"}],["line",{x1:"11",x2:"11",y1:"8",y2:"14",key:"1vmskp"}],["line",{x1:"8",x2:"14",y1:"11",y2:"11",key:"durymu"}]]);/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Ha=J("ZoomOut",[["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}],["line",{x1:"21",x2:"16.65",y1:"21",y2:"16.65",key:"13gj7c"}],["line",{x1:"8",x2:"14",y1:"11",y2:"11",key:"durymu"}]]),_="/api/dev";async function V(e){if((e.headers.get("content-type")||"").includes("application/json"))return e.json();const n=await e.text();throw new Error(n||`HTTP ${e.status}`)}async function Vs(){const e=await fetch(`${_}/analysis/uploads`);if(!e.ok)throw new Error(await e.text());return e.json()}async function Gs(e,s){const n=new URLSearchParams;s!=null&&s.deleteExperiments&&n.set("delete_experiments","true");const r=n.toString(),o=await fetch(`${_}/analysis/uploads/${encodeURIComponent(e)}${r?`?${r}`:""}`,{method:"DELETE"});if(!o.ok)throw new Error(await o.text());return await o.json().catch(()=>({}))}async function Ut(e,s="analysis_upload"){const n=new FormData;n.append("file",e),n.append("source",s);const r=await fetch(`${_}/analysis/upload`,{method:"POST",body:n}),o=await V(r);if(!r.ok)throw new Error(String(o.detail||r.status));return o}async function Ks(e){const s=await fetch(`${_}/analysis/uploads/import_from_debug`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({filename:e})}),n=await V(s);if(!s.ok)throw new Error(String(n.detail||s.status));return n}async function za(e){const s=await fetch(`${_}/analysis/uploads/replace_video`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)}),n=await V(s);if(!s.ok)throw new Error(String(n.detail||s.status));return n}async function qs(e,s){const n=await fetch(`${_}/analysis/solve/image`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({input_name:e,...s})}),r=await V(n);if(!n.ok)throw new Error(String(r.detail||n.status));return r}async function Js(e,s){const n=await fetch(`${_}/analysis/solve/batch`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({input_name:e,runs:s})}),r=await V(n);if(!n.ok)throw new Error(String(r.detail||n.status));return r}async function Bt(e){const s=await fetch(`${_}/analysis/presets?scope=${e}`);if(!s.ok)throw new Error(await s.text());return s.json()}async function Zs(e,s){const n=await fetch(`${_}/analysis/presets`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:e,params:s})}),r=await V(n);if(!n.ok)throw new Error(String(r.detail||n.status));return r}async function Ht(e){const s=await fetch(`${_}/analysis/experiments`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)}),n=await V(s);if(!s.ok)throw new Error(String(n.detail||s.status));return n}async function Ys(e){const s=await fetch(`${_}/analysis/experiments/${encodeURIComponent(e)}`,{method:"DELETE"});if(!s.ok)throw new Error(await s.text())}async function zt(e,s,n=30){const r=new URLSearchParams({page:String(s),page_size:String(n)});e&&r.set("q",e);const o=await fetch(`${_}/analysis/experiments?${r}`);if(!o.ok)throw new Error(await o.text());return o.json()}async function Xs(){const e=await fetch(`${_}/debug/files`);if(!e.ok)throw new Error(await e.text());return e.json()}function Qs(e){return`${_}/debug/files/${encodeURIComponent(e)}`}async function Wa(e){const s=await fetch(`${_}/debug/files/${encodeURIComponent(e)}/info`),n=await V(s);if(!s.ok)throw new Error(String(n.detail||s.status));return n}async function en(e){const s=await fetch(`${_}/analysis/uploads/${encodeURIComponent(e)}/info`),n=await V(s);if(!s.ok)throw new Error(String(n.detail||s.status));return n}function Wt(e){return`${_}/analysis/uploads/file?filename=${encodeURIComponent(e)}`}async function Va(e){const s=await fetch(`${_}/analysis/experiments/export?format=${e}`);if(!s.ok)throw new Error(await s.text());return s.text()}async function tn(e){const s=await fetch(`${_}/analysis/uploads/${encodeURIComponent(e)}/experiment_count`);if(!s.ok)throw new Error(await s.text());return s.json()}async function an(){const e=await fetch(`${_}/analysis/settings`);if(!e.ok)throw new Error(await e.text());return e.json()}async function sn(e){const s=await fetch(`${_}/analysis/solve/frame`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)}),n=await V(s);if(!s.ok)throw new Error(String(n.detail||s.status));return n}async function nn(e,s){const n=new FormData;n.append("file",e,"frame.jpg"),n.append("payload",JSON.stringify(s));const r=await fetch(`${_}/analysis/solve/frame_upload`,{method:"POST",body:n}),o=await V(r);if(!r.ok)throw new Error(String(o.detail||r.status));return o}function rn(e){return`${_}/analysis/experiments/${encodeURIComponent(e)}/asset`}async function ln(){const e=await fetch("/api/dev/system/info");if(!e.ok)throw new Error(await e.text());return e.json()}function ts(e,s,n,r,o){var g,x,h,j;if(!r)return;if(e.clearRect(0,0,s,n),o.all&&Array.isArray(r.stars_all_centroids)){e.fillStyle="rgba(156, 163, 175, 0.85)";for(const m of r.stars_all_centroids)e.beginPath(),e.arc(m.x,m.y,2.4,0,Math.PI*2),e.fill()}if(o.rejected&&Array.isArray(r.stars_rejected_centroids)){e.strokeStyle="rgba(248, 113, 113, 0.95)",e.lineWidth=1.6;for(const m of r.stars_rejected_centroids)e.beginPath(),e.moveTo(m.x-4,m.y-4),e.lineTo(m.x+4,m.y+4),e.moveTo(m.x+4,m.y-4),e.lineTo(m.x-4,m.y+4),e.stroke()}if(o.pattern&&Array.isArray(r.stars_pattern)){e.strokeStyle="rgba(251, 146, 60, 0.95)",e.lineWidth=2;for(const m of r.stars_pattern)e.beginPath(),e.arc(m.x,m.y,6,0,Math.PI*2),e.stroke()}if(o.matched&&Array.isArray(r.stars_matched)){e.strokeStyle="rgba(34, 197, 94, 0.95)",e.fillStyle="rgba(34, 197, 94, 0.95)",e.lineWidth=2,e.font="11px system-ui, sans-serif";for(const m of r.stars_matched)e.beginPath(),e.arc(m.x,m.y,7,0,Math.PI*2),e.stroke(),m.mag!=null&&e.fillText(`m${Number(m.mag).toFixed(1)}`,m.x+4,m.y-4)}const u=r.overlay_ext;if(Array.isArray(u==null?void 0:u.labels_topn)&&u.labels_topn.length>0){e.fillStyle="rgba(96, 165, 250, 0.95)",e.font="12px system-ui, sans-serif";for(const m of u.labels_topn){if(typeof(m==null?void 0:m.x)!="number"||typeof(m==null?void 0:m.y)!="number")continue;const C=typeof m.mag=="number"?` m${m.mag.toFixed(1)}`:"",v=`${m.name??"Star"}${C}`;e.fillText(v,m.x+8,m.y+14)}}const c=u==null?void 0:u.polar_guide;if(c&&typeof((g=c.frame_center)==null?void 0:g.x)=="number"&&typeof((x=c.frame_center)==null?void 0:x.y)=="number"&&typeof((h=c.target)==null?void 0:h.x)=="number"&&typeof((j=c.target)==null?void 0:j.y)=="number"){const m=c.frame_center.x,C=c.frame_center.y,v=c.target.x,L=c.target.y;e.strokeStyle="rgba(244, 63, 94, 0.95)",e.fillStyle="rgba(244, 63, 94, 0.95)",e.lineWidth=2,e.beginPath(),e.moveTo(m,C),e.lineTo(v,L),e.stroke(),e.beginPath(),e.arc(v,L,6,0,Math.PI*2),e.stroke();const R=typeof c.angular_sep_deg=="number"?c.angular_sep_deg.toFixed(2):"-";e.font="12px system-ui, sans-serif",e.fillText(`Polar ${R}deg`,v+10,L-6)}}function Ga(e,s,n,r){if(!n)return;const o=s.naturalWidth||1,u=s.naturalHeight||1;e.width=o,e.height=u,e.style.width=`${s.clientWidth}px`,e.style.height=`${s.clientHeight}px`;const c=e.getContext("2d");c&&ts(c,o,u,n,r)}function on(e,s,n,r){if(!n)return;const o=s.videoWidth||1,u=s.videoHeight||1;if(o<2||u<2)return;e.width=o,e.height=u,e.style.width=`${s.clientWidth}px`,e.style.height=`${s.clientHeight}px`;const c=e.getContext("2d");c&&ts(c,o,u,n,r)}var w;(function(e){e.LOAD="LOAD",e.EXEC="EXEC",e.FFPROBE="FFPROBE",e.WRITE_FILE="WRITE_FILE",e.READ_FILE="READ_FILE",e.DELETE_FILE="DELETE_FILE",e.RENAME="RENAME",e.CREATE_DIR="CREATE_DIR",e.LIST_DIR="LIST_DIR",e.DELETE_DIR="DELETE_DIR",e.ERROR="ERROR",e.DOWNLOAD="DOWNLOAD",e.PROGRESS="PROGRESS",e.LOG="LOG",e.MOUNT="MOUNT",e.UNMOUNT="UNMOUNT"})(w||(w={}));const cn=(()=>{let e=0;return()=>e++})(),dn=new Error("ffmpeg is not loaded, call `await ffmpeg.load()` first"),un=new Error("called FFmpeg.terminate()");var A,se,X,de,ue,lt,P;class mn{constructor(){ae(this,A,null);ae(this,se,{});ae(this,X,{});ae(this,de,[]);ae(this,ue,[]);F(this,"loaded",!1);ae(this,lt,()=>{f(this,A)&&(f(this,A).onmessage=({data:{id:s,type:n,data:r}})=>{switch(n){case w.LOAD:this.loaded=!0,f(this,se)[s](r);break;case w.MOUNT:case w.UNMOUNT:case w.EXEC:case w.FFPROBE:case w.WRITE_FILE:case w.READ_FILE:case w.DELETE_FILE:case w.RENAME:case w.CREATE_DIR:case w.LIST_DIR:case w.DELETE_DIR:f(this,se)[s](r);break;case w.LOG:f(this,de).forEach(o=>o(r));break;case w.PROGRESS:f(this,ue).forEach(o=>o(r));break;case w.ERROR:f(this,X)[s](r);break}delete f(this,se)[s],delete f(this,X)[s]})});ae(this,P,({type:s,data:n},r=[],o)=>f(this,A)?new Promise((u,c)=>{const g=cn();f(this,A)&&f(this,A).postMessage({id:g,type:s,data:n},r),f(this,se)[g]=u,f(this,X)[g]=c,o==null||o.addEventListener("abort",()=>{c(new DOMException(`Message # ${g} was aborted`,"AbortError"))},{once:!0})}):Promise.reject(dn));F(this,"load",({classWorkerURL:s,...n}={},{signal:r}={})=>(f(this,A)||(De(this,A,s?new Worker(new URL(s,import.meta.url),{type:"module"}):new Worker(new URL("/static/analysis-lab/assets/worker-BAOIWoxA.js",import.meta.url),{type:"module"})),f(this,lt).call(this)),f(this,P).call(this,{type:w.LOAD,data:n},void 0,r)));F(this,"exec",(s,n=-1,{signal:r}={})=>f(this,P).call(this,{type:w.EXEC,data:{args:s,timeout:n}},void 0,r));F(this,"ffprobe",(s,n=-1,{signal:r}={})=>f(this,P).call(this,{type:w.FFPROBE,data:{args:s,timeout:n}},void 0,r));F(this,"terminate",()=>{const s=Object.keys(f(this,X));for(const n of s)f(this,X)[n](un),delete f(this,X)[n],delete f(this,se)[n];f(this,A)&&(f(this,A).terminate(),De(this,A,null),this.loaded=!1)});F(this,"writeFile",(s,n,{signal:r}={})=>{const o=[];return n instanceof Uint8Array&&o.push(n.buffer),f(this,P).call(this,{type:w.WRITE_FILE,data:{path:s,data:n}},o,r)});F(this,"mount",(s,n,r)=>{const o=[];return f(this,P).call(this,{type:w.MOUNT,data:{fsType:s,options:n,mountPoint:r}},o)});F(this,"unmount",s=>{const n=[];return f(this,P).call(this,{type:w.UNMOUNT,data:{mountPoint:s}},n)});F(this,"readFile",(s,n="binary",{signal:r}={})=>f(this,P).call(this,{type:w.READ_FILE,data:{path:s,encoding:n}},void 0,r));F(this,"deleteFile",(s,{signal:n}={})=>f(this,P).call(this,{type:w.DELETE_FILE,data:{path:s}},void 0,n));F(this,"rename",(s,n,{signal:r}={})=>f(this,P).call(this,{type:w.RENAME,data:{oldPath:s,newPath:n}},void 0,r));F(this,"createDir",(s,{signal:n}={})=>f(this,P).call(this,{type:w.CREATE_DIR,data:{path:s}},void 0,n));F(this,"listDir",(s,{signal:n}={})=>f(this,P).call(this,{type:w.LIST_DIR,data:{path:s}},void 0,n));F(this,"deleteDir",(s,{signal:n}={})=>f(this,P).call(this,{type:w.DELETE_DIR,data:{path:s}},void 0,n))}on(s,n){s==="log"?f(this,de).push(n):s==="progress"&&f(this,ue).push(n)}off(s,n){s==="log"?De(this,de,f(this,de).filter(r=>r!==n)):s==="progress"&&De(this,ue,f(this,ue).filter(r=>r!==n))}}A=new WeakMap,se=new WeakMap,X=new WeakMap,de=new WeakMap,ue=new WeakMap,lt=new WeakMap,P=new WeakMap;var Ka;(function(e){e.MEMFS="MEMFS",e.NODEFS="NODEFS",e.NODERAWFS="NODERAWFS",e.IDBFS="IDBFS",e.WORKERFS="WORKERFS",e.PROXYFS="PROXYFS"})(Ka||(Ka={}));const xn=new Error("failed to get response body reader"),pn=new Error("failed to complete download"),fn="Content-Length",hn=e=>new Promise((s,n)=>{const r=new FileReader;r.onload=()=>{const{result:o}=r;o instanceof ArrayBuffer?s(new Uint8Array(o)):s(new Uint8Array)},r.onerror=o=>{var u,c;n(Error(`File could not be read! Code=${((c=(u=o==null?void 0:o.target)==null?void 0:u.error)==null?void 0:c.code)||-1}`))},r.readAsArrayBuffer(e)}),bn=async e=>{let s;if(typeof e=="string")/data:_data\/([a-zA-Z]*);base64,([^"]*)/.test(e)?s=atob(e.split(",")[1]).split("").map(n=>n.charCodeAt(0)):s=await(await fetch(e)).arrayBuffer();else if(e instanceof URL)s=await(await fetch(e)).arrayBuffer();else if(e instanceof File||e instanceof Blob)s=await hn(e);else return new Uint8Array;return new Uint8Array(s)},vn=async(e,s)=>{var o;const n=await fetch(e);let r;try{const u=parseInt(n.headers.get(fn)||"-1"),c=(o=n.body)==null?void 0:o.getReader();if(!c)throw xn;const g=[];let x=0;for(;;){const{done:m,value:C}=await c.read(),v=C?C.length:0;if(m){if(u!=-1&&u!==x)throw pn;s&&s({url:e,total:u,received:x,delta:v,done:m});break}g.push(C),x+=v,s&&s({url:e,total:u,received:x,delta:v,done:m})}const h=new Uint8Array(x);let j=0;for(const m of g)h.set(m,j),j+=m.length;r=h.buffer}catch(u){console.log("failed to send download progress event: ",u),r=await n.arrayBuffer()}return r},qa=async(e,s,n=!1,r)=>{const o=n?await vn(e,r):await(await fetch(e)).arrayBuffer(),u=new Blob([o],{type:s});return URL.createObjectURL(u)};let Vt=null,Oe=null,O;async function gn(){if(Vt)return Vt;if(Oe)return Oe;Oe=(async()=>{const e=new mn;e.on("progress",({progress:o})=>{O==null||O(o,"transcoding")}),O==null||O(.01,"loading_ffmpeg");const s="https://unpkg.com/@ffmpeg/core@0.12.6/dist/esm",n=await qa(`${s}/ffmpeg-core.js`,"text/javascript");O==null||O(.04,"loading_ffmpeg");const r=await qa(`${s}/ffmpeg-core.wasm`,"application/wasm");return O==null||O(.07,"loading_ffmpeg"),await e.load({coreURL:n,wasmURL:r}),Vt=e,e})();try{return await Oe}finally{Oe=null}}async function yn(e){const s=URL.createObjectURL(e);try{return await new Promise(r=>{const o=document.createElement("video");o.preload="metadata",o.onloadedmetadata=()=>{const u=Number(o.duration);r(Number.isFinite(u)&&u>0?u:null)},o.onerror=()=>r(null),o.src=s})}finally{URL.revokeObjectURL(s)}}async function Ja(e,s){const n=O;O=s;try{const r=await gn(),o="input.avi",u="output.mp4";s==null||s(.1,"writing_input"),await r.writeFile(o,await bn(e)),await r.exec(["-i",o,"-c:v","libx264","-preset","veryfast","-crf","24","-pix_fmt","yuv420p","-movflags","+faststart","-an",u]);const c=await r.readFile(u);s==null||s(.98,"packing_output");const g=new Blob([c],{type:"video/mp4"}),x=new File([g],e.name.replace(/\.avi$/i,".mp4"),{type:"video/mp4"});await r.deleteFile(o),await r.deleteFile(u);const h=await yn(x);return s==null||s(1,"done"),{file:x,duration_s:h}}finally{O=n}}function Jt(e){return e==null||Number.isNaN(e)?"—":e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/(1024*1024)).toFixed(2)} MB`}function Pe(e,s){if(!e)return"—";try{const n=new Date(e);return new Intl.DateTimeFormat(s==="en"?"en-GB":"zh-CN",{dateStyle:"short",timeStyle:"medium"}).format(n)}catch{return e}}function ce(e){return e==null?null:typeof e=="string"&&e.trim()?e.trim():typeof e=="number"&&!Number.isNaN(e)?String(e):null}function wn(e){return typeof e!="number"||e<=0?null:e>=1e6?`${(e/1e6).toFixed(2)} s`:e>=1e3?`${(e/1e3).toFixed(0)} ms`:`${e} µs`}function jn(e,s){if(!e)return[];const n=[],r=wn(e.exposure_us);r&&n.push({key:"meta.exposure",value:r});const o=ce(e.analogue_gain),u=ce(e.digital_gain);if(o||u){const C=[o?`A ${o}`:"",u?`D ${u}`:""].filter(Boolean);n.push({key:"meta.gain",value:C.join(" · ")})}const c=ce(e.fps);c&&n.push({key:"meta.fps",value:c});const g=ce(e.sensor);g&&n.push({key:"meta.sensor",value:g});const x=ce(e.color_mode);x&&n.push({key:"meta.colorMode",value:x});const h=ce(e.resolution);h&&n.push({key:"meta.outputResolution",value:h});const j=ce(e.modified);j&&n.push({key:"meta.fileTime",value:Pe(j,s)});const m=e.size;return typeof m=="number"&&m>0&&n.push({key:"meta.fileSize",value:Jt(m)}),n}function as(e){const s=n=>typeof n=="number"&&!Number.isNaN(n)?n:null;return e?{tSolveMs:s(e.t_solve_ms),tExtractMs:s(e.t_extract_ms),tPreprocessMs:s(e.t_preprocess_ms),tOpenDecodeMs:s(e.t_open_decode_ms),tBackendTotalMs:s(e.t_backend_total_ms),raDeg:s(e.ra_deg),decDeg:s(e.dec_deg),matches:s(e.matches),rmseArcsec:s(e.rmse_arcsec),prob:s(e.prob),status:typeof e.status=="string"?e.status:null}:{tSolveMs:null,tExtractMs:null,tPreprocessMs:null,tOpenDecodeMs:null,tBackendTotalMs:null,raDeg:null,decDeg:null,matches:null,rmseArcsec:null,prob:null,status:null}}function it(e){return e==null?"—":`${e.toFixed(4)}°`}function ss(e){if(e==null||typeof e!="number"||Number.isNaN(e)||e<0)return null;if(e>=1)return 0;if(e===0)return 100;const s=(1-e)*100;if(s<=100-1e-12)return Math.min(100,Math.max(0,s));const n=-Math.log10(Math.max(e,Number.MIN_VALUE)),r=14,u=Math.min(1,Math.max(0,(n-r)/(50-r)));return Math.min(100,Math.max(0,70+30*u))}function Nn(e){const s=ss(e);return s==null?"—":`${s.toFixed(1)}%`}function _n(e){if(e==null)return"—";if(typeof e=="number"&&!Number.isNaN(e)){const s=Math.abs(e);return s>0&&s<.001?e.toExponential(4):s>=0&&s<=1?`${(e*100).toFixed(4)}%`:String(e)}return String(e)}function ge(e,s){const n=s==null?void 0:s.tetra,r=n?n.Prob??n.prob:void 0,o=Nn(e),u=r!=null?_n(r):null;return{line:o,rawLine:u}}function st({result:e,t:s,roundTripMs:n}){const r=as(e??void 0);return e?t.jsxs("div",{className:"mt-2 space-y-2 text-[10px]",children:[t.jsxs("div",{className:"grid grid-cols-2 gap-2",children:[r.tBackendTotalMs!=null&&t.jsxs("div",{children:[t.jsx("div",{className:"text-on-surface-variant",children:s("lab.metric.backendTotalMs")}),t.jsxs("div",{className:"font-semibold tabular-nums text-on-surface",children:[r.tBackendTotalMs.toFixed(0)," ms"]})]}),r.tSolveMs!=null&&t.jsxs("div",{children:[t.jsx("div",{className:"text-on-surface-variant",children:s("lab.metric.solveComputeMs")}),t.jsxs("div",{className:"font-semibold tabular-nums text-on-surface",children:[r.tSolveMs.toFixed(0)," ms"]})]}),r.tOpenDecodeMs!=null&&t.jsxs("div",{children:[t.jsx("div",{className:"text-on-surface-variant",children:s("lab.metric.openDecodeMs")}),t.jsxs("div",{className:"font-semibold tabular-nums text-on-surface",children:[r.tOpenDecodeMs.toFixed(0)," ms"]})]}),r.tPreprocessMs!=null&&t.jsxs("div",{children:[t.jsx("div",{className:"text-on-surface-variant",children:s("lab.metric.preprocessMs")}),t.jsxs("div",{className:"font-semibold tabular-nums text-on-surface",children:[r.tPreprocessMs.toFixed(0)," ms"]})]}),r.tExtractMs!=null&&t.jsxs("div",{children:[t.jsx("div",{className:"text-on-surface-variant",children:s("lab.metric.extractMs")}),t.jsxs("div",{className:"font-semibold tabular-nums text-on-surface",children:[r.tExtractMs.toFixed(0)," ms"]})]}),n!=null&&t.jsxs("div",{children:[t.jsx("div",{className:"text-on-surface-variant",children:s("lab.metric.solveRoundTripMs")}),t.jsxs("div",{className:"font-semibold tabular-nums text-on-surface",children:[n.toFixed(0)," ms"]})]}),r.matches!=null&&t.jsxs("div",{children:[t.jsx("div",{className:"text-on-surface-variant",children:s("lab.metric.matches")}),t.jsx("div",{className:"font-semibold tabular-nums text-on-surface",children:r.matches})]}),r.rmseArcsec!=null&&t.jsxs("div",{children:[t.jsx("div",{className:"text-on-surface-variant",children:s("lab.metric.rmse")}),t.jsxs("div",{className:"font-semibold tabular-nums text-on-surface",children:[r.rmseArcsec.toFixed(2),"″"]})]}),r.prob!=null&&t.jsxs("div",{className:"col-span-2",children:[t.jsx("div",{className:"text-on-surface-variant",children:s("lab.metric.prob")}),t.jsx("div",{className:"font-semibold text-on-surface",children:ge(r.prob,e).line}),t.jsx("p",{className:"mt-0.5 text-[9px] leading-snug text-on-surface-variant/90",children:s("lab.metric.probHelp")}),ge(r.prob,e).rawLine&&t.jsxs("div",{className:"mt-1 text-[9px] text-on-surface-variant",children:[s("lab.metric.probRaw"),": ",ge(r.prob,e).rawLine,t.jsx("p",{className:"mt-0.5 text-[8px] leading-snug opacity-90",children:s("lab.metric.probRawHelp")})]})]})]}),e.centroid_quality!=null&&typeof e.centroid_quality=="object"&&t.jsxs("div",{className:"rounded border border-outline-variant/25 bg-surface-container-high/40 p-2",children:[t.jsxs("div",{className:"text-[9px] font-medium text-on-surface-variant",children:[s("lab.centroidQualityTitle"),typeof e.centroid_quality.level=="number"?` · L${e.centroid_quality.level}`:""]}),Array.isArray(e.centroid_quality.hints)&&e.centroid_quality.hints.length>0?t.jsx("ul",{className:"mt-1 list-inside list-disc text-[9px] leading-snug text-on-surface-variant",children:e.centroid_quality.hints.map((o,u)=>t.jsx("li",{children:o},u))}):null,(()=>{const o=e.centroid_quality.metrics;if(!o)return null;const u=o.input_count,c=o.output_count,g=o.removed_dense,x=o.removed_line;return typeof u!="number"||typeof c!="number"||typeof g!="number"||typeof x!="number"?null:t.jsx("p",{className:"mt-1 font-mono text-[9px] text-on-surface-variant/95",children:s("lab.centroidQualityMetrics",{in:u,out:c,dense:g,line:x})})})()]}),t.jsxs("div",{children:[t.jsx("div",{className:"text-on-surface-variant",children:s("lab.metric.radec")}),t.jsxs("div",{className:"font-mono text-[9px] text-on-surface",children:["α ",it(r.raDeg)," · δ ",it(r.decDeg)]})]}),r.status&&t.jsxs("div",{className:"rounded bg-surface-container-high px-2 py-1 text-[9px] font-mono text-on-surface",children:[s("lab.metric.status"),": ",r.status]})]}):t.jsx("p",{className:"mt-2 text-[10px] text-on-surface-variant",children:"—"})}function W({label:e,helpKey:s,value:n,onChange:r,type:o="number",step:u}){const{t:c}=$e(),g=s?c(s):void 0;return t.jsxs("label",{className:"block",children:[t.jsx("span",{className:"text-[10px] font-medium text-on-surface-variant",children:e}),g&&t.jsx("p",{className:"mb-1 mt-0.5 text-[9px] leading-snug text-on-surface-variant/85",children:g}),t.jsx("input",{type:o,step:u,className:"w-full rounded bg-surface-container-highest px-2 py-1",value:n===""?"":n,onChange:x=>{const h=x.target.value;r(h===""?void 0:Number(h))}})]})}function Sn({view:e,setView:s}){const{t:n,locale:r,setLocale:o}=$e();return t.jsxs("header",{className:"flex h-12 shrink-0 items-center justify-between border-b border-outline-variant/20 px-4",children:[t.jsxs("div",{className:"flex items-center gap-6",children:[t.jsx("span",{className:"font-headline text-sm font-bold tracking-wide text-on-surface",children:n("app.title")}),t.jsxs("nav",{className:"flex flex-wrap gap-3 text-xs",children:[t.jsx("button",{type:"button",className:e==="lab_image"?"text-primary":"text-on-surface-variant",onClick:()=>s("lab_image"),children:n("nav.labImage")}),t.jsx("button",{type:"button",className:e==="lab_video"?"text-primary":"text-on-surface-variant",onClick:()=>s("lab_video"),children:n("nav.labVideo")}),t.jsx("button",{type:"button",className:e==="pool"?"text-primary":"text-on-surface-variant",onClick:()=>s("pool"),children:n("nav.pool")}),t.jsx("button",{type:"button",className:e==="history"?"text-primary":"text-on-surface-variant",onClick:()=>s("history"),children:n("nav.history")})]})]}),t.jsxs("div",{className:"flex items-center gap-2",children:[t.jsxs("div",{className:"mr-2 flex gap-1 text-[10px]",children:[t.jsx("button",{type:"button",className:`rounded px-2 py-0.5 ${r==="zh"?"bg-primary-container text-on-primary-container":"text-on-surface-variant"}`,onClick:()=>o("zh"),children:n("lang.zh")}),t.jsx("button",{type:"button",className:`rounded px-2 py-0.5 ${r==="en"?"bg-primary-container text-on-primary-container":"text-on-surface-variant"}`,onClick:()=>o("en"),children:n("lang.en")})]}),t.jsxs("a",{className:"flex items-center gap-1 rounded border border-outline-variant/30 px-2 py-1 text-xs text-on-surface-variant hover:bg-surface-container",href:"/debug",children:[t.jsx(zs,{className:"h-3.5 w-3.5"})," ",n("nav.systemAdmin")]})]})]})}function kn({historyQ:e,setHistoryQ:s,historyPage:n,setHistoryPage:r,historyData:o,historyExpandId:u,setHistoryExpandId:c,historyTotalPages:g,onDeleteExperiment:x,onSearch:h}){var C;const{t:j,locale:m}=$e();return t.jsxs("main",{className:"og-scrollbar flex-1 overflow-auto p-4",children:[t.jsx("p",{className:"mb-4 rounded-lg border border-outline-variant/25 bg-surface-container-lowest/90 p-3 text-xs leading-relaxed text-on-surface-variant",children:j("history.intro")}),t.jsxs("div",{className:"mb-4 flex flex-wrap items-center gap-4",children:[t.jsxs("h2",{className:"flex items-center gap-2 text-lg font-semibold",children:[t.jsx(Hs,{className:"h-5 w-5"})," ",j("history.title")]}),t.jsx("input",{className:"rounded bg-surface-container-highest px-2 py-1 text-xs",placeholder:j("history.search"),value:e,onChange:v=>s(v.target.value)}),t.jsx("button",{type:"button",className:"rounded bg-surface-container px-2 py-1 text-xs",onClick:h,children:j("history.searchBtn")}),t.jsx("button",{type:"button",className:"rounded bg-primary-container/50 px-2 py-1 text-xs",onClick:()=>Va("json").then(v=>{const L=new Blob([v],{type:"application/json"}),R=document.createElement("a");R.href=URL.createObjectURL(L),R.download="experiments.json",R.click()}),children:j("history.exportJson")}),t.jsx("button",{type:"button",className:"rounded bg-primary-container/50 px-2 py-1 text-xs",onClick:()=>Va("csv").then(v=>{const L=new Blob([v],{type:"text/csv"}),R=document.createElement("a");R.href=URL.createObjectURL(L),R.download="experiments.csv",R.click()}),children:j("history.exportCsv")})]}),t.jsxs("div",{className:"flex flex-wrap items-center gap-3 text-xs text-on-surface-variant",children:[t.jsx("span",{children:j("history.total",{n:(o==null?void 0:o.total)??0})}),t.jsx("button",{type:"button",className:"rounded border border-outline-variant/30 px-2 py-0.5 disabled:opacity-40",disabled:n<=1,onClick:()=>r(v=>Math.max(1,v-1)),children:j("history.prev")}),t.jsxs("span",{children:[n," / ",g]}),t.jsx("button",{type:"button",className:"rounded border border-outline-variant/30 px-2 py-0.5 disabled:opacity-40",disabled:n>=g,onClick:()=>r(v=>v+1),children:j("history.next")})]}),t.jsx("ul",{className:"mt-2 space-y-2",children:(C=o==null?void 0:o.items)==null?void 0:C.map(v=>{const L=String(v.id??""),R=v.metrics,B=u===L;return t.jsxs("li",{className:"rounded border border-outline-variant/20 p-2 text-[11px]",children:[t.jsxs("div",{className:"flex flex-wrap items-start justify-between gap-2 font-mono",children:[t.jsxs("div",{children:[t.jsxs("div",{className:"text-on-surface",children:[Pe(String(v.created_at??""),m)," —"," ",String(v.input_name)," — ",String(v.preset_label)]}),t.jsxs("div",{className:"mt-1 text-on-surface-variant",children:[j("history.preset"),": ",String(v.preset_label)," · ",j("history.metrics"),":"," ","matches=",String((R==null?void 0:R.matches)??"—")," rmse=",String((R==null?void 0:R.rmse_arcsec)??"—")]})]}),t.jsxs("div",{className:"flex shrink-0 gap-1",children:[t.jsx("button",{type:"button",className:"rounded bg-surface-container px-2 py-0.5 text-[10px]",onClick:()=>c(B?null:L),children:j(B?"history.collapse":"history.detail")}),t.jsx("button",{type:"button",className:"rounded px-2 py-0.5 text-[10px] text-error hover:bg-error-container/20",title:j("history.delete"),onClick:()=>void x(L),children:j("history.delete")})]})]}),B&&t.jsxs("div",{className:"mt-2 space-y-2",children:[v.asset_snapshot_relpath?t.jsx("img",{src:rn(L),alt:"",className:"max-h-48 max-w-full rounded border border-outline-variant/20 object-contain"}):null,t.jsx("pre",{className:"og-scrollbar max-h-64 overflow-auto rounded bg-surface-container p-2 text-[10px]",children:JSON.stringify(v.result_json,null,2)})]})]},L)})})]})}function En({uploads:e,onDelete:s}){const{t:n,locale:r}=$e();return t.jsxs("main",{className:"og-scrollbar flex-1 overflow-auto p-4",children:[t.jsxs("h2",{className:"mb-4 flex items-center gap-2 text-lg font-semibold",children:[t.jsx(Bs,{className:"h-5 w-5"})," ",n("pool.title")]}),t.jsxs("table",{className:"w-full text-left text-xs",children:[t.jsx("thead",{children:t.jsxs("tr",{className:"border-b border-outline-variant/30 text-on-surface-variant",children:[t.jsx("th",{className:"py-2",children:n("pool.col.name")}),t.jsx("th",{className:"py-2",children:n("pool.col.source")}),t.jsx("th",{className:"py-2",children:n("pool.col.size")}),t.jsx("th",{className:"py-2",children:n("pool.col.time")}),t.jsx("th",{className:"w-16 py-2 text-center",children:n("pool.delete")})]})}),t.jsx("tbody",{children:e.map(o=>t.jsxs("tr",{className:"border-b border-outline-variant/10",children:[t.jsx("td",{className:"py-2 font-mono",children:o.filename}),t.jsx("td",{className:"py-2",children:o.source??n("common.placeholder")}),t.jsx("td",{className:"py-2",children:Jt(o.size)}),t.jsx("td",{className:"py-2",children:Pe(o.modified_at,r)}),t.jsx("td",{className:"py-2 text-center",children:t.jsx("button",{type:"button",className:"inline-flex rounded p-1 text-on-surface-variant hover:bg-error-container/30 hover:text-error",title:n("pool.delete"),"aria-label":n("pool.delete"),onClick:()=>void s(o.filename),children:t.jsx(es,{className:"h-3.5 w-3.5"})})})]},o.filename))})]})]})}function Gt(e){return e?{matches:e.matches,rmse_arcsec:e.rmse_arcsec,status:e.status,prob:e.prob,t_solve_ms:e.t_solve_ms}:{}}function Rn(e){var n,r;if(!e)return null;const s=e.solve_overlay;return(n=s==null?void 0:s.stars_matched)!=null&&n.length?s.stars_matched.length:(r=s==null?void 0:s.stars_all_centroids)!=null&&r.length?s.stars_all_centroids.length:typeof e.matches=="number"?e.matches:null}function Za(e){return/\.(jpe?g|png|webp|bmp|gif|fits?)$/i.test(e)}function ve(e){return/\.(mp4|mov|webm|mkv|avi)$/i.test(e)}function nt(e,s=16,n=14){return e.length<=s+n+1?e:`${e.slice(0,s)}...${e.slice(-n)}`}const Ya=10,rt=30,Kt=6,qt=()=>({hint_ra_deg:45,hint_dec_deg:80,fov_estimate:11,fov_max_error:void 0,solve_timeout_ms:1500,solve_profile:"balanced",max_image_side:1280,large_scale_bg_subtract:!1,detail_level:"summary",centroid_rejection_level:3,centroid:{sigma:2.5,max_area:400,min_area:5,filtsize:25,binary_open:!0,max_axis_ratio:void 0}});function Xa(e,s,n,r){const o=typeof r=="number"&&r>0?` ${e("lab.gateNextMs",{ms:r})}`:"",u=String(n??"").toLowerCase();return s==="SKIPPED_BUSY"?u.includes("recording")?`${e("lab.gate.skipRecording")}${o}`:u.includes("previous")?`${e("lab.gate.skipBusyInFlight")}${o}`:`${e("lab.gate.skipBusyFallback",{detail:String(n??"")})}${o}`:s==="SKIPPED_INTERVAL"?`${e("lab.gate.skipInterval")}${o}`:`${s??"?"}${n?`: ${String(n)}`:""}${o}`}async function Qa(){try{const e=await fetch("/api/dev/debug/camera/stream/status",{cache:"no-store",credentials:"same-origin"});if(!e.ok)return"fail";const s=await e.json();if(s.success===!1)return"fail";const n=Number(s.max_clients??0),r=Number(s.active_clients??0);return n>0&&r>=n?"busy":"ok"}catch{return"fail"}}function Mn(){var Ma,Ca,La,Ia,Da,Fa,Oa;const{t:e,locale:s}=$e(),[n,r]=l.useState("lab_image"),[o,u]=l.useState([]),[c,g]=l.useState(null),[x,h]=l.useState(qt),[j,m]=l.useState([]),[C,v]=l.useState([]),[L,R]=l.useState(new Set),[B,U]=l.useState(!1),[ot,y]=l.useState(null),[S,ye]=l.useState(null),[T,we]=l.useState(null),[Te,Zt]=l.useState([]),[ns,Yt]=l.useState([]),Xt=l.useRef(null),Qt=l.useRef(null),[je,rs]=l.useState(""),[Ae,ea]=l.useState(1),[Ue,ct]=l.useState(null),[ta,aa]=l.useState(null),[dt,sa]=l.useState(""),[G,is]=l.useState({matched:!0,pattern:!0,all:!0,rejected:!0}),[Be,na]=l.useState([]),[H,ra]=l.useState(null),[Ne,_e]=l.useState(1),[He,ia]=l.useState(!1),[me,ut]=l.useState(1),[la,ls]=l.useState({w:0,h:0}),[xe,os]=l.useState({w:0,h:0}),[oa,ca]=l.useState({w:0,h:0}),[k,Se]=l.useState("file"),[da,ze]=l.useState(()=>Date.now()),[mt,K]=l.useState("unknown"),[ke,xt]=l.useState(null),[Ee,ua]=l.useState(!1),[pe,ma]=l.useState(!1),[xa,cs]=l.useState(!1),[q,pt]=l.useState(!1),[ds,ft]=l.useState(null),[ht,bt]=l.useState(null),[vt,We]=l.useState(null),[gt,pa]=l.useState(!1),[us,yt]=l.useState({}),[fa,wt]=l.useState(!1),[jt,Z]=l.useState(null),[ms,Re]=l.useState(null),[Nt,Me]=l.useState(null),[_t,ha]=l.useState(null),[ba,va]=l.useState(!1),[ga,St]=l.useState(null),[xs,Q]=l.useState(null),[ya,wa]=l.useState(!1),[kt,ne]=l.useState(null),[ps,z]=l.useState(null),[Et,Rt]=l.useState(null),Mt=l.useRef(null),Ce=l.useRef(null),Le=l.useRef(null),Ct=l.useRef(0),re=l.useRef(null),ie=l.useRef(null),Lt=l.useRef(!1),It=l.useRef(!1),Dt=l.useRef(!1),Ft=l.useRef(!1),fe=l.useRef(null),fs=l.useCallback(async()=>{const a=Le.current;if(!a||a.naturalWidth<2)return null;const i=document.createElement("canvas");i.width=a.naturalWidth,i.height=a.naturalHeight;const d=i.getContext("2d");if(!d)return null;try{d.drawImage(a,0,0)}catch{return null}return await new Promise(p=>{i.toBlob(N=>p(N?URL.createObjectURL(N):null),"image/jpeg",.92)})},[]),[Ve,hs]=l.useState(null),[I,ja]=l.useState(null),Ge=l.useCallback((a,i)=>{const d=`${Date.now()}-${Math.random().toString(36).slice(2,9)}`;Yt(p=>[...p,{id:d,text:a,variant:i}]),window.setTimeout(()=>{Yt(p=>p.filter(N=>N.id!==d))},4800)},[]);l.useEffect(()=>{Xt.current=S},[S]),l.useEffect(()=>{Qt.current=T},[T]),l.useEffect(()=>{Dt.current=pe},[pe]),l.useEffect(()=>{Ft.current=Ee},[Ee]),l.useEffect(()=>{ot&&(Ge(ot,"error"),y(null))},[ot,Ge]),l.useEffect(()=>{Nt&&(Ge(Nt,"warn"),Me(null))},[Nt,Ge]);const Ke=l.useCallback(()=>{const a=Xt.current,i=Qt.current;if(!a&&!i)return;const d=`${Date.now()}-${Math.random().toString(36).slice(2,9)}`;Zt(p=>[{id:d,at:Date.now(),single:a??void 0,batch:i??void 0},...p].slice(0,Ya))},[]),qe=()=>{ie.current!=null&&(window.clearTimeout(ie.current),ie.current=null)},Je=()=>{re.current!=null&&(window.clearTimeout(re.current),re.current=null)},Ot=(I==null?void 0:I.star_analysis_min_interval_ms)??2e3,Pt=(I==null?void 0:I.star_analysis_max_interval_ms)??12e3,$t=l.useMemo(()=>{const a=(I==null?void 0:I.star_analysis_target_fps)??.6666666666666666,i=Math.min(Math.max(a,.2),5);return Math.round(1e3/i)},[I]),le=l.useMemo(()=>Math.min(Math.max(_t??$t,Ot),Pt),[$t,_t,Pt,Ot]),ee=l.useCallback(async()=>{const[a,i,d]=await Promise.all([Vs(),Bt("official"),Bt("user")]);u(a.files),m(i.presets),v(d.presets)},[]),Tt=l.useCallback(()=>{Xs().then(a=>na(a.files)).catch(()=>na([]))},[]);l.useEffect(()=>{an().then(a=>{ja(a);const i=Math.round(1e3/Math.min(Math.max(a.star_analysis_target_fps,.2),5)),d=Math.min(Math.max(i,a.star_analysis_min_interval_ms),a.star_analysis_max_interval_ms);ha(d);const p=a.centroid_rejection_level_default;typeof p=="number"&&p>=1&&p<=5&&h(N=>({...N,centroid_rejection_level:p}))}).catch(()=>ja(null))},[]),l.useEffect(()=>{ee().catch(a=>y(String(a)))},[ee]),l.useEffect(()=>{Tt()},[Tt]),l.useEffect(()=>{n==="history"&&zt(je,Ae,rt).then(ct).catch(a=>y(String(a)))},[n,je,Ae]),l.useEffect(()=>{if(!c){We(null);return}let a=!1;return pa(!0),(async()=>{try{const i=await en(c);if(a)return;let d={...i};try{d={...await Wa(c),...i}}catch{}We(d)}catch{try{const i=await Wa(c);a||We(i)}catch{a||We(null)}}finally{a||pa(!1)}})(),()=>{a=!0}},[c]),l.useEffect(()=>{ye(null),we(null),yt({}),wt(!1),Z(null),Re(null),Se("file"),xt(null),Zt([])},[c]);const Y=l.useMemo(()=>{const a=S==null?void 0:S.result;if(!a)return null;const i=a.solve_overlay||null;if(!i)return null;const d=a.overlay_ext||null;return{...i,overlay_ext:d||void 0}},[S]),te=l.useMemo(()=>(S==null?void 0:S.result)??null,[S]),Na=l.useMemo(()=>Rn(te),[te]),At=l.useMemo(()=>n==="lab_image"?la:n==="lab_video"?k==="file"?xe:oa:{w:0,h:0},[n,k,la,xe,oa]),Ze=c?Wt(c):"";l.useEffect(()=>{if(n!=="lab_image")return;const a=Mt.current,i=fe.current;if(!a||!i||!Y||!c)return;const d=()=>Ga(i,a,Y,G);a.complete?d():a.onload=d},[Y,c,S,G,n]),l.useEffect(()=>{if(n!=="lab_video"||k!=="file")return;const a=Ce.current,i=fe.current;if(!a||!i||!Y||!c)return;const d=()=>on(i,a,Y,G);return a.addEventListener("loadeddata",d),a.addEventListener("seeked",d),a.readyState>=2&&d(),()=>{a.removeEventListener("loadeddata",d),a.removeEventListener("seeked",d)}},[Y,G,n,k,c,S]),l.useEffect(()=>{if(n!=="lab_video"||k!=="camera")return;const a=Le.current,i=fe.current;if(!a||!i||!Y)return;const d=()=>Ga(i,a,Y,G);a.complete?d():a.onload=d},[Y,G,n,k,S,da,q]),l.useEffect(()=>{if(n!=="lab_video"||k!=="camera"){K("unknown");return}if(q){K("ok");return}let a=!1;return K("unknown"),(async()=>{try{const i=await Qa();if(a)return;if(i==="fail"){K("fail");return}if(i==="busy"){K("busy");return}K("ok"),Ct.current=0,ze(Date.now())}catch{a||K("fail")}})(),()=>{a=!0}},[n,k,q]),l.useEffect(()=>{const a=Mt.current;if(!a)return;const i=()=>ls({w:a.naturalWidth||0,h:a.naturalHeight||0});return i(),a.addEventListener("load",i),()=>a.removeEventListener("load",i)},[c,Ze]),l.useEffect(()=>{const a=Ce.current;if(!a)return;const i=()=>os({w:a.videoWidth||0,h:a.videoHeight||0});return a.addEventListener("loadedmetadata",i),a.addEventListener("loadeddata",i),i(),()=>{a.removeEventListener("loadedmetadata",i),a.removeEventListener("loadeddata",i)}},[c,Ze,n]);const bs=a=>{h({...qt(),...a,centroid:{...qt().centroid,...a.centroid}})},vs=async()=>{if(!c){y(e("err.selectFile"));return}y(null),U(!0);const a=performance.now();try{const i=await qs(c,x);Ke(),ye(i),we(null),Re("file"),Z(performance.now()-a)}catch(i){y(String(i)),Z(null)}finally{U(!1)}},gs=async()=>{if(!c){y(e("err.selectFile"));return}const a=[];for(const d of L){const N=[...j,...C].find($=>$.id===d);N&&a.push({label:N.name,params:structuredClone(N.params)})}if(a.length===0){y(e("err.selectPresets"));return}y(null),U(!0);const i=performance.now();try{const d=await Js(c,a);Ke(),we({results:d.results}),ye(null),yt({}),Re("file"),Z(performance.now()-i)}catch(d){y(String(d)),Z(null)}finally{U(!1)}},Ye=async a=>{if(Lt.current)return;y(null),Lt.current=!0;const i=performance.now();try{const d=await sn({source:"camera",overlay_topn_count:3,enable_polar_guide:!0,solve_interval_ms:le,solve_timeout_ms:Math.min(((I==null?void 0:I.solver_timeout_ms)??1500)*.6,1200),...x}),p=d.gate_status,N=d.next_allowed_in_ms,$=d.effective_interval_ms;if(p==="SKIPPED_INTERVAL"||p==="SKIPPED_BUSY"){if(Me(Xa(e,p,d.gate_reason,typeof N=="number"?N:null)),a){Je();const M=Math.max(50,Number(N??le));re.current=window.setTimeout(()=>{re.current=null,Ft.current&&Ye(!0)},M)}return}Me(null),d.result!=null&&(Ke(),ye(d)),we(null),Re("camera"),Se("camera");const E=d.result,D=typeof(E==null?void 0:E.status)=="string"?String(E.status):"";if(xa&&D==="MATCH_FOUND"){pt(!0),ft(d.frame_id!=null?String(d.frame_id):null);const M=await fs();bt(oe=>(oe&&URL.revokeObjectURL(oe),M)),et()}else if(a){Je();const M=Math.max(50,Number($??le));re.current=window.setTimeout(()=>{re.current=null,Ft.current&&Ye(!0)},M)}Z(performance.now()-i)}catch(d){y(String(d)),Z(null)}finally{Lt.current=!1}},ys=()=>void Ye(!1),Xe=async a=>{if(It.current||!c)return;const i=Ce.current;if(!i||i.videoWidth<2||i.videoHeight<2||ke)return;y(null),It.current=!0;const d=performance.now();try{const p=document.createElement("canvas");p.width=i.videoWidth,p.height=i.videoHeight;const N=p.getContext("2d");if(!N)throw new Error("无法创建画布上下文 / Cannot create canvas context");N.drawImage(i,0,0,p.width,p.height);const $=await new Promise((be,Fs)=>{p.toBlob(Pa=>Pa?be(Pa):Fs(new Error("帧编码失败 / Frame encode failed")),"image/jpeg",.92)}),E=await nn($,{...x,solve_interval_ms:le,solve_timeout_ms:Math.min(((I==null?void 0:I.solver_timeout_ms)??1500)*.6,1200),overlay_topn_count:3,enable_polar_guide:!0}),D=E.gate_status,M=E.next_allowed_in_ms,oe=E.effective_interval_ms;if(D==="SKIPPED_INTERVAL"||D==="SKIPPED_BUSY"){if(Me(Xa(e,D,E.gate_reason,typeof M=="number"?M:null)),a){qe();const be=Math.max(50,Number(M??le));ie.current=window.setTimeout(()=>{ie.current=null,Dt.current&&Xe(!0)},be)}return}if(Me(null),E.result!=null&&(Ke(),ye(E)),we(null),Re("file"),Z(performance.now()-d),a){qe();const be=Math.max(50,Number(oe??le));ie.current=window.setTimeout(()=>{ie.current=null,Dt.current&&Xe(!0)},be)}}catch(p){y(String(p)),Z(null)}finally{It.current=!1}},ws=()=>void Xe(!1),js=()=>{pe||!c||(ma(!0),qe(),Xe(!0))},_a=l.useMemo(()=>n!=="lab_video"||k!=="file"||!c||/\.avi$/i.test(c)||ke?!1:xe.w>1&&xe.h>1,[n,k,c,ke,xe.w,xe.h]),Sa=l.useMemo(()=>!!c&&/\.avi$/i.test(c),[c]),Ns=async()=>{if(!(!c||!Sa||ba)){y(null),va(!0),St(0),Q(e("lab.transcode.loading"));try{const a=Wt(c),i=await fetch(a,{cache:"no-store"});if(!i.ok)throw new Error(e("lab.transcode.fetchFailed"));const d=await i.blob(),p=new File([d],c,{type:"video/x-msvideo"}),N=await Ja(p,(E,D)=>{St(Math.max(0,Math.min(1,E))),D==="loading_ffmpeg"?Q(e("lab.transcode.loading")):D==="writing_input"?Q(e("lab.transcode.writingBuffer")):D==="transcoding"?Q(e("lab.transcode.running")):D==="packing_output"&&Q(e("lab.transcode.packaging"))});Q(e("lab.transcode.uploading"));const $=await Ut(N.file,"analysis_transcoded");await za({old_filename:c,new_filename:$.filename,duration_s:N.duration_s,nominal_fps:null,codec_fourcc:"libx264",container:"MP4"}),await ee(),g($.filename),St(1),Q(e("lab.transcode.done"))}catch(a){y(String(a)),Q(e("lab.transcode.failed"))}finally{va(!1)}}},_s=async a=>{z(e("sidebar.flowDownloadingPool")),ne(.19);const i=Wt(a),d=await fetch(i,{cache:"no-store"});if(!d.ok)throw new Error(e("lab.transcode.fetchFailed"));const p=await d.blob(),N=new File([p],a,{type:"video/x-msvideo"}),$=await Ja(N,(D,M)=>{ne(.2+Math.max(0,Math.min(1,D))*.6),M==="loading_ffmpeg"?z(e("sidebar.flowLoadingTranscoder")):M==="writing_input"?z(e("sidebar.flowWritingBuffer")):M==="transcoding"?z(e("sidebar.flowTranscoding")):M==="packing_output"&&z(e("sidebar.flowPackaging"))});z(e("sidebar.flowUploadingMp4")),ne(.86);const E=await Ut($.file,"debug_console_transcoded");return z(e("sidebar.flowReplacing")),ne(.94),await za({old_filename:a,new_filename:E.filename,duration_s:$.duration_s,nominal_fps:null,codec_fourcc:"libx264",container:"MP4"}),E.filename},Ss=async()=>{if(!H||ya)return;const a=/\.avi$/i.test(H);y(null),wa(!0),ne(.02),Rt(null);try{z(e("sidebar.flowImportingDebug"));const i=await Ks(H);ne(a?.18:1);let d=i.filename;a?d=await _s(i.filename):z(e("sidebar.flowNoTranscode")),await ee(),g(d),r(ve(d)?"lab_video":"lab_image"),ne(1),z(e("sidebar.flowDone")),Rt(e("sidebar.flowDoneMsg",{name:nt(d)}))}catch(i){y(String(i)),z(e("sidebar.flowFailed")),Rt(String(i))}finally{wa(!1)}},Qe=()=>{ma(!1),qe()},ks=()=>{Ee||q||(ua(!0),Je(),Ye(!0))},et=()=>{ua(!1),Je()},Es=async()=>{try{const a=Le.current;a&&(a.onload=null,a.onerror=null,a.src="",a.removeAttribute("src")),await new Promise(i=>window.requestAnimationFrame(()=>i())),await fetch("/api/dev/debug/camera/stop",{method:"POST"}),ze(Date.now()),k==="camera"&&Se("file")}catch{}},Rs=()=>{pt(!1),ft(null),bt(a=>(a&&URL.revokeObjectURL(a),null)),ze(Date.now())},Ms=a=>{R(i=>{const d=new Set(i);return d.has(a)?d.delete(a):d.add(a),d})},ka=async a=>{if(!window.confirm(e("delete.uploadFirst",{name:a})))return;let i=0;try{i=(await tn(a)).count}catch{i=0}if(i>0){if(!window.confirm(e("delete.uploadCascade",{n:i})))return}else if(!window.confirm(e("delete.uploadSecond")))return;U(!0),y(null);try{await Gs(a,{deleteExperiments:i>0}),await ee(),c===a&&g(null)}catch(d){y(String(d))}finally{U(!1)}},Cs=async a=>{if(window.confirm(e("delete.experimentFirst"))&&window.confirm(e("delete.experimentSecond"))){U(!0),y(null);try{await Ys(a),ta===a&&aa(null);const i=await zt(je,Ae,rt);ct(i)}catch(i){y(String(i))}finally{U(!1)}}},tt=a=>ut(Math.min(4,Math.max(.5,a))),Ls=Math.max(1,Math.ceil(((Ue==null?void 0:Ue.total)??0)/rt)),he=l.useMemo(()=>n==="lab_image"?Be.filter(a=>a.type==="image"||Za(a.name)):n==="lab_video"?Be.filter(a=>a.type==="video"||ve(a.name)):Be,[Be,n]),Ie=Math.max(1,Math.ceil(he.length/Kt)),Is=l.useMemo(()=>{const a=(Ne-1)*Kt;return he.slice(a,a+Kt)},[he,Ne]);l.useEffect(()=>{_e(1)},[n]),l.useEffect(()=>{_e(a=>Math.min(a,Ie))},[Ie]),l.useEffect(()=>{H&&!he.some(a=>a.name===H)&&ra(null)},[he,H]);const Ea=l.useMemo(()=>jn(vt,s),[vt,s]),Ds=l.useMemo(()=>n==="lab_image"?o.filter(a=>Za(a.filename)):n==="lab_video"?o.filter(a=>ve(a.filename)):o,[o,n]),b=l.useMemo(()=>as(te),[te]),Ra=l.useMemo(()=>ss(b.prob),[b.prob]);return l.useEffect(()=>{wt(!1)},[S]),l.useEffect(()=>{if(n!=="lab_video")return;let a;const i=()=>{ln().then(hs).catch(()=>{})};return i(),a=setInterval(i,1500),()=>{a&&clearInterval(a)}},[n]),l.useEffect(()=>{(n!=="lab_video"||k!=="camera")&&(et(),pt(!1),ft(null),bt(a=>(a&&URL.revokeObjectURL(a),null))),(n!=="lab_video"||k!=="file")&&Qe()},[n,k]),l.useEffect(()=>{c||Qe()},[c]),l.useEffect(()=>()=>{et(),Qe()},[]),t.jsxs("div",{className:"flex min-h-full flex-col bg-surface text-on-surface",children:[t.jsx(Sn,{view:n,setView:r}),t.jsxs("div",{className:"flex min-h-0 flex-1",children:[t.jsxs("aside",{className:"w-64 shrink-0 border-r border-outline-variant/15 bg-surface-container-lowest p-3 text-xs",children:[t.jsx("div",{className:"mb-3 font-semibold text-on-surface-variant",children:e("sidebar.assets")}),t.jsxs("label",{className:"mb-3 flex cursor-pointer items-center gap-2 rounded bg-primary-container/30 px-2 py-2 text-on-primary-container",children:[t.jsx(Ws,{className:"h-4 w-4"}),t.jsx("span",{children:e("sidebar.upload")}),t.jsx("input",{type:"file",accept:"image/*,video/*",className:"hidden",onChange:async a=>{var d;const i=(d=a.target.files)==null?void 0:d[0];if(i){U(!0),y(null);try{const p=await Ut(i);await ee(),g(p.filename)}catch(p){y(String(p))}finally{U(!1),a.target.value=""}}}})]}),t.jsxs("button",{type:"button",className:"mb-2 flex w-full items-center gap-1 text-left text-on-surface-variant hover:text-on-surface",onClick:()=>{ee().catch(a=>y(String(a))),_e(1),Tt()},children:[t.jsx(Us,{className:"h-3 w-3"})," ",e("sidebar.refresh")]}),t.jsx("div",{className:"og-scrollbar max-h-36 overflow-y-auto border-t border-outline-variant/10 pt-2",children:Ds.map(a=>t.jsxs("div",{className:`mb-1 flex items-center gap-0.5 rounded px-1 ${c===a.filename?"bg-surface-container":""}`,children:[t.jsxs("button",{type:"button",className:`min-w-0 flex-1 truncate rounded px-1 py-1 text-left ${c===a.filename?"text-primary":""}`,title:a.filename,onClick:()=>{g(a.filename),r(ve(a.filename)?"lab_video":"lab_image")},children:[t.jsx("span",{className:"block truncate",children:nt(a.filename)}),t.jsx("span",{className:"block text-[9px] text-on-surface-variant/90",children:ve(a.filename)?e("sidebar.assetTypeVideo"):e("sidebar.assetTypeImage")})]}),t.jsx("button",{type:"button",className:"shrink-0 rounded p-1 text-on-surface-variant hover:bg-surface-container-high hover:text-error",title:e("pool.delete"),"aria-label":e("pool.delete"),onClick:i=>{i.stopPropagation(),ka(a.filename)},children:t.jsx(es,{className:"h-3.5 w-3.5"})})]},a.filename))}),t.jsxs("div",{className:"mt-3 border-t border-outline-variant/10 pt-3",children:[t.jsxs("div",{className:"mb-2 flex items-start justify-between gap-2",children:[t.jsx("div",{className:"min-w-0 font-semibold leading-tight text-on-surface-variant",children:e("sidebar.debugCaptures")}),t.jsx("button",{type:"button",className:"shrink-0 rounded bg-primary px-2 py-1 text-[10px] font-medium text-on-primary disabled:opacity-40",disabled:!H||ya,title:H??void 0,onClick:()=>void Ss(),children:H&&/\.avi$/i.test(H)?e("sidebar.importToPoolWithTranscode"):e("sidebar.importToPoolDirect")})]}),kt!=null&&t.jsxs("div",{className:"mb-2 rounded border border-outline-variant/25 bg-surface-container-low px-2 py-2",children:[t.jsxs("div",{className:"flex items-center justify-between gap-2 text-[10px]",children:[t.jsx("span",{className:"text-on-surface-variant",children:ps||e("sidebar.flowPreparing")}),t.jsxs("span",{className:"font-mono text-on-surface",children:[Math.round((kt||0)*100),"%"]})]}),t.jsx("div",{className:"mt-1.5 h-1.5 w-full overflow-hidden rounded bg-surface-container-highest",children:t.jsx("div",{className:"h-full bg-primary transition-all",style:{width:`${Math.round((kt||0)*100)}%`}})}),Et&&t.jsx("div",{className:"mt-1 text-[10px] text-on-surface-variant",title:Et,children:Et})]}),he.length===0?t.jsx("p",{className:"text-[10px] text-on-surface-variant",children:e("sidebar.debugEmpty")}):t.jsxs(t.Fragment,{children:[t.jsx("div",{className:"og-scrollbar max-h-[min(22rem,55vh)] space-y-1.5 overflow-y-auto pr-0.5",children:Is.map(a=>t.jsxs("button",{type:"button",className:`flex w-full items-center gap-2 rounded-md border px-2 py-1.5 text-left transition-colors ${H===a.name?"border-primary bg-primary-container/20":"border-outline-variant/25 hover:bg-surface-container"}`,onClick:()=>ra(a.name),children:[a.type==="image"?t.jsx("img",{src:Qs(a.name),alt:"",className:"h-11 w-11 shrink-0 rounded object-cover"}):t.jsx("div",{className:"flex h-11 w-11 shrink-0 items-center justify-center rounded bg-surface-container-high text-[9px] text-on-surface-variant",children:"video"}),t.jsxs("div",{className:"min-w-0 flex-1",children:[t.jsx("div",{className:"truncate font-mono text-[10px] text-on-surface",title:a.name,children:nt(a.name)}),t.jsxs("div",{className:"text-[9px] text-on-surface-variant",children:[a.type==="video"||ve(a.name)?e("sidebar.assetTypeVideo"):e("sidebar.assetTypeImage")," ","· ",Pe(a.modified,s)," · ",Jt(a.size)]})]})]},a.name))}),t.jsxs("div",{className:"mt-2 flex items-center justify-between gap-1 text-[10px] text-on-surface-variant",children:[t.jsx("button",{type:"button",className:"rounded border border-outline-variant/30 px-2 py-0.5 disabled:opacity-40",disabled:Ne<=1,onClick:()=>_e(a=>Math.max(1,a-1)),children:e("history.prev")}),t.jsx("span",{className:"tabular-nums",children:e("sidebar.debugPage",{cur:Ne,total:Ie})}),t.jsx("button",{type:"button",className:"rounded border border-outline-variant/30 px-2 py-0.5 disabled:opacity-40",disabled:Ne>=Ie,onClick:()=>_e(a=>Math.min(Ie,a+1)),children:e("history.next")})]})]})]})]}),(n==="lab_image"||n==="lab_video")&&t.jsxs("div",{className:"flex min-h-0 min-w-0 flex-1",children:[t.jsxs("main",{className:"og-scrollbar min-h-0 min-w-0 flex-1 overflow-y-auto p-4",children:[n==="lab_video"&&t.jsxs("div",{className:"mb-3 max-w-5xl rounded-lg border border-outline-variant/25 bg-surface-container-low/80 p-3 text-[11px] leading-relaxed text-on-surface",children:[t.jsx("p",{className:"text-[10px] text-on-surface-variant",children:e("lab.videoLiveIntro")}),t.jsx("p",{className:"mt-1.5 text-[10px] leading-snug text-on-surface-variant/95",children:e("lab.videoMjpegHint")}),t.jsxs("div",{className:"mt-2 flex flex-wrap gap-2",children:[t.jsx("button",{type:"button",className:`rounded px-3 py-1.5 text-[11px] font-medium ${k==="file"?"bg-primary text-on-primary-container":"border border-outline-variant/40 bg-surface-container text-on-surface"}`,onClick:()=>Se("file"),children:e("lab.previewModeFile")}),t.jsx("button",{type:"button",className:`rounded px-3 py-1.5 text-[11px] font-medium ${k==="camera"?"bg-primary text-on-primary-container":"border border-outline-variant/40 bg-surface-container text-on-surface"}`,onClick:()=>Se("camera"),children:e("lab.previewModeCamera")}),t.jsx("button",{type:"button",className:"rounded border border-outline-variant/40 bg-surface-container px-3 py-1.5 text-[11px] font-medium text-on-surface",onClick:()=>void Es(),children:e("lab.stopCameraPreview")})]})]}),t.jsxs("div",{className:"relative aspect-video w-full max-w-5xl overflow-hidden rounded-lg border border-outline-variant/20 bg-surface-container-lowest",children:[Ra!=null&&t.jsxs("div",{className:"pointer-events-none absolute right-2 top-2 z-20 rounded border border-white/30 bg-black/55 px-2 py-1 text-[10px] font-semibold tabular-nums text-white",children:[e("lab.previewConfidence"),": ",Ra.toFixed(1),"%"]}),n==="lab_video"&&k==="camera"?t.jsx("div",{className:"relative flex h-full min-h-[220px] flex-col items-center justify-center gap-3 bg-black p-2",children:t.jsxs("div",{className:"relative inline-block max-h-[70vh] max-w-full",children:[q&&ht?t.jsx("img",{ref:Le,src:ht,alt:"",className:"max-h-[70vh] w-full min-h-[120px] object-contain",onLoad:a=>ca({w:a.currentTarget.naturalWidth,h:a.currentTarget.naturalHeight})}):q&&!ht?t.jsxs("div",{className:"flex min-h-[200px] w-full min-w-[280px] flex-col items-center justify-center gap-2 text-[11px] text-on-surface-variant",children:[t.jsx(at,{className:"h-8 w-8 animate-spin text-primary"}),t.jsx("span",{children:e("lab.cameraPreviewLoading")})]}):mt==="busy"?t.jsx("div",{className:"flex min-h-[200px] max-w-md flex-col items-center justify-center gap-2 px-4 text-center text-[11px] text-on-surface-variant",children:t.jsx("span",{className:"text-error",children:e("lab.videoMjpegHint")})}):mt==="fail"?t.jsx("div",{className:"flex min-h-[200px] max-w-md flex-col items-center justify-center gap-2 px-4 text-center text-[11px] text-on-surface-variant",children:t.jsx("span",{className:"text-error",children:e("lab.cameraStreamStatusFail")})}):mt==="unknown"?t.jsxs("div",{className:"flex min-h-[200px] w-full min-w-[280px] flex-col items-center justify-center gap-2 text-[11px] text-on-surface-variant",children:[t.jsx(at,{className:"h-8 w-8 animate-spin text-primary"}),t.jsx("span",{children:e("lab.cameraPreviewLoading")})]}):t.jsx("img",{ref:Le,src:`/api/dev/debug/camera/stream?t=${da}`,alt:"",className:"max-h-[70vh] w-full min-h-[120px] object-contain",onLoad:a=>ca({w:a.currentTarget.naturalWidth,h:a.currentTarget.naturalHeight}),onError:()=>{(async()=>{const a=await Qa();if(a==="busy"){K("busy");return}if(a==="fail"){K("fail");return}if(Ct.current>=6){K("fail");return}Ct.current+=1,window.setTimeout(()=>ze(Date.now()),400)})()}}),t.jsx("canvas",{ref:fe,className:"pointer-events-none absolute left-0 top-0"})]})}):c?t.jsx("div",{className:"og-scrollbar relative h-full min-h-[200px] overflow-auto",children:n==="lab_video"?t.jsxs(t.Fragment,{children:[t.jsxs("div",{className:"absolute left-2 top-2 z-20 flex flex-wrap gap-1",children:[t.jsxs("button",{type:"button",title:e("lab.grid"),className:`rounded border px-2 py-1 text-[10px] ${He?"border-primary bg-primary/20":"border-white/30 bg-black/40"} text-white`,onClick:()=>ia(a=>!a),children:[t.jsx(Aa,{className:"mr-1 inline h-3 w-3"}),e("lab.grid")]}),t.jsx("button",{type:"button",title:e("lab.zoomOut"),className:"rounded border border-white/30 bg-black/40 px-2 py-1 text-[10px] text-white",onClick:()=>tt(me-.25),children:t.jsx(Ha,{className:"inline h-3 w-3"})}),t.jsx("button",{type:"button",title:e("lab.zoomIn"),className:"rounded border border-white/30 bg-black/40 px-2 py-1 text-[10px] text-white",onClick:()=>tt(me+.25),children:t.jsx(Ba,{className:"inline h-3 w-3"})}),t.jsx("button",{type:"button",title:e("lab.zoomReset"),className:"rounded border border-white/30 bg-black/40 px-2 py-1 text-[10px] text-white",onClick:()=>ut(1),children:t.jsx(Ua,{className:"inline h-3 w-3"})})]}),t.jsxs("div",{className:"flex min-h-[200px] flex-col items-center justify-center gap-2 bg-black py-2",children:[t.jsx("div",{className:"inline-block origin-top-left transition-transform",style:{transform:`scale(${me})`},children:t.jsxs("div",{className:"relative inline-block",children:[He&&t.jsx("div",{className:"pointer-events-none absolute inset-0 z-[1]",style:{backgroundImage:["linear-gradient(to right, rgba(255,255,255,0.12) 1px, transparent 1px)","linear-gradient(to bottom, rgba(255,255,255,0.12) 1px, transparent 1px)"].join(","),backgroundSize:"48px 48px"}}),t.jsx("video",{ref:Ce,src:Ze,loop:!0,playsInline:!0,autoPlay:!0,muted:!0,preload:"metadata",className:"max-h-[70vh] w-full max-w-full object-contain",onError:()=>xt(e("lab.videoPreviewFailed")),onLoadedData:()=>{xt(null);const a=Ce.current;a&&a.play().catch(()=>{})}}),t.jsx("canvas",{ref:fe,className:"pointer-events-none absolute left-0 top-0"})]})}),ke&&t.jsx("div",{className:"rounded border border-error/40 bg-error-container/20 px-3 py-1.5 text-[11px] text-error",children:ke}),Sa&&t.jsxs("div",{className:"max-w-3xl rounded border border-primary/40 bg-primary/10 px-3 py-2 text-[11px] text-on-surface",children:[t.jsx("div",{className:"font-semibold text-primary",children:e("lab.transcode.title")}),t.jsx("div",{className:"mt-1 text-on-surface-variant",children:e("lab.transcode.desc")}),ga!=null&&t.jsxs("div",{className:"mt-2",children:[t.jsx("div",{className:"mb-1 text-[10px] text-on-surface-variant",children:xs||e("lab.transcode.running")}),t.jsx("div",{className:"h-1.5 w-full overflow-hidden rounded bg-surface-container-highest",children:t.jsx("div",{className:"h-full bg-primary transition-all",style:{width:`${Math.round(ga*100)}%`}})})]}),t.jsx("div",{className:"mt-2",children:t.jsx("button",{type:"button",className:"rounded bg-primary px-3 py-1.5 text-[11px] font-medium text-on-primary disabled:opacity-50",disabled:ba,onClick:()=>void Ns(),children:e("lab.transcode.button")})})]})]})]}):t.jsxs(t.Fragment,{children:[t.jsxs("div",{className:"absolute left-2 top-2 z-20 flex flex-wrap gap-1",children:[t.jsxs("button",{type:"button",title:e("lab.grid"),className:`rounded border px-2 py-1 text-[10px] ${He?"border-primary bg-primary/20":"border-white/30 bg-black/40"} text-white`,onClick:()=>ia(a=>!a),children:[t.jsx(Aa,{className:"mr-1 inline h-3 w-3"}),e("lab.grid")]}),t.jsx("button",{type:"button",title:e("lab.zoomOut"),className:"rounded border border-white/30 bg-black/40 px-2 py-1 text-[10px] text-white",onClick:()=>tt(me-.25),children:t.jsx(Ha,{className:"inline h-3 w-3"})}),t.jsx("button",{type:"button",title:e("lab.zoomIn"),className:"rounded border border-white/30 bg-black/40 px-2 py-1 text-[10px] text-white",onClick:()=>tt(me+.25),children:t.jsx(Ba,{className:"inline h-3 w-3"})}),t.jsx("button",{type:"button",title:e("lab.zoomReset"),className:"rounded border border-white/30 bg-black/40 px-2 py-1 text-[10px] text-white",onClick:()=>ut(1),children:t.jsx(Ua,{className:"inline h-3 w-3"})})]}),t.jsx("div",{className:"inline-block origin-top-left transition-transform",style:{transform:`scale(${me})`},children:t.jsxs("div",{className:"relative inline-block",children:[He&&t.jsx("div",{className:"pointer-events-none absolute inset-0 z-[1]",style:{backgroundImage:["linear-gradient(to right, rgba(255,255,255,0.12) 1px, transparent 1px)","linear-gradient(to bottom, rgba(255,255,255,0.12) 1px, transparent 1px)"].join(","),backgroundSize:"48px 48px"}}),t.jsx("img",{ref:Mt,src:Ze,alt:"preview",className:"max-h-[70vh] w-full object-contain"}),t.jsx("canvas",{ref:fe,className:"pointer-events-none absolute left-0 top-0"})]})})]})}):t.jsx("div",{className:"flex h-full items-center justify-center px-4 text-center text-on-surface-variant",children:e(n==="lab_video"?"lab.selectOrUploadVideo":"lab.selectOrUpload")}),B&&t.jsx("div",{className:"absolute inset-0 flex items-center justify-center bg-black/40",children:t.jsx(at,{className:"h-8 w-8 animate-spin text-primary"})})]}),(c&&n==="lab_image"||n==="lab_video"&&(k==="file"&&c||k==="camera"))&&t.jsxs("div",{className:"mt-2 flex flex-wrap items-center gap-3 rounded-lg border border-outline-variant/25 bg-surface-container-lowest/90 px-3 py-2 text-xs text-on-surface",children:[t.jsx("span",{className:"shrink-0 font-medium text-on-surface-variant",children:e("lab.layers")}),t.jsx("div",{className:"flex flex-wrap gap-x-4 gap-y-1",children:["matched","pattern","all","rejected"].map(a=>t.jsxs("label",{className:"flex cursor-pointer items-center gap-1",children:[t.jsx("input",{type:"checkbox",className:"accent-primary",checked:G[a],onChange:i=>is(d=>({...d,[a]:i.target.checked}))}),t.jsx("span",{children:e(a==="matched"?"lab.layer.matched":a==="pattern"?"lab.layer.pattern":a==="all"?"lab.layer.all":"lab.layer.rejected")})]},a))})]}),(c||n==="lab_video"&&k==="camera")&&t.jsxs(t.Fragment,{children:[t.jsxs("div",{className:"mt-2 grid gap-2 sm:grid-cols-2",children:[t.jsxs("details",{open:!0,className:"rounded-lg border border-outline-variant/25 bg-surface-container-lowest/90",children:[t.jsxs("summary",{className:"flex cursor-pointer list-none items-center justify-between gap-2 px-3 py-2 text-xs font-semibold text-on-surface [&::-webkit-details-marker]:hidden",children:[e("lab.solveSection"),t.jsx(Fe,{className:"h-4 w-4 shrink-0 text-on-surface-variant"})]}),t.jsx("div",{className:"border-t border-outline-variant/15 px-3 py-2 text-[10px]",children:te?t.jsxs("div",{className:"space-y-1 text-on-surface",children:[b.tSolveMs!=null&&t.jsxs("div",{className:"space-y-0.5",children:[t.jsxs("div",{className:"flex justify-between gap-2",children:[t.jsx("span",{className:"text-on-surface-variant",children:e("lab.metric.solveComputeMs")}),t.jsxs("span",{className:"font-mono tabular-nums",children:[b.tSolveMs.toFixed(0)," ms"]})]}),t.jsx("p",{className:"text-[8px] leading-snug text-on-surface-variant/90",children:e("lab.metric.solveComputeHelp")})]}),jt!=null&&t.jsxs("div",{className:"space-y-0.5 border-t border-outline-variant/10 pt-1",children:[t.jsxs("div",{className:"flex justify-between gap-2",children:[t.jsx("span",{className:"text-on-surface-variant",children:e("lab.metric.solveRoundTripMs")}),t.jsxs("span",{className:"font-mono tabular-nums",children:[jt.toFixed(0)," ms"]})]}),t.jsx("p",{className:"text-[8px] leading-snug text-on-surface-variant/90",children:e("lab.metric.solveRoundTripHelp")})]}),(b.tBackendTotalMs!=null||b.tOpenDecodeMs!=null||b.tPreprocessMs!=null||b.tExtractMs!=null||b.tSolveMs!=null)&&t.jsxs("div",{className:"space-y-0.5 border-t border-outline-variant/10 pt-1",children:[t.jsxs("div",{className:"flex justify-between gap-2",children:[t.jsx("span",{className:"text-on-surface-variant",children:e("lab.metric.backendTotalMs")}),t.jsx("span",{className:"font-mono tabular-nums",children:b.tBackendTotalMs!=null?`${b.tBackendTotalMs.toFixed(0)} ms`:e("common.placeholder")})]}),t.jsxs("div",{className:"flex flex-wrap gap-x-2 gap-y-0.5 text-[8px] text-on-surface-variant/90",children:[t.jsxs("span",{children:[e("lab.metric.openDecodeMs"),":"," ",b.tOpenDecodeMs!=null?`${b.tOpenDecodeMs.toFixed(0)} ms`:e("common.placeholder")]}),t.jsxs("span",{children:[e("lab.metric.preprocessMs"),":"," ",b.tPreprocessMs!=null?`${b.tPreprocessMs.toFixed(0)} ms`:e("common.placeholder")]}),t.jsxs("span",{children:[e("lab.metric.extractMs"),":"," ",b.tExtractMs!=null?`${b.tExtractMs.toFixed(0)} ms`:e("common.placeholder")]}),t.jsxs("span",{children:[e("lab.metric.solveOnlyMs"),":"," ",b.tSolveMs!=null?`${b.tSolveMs.toFixed(0)} ms`:e("common.placeholder")]})]})]}),(b.raDeg!=null||b.decDeg!=null)&&t.jsxs("div",{className:"leading-tight",children:[t.jsx("span",{className:"text-on-surface-variant",children:e("lab.metric.radec")}),t.jsxs("div",{className:"mt-0.5 font-mono text-[9px]",children:["α ",it(b.raDeg)," · δ"," ",it(b.decDeg)]})]}),t.jsxs("div",{className:"flex flex-wrap gap-x-2 gap-y-0.5 text-[9px]",children:[b.matches!=null&&t.jsxs("span",{children:[t.jsx("span",{className:"text-on-surface-variant",children:e("lab.metric.matches")})," ",t.jsx("span",{className:"font-mono",children:b.matches})]}),b.rmseArcsec!=null&&t.jsxs("span",{children:[t.jsx("span",{className:"text-on-surface-variant",children:e("lab.metric.rmse")})," ",t.jsxs("span",{className:"font-mono",children:[b.rmseArcsec.toFixed(2),"″"]})]}),b.prob!=null&&t.jsxs("span",{className:"inline-flex max-w-full flex-col gap-0.5",children:[t.jsxs("span",{children:[t.jsx("span",{className:"text-on-surface-variant",children:e("lab.metric.prob")})," ",t.jsx("span",{className:"font-mono",children:ge(b.prob,te??void 0).line})]}),t.jsx("span",{className:"text-[8px] leading-snug text-on-surface-variant/90",children:e("lab.metric.probHelp")}),ge(b.prob,te??void 0).rawLine&&t.jsxs(t.Fragment,{children:[t.jsxs("span",{className:"text-[8px] text-on-surface-variant",children:[e("lab.metric.probRaw"),":"," ",ge(b.prob,te??void 0).rawLine]}),t.jsx("span",{className:"text-[8px] leading-snug text-on-surface-variant/90",children:e("lab.metric.probRawHelp")})]})]})]}),b.status&&t.jsxs("div",{className:"border-t border-outline-variant/15 pt-1 text-[9px]",children:[t.jsxs("span",{className:"text-on-surface-variant",children:[e("lab.metric.status")," "]}),t.jsx("span",{className:"font-mono text-secondary",children:b.status})]})]}):t.jsx("p",{className:"text-on-surface-variant",children:e("common.placeholder")})})]}),t.jsxs("details",{open:!0,className:"rounded-lg border border-outline-variant/25 bg-surface-container-lowest/90",children:[t.jsxs("summary",{className:"flex cursor-pointer list-none items-center justify-between gap-2 px-3 py-2 text-xs font-semibold text-on-surface [&::-webkit-details-marker]:hidden",children:[e("lab.imageSection"),t.jsx(Fe,{className:"h-4 w-4 shrink-0 text-on-surface-variant"})]}),t.jsxs("div",{className:"space-y-0.5 border-t border-outline-variant/15 px-3 py-2 text-[10px]",children:[t.jsxs("div",{className:"flex justify-between gap-2",children:[t.jsx("span",{className:"text-on-surface-variant",children:e("lab.resolution")}),t.jsx("span",{className:"font-mono tabular-nums",children:At.w>0?`${At.w}×${At.h}`:e("common.placeholder")})]}),t.jsxs("div",{className:"flex justify-between gap-2",children:[t.jsx("span",{className:"text-on-surface-variant",children:e("lab.starsDetected")}),t.jsx("span",{className:"font-mono tabular-nums",children:Na??e("common.placeholder")})]}),t.jsxs("div",{className:"flex justify-between gap-2",children:[t.jsx("span",{className:"text-on-surface-variant",children:e("lab.fwhm")}),t.jsx("span",{children:e("common.placeholder")})]})]})]})]}),c&&t.jsxs(t.Fragment,{children:[t.jsxs("div",{className:"mt-2 flex flex-wrap gap-4 text-xs text-on-surface-variant",children:[t.jsxs("span",{children:[e("lab.file"),":"," ",t.jsx("span",{className:"font-mono text-on-surface",title:c,children:nt(c,22,18)})]}),((Ma=o.find(a=>a.filename===c))==null?void 0:Ma.source)&&t.jsxs("span",{className:"rounded bg-surface-container-high px-2 py-0.5",children:[e("lab.source"),":"," ",(Ca=o.find(a=>a.filename===c))==null?void 0:Ca.source]})]}),t.jsxs("details",{open:!0,className:"mt-2 rounded-lg border border-outline-variant/25 bg-surface-container-lowest/90 text-xs shadow-sm",children:[t.jsxs("summary",{className:"flex cursor-pointer list-none items-center gap-2 px-3 py-2 font-semibold text-on-surface [&::-webkit-details-marker]:hidden",children:[e("lab.meta.title"),gt&&t.jsx(at,{className:"h-3.5 w-3.5 animate-spin"}),t.jsx(Fe,{className:"ml-auto h-4 w-4 shrink-0 text-on-surface-variant"})]}),t.jsx("div",{className:"border-t border-outline-variant/15 p-3 pt-2",children:Ea.length>0?t.jsx("dl",{className:"grid grid-cols-2 gap-x-4 gap-y-2 sm:grid-cols-3",children:Ea.map(a=>t.jsxs("div",{children:[t.jsx("dt",{className:"text-[10px] text-on-surface-variant",children:e(a.key)}),t.jsx("dd",{className:"text-[11px] font-medium text-on-surface",children:a.value})]},`${a.key}-${a.value}`))}):vt&&!gt?t.jsx("p",{className:"text-[10px] leading-relaxed text-on-surface-variant",children:e("lab.meta.partial")}):gt?null:t.jsx("p",{className:"text-[10px] text-on-surface-variant",children:e("lab.meta.noSidecar")})})]})]})]}),(c||n==="lab_video"&&k==="camera")&&(Te.length>0||S||T)&&t.jsxs(t.Fragment,{children:[Te.length>0&&t.jsxs("section",{className:"mt-3 rounded-lg border border-outline-variant/25 bg-surface-container-lowest/90",children:[t.jsxs("div",{className:"flex h-9 shrink-0 items-center border-b border-outline-variant/15 px-3 text-[10px] uppercase text-on-surface-variant",children:[t.jsx("span",{children:e("results.solveHistoryTitle")}),t.jsxs("span",{className:"ml-2 normal-case text-on-surface-variant/70",children:["(",Te.length,"/",Ya,")"]})]}),t.jsx("div",{className:"og-scrollbar max-h-[min(36vh,18rem)] space-y-2 overflow-y-auto p-3",children:Te.map(a=>{var $;const i=a.batch,d=a.single,p=Pe(new Date(a.at).toISOString(),s),N=i&&i.results.length?e("results.historyBatch",{count:i.results.length}):e("results.historySingle");return t.jsxs("details",{className:"rounded border border-outline-variant/20 bg-surface-container/80 text-xs",children:[t.jsx("summary",{className:"cursor-pointer list-none px-3 py-2 font-medium text-on-surface [&::-webkit-details-marker]:hidden",children:t.jsxs("span",{children:[p," · ",N]})}),t.jsxs("div",{className:"border-t border-outline-variant/15 p-3 pt-2",children:[($=i==null?void 0:i.results)!=null&&$.length?t.jsx("div",{className:"og-scrollbar flex gap-2 overflow-x-auto pb-1",children:i.results.map((E,D)=>{const M=E.result,oe=E.success===!0;return t.jsxs("div",{className:"min-w-[12rem] max-w-xs shrink-0 rounded border border-outline-variant/20 bg-surface-container-lowest p-2",children:[t.jsx("div",{className:"border-b border-outline-variant/10 pb-1 text-[10px] font-semibold text-secondary",children:String(E.label??D)}),oe&&M?t.jsx(st,{result:M,t:e,roundTripMs:null}):t.jsx("div",{className:"mt-1 text-[10px] text-error",children:String(E.error??"—")})]},D)})}):null,d&&d.result?t.jsx("div",{className:"mt-2 rounded border border-outline-variant/15 p-2",children:t.jsx(st,{result:d.result,t:e,roundTripMs:null})}):null,t.jsxs("details",{className:"mt-2",children:[t.jsx("summary",{className:"cursor-pointer text-[10px] text-primary hover:underline",children:e("results.viewRaw")}),t.jsx("pre",{className:"og-scrollbar mt-1 max-h-32 overflow-auto rounded bg-surface-container-highest p-2 text-[9px] text-on-surface-variant",children:JSON.stringify({single:d,batch:i},null,2)})]})]})]},a.id)})})]}),(S||T)&&t.jsxs("section",{className:"mt-3 max-h-[min(50vh,28rem)] rounded-lg border border-outline-variant/25 bg-surface-container-lowest/95",children:[t.jsxs("div",{className:"flex h-9 shrink-0 items-center justify-between border-b border-outline-variant/15 px-3 text-[10px] uppercase text-on-surface-variant",children:[t.jsx("span",{children:e("results.title")}),t.jsxs("div",{className:"flex gap-3",children:[(La=T==null?void 0:T.results)!=null&&La.length?t.jsx("button",{type:"button",className:"rounded bg-surface-container px-2 py-0.5 normal-case",onClick:async()=>{if(!(!c||!T)){for(const a of T.results){if(!a.success)continue;const i=a.result;await Ht({input_name:c,preset_label:String(a.label),result_json:a,metrics:Gt(i??null),replay:{layers:G,params:x}})}y(null)}},children:e("results.saveBatchAll")}):null,S&&t.jsx("button",{type:"button",className:"rounded bg-surface-container px-2 py-0.5 normal-case",onClick:async()=>{if(!c&&ms!=="camera")return;const a=S.result;await Ht({input_name:c??e("lab.cameraSnapshotName"),preset_label:"manual",result_json:S,metrics:Gt(a??null),replay:{layers:G,params:x}}),y(null)},children:e("results.saveCurrent")})]})]}),t.jsx("div",{className:"og-scrollbar min-h-0 overflow-y-auto p-3",children:t.jsxs("div",{className:"og-scrollbar flex gap-3 overflow-x-auto text-xs",children:[T==null?void 0:T.results.map((a,i)=>{const d=a.result,p=us[i]??!1;return t.jsxs("div",{className:"min-w-[15rem] max-w-sm shrink-0 rounded-lg border border-outline-variant/25 bg-surface-container p-3 shadow-sm",children:[t.jsx("div",{className:"border-b border-outline-variant/15 pb-2 font-semibold text-secondary",children:String(a.label)}),a.success?t.jsxs(t.Fragment,{children:[t.jsx(st,{result:d,t:e,roundTripMs:null}),t.jsx("button",{type:"button",className:"mt-2 text-[10px] text-primary hover:underline",onClick:()=>yt(N=>({...N,[i]:!p})),children:e(p?"results.hideRaw":"results.viewRaw")}),p&&t.jsx("pre",{className:"og-scrollbar mt-1 max-h-40 overflow-auto rounded bg-surface-container-highest p-2 text-[9px] text-on-surface-variant",children:JSON.stringify(a,null,2)})]}):t.jsx("div",{className:"mt-2 text-[10px] text-error",children:String(a.error)}),a.success===!0&&c?t.jsx("button",{type:"button",className:"mt-3 w-full rounded bg-surface-container-high py-1.5 text-[10px] font-medium",onClick:()=>Ht({input_name:c,preset_label:String(a.label),result_json:a,metrics:Gt(d??null),replay:{layers:G,params:x}}).catch(N=>y(String(N))),children:e("results.saveRow")}):null]},i)}),S&&!T&&t.jsxs("div",{className:"min-w-[16rem] max-w-md shrink-0 rounded-lg border border-outline-variant/25 bg-surface-container p-3 shadow-sm",children:[t.jsx("div",{className:"border-b border-outline-variant/15 pb-2 text-[10px] font-semibold uppercase text-on-surface-variant",children:e("results.title")}),t.jsx(st,{result:S.result,t:e,roundTripMs:jt}),t.jsx("button",{type:"button",className:"mt-2 text-[10px] text-primary hover:underline",onClick:()=>wt(a=>!a),children:e(fa?"results.hideRaw":"results.viewRaw")}),fa&&t.jsx("pre",{className:"og-scrollbar mt-1 max-h-48 overflow-auto rounded bg-surface-container-highest p-2 text-[9px] text-on-surface-variant",children:JSON.stringify(S,null,2)})]})]})})]})]})]}),t.jsxs("aside",{className:"flex w-80 shrink-0 flex-col border-l border-outline-variant/15 bg-surface-container-low text-xs min-h-0",children:[n!=="lab_video"&&t.jsxs("div",{className:"shrink-0 space-y-2 border-b border-outline-variant/20 p-4 pb-3",children:[t.jsx("button",{type:"button",className:"w-full rounded bg-primary py-2.5 font-semibold text-on-primary-container",onClick:vs,disabled:B,children:e("btn.solveOne")}),t.jsx("button",{type:"button",className:"w-full rounded bg-secondary/80 py-2.5 font-semibold text-on-secondary",onClick:gs,disabled:B,children:e("btn.solveBatch")})]}),n==="lab_video"&&t.jsx("div",{className:"shrink-0 space-y-2 border-b border-outline-variant/20 p-4 pb-3",children:k==="file"?t.jsxs(t.Fragment,{children:[t.jsx("button",{type:"button",className:"w-full rounded bg-primary py-2.5 font-semibold text-on-primary-container disabled:opacity-40",onClick:()=>js(),disabled:B||!_a||pe,children:e("lab.solveFileStart")}),t.jsx("button",{type:"button",className:"w-full rounded bg-error/80 py-2.5 font-semibold text-on-error disabled:opacity-40",onClick:()=>Qe(),disabled:!pe,children:e("lab.solveFileStop")}),t.jsx("button",{type:"button",className:"w-full rounded bg-secondary/80 py-2.5 font-semibold text-on-secondary disabled:opacity-40",onClick:()=>void ws(),disabled:B||!_a||pe,children:e("lab.solveCurrentFrame")})]}):t.jsxs(t.Fragment,{children:[t.jsx("button",{type:"button",className:"w-full rounded bg-primary py-2.5 font-semibold text-on-primary-container disabled:opacity-40",onClick:()=>ks(),disabled:B||Ee||q,children:e("lab.solveCameraStart")}),t.jsx("button",{type:"button",className:"w-full rounded bg-error/80 py-2.5 font-semibold text-on-error disabled:opacity-40",onClick:()=>et(),disabled:!Ee,children:e("lab.solveCameraStop")}),t.jsx("button",{type:"button",className:"w-full rounded bg-secondary/80 py-2.5 font-semibold text-on-secondary disabled:opacity-40",onClick:()=>void ys(),disabled:B||q,children:e("lab.solveCameraFrame")}),t.jsxs("label",{className:"flex items-center gap-2 rounded border border-outline-variant/25 px-2 py-1.5 text-[11px]",children:[t.jsx("input",{type:"checkbox",checked:xa,onChange:a=>cs(a.target.checked)}),t.jsx("span",{children:"自动保持(解算成功后冻结) / Auto Hold"})]}),q&&t.jsx("button",{type:"button",className:"w-full rounded bg-tertiary/80 py-2.5 font-semibold text-on-tertiary disabled:opacity-40",onClick:()=>Rs(),children:"继续实时 / Resume Live"}),q&&t.jsxs("div",{className:"text-[10px] text-on-surface-variant",children:["已冻结帧 / Frozen frame ",ds??"-"]})]})}),n==="lab_video"&&Ve&&t.jsxs("div",{className:"shrink-0 border-b border-outline-variant/20 px-4 py-2 text-[10px] text-on-surface-variant",children:[t.jsx("div",{className:"font-medium text-on-surface",children:e("lab.systemLoad")}),t.jsxs("div",{children:["CPU ",Ve.cpu_usage,"% · RAM ",Ve.memory_usage,"% ·"," ",Ve.temperature,"°C"]})]}),t.jsxs("div",{className:"og-scrollbar min-h-0 flex-1 overflow-y-auto p-4",children:[t.jsxs("details",{open:!0,className:"rounded-lg border border-outline-variant/20 bg-surface-container-highest/30",children:[t.jsxs("summary",{className:"flex cursor-pointer list-none items-center justify-between gap-2 px-3 py-2 font-semibold text-on-surface-variant [&::-webkit-details-marker]:hidden",children:[e("params.title"),t.jsx(Fe,{className:"h-4 w-4 shrink-0"})]}),t.jsxs("div",{className:"border-t border-outline-variant/15 p-3 pt-2",children:[t.jsx("p",{className:"mb-3 text-[10px] leading-relaxed text-on-surface-variant/90",children:e("params.blockSolveIntro")}),t.jsxs("div",{className:"space-y-2",children:[t.jsxs("label",{className:"block",children:[t.jsx("span",{className:"text-[10px] font-medium text-on-surface-variant",children:e("params.solveProfile")}),t.jsx("p",{className:"mb-1 mt-0.5 text-[9px] leading-snug text-on-surface-variant/85",children:e("params.solveProfileHelp")}),t.jsxs("select",{className:"w-full rounded bg-surface-container-highest px-2 py-1",value:x.solve_profile??"balanced",onChange:a=>h(i=>({...i,solve_profile:a.target.value})),children:[t.jsx("option",{value:"speed",children:e("params.solveProfileSpeed")}),t.jsx("option",{value:"balanced",children:e("params.solveProfileBalanced")}),t.jsx("option",{value:"robust",children:e("params.solveProfileRobust")})]})]}),t.jsx(W,{label:e("params.fov"),helpKey:"params.fovHelp",value:x.fov_estimate??"",onChange:a=>h(i=>({...i,fov_estimate:a}))}),t.jsx(W,{label:e("params.fovErr"),helpKey:"params.fovErrHelp",value:x.fov_max_error??"",onChange:a=>h(i=>({...i,fov_max_error:a}))}),t.jsx(W,{label:e("params.timeout"),helpKey:"params.timeoutHelp",value:x.solve_timeout_ms??"",onChange:a=>h(i=>({...i,solve_timeout_ms:a}))}),t.jsx(W,{label:e("params.solveIntervalMs"),helpKey:"params.solveIntervalMsHelp",value:_t??"",onChange:a=>ha(a==null?$t:Math.round(a))}),t.jsx("p",{className:"text-[9px] leading-snug text-on-surface-variant/80",children:e("params.solveIntervalBound",{min:Ot,max:Pt,effective:le})}),n==="lab_video"&&t.jsxs("label",{className:"mt-2 block",children:[t.jsx("span",{className:"text-[10px] font-medium text-on-surface-variant",children:e("params.centroidRejectionLevel")}),t.jsx("p",{className:"mb-1 mt-0.5 text-[9px] leading-snug text-on-surface-variant/85",children:e("params.centroidRejectionLevelHelp")}),t.jsxs("div",{className:"flex items-center gap-2",children:[t.jsx("span",{className:"w-5 shrink-0 text-center text-[9px] text-on-surface-variant",children:"1"}),t.jsx("input",{type:"range",min:1,max:5,step:1,className:"h-2 flex-1 accent-primary",value:x.centroid_rejection_level??3,onChange:a=>h(i=>({...i,centroid_rejection_level:Number(a.target.value)}))}),t.jsx("span",{className:"w-5 shrink-0 text-center text-[9px] text-on-surface-variant",children:"5"}),t.jsx("span",{className:"ml-1 min-w-[1.25rem] tabular-nums text-[10px] font-semibold text-on-surface",children:x.centroid_rejection_level??3})]}),t.jsx("p",{className:"mt-0.5 text-[9px] text-on-surface-variant/90",children:e("params.centroidRejectionScale")})]}),t.jsx(W,{label:e("params.ra"),helpKey:"params.raHelp",value:x.hint_ra_deg??"",onChange:a=>h(i=>({...i,hint_ra_deg:a}))}),t.jsx(W,{label:e("params.dec"),helpKey:"params.decHelp",value:x.hint_dec_deg??"",onChange:a=>h(i=>({...i,hint_dec_deg:a}))}),t.jsx(W,{label:e("params.maxSide"),helpKey:"params.maxSideHelp",value:x.max_image_side??"",onChange:a=>h(i=>({...i,max_image_side:a}))}),t.jsxs("label",{className:"mt-1.5 flex cursor-pointer items-center gap-2",children:[t.jsx("input",{type:"checkbox",className:"accent-primary",checked:x.detail_level==="full",onChange:a=>h(i=>({...i,detail_level:a.target.checked?"full":"summary"}))}),t.jsx("span",{className:"text-[10px] text-on-surface-variant",children:e("params.detailLevelFull")})]}),t.jsxs("label",{className:"mt-1.5 flex cursor-pointer items-start gap-2",children:[t.jsx("input",{type:"checkbox",className:"accent-primary mt-0.5",checked:!!x.large_scale_bg_subtract,onChange:a=>h(i=>({...i,large_scale_bg_subtract:a.target.checked}))}),t.jsxs("span",{children:[t.jsx("span",{className:"text-[10px] text-on-surface-variant",children:e("params.largeScaleBg")}),t.jsx("p",{className:"mt-0.5 text-[9px] leading-snug text-on-surface-variant/85",children:e("params.largeScaleBgHelp")})]})]})]})]})]}),t.jsxs("details",{open:!0,className:"mt-3 rounded-lg border border-outline-variant/20 bg-surface-container-highest/30",children:[t.jsxs("summary",{className:"flex cursor-pointer list-none items-center justify-between gap-2 px-3 py-2 font-semibold text-on-surface-variant [&::-webkit-details-marker]:hidden",children:[e("params.centroid"),t.jsx(Fe,{className:"h-4 w-4 shrink-0"})]}),t.jsxs("div",{className:"border-t border-outline-variant/15 p-3 pt-2",children:[t.jsx("p",{className:"mb-3 text-[10px] leading-relaxed text-on-surface-variant/90",children:e("params.blockCentroidIntro")}),t.jsxs("div",{className:"space-y-2",children:[t.jsx(W,{label:e("params.sigma"),helpKey:"params.sigmaHelp",step:.1,value:((Ia=x.centroid)==null?void 0:Ia.sigma)??"",onChange:a=>h(i=>({...i,centroid:{...i.centroid,sigma:a}}))}),t.jsx(W,{label:e("params.maxArea"),helpKey:"params.maxAreaHelp",value:((Da=x.centroid)==null?void 0:Da.max_area)??"",onChange:a=>h(i=>({...i,centroid:{...i.centroid,max_area:a}}))}),t.jsx(W,{label:e("params.minArea"),helpKey:"params.minAreaHelp",value:((Fa=x.centroid)==null?void 0:Fa.min_area)??"",onChange:a=>h(i=>({...i,centroid:{...i.centroid,min_area:a}}))}),t.jsx(W,{label:e("params.filtsize"),helpKey:"params.filtsizeHelp",step:2,value:((Oa=x.centroid)==null?void 0:Oa.filtsize)??"",onChange:a=>h(i=>({...i,centroid:{...i.centroid,filtsize:a}}))})]})]})]}),t.jsxs("div",{className:"mt-4 rounded-md border border-outline-variant/20 bg-surface-container-highest/40 p-3",children:[t.jsx("div",{className:"mb-2 font-semibold text-on-surface-variant",children:e("sidebar.batchPresets")}),t.jsx("p",{className:"mb-2 text-[10px] leading-snug text-on-surface-variant",children:e("sidebar.batchHint")}),t.jsx("div",{className:"og-scrollbar max-h-36 space-y-1.5 overflow-y-auto",children:[...j,...C].map(a=>t.jsxs("label",{className:"flex cursor-pointer items-center gap-2",children:[t.jsx("input",{type:"checkbox",checked:L.has(a.id),onChange:()=>Ms(a.id)}),t.jsx("span",{className:"truncate",children:a.name})]},a.id))})]}),t.jsxs("div",{className:"mt-6 border-t border-outline-variant/20 pt-4",children:[t.jsx("div",{className:"mb-2 font-semibold",children:e("btn.applyPresets")}),t.jsx("div",{className:"og-scrollbar max-h-24 space-y-1 overflow-y-auto",children:[...j,...C].map(a=>t.jsx("button",{type:"button",className:"block w-full truncate text-left text-primary hover:underline",onClick:()=>bs(a.params),children:a.name},a.id))}),t.jsxs("div",{className:"mt-3 flex gap-1",children:[t.jsx("input",{className:"flex-1 rounded bg-surface-container-highest px-2 py-1",placeholder:e("placeholder.newPreset"),value:dt,onChange:a=>sa(a.target.value)}),t.jsx("button",{type:"button",className:"rounded bg-surface-container-high px-2",onClick:async()=>{if(dt.trim()){U(!0);try{await Zs(dt.trim(),x),sa(""),await ee();const a=await Bt("user");v(a.presets)}catch(a){y(String(a))}finally{U(!1)}}},children:e("btn.savePreset")})]})]})]})]})]}),n==="pool"&&t.jsx(En,{uploads:o,onDelete:a=>void ka(a)}),n==="history"&&t.jsx(kn,{historyQ:je,setHistoryQ:rs,historyPage:Ae,setHistoryPage:ea,historyData:Ue,historyExpandId:ta,setHistoryExpandId:aa,historyTotalPages:Ls,onDeleteExperiment:a=>void Cs(a),onSearch:()=>zt(je,1,rt).then(a=>{ea(1),ct(a)})})]}),t.jsx("div",{className:"pointer-events-none fixed bottom-4 right-4 z-[100] flex max-w-sm flex-col gap-2",children:ns.map(a=>t.jsx("div",{className:`pointer-events-auto rounded border px-3 py-2 text-left text-xs shadow-lg ${a.variant==="error"?"border-error/50 bg-error-container/95 text-on-error-container":a.variant==="warn"?"border-amber-500/40 bg-surface-container-highest/95 text-on-surface":"border-outline-variant/40 bg-surface-container-high/95 text-on-surface"}`,children:a.text},a.id))})]})}$s.createRoot(document.getElementById("root")).render(t.jsx(Ts.StrictMode,{children:t.jsx(As,{children:t.jsx(Mn,{})})}));
diff --git a/web/static/analysis-lab/assets/camera-Dzx-jn-A.js b/web/static/analysis-lab/assets/camera-Dzx-jn-A.js
deleted file mode 100644
index 3e4a7c4..0000000
--- a/web/static/analysis-lab/assets/camera-Dzx-jn-A.js
+++ /dev/null
@@ -1,61 +0,0 @@
-import{r as s,j as e,a as ga,R as ya}from"./client-D1ZVDB-N.js";import{c as k,u as va,T as wa,I as ja}from"./index-CutgeBjy.js";import{u as Na,S as Sa,C as Ft,r as ka,b as Ca}from"./http-ChPtkS1w.js";/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const _t=k("Circle",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}]]);/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Ma=k("Download",[["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["polyline",{points:"7 10 12 15 17 10",key:"2ggqvy"}],["line",{x1:"12",x2:"12",y1:"15",y2:"3",key:"1vk2je"}]]);/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Ra=k("FileText",[["path",{d:"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z",key:"1rqfz7"}],["path",{d:"M14 2v4a2 2 0 0 0 2 2h4",key:"tnqrlb"}],["path",{d:"M10 9H8",key:"b1mrlr"}],["path",{d:"M16 13H8",key:"t4e002"}],["path",{d:"M16 17H8",key:"z1uh3a"}]]);/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Ea=k("FolderOpen",[["path",{d:"m6 14 1.5-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.54 6a2 2 0 0 1-1.95 1.5H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H18a2 2 0 0 1 2 2v2",key:"usdka0"}]]);/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Ba=k("Info",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M12 16v-4",key:"1dtifu"}],["path",{d:"M12 8h.01",key:"e9boi3"}]]);/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Pa=k("Moon",[["path",{d:"M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z",key:"a7tn18"}]]);/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const $t=k("Play",[["polygon",{points:"6 3 20 12 6 21 6 3",key:"1oa8hb"}]]);/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Fa=k("Settings2",[["path",{d:"M20 7h-9",key:"3s1dr2"}],["path",{d:"M14 17H5",key:"gfn3mx"}],["circle",{cx:"17",cy:"17",r:"3",key:"18b49y"}],["circle",{cx:"7",cy:"7",r:"3",key:"dfmy0x"}]]);/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const _a=k("SlidersHorizontal",[["line",{x1:"21",x2:"14",y1:"4",y2:"4",key:"obuewd"}],["line",{x1:"10",x2:"3",y1:"4",y2:"4",key:"1q6298"}],["line",{x1:"21",x2:"12",y1:"12",y2:"12",key:"1iu8h1"}],["line",{x1:"8",x2:"3",y1:"12",y2:"12",key:"ntss68"}],["line",{x1:"21",x2:"16",y1:"20",y2:"20",key:"14d8ph"}],["line",{x1:"12",x2:"3",y1:"20",y2:"20",key:"m0wm8r"}],["line",{x1:"14",x2:"14",y1:"2",y2:"6",key:"14e1ph"}],["line",{x1:"8",x2:"8",y1:"10",y2:"14",key:"1i6ji0"}],["line",{x1:"16",x2:"16",y1:"18",y2:"22",key:"1lctlv"}]]);/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const $a=k("Square",[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",key:"afitv7"}]]);/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Ta=k("Sun",[["circle",{cx:"12",cy:"12",r:"4",key:"4exip2"}],["path",{d:"M12 2v2",key:"tus03m"}],["path",{d:"M12 20v2",key:"1lh1kg"}],["path",{d:"m4.93 4.93 1.41 1.41",key:"149t6j"}],["path",{d:"m17.66 17.66 1.41 1.41",key:"ptbguv"}],["path",{d:"M2 12h2",key:"1t8f8n"}],["path",{d:"M20 12h2",key:"1q8mjw"}],["path",{d:"m6.34 17.66-1.41 1.41",key:"1m8zz5"}],["path",{d:"m19.07 4.93-1.41 1.41",key:"1shlcs"}]]);/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Ia=k("X",[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]]),Oa="/api/dev/debug";function z(a){return`${Oa}${a}`}async function c(a,h={}){const f=a.startsWith("/api/debug/")?a.replace("/api/debug/","/api/dev/debug/"):a;return ka(f,h)}const Da=["640x360","1280x720","1600x900","1920x1080"],Aa=[0,90,180,270],Ne=12;function S(a,h,f){return Math.min(f,Math.max(h,a))}function j(a,h){return a==null||Number.isNaN(Number(a))?h:Number(a)}function Tt(a){if(!a)return"0 B";const h=["B","KB","MB","GB"];let f=0,b=a;for(;b>=1024&&f0&&b>=f?"busy":"ok"}catch{return"fail"}}function E({label:a,value:h,min:f,max:b,step:n,onChange:Y,disabled:d=!1,unit:q=""}){return e.jsxs("label",{className:`block ${d?"opacity-50":""}`,children:[e.jsxs("div",{className:"mb-1 flex items-center justify-between",children:[e.jsx("span",{children:a}),e.jsxs("span",{className:"font-mono text-[11px]",children:[h.toFixed(n>=1?0:n>=.1?1:2),q]})]}),e.jsx("input",{type:"range",min:f,max:b,step:n,value:h,disabled:d,onChange:ee=>Y(Number(ee.target.value)),className:"w-full accent-primary"})]})}function Ha(){var Nt,St,kt,Ct,Mt,Rt;const{t:a,locale:h,setLocale:f}=va(),{info:b}=Na(),[n,Y]=s.useState(null),[d,q]=s.useState(!1),[ee,Se]=s.useState(()=>Date.now()),[ke,o]=s.useState(null),[te,D]=s.useState(null),[qe,A]=s.useState(!1),[L,m]=s.useState(null),[B,ae]=s.useState(!1),[Ce,Le]=s.useState(!1),[Ue,We]=s.useState(!1),[Ve,Je]=s.useState("5"),[Ke,Xe]=s.useState("1280x720"),[Ze,Qe]=s.useState("supersample"),[Ot,U]=s.useState(!1),[Ye,Dt]=s.useState(!0),[Me,At]=s.useState(!0),[Re,Ht]=s.useState(!1),[et,Gt]=s.useState(!1),[tt,zt]=s.useState(!1),[Ee,qt]=s.useState({mean:0,std:0,over:0}),[Lt,re]=s.useState(0),[at,Be]=s.useState(null),[rt,nt]=s.useState(0),[st,ot]=s.useState(180),[ne,it]=s.useState(!1),[se,ct]=s.useState(!1),[i,y]=s.useState({exposure:5e3,gain:1,digitalGain:1,autoExposure:!0,contrast:1,brightness:0,saturation:1,sharpness:1,noiseReduction:0,whiteBalanceMode:"auto",whiteBalanceGainR:1,whiteBalanceGainB:1,colorMode:"color"}),[oe,g]=s.useState(!1),[Pe,lt]=s.useState(""),[dt,mt]=s.useState(""),[ut,Ut]=s.useState([]),[Fe,F]=s.useState(!1),[W,Wt]=s.useState([]),[pt,xt]=s.useState(!1),[v,_e]=s.useState(null),[Vt,$e]=s.useState(!1),[ie,Te]=s.useState(null),[Ie,ce]=s.useState(1),C=s.useRef(null),le=s.useRef(null),V=s.useRef(null),de=s.useRef(null),me=s.useRef(null),_=s.useRef(null),ue=s.useRef(null),O=s.useRef(!1),pe=s.useRef(null),$=s.useRef([]),H=s.useRef(0),Oe=s.useRef(null),ht=()=>{$.current=[],H.current=0,pe.current=null,re(0)},w=async()=>{try{const t=await c("/api/debug/camera/status",{cache:"no-store"});Y(t),t.streaming||(q(!1),O.current=!1)}catch(t){o(t instanceof Error?t.message:String(t))}},J=()=>{ue.current!=null&&(window.clearTimeout(ue.current),ue.current=null)},bt=async()=>{if(!B){ae(!0),o(null),D(null),A(!1);try{J(),n!=null&&n.streaming||await c("/api/debug/camera/start",{method:"POST"});const t=Date.now(),r=await It();if(r==="busy"){D(a("cam.err.streamBusy")),A(!0);return}if(r==="fail"){D(a("cam.err.streamProbeFailed")),A(!1);return}q(!0),O.current=!0,m(a("cam.notice.previewStart")),ht(),pe.current=performance.now(),Se(t),await w()}catch(t){o(t instanceof Error?t.message:String(t))}finally{ae(!1)}}},Jt=async()=>{if(!B){ae(!0),o(null);try{J(),q(!1),O.current=!1,D(null),A(!1),ht(),Y(t=>t&&{...t,streaming:!1,recording:!1}),C.current&&(C.current.onload=null,C.current.onerror=null,C.current.src="",C.current.removeAttribute("src")),Se(Date.now()),await new Promise(t=>window.requestAnimationFrame(()=>t())),await c("/api/debug/camera/stop",{method:"POST"}),m(a("cam.notice.previewStop")),await w()}catch(t){o(t instanceof Error?t.message:String(t))}finally{ae(!1)}}},Kt=async()=>{if(!(!d&&!(n!=null&&n.streaming))){We(!0),o(null);try{const t=await c("/api/debug/camera/capture",{method:"POST"});m(a("cam.notice.captureSaved",{name:t.filename||"capture"}))}catch(t){o(t instanceof Error?t.message:String(t))}finally{We(!1)}}},Xt=async()=>{if(!Ce){Le(!0),o(null);try{if(n!=null&&n.recording)await c("/api/debug/camera/record/stop",{method:"POST"}),m(a("cam.notice.recordStop"));else{const t=await c("/api/debug/camera/record/start",{method:"POST"});m(a("cam.notice.recordStart",{name:t.filename||"video.avi"}))}await w()}catch(t){o(t instanceof Error?t.message:String(t))}finally{Le(!1)}}},Zt=async()=>{o(null);try{const t=S(parseInt(Ve,10)||5,1,60);await c(`/api/debug/camera/fps?fps=${t}`,{method:"POST"});const[r,u]=Ke.split("x").map(p=>parseInt(p,10));r&&u&&await c(`/api/debug/camera/size?width=${r}&height=${u}`,{method:"POST"}),await c(`/api/debug/camera/sampling?mode=${encodeURIComponent(Ze)}`,{method:"POST"}),m(a("cam.notice.runtimeApplied")),U(!1),await w()}catch(t){o(t instanceof Error?t.message:String(t))}},Qt=async()=>{o(null);try{await c("/api/debug/camera/settings",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)}),m(a("cam.notice.settingsApplied")),g(!1),await w()}catch(t){o(t instanceof Error?t.message:String(t))}},Yt=async()=>{o(null);try{await c(`/api/debug/camera/auto-exposure?enabled=${i.autoExposure?"true":"false"}`,{method:"POST"}),await c(`/api/debug/camera/white-balance?mode=${encodeURIComponent(i.whiteBalanceMode)}&gain_r=${i.whiteBalanceGainR}&gain_b=${i.whiteBalanceGainB}`,{method:"POST"}),await c(`/api/debug/camera/color-mode?color_mode=${encodeURIComponent(i.colorMode)}`,{method:"POST"}),m(a("cam.notice.modeApplied")),await w()}catch(t){o(t instanceof Error?t.message:String(t))}},ea=t=>{t&&(y({exposure:S(Math.round(j(t.exposure_us,5e3)),100,12e4),gain:S(j(t.analogue_gain,1),1,24),digitalGain:S(j(t.digital_gain,1),1,8),autoExposure:!!(t.auto_exposure??!0),contrast:S(j(t.contrast,1),0,2),brightness:S(j(t.brightness,0),-1,1),saturation:S(j(t.saturation,1),0,2),sharpness:S(j(t.sharpness,1),0,2),noiseReduction:S(Math.round(j(t.noise_reduction,0)),0,4),whiteBalanceMode:String(t.white_balance_mode??"auto"),whiteBalanceGainR:S(j(t.white_balance_gain_r,1),.1,3),whiteBalanceGainB:S(j(t.white_balance_gain_b,1),.1,3),colorMode:String(t.color_mode??"color")}),Je(String(Math.round(j(t.fps,5)))),Xe(`${Math.round(j(t.width,1280))}x${Math.round(j(t.height,720))}`),Qe(String(t.sampling_mode??"supersample")),U(!1),ot(S(Math.round(j(t.rotation,180)),0,270)),it(!!t.flip_horizontal),ct(!!t.flip_vertical),g(!1))},ft=async(t,r)=>{o(null);try{await c("/api/debug/camera/mirror",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({flip_horizontal:t,flip_vertical:r})}),it(t),ct(r),m(a("cam.notice.mirrorApplied")),await w()}catch(u){o(u instanceof Error?u.message:String(u))}},ta=async t=>{o(null);try{await c(`/api/debug/camera/rotation/${t}`,{method:"POST"}),ot(t),m(a("cam.notice.rotationApplied",{value:t})),await w()}catch(r){o(r instanceof Error?r.message:String(r))}},gt=async t=>{o(null);try{await c(`/api/debug/camera/night-mode?enabled=${t?"true":"false"}`,{method:"POST"}),m(a(t?"cam.notice.nightOn":"cam.notice.nightOff")),await w()}catch(r){o(r instanceof Error?r.message:String(r))}},aa=async()=>{o(null);try{await c("/api/debug/camera/reset",{method:"POST"}),m(a("cam.notice.reset")),await w()}catch(t){o(t instanceof Error?t.message:String(t))}},ra=async()=>{o(null);try{await c("/api/debug/camera/backup-settings",{method:"POST"}),m(a("cam.notice.backup"))}catch(t){o(t instanceof Error?t.message:String(t))}},na=async()=>{o(null);try{await c("/api/debug/camera/restore-settings",{method:"POST"}),m(a("cam.notice.restore")),await w()}catch(t){o(t instanceof Error?t.message:String(t))}},xe=async()=>{F(!0);try{const t=await c("/api/debug/camera/presets",{cache:"no-store"});Ut(t.presets??[])}catch(t){o(t instanceof Error?t.message:String(t))}finally{F(!1)}},sa=async()=>{const t=Pe.trim();if(t){F(!0),o(null);try{await c("/api/debug/camera/presets",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:t,description:dt.trim(),exposure_us:i.exposure,analogue_gain:i.gain,digital_gain:i.digitalGain,auto_exposure:i.autoExposure,contrast:i.contrast,brightness:i.brightness,saturation:i.saturation,sharpness:i.sharpness,noise_reduction:i.noiseReduction,white_balance_mode:i.whiteBalanceMode,white_balance_gain_r:i.whiteBalanceGainR,white_balance_gain_b:i.whiteBalanceGainB,rotation:st,flip_horizontal:ne,flip_vertical:se,color_mode:i.colorMode})}),m(a("cam.notice.presetSaved",{name:t})),lt(""),mt(""),await xe()}catch(r){o(r instanceof Error?r.message:String(r))}finally{F(!1)}}},oa=async t=>{F(!0),o(null);try{await c(`/api/debug/camera/presets/${encodeURIComponent(t)}/apply`,{method:"POST"}),m(a("cam.notice.presetApplied",{name:t})),await w(),await xe()}catch(r){o(r instanceof Error?r.message:String(r))}finally{F(!1)}},ia=async t=>{if(window.confirm(a("cam.confirm.deletePreset",{name:t}))){F(!0),o(null);try{await c(`/api/debug/camera/presets/${encodeURIComponent(t)}`,{method:"DELETE"}),m(a("cam.notice.presetDeleted",{name:t})),await xe()}catch(r){o(r instanceof Error?r.message:String(r))}finally{F(!1)}}},De=async()=>{xt(!0);try{const t=await c("/api/debug/files",{cache:"no-store"});Wt(t.files??[]),ce(1)}catch(t){o(t instanceof Error?t.message:String(t))}finally{xt(!1)}},Ae=()=>{Te(null),_e(null),$e(!1)},ca=async t=>{if(ie===t){Ae();return}Te(t),_e(null),$e(!0),o(null);try{const r=await c(`/api/debug/files/${encodeURIComponent(t)}/info`,{cache:"no-store"});_e(r)}catch(r){o(r instanceof Error?r.message:String(r)),Te(null)}finally{$e(!1)}},la=t=>{const r=(p,l)=>{const x=document.createElement("a");x.href=l,x.download=p,document.body.appendChild(x),x.click(),document.body.removeChild(x)};r(t,`${z("/files")}/${encodeURIComponent(t)}`);const u=t.match(/\.(jpe?g|png|bmp|tiff?|webp|mp4|avi|mov|mkv|wmv|flv|webm|m4v)$/i);if(u){const l=`${t.slice(0,-u[0].length)}.txt`;(async()=>{try{if(!(await fetch(`${z("/files")}/${encodeURIComponent(l)}`)).ok)return;r(l,`${z("/files")}/${encodeURIComponent(l)}`),m(a("cam.notice.downloadWithSidecar",{name:t,sidecar:l}))}catch{m(a("cam.notice.download",{name:t}))}})();return}m(a("cam.notice.download",{name:t}))},da=async t=>{if(window.confirm(a("cam.confirm.deleteFile",{name:t}))){o(null);try{await c(`/api/debug/files/${encodeURIComponent(t)}`,{method:"DELETE"}),m(a("cam.notice.fileDeleted",{name:t})),(ie===t||(v==null?void 0:v.filename)===t)&&Ae(),await De()}catch(r){o(r instanceof Error?r.message:String(r))}}},ma=()=>{if(!Ye||!C.current||!le.current)return;const t=C.current;if(!t.naturalWidth||!t.naturalHeight||(V.current||(V.current=document.createElement("canvas"),de.current=V.current.getContext("2d",{willReadFrequently:!0})),me.current||(me.current=le.current.getContext("2d")),!de.current||!me.current))return;const u=Math.min(1,320/t.naturalWidth),p=Math.max(1,Math.round(t.naturalWidth*u)),l=Math.max(1,Math.round(t.naturalHeight*u));V.current.width=p,V.current.height=l,de.current.drawImage(t,0,0,p,l);const x=de.current.getImageData(0,0,p,l).data,P=new Array(256).fill(0),N=new Array(256).fill(0),K=new Array(256).fill(0),Ge=new Array(256).fill(0);let Et=0,Bt=0,Pt=0;const G=p*l;for(let R=0;R=250&&(Pt+=1)}const ze=G?Et/G:0,ba=G?Bt/G-ze*ze:0;qt({mean:ze,std:Math.sqrt(Math.max(0,ba)),over:G?Pt/G*100:0});const ye=le.current,M=me.current,ve=window.devicePixelRatio||1,X=Math.max(1,ye.clientWidth||240),Z=Math.max(1,ye.clientHeight||120);ye.width=Math.floor(X*ve),ye.height=Math.floor(Z*ve),M.setTransform(ve,0,0,ve,0,0),M.clearRect(0,0,X,Z);const fa=Math.max(1,...Me?[Math.max(...P),Math.max(...N),Math.max(...K)]:[0],...Re?[Math.max(...Ge)]:[0]),we=(R,je)=>{M.beginPath(),M.strokeStyle=je,M.lineWidth=1.2;for(let T=0;T<256;T+=1){const Q=T/255*X,I=Z-R[T]/fa*Z;T===0?M.moveTo(Q,I):M.lineTo(Q,I)}M.stroke()};if(Me&&(we(P,"rgba(255,80,80,0.85)"),we(N,"rgba(80,255,80,0.85)"),we(K,"rgba(80,160,255,0.85)")),Re&&we(Ge,"rgba(255,255,255,0.95)"),et){const R=.9803921568627451*X;M.fillStyle="rgba(255,100,100,0.12)",M.fillRect(R,0,X-R,Z)}};s.useEffect(()=>{w(),xe(),De();const t=window.setInterval(()=>{document.hidden||w()},2e3);return()=>window.clearInterval(t)},[n==null?void 0:n.streaming]),s.useEffect(()=>{!(n!=null&&n.info)||oe||ea(n.info)},[n==null?void 0:n.info,oe]),s.useEffect(()=>{if(!(n!=null&&n.recording)){_.current&&(window.clearInterval(_.current),_.current=null),nt(0);return}if(_.current)return;const t=Date.now();return _.current=window.setInterval(()=>{nt(Math.max(0,Math.floor((Date.now()-t)/1e3)))},1e3),()=>{_.current&&(window.clearInterval(_.current),_.current=null)}},[n==null?void 0:n.recording]),s.useEffect(()=>{O.current=d,d||J()},[d]),s.useEffect(()=>{d&&($.current=[],H.current=0)},[ee,d]),s.useEffect(()=>{if(!d){$.current=[],H.current=0,re(0);return}if(!Oe.current){const l=document.createElement("canvas");l.width=32,l.height=32,Oe.current=l}const r=Oe.current.getContext("2d",{willReadFrequently:!0});if(!r)return;let u=0;const p=()=>{const l=C.current;if(l!=null&&l.complete&&l.naturalWidth>0)try{r.drawImage(l,0,0,32,32);const x=r.getImageData(0,0,32,32).data;let P=2166136261;for(let N=0;Nwindow.cancelAnimationFrame(u)},[d]),s.useEffect(()=>{if(!d){re(0);return}const t=window.setInterval(()=>{const u=performance.now()-1e3,p=$.current;for(;p.length&&p[0]window.clearInterval(t)},[d]),s.useEffect(()=>{if(!d){Be(null);return}let t=!1;const r=async()=>{try{const p=await fetch(`${z("/camera/stream/status")}`,{cache:"no-store",credentials:"same-origin"});if(!p.ok||t)return;const l=await p.json(),x=Number(l.target_preview_fps??0);t||Be(Number.isFinite(x)&&x>=0?x:null)}catch{t||Be(null)}};r();const u=window.setInterval(r,2e3);return()=>{t=!0,window.clearInterval(u)}},[d]),s.useEffect(()=>{if(!L)return;const t=window.setTimeout(()=>m(null),3200);return()=>window.clearTimeout(t)},[L]),s.useEffect(()=>()=>J(),[]),s.useEffect(()=>{const t=Math.max(1,Math.ceil(W.length/Ne));Ie>t&&ce(t)},[W.length,Ie]);const ua=d?`${z("/camera/stream")}?t=${ee}`:"",he=i.autoExposure,He=i.whiteBalanceMode==="manual",yt=!!((Nt=n==null?void 0:n.info)!=null&&Nt.night_mode),vt=d,be=!B&&!d&&!(n!=null&&n.recording),wt=!B&&d,pa=!B&&!Ue&&vt,xa=!B&&!Ce&&vt,fe=Math.max(1,Math.ceil(W.length/Ne)),ge=Math.min(Ie,fe),jt=(ge-1)*Ne,ha=W.slice(jt,jt+Ne);return e.jsxs("div",{className:"min-h-screen bg-background text-on-surface",children:[e.jsx("header",{className:"sticky top-0 z-30 border-b border-outline-variant/20 bg-surface-container-low/90 px-4 py-3 backdrop-blur",children:e.jsxs("div",{className:"flex w-full items-center justify-between gap-4",children:[e.jsx("div",{children:e.jsx("h1",{className:"font-headline text-xl font-bold text-primary",children:`OGScope ${a("cam.title")}`})}),e.jsxs("div",{className:"flex items-center gap-2 text-xs",children:[e.jsx("button",{type:"button",className:`inline-flex h-7 items-center rounded px-2 py-1 ${h==="zh"?"bg-primary-container text-on-primary-container":"text-on-surface-variant"}`,onClick:()=>f("zh"),children:a("lang.zh")}),e.jsx("button",{type:"button",className:`inline-flex h-7 items-center rounded px-2 py-1 ${h==="en"?"bg-primary-container text-on-primary-container":"text-on-surface-variant"}`,onClick:()=>f("en"),children:a("lang.en")}),e.jsxs("a",{href:"/debug",className:"inline-flex h-7 items-center gap-1 rounded border border-outline-variant/30 px-2 py-1 hover:bg-surface-container",children:[e.jsx(Fa,{className:"h-3.5 w-3.5"})," ",a("cam.btn.system")]})]})]})}),e.jsxs("main",{className:"mx-auto grid max-w-[1880px] grid-cols-12 gap-4 p-4",children:[e.jsxs("aside",{className:"order-3 col-span-12 space-y-4 xl:order-1 xl:col-span-2",children:[e.jsxs("section",{className:"rounded-xl border border-outline-variant/20 bg-surface-container p-4",children:[e.jsx("div",{className:"mb-2 text-sm font-semibold uppercase tracking-wider",children:a("cam.controls.tools")}),e.jsx("div",{className:"mb-2 flex flex-wrap gap-1 text-xs",children:Aa.map(t=>e.jsxs("button",{type:"button",onClick:()=>void ta(t),className:`rounded border px-2 py-1 ${st===t?"border-primary text-primary":"border-outline-variant/30"}`,children:[t,"°"]},t))}),e.jsx("div",{className:"mb-2 text-[11px] text-on-surface-variant",children:a("cam.mirror.hint")}),e.jsxs("div",{className:"mb-2 flex flex-wrap gap-1 text-xs",children:[e.jsx("button",{type:"button",onClick:()=>void ft(!ne,se),className:`rounded border px-2 py-1 ${ne?"border-primary text-primary":"border-outline-variant/30"}`,children:a("cam.mirror.horizontal")}),e.jsx("button",{type:"button",onClick:()=>void ft(ne,!se),className:`rounded border px-2 py-1 ${se?"border-primary text-primary":"border-outline-variant/30"}`,children:a("cam.mirror.vertical")})]}),e.jsxs("div",{className:"flex flex-wrap gap-2 text-xs",children:[e.jsxs("button",{type:"button",onClick:()=>void ra(),className:"rounded border border-outline-variant/40 px-2 py-1",children:[e.jsx(Sa,{className:"mr-1 inline h-3.5 w-3.5"}),a("cam.controls.backup")]}),e.jsxs("button",{type:"button",onClick:()=>void na(),className:"rounded border border-outline-variant/40 px-2 py-1",children:[e.jsx(Ea,{className:"mr-1 inline h-3.5 w-3.5"}),a("cam.controls.restore")]}),e.jsx("button",{type:"button",onClick:()=>void aa(),className:"rounded border border-outline-variant/40 px-2 py-1",children:a("cam.controls.reset")})]})]}),e.jsxs("section",{className:"rounded-xl border border-outline-variant/20 bg-surface-container p-4 text-xs",children:[e.jsx("h2",{className:"mb-3 text-sm font-semibold uppercase tracking-wider",children:a("cam.quick.title")}),e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsx("span",{className:"text-[11px] text-on-surface-variant",children:a("cam.quick.nightHint")}),e.jsxs("button",{type:"button",onClick:()=>void gt(!0),className:`rounded border px-2 py-1 ${yt?"border-primary/70 text-primary":"border-outline-variant/40"}`,children:[e.jsx(Pa,{className:"mr-1 inline h-3.5 w-3.5"}),a("cam.controls.nightOn")]}),e.jsxs("button",{type:"button",onClick:()=>void gt(!1),className:`rounded border px-2 py-1 ${yt?"border-error/60 text-error":"border-outline-variant/40 text-on-surface-variant"}`,children:[e.jsx(Ta,{className:"mr-1 inline h-3.5 w-3.5"}),a("cam.controls.nightOff")]})]})]}),e.jsxs("section",{className:"rounded-xl border border-outline-variant/20 bg-surface-container p-4 text-xs",children:[e.jsx("h2",{className:"mb-3 text-sm font-semibold uppercase tracking-wider",children:a("cam.presets.title")}),e.jsxs("div",{className:"grid grid-cols-2 gap-2",children:[e.jsx("input",{value:Pe,onChange:t=>lt(t.target.value),placeholder:a("cam.presets.name"),className:"rounded border border-outline-variant/30 bg-surface-container-low px-2 py-1.5"}),e.jsx("input",{value:dt,onChange:t=>mt(t.target.value),placeholder:a("cam.presets.desc"),className:"rounded border border-outline-variant/30 bg-surface-container-low px-2 py-1.5"})]}),e.jsx("div",{className:"mt-2",children:e.jsx("button",{type:"button",disabled:Fe||!Pe.trim(),onClick:()=>void sa(),className:"rounded border border-outline-variant/40 px-2 py-1 disabled:opacity-50",children:a("cam.presets.save")})}),e.jsxs("div",{className:"mt-3 max-h-48 space-y-2 overflow-auto",children:[ut.length===0&&e.jsx("div",{className:"text-on-surface-variant",children:a("cam.presets.empty")}),ut.map(t=>e.jsxs("div",{className:"rounded border border-outline-variant/20 p-2",children:[e.jsx("div",{className:"font-semibold",children:t.name}),e.jsx("div",{className:"text-on-surface-variant",children:t.description||a("cam.presets.noDesc")}),e.jsxs("div",{className:"mt-1 text-on-surface-variant",children:[a("cam.controls.exposure"),": ",t.exposure_us,"us | ",a("cam.controls.gain"),": ",t.analogue_gain]}),e.jsxs("div",{className:"mt-2 flex gap-2",children:[e.jsx("button",{type:"button",disabled:Fe,onClick:()=>void oa(t.name),className:"rounded border border-outline-variant/40 px-2 py-1 disabled:opacity-50",children:a("cam.presets.apply")}),e.jsx("button",{type:"button",disabled:Fe,onClick:()=>void ia(t.name),className:"rounded border border-outline-variant/40 px-2 py-1 disabled:opacity-50",children:a("cam.presets.delete")})]})]},t.name))]})]})]}),e.jsxs("section",{className:"order-1 col-span-12 grid grid-cols-12 items-start gap-4 xl:order-2 xl:col-span-10",children:[e.jsxs("div",{className:"col-span-12 space-y-4 xl:col-span-9",children:[e.jsxs("section",{className:"self-start rounded-xl border border-outline-variant/20 bg-surface-container p-4",children:[e.jsxs("div",{className:"mb-2 grid grid-cols-3 gap-2 text-xs",children:[e.jsxs("div",{className:"rounded border border-outline-variant/20 bg-surface-container-low px-2 py-1",children:["CPU: ",e.jsxs("span",{className:"font-mono",children:[Number((b==null?void 0:b.cpu_usage)??0).toFixed(1),"%"]})]}),e.jsxs("div",{className:"rounded border border-outline-variant/20 bg-surface-container-low px-2 py-1",children:["MEM: ",e.jsxs("span",{className:"font-mono",children:[Number((b==null?void 0:b.memory_usage)??0).toFixed(1),"%"]})]}),e.jsxs("div",{className:"rounded border border-outline-variant/20 bg-surface-container-low px-2 py-1",children:["TEMP: ",e.jsxs("span",{className:"font-mono",children:[Number((b==null?void 0:b.temperature)??0).toFixed(1),"°C"]})]})]}),e.jsxs("div",{className:"mb-2 flex flex-wrap items-start justify-between gap-2",children:[e.jsxs("div",{children:[e.jsx("h2",{className:"text-sm font-semibold uppercase tracking-wider",children:a("cam.preview.title")}),e.jsx("p",{className:"mt-1 max-w-2xl text-[11px] leading-snug text-on-surface-variant",children:a("cam.hint.mjpegSingleStream")})]}),e.jsxs("div",{className:"shrink-0 font-mono text-xs text-on-surface-variant",children:[a("cam.preview.state"),": ",n!=null&&n.streaming?a("cam.state.streaming"):a("cam.state.idle")]})]}),te&&!d&&e.jsxs("div",{className:"mb-2 rounded-lg border border-error/35 bg-error-container/15 px-3 py-2 text-left",role:"alert",children:[e.jsx("p",{className:"text-sm font-medium text-error",children:te}),qe?e.jsx("p",{className:"mt-1 max-w-2xl text-[11px] leading-snug text-on-surface-variant",children:a("cam.err.streamBusyHint")}):e.jsx("p",{className:"mt-1 max-w-2xl text-[11px] leading-snug text-on-surface-variant",children:a("cam.err.streamProbeDetail")})]}),e.jsxs("div",{className:"relative aspect-video overflow-hidden rounded border border-outline-variant/20 bg-black",children:[d&&te&&e.jsxs("div",{className:"pointer-events-none absolute inset-0 z-20 flex flex-col items-center justify-center gap-2 bg-black/80 px-4 text-center",role:"alert",children:[e.jsx("p",{className:"text-sm font-medium text-error",children:te}),qe?e.jsx("p",{className:"max-w-md text-[11px] leading-snug text-on-surface-variant",children:a("cam.err.streamBusyHint")}):e.jsx("p",{className:"max-w-md text-[11px] leading-snug text-on-surface-variant",children:a("cam.err.streamProbeDetail")})]}),d?e.jsx("img",{ref:C,alt:"camera-preview",className:"h-full w-full object-contain",src:ua,onLoad:()=>{D(null),A(!1),ma()},onError:()=>{var r;if(J(),!O.current)return;const t=(r=C.current)==null?void 0:r.src;(async()=>{if(!O.current||!t)return;if(await It()==="busy"){D(a("cam.err.streamBusy")),A(!0);return}ue.current=window.setTimeout(()=>{O.current&&Se(Date.now())},400)})()}}):e.jsxs("div",{className:"flex h-full w-full flex-col items-center justify-center gap-3 text-center text-on-surface-variant",children:[e.jsx(Ft,{className:"h-10 w-10 text-primary/80"}),e.jsx("div",{className:"text-sm",children:a("cam.preview.emptyTitle")}),e.jsx("div",{className:"text-xs",children:a("cam.preview.emptyDesc")}),e.jsxs("button",{type:"button",disabled:!be,onClick:()=>void bt(),className:`rounded px-3 py-1.5 text-xs disabled:opacity-50 ${be?"bg-primary-container text-on-primary-container":"border border-outline-variant/40 text-on-surface-variant"}`,children:[e.jsx($t,{className:"mr-1 inline h-3.5 w-3.5"}),a(B?"cam.btn.starting":"cam.btn.start")]})]}),(n==null?void 0:n.recording)&&e.jsxs("div",{className:"absolute right-3 top-3 flex items-center gap-2 rounded bg-black/60 px-2 py-1 text-xs text-error",children:[e.jsx(_t,{className:"h-3.5 w-3.5 fill-current"})," ",a("cam.state.rec"),e.jsx("span",{className:"font-mono",children:`${Math.floor(rt/60).toString().padStart(2,"0")}:${(rt%60).toString().padStart(2,"0")}`})]}),e.jsx("div",{className:"absolute left-3 top-3",children:e.jsx("button",{type:"button",onClick:()=>zt(t=>!t),className:"rounded border border-outline-variant/40 bg-black/70 px-2 py-1 text-xs text-white",children:a(tt?"cam.hist.expand":"cam.hist.collapse")})}),!tt&&e.jsxs("div",{className:"absolute left-3 top-12 w-[360px] max-w-[calc(100%-1.5rem)] rounded border border-outline-variant/30 bg-black/70 p-2 text-white",children:[e.jsxs("div",{className:"mb-2 flex flex-wrap items-center gap-3 text-[11px]",children:[e.jsxs("label",{className:"inline-flex items-center gap-1",children:[e.jsx("input",{type:"checkbox",checked:Ye,onChange:t=>Dt(t.target.checked)})," ",a("cam.hist.enabled")]}),e.jsxs("label",{className:"inline-flex items-center gap-1",children:[e.jsx("input",{type:"checkbox",checked:Me,onChange:t=>At(t.target.checked)})," RGB"]}),e.jsxs("label",{className:"inline-flex items-center gap-1",children:[e.jsx("input",{type:"checkbox",checked:Re,onChange:t=>Ht(t.target.checked)})," ",a("cam.hist.luminance")]}),e.jsxs("label",{className:"inline-flex items-center gap-1",children:[e.jsx("input",{type:"checkbox",checked:et,onChange:t=>Gt(t.target.checked)})," ",a("cam.hist.over")]})]}),e.jsx("canvas",{ref:le,className:"h-24 w-full rounded border border-white/20 bg-black/60"}),e.jsxs("div",{className:"mt-2 grid grid-cols-3 gap-2 text-[11px]",children:[e.jsxs("div",{children:["mean: ",e.jsx("span",{className:"font-mono",children:Ee.mean.toFixed(1)})]}),e.jsxs("div",{children:["std: ",e.jsx("span",{className:"font-mono",children:Ee.std.toFixed(1)})]}),e.jsxs("div",{children:["over: ",e.jsxs("span",{className:"font-mono",children:[Ee.over.toFixed(2),"%"]})]})]})]})]}),e.jsxs("div",{className:"mt-3 grid grid-cols-1 gap-2 text-xs md:grid-cols-4",children:[e.jsxs("div",{className:"rounded border border-outline-variant/20 bg-surface-container-low px-2 py-1.5 text-left",children:[e.jsxs("span",{className:"text-on-surface-variant",children:[a("cam.stats.frameFps"),": "]}),e.jsx("span",{className:"font-mono text-on-surface",children:Lt.toFixed(2)}),e.jsx("p",{className:"mt-0.5 text-[10px] leading-tight text-on-surface-variant/90",children:a("cam.stats.fpsMeasureNote")})]}),e.jsxs("div",{className:"rounded border border-outline-variant/20 bg-surface-container-low px-2 py-1.5 text-left",children:[e.jsxs("span",{className:"text-on-surface-variant",children:[a("cam.stats.targetFps"),": "]}),e.jsx("span",{className:"font-mono text-on-surface",children:Number(((St=n==null?void 0:n.info)==null?void 0:St.fps)??0).toFixed(2)})]}),e.jsxs("div",{className:"rounded border border-outline-variant/20 bg-surface-container-low px-2 py-1.5 text-left",children:[e.jsxs("span",{className:"text-on-surface-variant",children:[a("cam.stats.streamPacingFps"),": "]}),e.jsx("span",{className:"font-mono text-on-surface",children:at!=null?String(at):"—"}),e.jsx("p",{className:"mt-0.5 text-[10px] leading-tight text-on-surface-variant/90",children:a("cam.stats.streamPacingHint")})]}),e.jsxs("div",{className:"rounded border border-outline-variant/20 bg-surface-container-low px-2 py-1.5 text-left",children:[e.jsxs("span",{className:"text-on-surface-variant",children:[a("cam.stats.uptime"),": "]}),e.jsx("span",{className:"font-mono text-on-surface",children:pe.current!=null?`${Math.max(0,Math.round((performance.now()-pe.current)/1e3))}s`:"0s"})]}),e.jsxs("div",{className:"rounded border border-outline-variant/20 bg-surface-container-low px-2 py-1.5 text-left",children:[e.jsxs("span",{className:"text-on-surface-variant",children:[a("cam.system.sensor"),": "]}),e.jsx("span",{className:"font-mono",children:String(((kt=n==null?void 0:n.info)==null?void 0:kt.sensor)??"—")})]}),e.jsxs("div",{className:"rounded border border-outline-variant/20 bg-surface-container-low px-2 py-1.5 text-left",children:[e.jsxs("span",{className:"text-on-surface-variant",children:[a("cam.controls.resolution"),": "]}),e.jsx("span",{className:"font-mono",children:`${((Ct=n==null?void 0:n.info)==null?void 0:Ct.width)??"—"}x${((Mt=n==null?void 0:n.info)==null?void 0:Mt.height)??"—"}`})]}),e.jsxs("div",{className:"rounded border border-outline-variant/20 bg-surface-container-low px-2 py-1.5 text-left",children:[e.jsxs("span",{className:"text-on-surface-variant",children:[a("cam.preview.mode"),": "]}),e.jsx("span",{className:"font-mono",children:(Rt=n==null?void 0:n.info)!=null&&Rt.auto_exposure?a("cam.controls.auto"):a("cam.controls.manual")})]})]}),e.jsxs("div",{className:"mt-3 flex flex-wrap gap-2",children:[e.jsxs("button",{type:"button",disabled:!be,onClick:()=>void bt(),className:`rounded px-3 py-2 text-sm disabled:opacity-50 ${be?"bg-primary-container text-on-primary-container":"border border-outline-variant/40 text-on-surface-variant"}`,children:[e.jsx($t,{className:"inline h-4 w-4"})," ",a(B?"cam.btn.starting":"cam.btn.start")]}),e.jsxs("button",{type:"button",disabled:!wt,onClick:()=>void Jt(),className:`rounded border px-3 py-2 text-sm disabled:opacity-50 ${wt?"border-error/60 text-error hover:bg-error/10":"border-outline-variant/40 text-on-surface-variant"}`,children:[e.jsx($a,{className:"inline h-4 w-4"})," ",a(B?"cam.btn.stopping":"cam.btn.stop")]}),e.jsxs("button",{type:"button",disabled:!pa,onClick:()=>void Kt(),className:"rounded border border-outline-variant/40 px-3 py-2 text-sm disabled:opacity-50",children:[e.jsx(Ft,{className:"inline h-4 w-4"})," ",a(Ue?"cam.btn.capturing":"cam.btn.capture")]}),e.jsxs("button",{type:"button",disabled:!xa,onClick:()=>void Xt(),className:`rounded border px-3 py-2 text-sm disabled:opacity-50 ${n!=null&&n.recording?"border-error/60 bg-error/10 text-error":"border-outline-variant/40"}`,children:[e.jsx(_t,{className:"inline h-4 w-4"})," ",Ce?a("cam.btn.recordBusy"):n!=null&&n.recording?a("cam.btn.recordStop"):a("cam.btn.recordStart")]})]})]}),e.jsxs("section",{className:"rounded-xl border border-outline-variant/20 bg-surface-container p-4 text-xs",children:[e.jsx("h2",{className:"mb-3 text-sm font-semibold uppercase tracking-wider",children:a("cam.files.title")}),e.jsx("div",{className:"mb-2",children:e.jsx("button",{type:"button",disabled:pt,onClick:()=>void De(),className:"rounded border border-outline-variant/40 px-2 py-1 disabled:opacity-50",children:a("cam.files.refresh")})}),Vt&&e.jsx("div",{className:"mb-2 text-on-surface-variant",children:a("cam.files.loadingInfo")}),v&&e.jsxs("div",{className:"mb-3 rounded border border-outline-variant/20 p-2",children:[e.jsxs("div",{className:"mb-2 flex items-center justify-between gap-2 font-semibold",children:[e.jsxs("div",{className:"flex min-w-0 items-center gap-1",children:[e.jsx(Ra,{className:"h-3.5 w-3.5 shrink-0"}),e.jsx("span",{className:"truncate",children:v.filename})]}),e.jsx("button",{type:"button",onClick:()=>Ae(),className:"shrink-0 rounded border border-outline-variant/40 p-1 text-on-surface-variant hover:bg-surface-container","aria-label":a("cam.files.closeDetail"),children:e.jsx(Ia,{className:"h-3.5 w-3.5"})})]}),e.jsxs("div",{children:[a("cam.files.size"),": ",e.jsx("span",{className:"font-mono",children:Tt(v.size)})]}),e.jsxs("div",{children:[a("cam.files.type"),": ",e.jsx("span",{className:"font-mono",children:v.type})]}),e.jsxs("div",{children:[a("cam.files.modified"),": ",e.jsx("span",{className:"font-mono",children:new Date(v.modified).toLocaleString()})]}),v.exposure_us!=null&&e.jsxs("div",{children:[a("cam.controls.exposure"),": ",e.jsxs("span",{className:"font-mono",children:[v.exposure_us,"us"]})]}),v.analogue_gain!=null&&e.jsxs("div",{children:[a("cam.controls.gain"),": ",e.jsx("span",{className:"font-mono",children:v.analogue_gain})]}),v.resolution&&e.jsxs("div",{children:[a("cam.controls.resolution"),": ",e.jsx("span",{className:"font-mono",children:v.resolution})]})]}),e.jsxs("div",{className:"max-h-96 space-y-2 overflow-auto",children:[W.length===0&&!pt&&e.jsx("div",{className:"text-on-surface-variant",children:a("cam.files.empty")}),ha.map(t=>e.jsxs("div",{className:`rounded border p-2 ${ie===t.name?"border-primary/50 bg-primary/5":"border-outline-variant/20"}`,children:[e.jsxs("div",{className:"flex items-center justify-between gap-2",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsx("div",{className:"truncate font-semibold",children:t.name}),e.jsxs("div",{className:"text-on-surface-variant",children:[Tt(t.size)," | ",new Date(t.modified).toLocaleString()]})]}),e.jsx("div",{className:"shrink-0 text-on-surface-variant",children:t.type})]}),e.jsxs("div",{className:"mt-2 flex gap-2",children:[e.jsxs("button",{type:"button",onClick:()=>la(t.name),className:"rounded border border-outline-variant/40 px-2 py-1",children:[e.jsx(Ma,{className:"mr-1 inline h-3.5 w-3.5"}),a("cam.files.download")]}),e.jsxs("button",{type:"button",onClick:()=>void ca(t.name),className:`rounded border px-2 py-1 ${ie===t.name?"border-primary text-primary":"border-outline-variant/40"}`,children:[e.jsx(Ba,{className:"mr-1 inline h-3.5 w-3.5"}),a("cam.files.info")]}),e.jsxs("button",{type:"button",onClick:()=>void da(t.name),className:"rounded border border-outline-variant/40 px-2 py-1",children:[e.jsx(wa,{className:"mr-1 inline h-3.5 w-3.5"}),a("cam.files.delete")]})]})]},t.name))]}),e.jsxs("div",{className:"mt-3 flex items-center justify-between",children:[e.jsx("button",{type:"button",disabled:ge<=1,onClick:()=>ce(t=>Math.max(1,t-1)),className:"rounded border border-outline-variant/40 px-2 py-1 disabled:opacity-50",children:a("cam.files.prev")}),e.jsx("div",{className:"text-on-surface-variant",children:a("cam.files.page",{current:ge,total:fe})}),e.jsx("button",{type:"button",disabled:ge>=fe,onClick:()=>ce(t=>Math.min(fe,t+1)),className:"rounded border border-outline-variant/40 px-2 py-1 disabled:opacity-50",children:a("cam.files.next")})]})]})]}),e.jsxs("section",{className:"col-span-12 grid grid-cols-12 gap-4 xl:col-span-3 xl:self-start",children:[e.jsxs("section",{className:"col-span-12 rounded-xl border border-outline-variant/20 bg-surface-container p-4",children:[e.jsx("h2",{className:"mb-3 text-sm font-semibold uppercase tracking-wider",children:a("cam.controls.title")}),e.jsxs("div",{className:"space-y-3 text-xs",children:[e.jsxs("div",{className:"grid grid-cols-2 gap-2",children:[e.jsxs("label",{className:"block",children:[a("cam.controls.fps"),e.jsx("input",{value:Ve,onChange:t=>{Je(t.target.value),U(!0)},className:"mt-1 w-full rounded border border-outline-variant/30 bg-surface-container-low px-2 py-1.5"})]}),e.jsxs("label",{className:"block",children:[a("cam.controls.sampling"),e.jsxs("select",{value:Ze,onChange:t=>{Qe(t.target.value),U(!0)},className:"mt-1 w-full rounded border border-outline-variant/30 bg-surface-container-low px-2 py-1.5",children:[e.jsx("option",{value:"supersample",children:"supersample"}),e.jsx("option",{value:"native",children:"native"}),e.jsx("option",{value:"crop",children:"crop"})]})]})]}),e.jsxs("label",{className:"block",children:[a("cam.controls.resolution"),e.jsx("div",{className:"mt-1 grid grid-cols-2 gap-1",children:Da.map(t=>e.jsx("button",{type:"button",onClick:()=>{Xe(t),U(!0)},className:`rounded border px-2 py-1 ${Ke===t?"border-primary text-primary":"border-outline-variant/30"}`,children:t},t))})]}),e.jsx("button",{type:"button",disabled:!Ot,onClick:()=>void Zt(),className:"w-full rounded border border-outline-variant/40 px-2 py-1.5 disabled:opacity-50",children:a("cam.controls.applyRuntime")})]})]}),e.jsxs("section",{className:"col-span-12 rounded-xl border border-outline-variant/20 bg-surface-container p-4",children:[e.jsxs("div",{className:"mb-2 flex items-center gap-1 text-sm font-semibold uppercase tracking-wider",children:[e.jsx(_a,{className:"h-3.5 w-3.5"})," ",a("cam.controls.core")]}),oe&&e.jsx("div",{className:"mb-2 rounded border border-primary/40 bg-primary/10 px-2 py-1 text-[11px] text-primary",children:a("cam.controls.pendingChanges")}),he&&e.jsx("p",{className:"mb-2 text-[11px] text-on-surface-variant",children:a("cam.controls.lockedByAe")}),e.jsxs("div",{className:"grid grid-cols-2 gap-2 text-xs",children:[e.jsx(E,{label:a("cam.controls.exposure"),value:i.exposure,min:100,max:12e4,step:100,unit:"us",disabled:he,onChange:t=>{g(!0),y(r=>({...r,exposure:t}))}}),e.jsx(E,{label:a("cam.controls.gain"),value:i.gain,min:1,max:24,step:.1,disabled:he,onChange:t=>{g(!0),y(r=>({...r,gain:Number(t.toFixed(1))}))}}),e.jsx(E,{label:a("cam.controls.digitalGain"),value:i.digitalGain,min:1,max:8,step:.1,disabled:he,onChange:t=>{g(!0),y(r=>({...r,digitalGain:Number(t.toFixed(1))}))}}),e.jsx(E,{label:a("cam.controls.noiseReduction"),value:i.noiseReduction,min:0,max:4,step:1,onChange:t=>{g(!0),y(r=>({...r,noiseReduction:Math.round(t)}))}}),e.jsx(E,{label:a("cam.controls.contrast"),value:i.contrast,min:0,max:2,step:.1,onChange:t=>{g(!0),y(r=>({...r,contrast:Number(t.toFixed(1))}))}}),e.jsx(E,{label:a("cam.controls.brightness"),value:i.brightness,min:-1,max:1,step:.1,onChange:t=>{g(!0),y(r=>({...r,brightness:Number(t.toFixed(1))}))}}),e.jsx(E,{label:a("cam.controls.saturation"),value:i.saturation,min:0,max:2,step:.1,onChange:t=>{g(!0),y(r=>({...r,saturation:Number(t.toFixed(1))}))}}),e.jsx(E,{label:a("cam.controls.sharpness"),value:i.sharpness,min:0,max:2,step:.1,onChange:t=>{g(!0),y(r=>({...r,sharpness:Number(t.toFixed(1))}))}})]}),e.jsx("div",{className:"mt-2",children:e.jsx("button",{type:"button",disabled:!oe,onClick:()=>void Qt(),className:"w-full rounded border border-outline-variant/40 px-2 py-1.5 disabled:opacity-50",children:a("cam.controls.applySettings")})})]}),e.jsxs("section",{className:"col-span-12 rounded-xl border border-outline-variant/20 bg-surface-container p-4",children:[e.jsx("div",{className:"mb-2 text-sm font-semibold uppercase tracking-wider",children:a("cam.controls.mode")}),e.jsxs("div",{className:"grid grid-cols-2 gap-2 text-xs",children:[e.jsxs("label",{className:"block",children:[a("cam.controls.autoExposure"),e.jsxs("select",{value:i.autoExposure?"auto":"manual",onChange:t=>{y(r=>({...r,autoExposure:t.target.value==="auto"})),g(!0)},className:"mt-1 w-full rounded border border-outline-variant/30 bg-surface-container-low px-2 py-1.5",children:[e.jsx("option",{value:"auto",children:a("cam.controls.auto")}),e.jsx("option",{value:"manual",children:a("cam.controls.manual")})]})]}),e.jsxs("label",{className:"block",children:[a("cam.controls.colorMode"),e.jsxs("select",{value:i.colorMode,onChange:t=>{y(r=>({...r,colorMode:t.target.value})),g(!0)},className:"mt-1 w-full rounded border border-outline-variant/30 bg-surface-container-low px-2 py-1.5",children:[e.jsx("option",{value:"color",children:a("cam.controls.color")}),e.jsx("option",{value:"mono",children:a("cam.controls.mono")})]})]}),e.jsxs("label",{className:"block",children:[a("cam.controls.whiteBalance"),e.jsxs("select",{value:i.whiteBalanceMode,onChange:t=>{y(r=>({...r,whiteBalanceMode:t.target.value})),g(!0)},className:"mt-1 w-full rounded border border-outline-variant/30 bg-surface-container-low px-2 py-1.5",children:[e.jsx("option",{value:"auto",children:a("cam.controls.auto")}),e.jsx("option",{value:"manual",children:a("cam.controls.manual")}),e.jsx("option",{value:"night",children:a("cam.controls.night")})]})]}),e.jsx(E,{label:"R Gain",value:i.whiteBalanceGainR,min:.1,max:3,step:.1,disabled:!He,onChange:t=>{y(r=>({...r,whiteBalanceGainR:Number(t.toFixed(1))})),g(!0)}}),e.jsx(E,{label:"B Gain",value:i.whiteBalanceGainB,min:.1,max:3,step:.1,disabled:!He,onChange:t=>{y(r=>({...r,whiteBalanceGainB:Number(t.toFixed(1))})),g(!0)}})]}),!He&&e.jsx("p",{className:"mt-2 text-[11px] text-on-surface-variant",children:a("cam.controls.lockedByWb")}),e.jsx("div",{className:"mt-2",children:e.jsx("button",{type:"button",onClick:()=>void Yt(),className:"w-full rounded border border-outline-variant/40 px-2 py-1.5",children:a("cam.controls.applyMode")})})]})]})]})]}),(ke||L)&&e.jsxs("div",{className:"fixed bottom-4 right-4 z-40 max-w-md space-y-2 text-xs",children:[ke&&e.jsx("div",{className:"rounded border border-error/40 bg-error-container/20 px-3 py-2 text-on-error-container",children:ke}),L&&e.jsx("div",{className:"rounded border border-primary/30 bg-primary/10 px-3 py-2 text-on-surface",children:L})]})]})}ga.createRoot(document.getElementById("root")).render(e.jsx(ya.StrictMode,{children:e.jsx(ja,{children:e.jsx(Ca,{children:e.jsx(Ha,{})})})}));
diff --git a/web/static/analysis-lab/assets/camera-hzdNY7z2.js b/web/static/analysis-lab/assets/camera-hzdNY7z2.js
new file mode 100644
index 0000000..9d54da9
--- /dev/null
+++ b/web/static/analysis-lab/assets/camera-hzdNY7z2.js
@@ -0,0 +1,61 @@
+import{r as s,j as e,a as Ft,R as $t}from"./client-D1ZVDB-N.js";import{c as _,u as Bt,T as Pt,I as Tt}from"./index-C78KOEFu.js";import{u as It,S as Ot,C as Ua,r as Dt,b as At}from"./http-VZMNcsmS.js";/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const La=_("Circle",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}]]);/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const Ht=_("Download",[["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["polyline",{points:"7 10 12 15 17 10",key:"2ggqvy"}],["line",{x1:"12",x2:"12",y1:"15",y2:"3",key:"1vk2je"}]]);/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const zt=_("FileText",[["path",{d:"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z",key:"1rqfz7"}],["path",{d:"M14 2v4a2 2 0 0 0 2 2h4",key:"tnqrlb"}],["path",{d:"M10 9H8",key:"b1mrlr"}],["path",{d:"M16 13H8",key:"t4e002"}],["path",{d:"M16 17H8",key:"z1uh3a"}]]);/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const Gt=_("FolderOpen",[["path",{d:"m6 14 1.5-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.54 6a2 2 0 0 1-1.95 1.5H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H18a2 2 0 0 1 2 2v2",key:"usdka0"}]]);/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const qt=_("Info",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M12 16v-4",key:"1dtifu"}],["path",{d:"M12 8h.01",key:"e9boi3"}]]);/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const Ut=_("Moon",[["path",{d:"M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z",key:"a7tn18"}]]);/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const Wa=_("Play",[["polygon",{points:"6 3 20 12 6 21 6 3",key:"1oa8hb"}]]);/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const Lt=_("Settings2",[["path",{d:"M20 7h-9",key:"3s1dr2"}],["path",{d:"M14 17H5",key:"gfn3mx"}],["circle",{cx:"17",cy:"17",r:"3",key:"18b49y"}],["circle",{cx:"7",cy:"7",r:"3",key:"dfmy0x"}]]);/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const Wt=_("SlidersHorizontal",[["line",{x1:"21",x2:"14",y1:"4",y2:"4",key:"obuewd"}],["line",{x1:"10",x2:"3",y1:"4",y2:"4",key:"1q6298"}],["line",{x1:"21",x2:"12",y1:"12",y2:"12",key:"1iu8h1"}],["line",{x1:"8",x2:"3",y1:"12",y2:"12",key:"ntss68"}],["line",{x1:"21",x2:"16",y1:"20",y2:"20",key:"14d8ph"}],["line",{x1:"12",x2:"3",y1:"20",y2:"20",key:"m0wm8r"}],["line",{x1:"14",x2:"14",y1:"2",y2:"6",key:"14e1ph"}],["line",{x1:"8",x2:"8",y1:"10",y2:"14",key:"1i6ji0"}],["line",{x1:"16",x2:"16",y1:"18",y2:"22",key:"1lctlv"}]]);/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const Vt=_("Square",[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",key:"afitv7"}]]);/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const Jt=_("Sun",[["circle",{cx:"12",cy:"12",r:"4",key:"4exip2"}],["path",{d:"M12 2v2",key:"tus03m"}],["path",{d:"M12 20v2",key:"1lh1kg"}],["path",{d:"m4.93 4.93 1.41 1.41",key:"149t6j"}],["path",{d:"m17.66 17.66 1.41 1.41",key:"ptbguv"}],["path",{d:"M2 12h2",key:"1t8f8n"}],["path",{d:"M20 12h2",key:"1q8mjw"}],["path",{d:"m6.34 17.66-1.41 1.41",key:"1m8zz5"}],["path",{d:"m19.07 4.93-1.41 1.41",key:"1shlcs"}]]);/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const Kt=_("X",[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]]),Xt="/api/dev/debug";function L(t){return`${Xt}${t}`}async function d(t,b={}){const v=t.startsWith("/api/debug/")?t.replace("/api/debug/","/api/dev/debug/"):t;return Dt(v,b)}const Zt=["640x360","1280x720","1600x900","1920x1020"],Qt=[0,90,180,270],ke=12;function j(t,b,v){return Math.min(v,Math.max(b,t))}function w(t,b){return t==null||Number.isNaN(Number(t))?b:Number(t)}function Va(t){if(!t)return"0 B";const b=["B","KB","MB","GB"];let v=0,g=t;for(;g>=1024&&v0&&g>=v?"busy":"ok"}catch{return"fail"}}function $({label:t,value:b,min:v,max:g,step:n,onChange:_e,disabled:u=!1,unit:W=""}){return e.jsxs("label",{className:`block ${u?"opacity-50":""}`,children:[e.jsxs("div",{className:"mb-1 flex items-center justify-between",children:[e.jsx("span",{children:t}),e.jsxs("span",{className:"font-mono text-[11px]",children:[b.toFixed(n>=1?0:n>=.1?1:2),W]})]}),e.jsx("input",{type:"range",min:v,max:g,step:n,value:b,disabled:u,onChange:te=>_e(Number(te.target.value)),className:"w-full accent-primary"})]})}function Yt(){var Ra,Ea,Ma,Fa,$a,Ba,Pa,Ta,Ia,Oa,Da,Aa,Ha;const{t,locale:b,setLocale:v}=Bt(),{info:g}=It(),[n,_e]=s.useState(null),[u,W]=s.useState(!1),[te,Ce]=s.useState(()=>Date.now()),[Re,i]=s.useState(null),[re,H]=s.useState(null),[We,z]=s.useState(!1),[V,p]=s.useState(null),[M,ne]=s.useState(!1),[Ee,Ve]=s.useState(!1),[Je,Ke]=s.useState(!1),[Xe,Ze]=s.useState("8"),[Qe,Ye]=s.useState("8"),[ea,aa]=s.useState("1280x720"),[ta,ra]=s.useState("supersample"),[J,G]=s.useState(!1),[na,Ka]=s.useState(!0),[Me,Xa]=s.useState(!0),[Fe,Za]=s.useState(!1),[sa,Qa]=s.useState(!1),[oa,Ya]=s.useState(!1),[$e,et]=s.useState({mean:0,std:0,over:0}),[at,se]=s.useState(0),[o,Be]=s.useState(null),[ia,ca]=s.useState(0),[la,da]=s.useState(180),[oe,ua]=s.useState(!1),[ie,ma]=s.useState(!1),[c,y]=s.useState({exposure:5e3,gain:1,digitalGain:1,autoExposure:!0,contrast:1,brightness:0,saturation:1,sharpness:1,noiseReduction:0,noiseReductionMode:"fast",aeFlickerMode:"off",autoExposureMaxUs:2e6,whiteBalanceMode:"auto",whiteBalanceGainR:1,whiteBalanceGainB:1,colorMode:"color"}),[ce,f]=s.useState(!1),[Pe,pa]=s.useState(""),[xa,ha]=s.useState(""),[fa,tt]=s.useState([]),[Te,B]=s.useState(!1),[K,rt]=s.useState([]),[ba,ga]=s.useState(!1),[N,Ie]=s.useState(null),[nt,Oe]=s.useState(!1),[le,De]=s.useState(null),[Ae,de]=s.useState(1),C=s.useRef(null),ue=s.useRef(null),X=s.useRef(null),me=s.useRef(null),pe=s.useRef(null),P=s.useRef(null),xe=s.useRef(null),D=s.useRef(!1),he=s.useRef(null),T=s.useRef([]),q=s.useRef(0),He=s.useRef(null),va=()=>{T.current=[],q.current=0,he.current=null,se(0)},S=async()=>{try{const a=await d("/api/debug/camera/status",{cache:"no-store"});_e(a),a.streaming||(W(!1),D.current=!1)}catch(a){i(a instanceof Error?a.message:String(a))}},Z=()=>{xe.current!=null&&(window.clearTimeout(xe.current),xe.current=null)},ya=async()=>{if(!M){ne(!0),i(null),H(null),z(!1);try{Z(),n!=null&&n.streaming||await d("/api/debug/camera/start",{method:"POST"});const a=Date.now(),r=await Ja();if(r==="busy"){H(t("cam.err.streamBusy")),z(!0);return}if(r==="fail"){H(t("cam.err.streamProbeFailed")),z(!1);return}W(!0),D.current=!0,p(t("cam.notice.previewStart")),va(),he.current=performance.now(),Ce(a),await S()}catch(a){i(a instanceof Error?a.message:String(a))}finally{ne(!1)}}},st=async()=>{if(!M){ne(!0),i(null);try{Z(),W(!1),D.current=!1,H(null),z(!1),va(),C.current&&(C.current.onload=null,C.current.onerror=null,C.current.src="",C.current.removeAttribute("src")),Ce(Date.now()),p(t("cam.notice.previewStop"))}catch(a){i(a instanceof Error?a.message:String(a))}finally{ne(!1)}}},ot=async()=>{if(!(!u&&!(n!=null&&n.streaming))){Ke(!0),i(null);try{const a=await d("/api/debug/camera/capture",{method:"POST"});p(t("cam.notice.captureSaved",{name:a.filename||"capture"}))}catch(a){i(a instanceof Error?a.message:String(a))}finally{Ke(!1)}}},it=async()=>{if(!Ee){Ve(!0),i(null);try{if(n!=null&&n.recording)await d("/api/debug/camera/record/stop",{method:"POST"}),p(t("cam.notice.recordStop"));else{const a=await d("/api/debug/camera/record/start",{method:"POST"});p(t("cam.notice.recordStart",{name:a.filename||"video.avi"}))}await S()}catch(a){i(a instanceof Error?a.message:String(a))}finally{Ve(!1)}}},ct=async()=>{i(null);try{const a=j(parseInt(Xe,10)||5,1,60);await d(`/api/debug/camera/fps?fps=${a}`,{method:"POST"});const r=j(parseInt(Qe,10)||8,1,30);await d(`/api/debug/camera/preview-fps?fps=${r}`,{method:"POST"});const[m,x]=ea.split("x").map(l=>parseInt(l,10));m&&x&&await d(`/api/debug/camera/size?width=${m}&height=${x}`,{method:"POST"}),await d(`/api/debug/camera/sampling?mode=${encodeURIComponent(ta)}`,{method:"POST"}),p(t("cam.notice.runtimeApplied")),G(!1),await S()}catch(a){i(a instanceof Error?a.message:String(a))}},lt=async()=>{i(null);try{await d("/api/debug/camera/settings",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(c)}),p(t("cam.notice.settingsApplied")),f(!1),await S()}catch(a){i(a instanceof Error?a.message:String(a))}},dt=async()=>{i(null);try{await d("/api/debug/camera/settings",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(c)}),p(t("cam.notice.modeApplied")),f(!1),await S()}catch(a){i(a instanceof Error?a.message:String(a))}},ut=(a,r)=>{if(!a)return;const m=(r==null?void 0:r.syncRuntime)??!0;y({exposure:j(Math.round(w(a.exposure_us,5e3)),100,12e4),gain:j(w(a.analogue_gain,1),1,24),digitalGain:j(w(a.digital_gain,1),1,8),autoExposure:!!(a.auto_exposure??!0),contrast:j(w(a.contrast,1),0,2),brightness:j(w(a.brightness,0),-1,1),saturation:j(w(a.saturation,1),0,2),sharpness:j(w(a.sharpness,1),0,2),noiseReduction:j(Math.round(w(a.noise_reduction,0)),0,4),noiseReductionMode:String(a.noise_reduction_mode??"fast"),aeFlickerMode:String(a.ae_flicker_mode??"off"),autoExposureMaxUs:j(Math.round(w(a.auto_exposure_max_us,2e6)),1e4,1e7),whiteBalanceMode:String(a.white_balance_mode??"auto"),whiteBalanceGainR:j(w(a.white_balance_gain_r,1),.1,3),whiteBalanceGainB:j(w(a.white_balance_gain_b,1),.1,3),colorMode:String(a.color_mode??"color")}),m&&(Ze(String(Math.round(w(a.fps,8)))),aa(`${Math.round(w(a.width,1280))}x${Math.round(w(a.height,720))}`),ra(String(a.sampling_mode??"supersample")),G(!1)),da(j(Math.round(w(a.rotation,180)),0,270)),ua(!!a.flip_horizontal),ma(!!a.flip_vertical),f(!1)},ja=async(a,r)=>{i(null);try{await d("/api/debug/camera/mirror",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({flip_horizontal:a,flip_vertical:r})}),ua(a),ma(r),p(t("cam.notice.mirrorApplied")),await S()}catch(m){i(m instanceof Error?m.message:String(m))}},mt=async a=>{i(null);try{await d(`/api/debug/camera/rotation/${a}`,{method:"POST"}),da(a),p(t("cam.notice.rotationApplied",{value:a})),await S()}catch(r){i(r instanceof Error?r.message:String(r))}},wa=async a=>{i(null);try{await d(`/api/debug/camera/night-mode?enabled=${a?"true":"false"}`,{method:"POST"}),p(t(a?"cam.notice.nightOn":"cam.notice.nightOff")),await S()}catch(r){i(r instanceof Error?r.message:String(r))}},pt=async()=>{i(null);try{await d("/api/debug/camera/reset",{method:"POST"}),p(t("cam.notice.reset")),await S()}catch(a){i(a instanceof Error?a.message:String(a))}},xt=async()=>{i(null);try{await d("/api/debug/camera/backup-settings",{method:"POST"}),p(t("cam.notice.backup"))}catch(a){i(a instanceof Error?a.message:String(a))}},ht=async()=>{i(null);try{await d("/api/debug/camera/restore-settings",{method:"POST"}),p(t("cam.notice.restore")),await S()}catch(a){i(a instanceof Error?a.message:String(a))}},fe=async()=>{B(!0);try{const a=await d("/api/debug/camera/presets",{cache:"no-store"});tt(a.presets??[])}catch(a){i(a instanceof Error?a.message:String(a))}finally{B(!1)}},ft=async()=>{const a=Pe.trim();if(a){B(!0),i(null);try{await d("/api/debug/camera/presets",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:a,description:xa.trim(),exposure_us:c.exposure,analogue_gain:c.gain,digital_gain:c.digitalGain,auto_exposure:c.autoExposure,contrast:c.contrast,brightness:c.brightness,saturation:c.saturation,sharpness:c.sharpness,noise_reduction:c.noiseReduction,white_balance_mode:c.whiteBalanceMode,white_balance_gain_r:c.whiteBalanceGainR,white_balance_gain_b:c.whiteBalanceGainB,rotation:la,flip_horizontal:oe,flip_vertical:ie,color_mode:c.colorMode})}),p(t("cam.notice.presetSaved",{name:a})),pa(""),ha(""),await fe()}catch(r){i(r instanceof Error?r.message:String(r))}finally{B(!1)}}},bt=async a=>{B(!0),i(null);try{await d(`/api/debug/camera/presets/${encodeURIComponent(a)}/apply`,{method:"POST"}),p(t("cam.notice.presetApplied",{name:a})),await S(),await fe()}catch(r){i(r instanceof Error?r.message:String(r))}finally{B(!1)}},gt=async a=>{if(window.confirm(t("cam.confirm.deletePreset",{name:a}))){B(!0),i(null);try{await d(`/api/debug/camera/presets/${encodeURIComponent(a)}`,{method:"DELETE"}),p(t("cam.notice.presetDeleted",{name:a})),await fe()}catch(r){i(r instanceof Error?r.message:String(r))}finally{B(!1)}}},ze=async()=>{ga(!0);try{const a=await d("/api/debug/files",{cache:"no-store"});rt(a.files??[]),de(1)}catch(a){i(a instanceof Error?a.message:String(a))}finally{ga(!1)}},Ge=()=>{De(null),Ie(null),Oe(!1)},vt=async a=>{if(le===a){Ge();return}De(a),Ie(null),Oe(!0),i(null);try{const r=await d(`/api/debug/files/${encodeURIComponent(a)}/info`,{cache:"no-store"});Ie(r)}catch(r){i(r instanceof Error?r.message:String(r)),De(null)}finally{Oe(!1)}},yt=a=>{const r=(x,l)=>{const h=document.createElement("a");h.href=l,h.download=x,document.body.appendChild(h),h.click(),document.body.removeChild(h)};r(a,`${L("/files")}/${encodeURIComponent(a)}`);const m=a.match(/\.(jpe?g|png|bmp|tiff?|webp|mp4|avi|mov|mkv|wmv|flv|webm|m4v)$/i);if(m){const l=`${a.slice(0,-m[0].length)}.txt`;(async()=>{try{if(!(await fetch(`${L("/files")}/${encodeURIComponent(l)}`)).ok)return;r(l,`${L("/files")}/${encodeURIComponent(l)}`),p(t("cam.notice.downloadWithSidecar",{name:a,sidecar:l}))}catch{p(t("cam.notice.download",{name:a}))}})();return}p(t("cam.notice.download",{name:a}))},jt=async a=>{if(window.confirm(t("cam.confirm.deleteFile",{name:a}))){i(null);try{await d(`/api/debug/files/${encodeURIComponent(a)}`,{method:"DELETE"}),p(t("cam.notice.fileDeleted",{name:a})),(le===a||(N==null?void 0:N.filename)===a)&&Ge(),await ze()}catch(r){i(r instanceof Error?r.message:String(r))}}},wt=()=>{if(!na||!C.current||!ue.current)return;const a=C.current;if(!a.naturalWidth||!a.naturalHeight||(X.current||(X.current=document.createElement("canvas"),me.current=X.current.getContext("2d",{willReadFrequently:!0})),pe.current||(pe.current=ue.current.getContext("2d")),!me.current||!pe.current))return;const m=Math.min(1,320/a.naturalWidth),x=Math.max(1,Math.round(a.naturalWidth*m)),l=Math.max(1,Math.round(a.naturalHeight*m));X.current.width=x,X.current.height=l,me.current.drawImage(a,0,0,x,l);const h=me.current.getImageData(0,0,x,l).data,F=new Array(256).fill(0),k=new Array(256).fill(0),Q=new Array(256).fill(0),Ue=new Array(256).fill(0);let za=0,Ga=0,qa=0;const U=x*l;for(let E=0;E=250&&(qa+=1)}const Le=U?za/U:0,Et=U?Ga/U-Le*Le:0;et({mean:Le,std:Math.sqrt(Math.max(0,Et)),over:U?qa/U*100:0});const je=ue.current,R=pe.current,we=window.devicePixelRatio||1,Y=Math.max(1,je.clientWidth||240),ee=Math.max(1,je.clientHeight||120);je.width=Math.floor(Y*we),je.height=Math.floor(ee*we),R.setTransform(we,0,0,we,0,0),R.clearRect(0,0,Y,ee);const Mt=Math.max(1,...Me?[Math.max(...F),Math.max(...k),Math.max(...Q)]:[0],...Fe?[Math.max(...Ue)]:[0]),Ne=(E,Se)=>{R.beginPath(),R.strokeStyle=Se,R.lineWidth=1.2;for(let I=0;I<256;I+=1){const ae=I/255*Y,O=ee-E[I]/Mt*ee;I===0?R.moveTo(ae,O):R.lineTo(ae,O)}R.stroke()};if(Me&&(Ne(F,"rgba(255,80,80,0.85)"),Ne(k,"rgba(80,255,80,0.85)"),Ne(Q,"rgba(80,160,255,0.85)")),Fe&&Ne(Ue,"rgba(255,255,255,0.95)"),sa){const E=.9803921568627451*Y;R.fillStyle="rgba(255,100,100,0.12)",R.fillRect(E,0,Y-E,ee)}};s.useEffect(()=>{S(),fe(),ze();const a=window.setInterval(()=>{document.hidden||S()},2e3);return()=>window.clearInterval(a)},[n==null?void 0:n.streaming]),s.useEffect(()=>{!(n!=null&&n.info)||ce||ut(n.info,{syncRuntime:!J})},[n==null?void 0:n.info,ce,J]),s.useEffect(()=>{if(!(n!=null&&n.recording)){P.current&&(window.clearInterval(P.current),P.current=null),ca(0);return}if(P.current)return;const a=Date.now();return P.current=window.setInterval(()=>{ca(Math.max(0,Math.floor((Date.now()-a)/1e3)))},1e3),()=>{P.current&&(window.clearInterval(P.current),P.current=null)}},[n==null?void 0:n.recording]),s.useEffect(()=>{D.current=u,u||Z()},[u]),s.useEffect(()=>{u&&(T.current=[],q.current=0)},[te,u]),s.useEffect(()=>{if(!u){T.current=[],q.current=0,se(0);return}if(!He.current){const l=document.createElement("canvas");l.width=32,l.height=32,He.current=l}const r=He.current.getContext("2d",{willReadFrequently:!0});if(!r)return;let m=0;const x=()=>{const l=C.current;if(l!=null&&l.complete&&l.naturalWidth>0)try{r.drawImage(l,0,0,32,32);const h=r.getImageData(0,0,32,32).data;let F=2166136261;for(let k=0;kwindow.cancelAnimationFrame(m)},[u]),s.useEffect(()=>{if(!u){se(0);return}const a=window.setInterval(()=>{const m=performance.now()-1e3,x=T.current;for(;x.length&&x[0]window.clearInterval(a)},[u]),s.useEffect(()=>{if(!u){Be(null);return}let a=!1;const r=async()=>{try{const x=await fetch(`${L("/camera/stream/status")}`,{cache:"no-store",credentials:"same-origin"});if(!x.ok||a)return;const l=await x.json();if(!a){Be(l);const h=Number(l.preview_target_fps??l.target_preview_fps??0);Number.isFinite(h)&&h>0&&!J&&Ye(String(Math.round(h)))}}catch{a||Be(null)}};r();const m=window.setInterval(r,2e3);return()=>{a=!0,window.clearInterval(m)}},[u,J]),s.useEffect(()=>{if(!V)return;const a=window.setTimeout(()=>p(null),3200);return()=>window.clearTimeout(a)},[V]),s.useEffect(()=>()=>Z(),[]),s.useEffect(()=>{const a=Math.max(1,Math.ceil(K.length/ke));Ae>a&&de(a)},[K.length,Ae]);const Nt=u?`${L("/camera/stream")}?t=${te}`:"",be=c.autoExposure,qe=c.whiteBalanceMode==="manual",A=((Ra=n==null?void 0:n.info)==null?void 0:Ra.capabilities)??{},St=Array.isArray(A.awb_modes)&&A.awb_modes.length>0?A.awb_modes:["auto","daylight","cloudy","tungsten","fluorescent","indoor","manual","night"],kt=Array.isArray(A.noise_reduction_modes)&&A.noise_reduction_modes.length>0?A.noise_reduction_modes:["off","fast","high_quality"],Na=A.manual_digital_gain!==!1,Sa=!!((Ea=n==null?void 0:n.info)!=null&&Ea.night_mode),ka=u,ge=!M&&!u&&!(n!=null&&n.recording),_a=!M&&u,_t=!M&&!Je&&ka,Ct=!M&&!Ee&&ka,ve=Math.max(1,Math.ceil(K.length/ke)),ye=Math.min(Ae,ve),Ca=(ye-1)*ke,Rt=K.slice(Ca,Ca+ke);return e.jsxs("div",{className:"min-h-screen bg-background text-on-surface",children:[e.jsx("header",{className:"sticky top-0 z-30 border-b border-outline-variant/20 bg-surface-container-low/90 px-4 py-3 backdrop-blur",children:e.jsxs("div",{className:"flex w-full items-center justify-between gap-4",children:[e.jsx("div",{children:e.jsx("h1",{className:"font-headline text-xl font-bold text-primary",children:`OGScope ${t("cam.title")}`})}),e.jsxs("div",{className:"flex items-center gap-2 text-xs",children:[e.jsx("button",{type:"button",className:`inline-flex h-7 items-center rounded px-2 py-1 ${b==="zh"?"bg-primary-container text-on-primary-container":"text-on-surface-variant"}`,onClick:()=>v("zh"),children:t("lang.zh")}),e.jsx("button",{type:"button",className:`inline-flex h-7 items-center rounded px-2 py-1 ${b==="en"?"bg-primary-container text-on-primary-container":"text-on-surface-variant"}`,onClick:()=>v("en"),children:t("lang.en")}),e.jsxs("a",{href:"/debug",className:"inline-flex h-7 items-center gap-1 rounded border border-outline-variant/30 px-2 py-1 hover:bg-surface-container",children:[e.jsx(Lt,{className:"h-3.5 w-3.5"})," ",t("cam.btn.system")]})]})]})}),e.jsxs("main",{className:"mx-auto grid max-w-[1880px] grid-cols-12 gap-4 p-4",children:[e.jsxs("aside",{className:"order-3 col-span-12 space-y-4 xl:order-1 xl:col-span-2",children:[e.jsxs("section",{className:"rounded-xl border border-outline-variant/20 bg-surface-container p-4",children:[e.jsx("div",{className:"mb-2 text-sm font-semibold uppercase tracking-wider",children:t("cam.controls.tools")}),e.jsx("div",{className:"mb-2 flex flex-wrap gap-1 text-xs",children:Qt.map(a=>e.jsxs("button",{type:"button",onClick:()=>void mt(a),className:`rounded border px-2 py-1 ${la===a?"border-primary text-primary":"border-outline-variant/30"}`,children:[a,"°"]},a))}),e.jsx("div",{className:"mb-2 text-[11px] text-on-surface-variant",children:t("cam.mirror.hint")}),e.jsxs("div",{className:"mb-2 flex flex-wrap gap-1 text-xs",children:[e.jsx("button",{type:"button",onClick:()=>void ja(!oe,ie),className:`rounded border px-2 py-1 ${oe?"border-primary text-primary":"border-outline-variant/30"}`,children:t("cam.mirror.horizontal")}),e.jsx("button",{type:"button",onClick:()=>void ja(oe,!ie),className:`rounded border px-2 py-1 ${ie?"border-primary text-primary":"border-outline-variant/30"}`,children:t("cam.mirror.vertical")})]}),e.jsxs("div",{className:"flex flex-wrap gap-2 text-xs",children:[e.jsxs("button",{type:"button",onClick:()=>void xt(),className:"rounded border border-outline-variant/40 px-2 py-1",children:[e.jsx(Ot,{className:"mr-1 inline h-3.5 w-3.5"}),t("cam.controls.backup")]}),e.jsxs("button",{type:"button",onClick:()=>void ht(),className:"rounded border border-outline-variant/40 px-2 py-1",children:[e.jsx(Gt,{className:"mr-1 inline h-3.5 w-3.5"}),t("cam.controls.restore")]}),e.jsx("button",{type:"button",onClick:()=>void pt(),className:"rounded border border-outline-variant/40 px-2 py-1",children:t("cam.controls.reset")})]})]}),e.jsxs("section",{className:"rounded-xl border border-outline-variant/20 bg-surface-container p-4 text-xs",children:[e.jsx("h2",{className:"mb-3 text-sm font-semibold uppercase tracking-wider",children:t("cam.quick.title")}),e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsx("span",{className:"text-[11px] text-on-surface-variant",children:t("cam.quick.nightHint")}),e.jsxs("button",{type:"button",onClick:()=>void wa(!0),className:`rounded border px-2 py-1 ${Sa?"border-primary/70 text-primary":"border-outline-variant/40"}`,children:[e.jsx(Ut,{className:"mr-1 inline h-3.5 w-3.5"}),t("cam.controls.nightOn")]}),e.jsxs("button",{type:"button",onClick:()=>void wa(!1),className:`rounded border px-2 py-1 ${Sa?"border-error/60 text-error":"border-outline-variant/40 text-on-surface-variant"}`,children:[e.jsx(Jt,{className:"mr-1 inline h-3.5 w-3.5"}),t("cam.controls.nightOff")]})]})]}),e.jsxs("section",{className:"rounded-xl border border-outline-variant/20 bg-surface-container p-4 text-xs",children:[e.jsx("h2",{className:"mb-3 text-sm font-semibold uppercase tracking-wider",children:t("cam.presets.title")}),e.jsxs("div",{className:"grid grid-cols-2 gap-2",children:[e.jsx("input",{value:Pe,onChange:a=>pa(a.target.value),placeholder:t("cam.presets.name"),className:"rounded border border-outline-variant/30 bg-surface-container-low px-2 py-1.5"}),e.jsx("input",{value:xa,onChange:a=>ha(a.target.value),placeholder:t("cam.presets.desc"),className:"rounded border border-outline-variant/30 bg-surface-container-low px-2 py-1.5"})]}),e.jsx("div",{className:"mt-2",children:e.jsx("button",{type:"button",disabled:Te||!Pe.trim(),onClick:()=>void ft(),className:"rounded border border-outline-variant/40 px-2 py-1 disabled:opacity-50",children:t("cam.presets.save")})}),e.jsxs("div",{className:"mt-3 max-h-48 space-y-2 overflow-auto",children:[fa.length===0&&e.jsx("div",{className:"text-on-surface-variant",children:t("cam.presets.empty")}),fa.map(a=>e.jsxs("div",{className:"rounded border border-outline-variant/20 p-2",children:[e.jsx("div",{className:"font-semibold",children:a.name}),e.jsx("div",{className:"text-on-surface-variant",children:a.description||t("cam.presets.noDesc")}),e.jsxs("div",{className:"mt-1 text-on-surface-variant",children:[t("cam.controls.exposure"),": ",a.exposure_us,"us | ",t("cam.controls.gain"),": ",a.analogue_gain]}),e.jsxs("div",{className:"mt-2 flex gap-2",children:[e.jsx("button",{type:"button",disabled:Te,onClick:()=>void bt(a.name),className:"rounded border border-outline-variant/40 px-2 py-1 disabled:opacity-50",children:t("cam.presets.apply")}),e.jsx("button",{type:"button",disabled:Te,onClick:()=>void gt(a.name),className:"rounded border border-outline-variant/40 px-2 py-1 disabled:opacity-50",children:t("cam.presets.delete")})]})]},a.name))]})]})]}),e.jsxs("section",{className:"order-1 col-span-12 grid grid-cols-12 items-start gap-4 xl:order-2 xl:col-span-10",children:[e.jsxs("div",{className:"col-span-12 space-y-4 xl:col-span-9",children:[e.jsxs("section",{className:"self-start rounded-xl border border-outline-variant/20 bg-surface-container p-4",children:[e.jsxs("div",{className:"mb-2 grid grid-cols-3 gap-2 text-xs",children:[e.jsxs("div",{className:"rounded border border-outline-variant/20 bg-surface-container-low px-2 py-1",children:["CPU: ",e.jsxs("span",{className:"font-mono",children:[Number((g==null?void 0:g.cpu_usage)??0).toFixed(1),"%"]})]}),e.jsxs("div",{className:"rounded border border-outline-variant/20 bg-surface-container-low px-2 py-1",children:["MEM: ",e.jsxs("span",{className:"font-mono",children:[Number((g==null?void 0:g.memory_usage)??0).toFixed(1),"%"]})]}),e.jsxs("div",{className:"rounded border border-outline-variant/20 bg-surface-container-low px-2 py-1",children:["TEMP: ",e.jsxs("span",{className:"font-mono",children:[Number((g==null?void 0:g.temperature)??0).toFixed(1),"°C"]})]})]}),e.jsxs("div",{className:"mb-2 flex flex-wrap items-start justify-between gap-2",children:[e.jsxs("div",{children:[e.jsx("h2",{className:"text-sm font-semibold uppercase tracking-wider",children:t("cam.preview.title")}),e.jsx("p",{className:"mt-1 max-w-2xl text-[11px] leading-snug text-on-surface-variant",children:t("cam.hint.mjpegSingleStream")})]}),e.jsxs("div",{className:"shrink-0 font-mono text-xs text-on-surface-variant",children:[t("cam.preview.state"),": ",t(u?"cam.state.streaming":"cam.state.idle")]})]}),re&&!u&&e.jsxs("div",{className:"mb-2 rounded-lg border border-error/35 bg-error-container/15 px-3 py-2 text-left",role:"alert",children:[e.jsx("p",{className:"text-sm font-medium text-error",children:re}),We?e.jsx("p",{className:"mt-1 max-w-2xl text-[11px] leading-snug text-on-surface-variant",children:t("cam.err.streamBusyHint")}):e.jsx("p",{className:"mt-1 max-w-2xl text-[11px] leading-snug text-on-surface-variant",children:t("cam.err.streamProbeDetail")})]}),e.jsxs("div",{className:"relative aspect-video overflow-hidden rounded border border-outline-variant/20 bg-black",children:[u&&re&&e.jsxs("div",{className:"pointer-events-none absolute inset-0 z-20 flex flex-col items-center justify-center gap-2 bg-black/80 px-4 text-center",role:"alert",children:[e.jsx("p",{className:"text-sm font-medium text-error",children:re}),We?e.jsx("p",{className:"max-w-md text-[11px] leading-snug text-on-surface-variant",children:t("cam.err.streamBusyHint")}):e.jsx("p",{className:"max-w-md text-[11px] leading-snug text-on-surface-variant",children:t("cam.err.streamProbeDetail")})]}),u?e.jsx("img",{ref:C,alt:"camera-preview",className:"h-full w-full object-contain",src:Nt,onLoad:()=>{H(null),z(!1),wt()},onError:()=>{var r;if(Z(),!D.current)return;const a=(r=C.current)==null?void 0:r.src;(async()=>{if(!D.current||!a)return;if(await Ja()==="busy"){H(t("cam.err.streamBusy")),z(!0);return}xe.current=window.setTimeout(()=>{D.current&&Ce(Date.now())},400)})()}}):e.jsxs("div",{className:"flex h-full w-full flex-col items-center justify-center gap-3 text-center text-on-surface-variant",children:[e.jsx(Ua,{className:"h-10 w-10 text-primary/80"}),e.jsx("div",{className:"text-sm",children:t("cam.preview.emptyTitle")}),e.jsx("div",{className:"text-xs",children:t("cam.preview.emptyDesc")}),e.jsxs("button",{type:"button",disabled:!ge,onClick:()=>void ya(),className:`rounded px-3 py-1.5 text-xs disabled:opacity-50 ${ge?"bg-primary-container text-on-primary-container":"border border-outline-variant/40 text-on-surface-variant"}`,children:[e.jsx(Wa,{className:"mr-1 inline h-3.5 w-3.5"}),t(M?"cam.btn.starting":"cam.btn.start")]})]}),(n==null?void 0:n.recording)&&e.jsxs("div",{className:"absolute right-3 top-3 flex items-center gap-2 rounded bg-black/60 px-2 py-1 text-xs text-error",children:[e.jsx(La,{className:"h-3.5 w-3.5 fill-current"})," ",t("cam.state.rec"),e.jsx("span",{className:"font-mono",children:`${Math.floor(ia/60).toString().padStart(2,"0")}:${(ia%60).toString().padStart(2,"0")}`})]}),e.jsx("div",{className:"absolute left-3 top-3",children:e.jsx("button",{type:"button",onClick:()=>Ya(a=>!a),className:"rounded border border-outline-variant/40 bg-black/70 px-2 py-1 text-xs text-white",children:t(oa?"cam.hist.expand":"cam.hist.collapse")})}),!oa&&e.jsxs("div",{className:"absolute left-3 top-12 w-[360px] max-w-[calc(100%-1.5rem)] rounded border border-outline-variant/30 bg-black/70 p-2 text-white",children:[e.jsxs("div",{className:"mb-2 flex flex-wrap items-center gap-3 text-[11px]",children:[e.jsxs("label",{className:"inline-flex items-center gap-1",children:[e.jsx("input",{type:"checkbox",checked:na,onChange:a=>Ka(a.target.checked)})," ",t("cam.hist.enabled")]}),e.jsxs("label",{className:"inline-flex items-center gap-1",children:[e.jsx("input",{type:"checkbox",checked:Me,onChange:a=>Xa(a.target.checked)})," RGB"]}),e.jsxs("label",{className:"inline-flex items-center gap-1",children:[e.jsx("input",{type:"checkbox",checked:Fe,onChange:a=>Za(a.target.checked)})," ",t("cam.hist.luminance")]}),e.jsxs("label",{className:"inline-flex items-center gap-1",children:[e.jsx("input",{type:"checkbox",checked:sa,onChange:a=>Qa(a.target.checked)})," ",t("cam.hist.over")]})]}),e.jsx("canvas",{ref:ue,className:"h-24 w-full rounded border border-white/20 bg-black/60"}),e.jsxs("div",{className:"mt-2 grid grid-cols-3 gap-2 text-[11px]",children:[e.jsxs("div",{children:["mean: ",e.jsx("span",{className:"font-mono",children:$e.mean.toFixed(1)})]}),e.jsxs("div",{children:["std: ",e.jsx("span",{className:"font-mono",children:$e.std.toFixed(1)})]}),e.jsxs("div",{children:["over: ",e.jsxs("span",{className:"font-mono",children:[$e.over.toFixed(2),"%"]})]})]})]})]}),e.jsxs("div",{className:"mt-3 grid grid-cols-1 gap-2 text-xs md:grid-cols-4",children:[e.jsxs("div",{className:"rounded border border-outline-variant/20 bg-surface-container-low px-2 py-1.5 text-left",children:[e.jsxs("span",{className:"text-on-surface-variant",children:[t("cam.stats.frameFps"),": "]}),e.jsx("span",{className:"font-mono text-on-surface",children:Number((o==null?void 0:o.actual_preview_fps)??at).toFixed(2)}),e.jsx("p",{className:"mt-0.5 text-[10px] leading-tight text-on-surface-variant/90",children:t("cam.stats.fpsMeasureNote")})]}),e.jsxs("div",{className:"rounded border border-outline-variant/20 bg-surface-container-low px-2 py-1.5 text-left",children:[e.jsxs("span",{className:"text-on-surface-variant",children:[t("cam.stats.targetFps"),": "]}),e.jsx("span",{className:"font-mono text-on-surface",children:Number(((Ma=n==null?void 0:n.info)==null?void 0:Ma.fps)??0).toFixed(2)})]}),e.jsxs("div",{className:"rounded border border-outline-variant/20 bg-surface-container-low px-2 py-1.5 text-left",children:[e.jsxs("span",{className:"text-on-surface-variant",children:[t("cam.stats.streamPacingFps"),": "]}),e.jsx("span",{className:"font-mono text-on-surface",children:(o==null?void 0:o.preview_target_fps)??(o==null?void 0:o.target_preview_fps)??"—"}),e.jsx("p",{className:"mt-0.5 text-[10px] leading-tight text-on-surface-variant/90",children:t("cam.stats.streamPacingHint")})]}),e.jsxs("div",{className:"rounded border border-outline-variant/20 bg-surface-container-low px-2 py-1.5 text-left",children:[e.jsxs("span",{className:"text-on-surface-variant",children:[t("cam.stats.captureFps"),": "]}),e.jsx("span",{className:"font-mono text-on-surface",children:Number((o==null?void 0:o.actual_capture_fps)??0).toFixed(2)})]}),e.jsxs("div",{className:"rounded border border-outline-variant/20 bg-surface-container-low px-2 py-1.5 text-left",children:[e.jsxs("span",{className:"text-on-surface-variant",children:[t("cam.stats.encodeMs"),": "]}),e.jsxs("span",{className:"font-mono text-on-surface",children:[Number((o==null?void 0:o.jpeg_average_encode_ms)??0).toFixed(1)," ms"]})]}),e.jsxs("div",{className:"rounded border border-outline-variant/20 bg-surface-container-low px-2 py-1.5 text-left",children:[e.jsxs("span",{className:"text-on-surface-variant",children:[t("cam.stats.encoder"),": "]}),e.jsx("span",{className:"font-mono text-on-surface",children:String((o==null?void 0:o.preview_encoder)??"—")}),e.jsx("p",{className:"mt-0.5 text-[10px] leading-tight text-on-surface-variant/90",children:`${(o==null?void 0:o.jpeg_source_format)??"RGB888"} / fail ${(o==null?void 0:o.jpeg_encode_failures)??0}`})]}),e.jsxs("div",{className:"rounded border border-outline-variant/20 bg-surface-container-low px-2 py-1.5 text-left",children:[e.jsxs("span",{className:"text-on-surface-variant",children:[t("cam.stats.consumers"),": "]}),e.jsx("span",{className:"font-mono text-on-surface",children:`${(o==null?void 0:o.preview_consumers)??0}/${(o==null?void 0:o.analysis_consumers)??0}/${(o==null?void 0:o.recording_consumers)??0}`})]}),e.jsxs("div",{className:"rounded border border-outline-variant/20 bg-surface-container-low px-2 py-1.5 text-left",children:[e.jsxs("span",{className:"text-on-surface-variant",children:[t("cam.stats.cameraMemory"),": "]}),e.jsx("span",{className:"font-mono text-on-surface",children:`${Math.round(Number((o==null?void 0:o.process_rss_kb)??0)/1024)} / ${Math.round(Number((o==null?void 0:o.process_swap_kb)??0)/1024)} MB`})]}),(o==null?void 0:o.throttle_reason)==="auto_exposure_long"&&e.jsx("div",{className:"rounded border border-tertiary/40 bg-tertiary-container/20 px-2 py-1.5 text-left text-tertiary",children:t("cam.stats.longExposureThrottle",{exposure:Math.round(Number(o.actual_exposure_us??0)/1e3)})}),e.jsxs("div",{className:"rounded border border-outline-variant/20 bg-surface-container-low px-2 py-1.5 text-left",children:[e.jsxs("span",{className:"text-on-surface-variant",children:[t("cam.stats.uptime"),": "]}),e.jsx("span",{className:"font-mono text-on-surface",children:he.current!=null?`${Math.max(0,Math.round((performance.now()-he.current)/1e3))}s`:"0s"})]}),e.jsxs("div",{className:"rounded border border-outline-variant/20 bg-surface-container-low px-2 py-1.5 text-left",children:[e.jsxs("span",{className:"text-on-surface-variant",children:[t("cam.system.sensor"),": "]}),e.jsx("span",{className:"font-mono",children:String(((Fa=n==null?void 0:n.info)==null?void 0:Fa.sensor)??"—")})]}),e.jsxs("div",{className:"rounded border border-outline-variant/20 bg-surface-container-low px-2 py-1.5 text-left",children:[e.jsxs("span",{className:"text-on-surface-variant",children:[t("cam.stats.driver"),": "]}),e.jsx("span",{className:"font-mono",children:String((($a=n==null?void 0:n.info)==null?void 0:$a.driver)??(o==null?void 0:o.camera_driver)??"—")}),e.jsx("p",{className:"mt-0.5 text-[10px] leading-tight text-on-surface-variant/90",children:`${((Ba=n==null?void 0:n.info)==null?void 0:Ba.backend)??(o==null?void 0:o.camera_backend)??"—"} · lores ${(Pa=n==null?void 0:n.info)!=null&&Pa.lores_available||o!=null&&o.lores_available?"on":"off"}`})]}),e.jsxs("div",{className:"rounded border border-outline-variant/20 bg-surface-container-low px-2 py-1.5 text-left",children:[e.jsxs("span",{className:"text-on-surface-variant",children:[t("cam.stats.metadata"),": "]}),e.jsx("span",{className:"font-mono",children:((Ta=n==null?void 0:n.info)==null?void 0:Ta.lux)!=null?`${Number(n.info.lux).toFixed(1)} lux`:"—"}),e.jsx("p",{className:"mt-0.5 text-[10px] leading-tight text-on-surface-variant/90",children:((Ia=n==null?void 0:n.info)==null?void 0:Ia.colour_temperature)!=null?`${Math.round(Number(n.info.colour_temperature))}K`:"—"})]}),e.jsxs("div",{className:"rounded border border-outline-variant/20 bg-surface-container-low px-2 py-1.5 text-left",children:[e.jsxs("span",{className:"text-on-surface-variant",children:[t("cam.controls.resolution"),": "]}),e.jsx("span",{className:"font-mono",children:`${((Oa=n==null?void 0:n.info)==null?void 0:Oa.width)??"—"}x${((Da=n==null?void 0:n.info)==null?void 0:Da.height)??"—"}`})]}),e.jsxs("div",{className:"rounded border border-outline-variant/20 bg-surface-container-low px-2 py-1.5 text-left",children:[e.jsxs("span",{className:"text-on-surface-variant",children:[t("cam.preview.mode"),": "]}),e.jsx("span",{className:"font-mono",children:(Aa=n==null?void 0:n.info)!=null&&Aa.auto_exposure?t("cam.controls.auto"):t("cam.controls.manual")})]})]}),e.jsxs("div",{className:"mt-3 flex flex-wrap gap-2",children:[e.jsxs("button",{type:"button",disabled:!ge,onClick:()=>void ya(),className:`rounded px-3 py-2 text-sm disabled:opacity-50 ${ge?"bg-primary-container text-on-primary-container":"border border-outline-variant/40 text-on-surface-variant"}`,children:[e.jsx(Wa,{className:"inline h-4 w-4"})," ",t(M?"cam.btn.starting":"cam.btn.start")]}),e.jsxs("button",{type:"button",disabled:!_a,onClick:()=>void st(),className:`rounded border px-3 py-2 text-sm disabled:opacity-50 ${_a?"border-error/60 text-error hover:bg-error/10":"border-outline-variant/40 text-on-surface-variant"}`,children:[e.jsx(Vt,{className:"inline h-4 w-4"})," ",t(M?"cam.btn.stopping":"cam.btn.stop")]}),e.jsxs("button",{type:"button",disabled:!_t,onClick:()=>void ot(),className:"rounded border border-outline-variant/40 px-3 py-2 text-sm disabled:opacity-50",children:[e.jsx(Ua,{className:"inline h-4 w-4"})," ",t(Je?"cam.btn.capturing":"cam.btn.capture")]}),e.jsxs("button",{type:"button",disabled:!Ct,onClick:()=>void it(),className:`rounded border px-3 py-2 text-sm disabled:opacity-50 ${n!=null&&n.recording?"border-error/60 bg-error/10 text-error":"border-outline-variant/40"}`,children:[e.jsx(La,{className:"inline h-4 w-4"})," ",Ee?t("cam.btn.recordBusy"):n!=null&&n.recording?t("cam.btn.recordStop"):t("cam.btn.recordStart")]})]})]}),e.jsxs("section",{className:"rounded-xl border border-outline-variant/20 bg-surface-container p-4 text-xs",children:[e.jsx("h2",{className:"mb-3 text-sm font-semibold uppercase tracking-wider",children:t("cam.files.title")}),e.jsx("div",{className:"mb-2",children:e.jsx("button",{type:"button",disabled:ba,onClick:()=>void ze(),className:"rounded border border-outline-variant/40 px-2 py-1 disabled:opacity-50",children:t("cam.files.refresh")})}),nt&&e.jsx("div",{className:"mb-2 text-on-surface-variant",children:t("cam.files.loadingInfo")}),N&&e.jsxs("div",{className:"mb-3 rounded border border-outline-variant/20 p-2",children:[e.jsxs("div",{className:"mb-2 flex items-center justify-between gap-2 font-semibold",children:[e.jsxs("div",{className:"flex min-w-0 items-center gap-1",children:[e.jsx(zt,{className:"h-3.5 w-3.5 shrink-0"}),e.jsx("span",{className:"truncate",children:N.filename})]}),e.jsx("button",{type:"button",onClick:()=>Ge(),className:"shrink-0 rounded border border-outline-variant/40 p-1 text-on-surface-variant hover:bg-surface-container","aria-label":t("cam.files.closeDetail"),children:e.jsx(Kt,{className:"h-3.5 w-3.5"})})]}),e.jsxs("div",{children:[t("cam.files.size"),": ",e.jsx("span",{className:"font-mono",children:Va(N.size)})]}),e.jsxs("div",{children:[t("cam.files.type"),": ",e.jsx("span",{className:"font-mono",children:N.type})]}),e.jsxs("div",{children:[t("cam.files.modified"),": ",e.jsx("span",{className:"font-mono",children:new Date(N.modified).toLocaleString()})]}),N.exposure_us!=null&&e.jsxs("div",{children:[t("cam.controls.exposure"),": ",e.jsxs("span",{className:"font-mono",children:[N.exposure_us,"us"]})]}),N.analogue_gain!=null&&e.jsxs("div",{children:[t("cam.controls.gain"),": ",e.jsx("span",{className:"font-mono",children:N.analogue_gain})]}),N.resolution&&e.jsxs("div",{children:[t("cam.controls.resolution"),": ",e.jsx("span",{className:"font-mono",children:N.resolution})]})]}),e.jsxs("div",{className:"max-h-96 space-y-2 overflow-auto",children:[K.length===0&&!ba&&e.jsx("div",{className:"text-on-surface-variant",children:t("cam.files.empty")}),Rt.map(a=>e.jsxs("div",{className:`rounded border p-2 ${le===a.name?"border-primary/50 bg-primary/5":"border-outline-variant/20"}`,children:[e.jsxs("div",{className:"flex items-center justify-between gap-2",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsx("div",{className:"truncate font-semibold",children:a.name}),e.jsxs("div",{className:"text-on-surface-variant",children:[Va(a.size)," | ",new Date(a.modified).toLocaleString()]})]}),e.jsx("div",{className:"shrink-0 text-on-surface-variant",children:a.type})]}),e.jsxs("div",{className:"mt-2 flex gap-2",children:[e.jsxs("button",{type:"button",onClick:()=>yt(a.name),className:"rounded border border-outline-variant/40 px-2 py-1",children:[e.jsx(Ht,{className:"mr-1 inline h-3.5 w-3.5"}),t("cam.files.download")]}),e.jsxs("button",{type:"button",onClick:()=>void vt(a.name),className:`rounded border px-2 py-1 ${le===a.name?"border-primary text-primary":"border-outline-variant/40"}`,children:[e.jsx(qt,{className:"mr-1 inline h-3.5 w-3.5"}),t("cam.files.info")]}),e.jsxs("button",{type:"button",onClick:()=>void jt(a.name),className:"rounded border border-outline-variant/40 px-2 py-1",children:[e.jsx(Pt,{className:"mr-1 inline h-3.5 w-3.5"}),t("cam.files.delete")]})]})]},a.name))]}),e.jsxs("div",{className:"mt-3 flex items-center justify-between",children:[e.jsx("button",{type:"button",disabled:ye<=1,onClick:()=>de(a=>Math.max(1,a-1)),className:"rounded border border-outline-variant/40 px-2 py-1 disabled:opacity-50",children:t("cam.files.prev")}),e.jsx("div",{className:"text-on-surface-variant",children:t("cam.files.page",{current:ye,total:ve})}),e.jsx("button",{type:"button",disabled:ye>=ve,onClick:()=>de(a=>Math.min(ve,a+1)),className:"rounded border border-outline-variant/40 px-2 py-1 disabled:opacity-50",children:t("cam.files.next")})]})]})]}),e.jsxs("section",{className:"col-span-12 grid grid-cols-12 gap-4 xl:col-span-3 xl:self-start",children:[e.jsxs("section",{className:"col-span-12 rounded-xl border border-outline-variant/20 bg-surface-container p-4",children:[e.jsx("h2",{className:"mb-3 text-sm font-semibold uppercase tracking-wider",children:t("cam.controls.title")}),e.jsxs("div",{className:"space-y-3 text-xs",children:[e.jsxs("div",{className:"grid grid-cols-2 gap-2",children:[e.jsxs("label",{className:"block",children:[t("cam.controls.sensorFps"),e.jsx("input",{value:Xe,onChange:a=>{Ze(a.target.value),G(!0)},className:"mt-1 w-full rounded border border-outline-variant/30 bg-surface-container-low px-2 py-1.5"})]}),e.jsxs("label",{className:"block",children:[t("cam.controls.previewFps"),e.jsx("input",{value:Qe,onChange:a=>{Ye(a.target.value),G(!0)},className:"mt-1 w-full rounded border border-outline-variant/30 bg-surface-container-low px-2 py-1.5"})]})]}),e.jsx("div",{className:"grid grid-cols-1 gap-2",children:e.jsxs("label",{className:"block",children:[t("cam.controls.sampling"),e.jsxs("select",{value:ta,onChange:a=>{ra(a.target.value),G(!0)},className:"mt-1 w-full rounded border border-outline-variant/30 bg-surface-container-low px-2 py-1.5",children:[e.jsx("option",{value:"supersample",children:"supersample"}),e.jsx("option",{value:"native",children:"native"}),e.jsx("option",{value:"crop",children:"crop"})]})]})}),e.jsxs("label",{className:"block",children:[t("cam.controls.resolution"),e.jsx("div",{className:"mt-1 grid grid-cols-2 gap-1",children:Zt.map(a=>e.jsx("button",{type:"button",onClick:()=>{aa(a),G(!0)},className:`rounded border px-2 py-1 ${ea===a?"border-primary text-primary":"border-outline-variant/30"}`,children:a},a))})]}),e.jsx("button",{type:"button",disabled:!J,onClick:()=>void ct(),className:"w-full rounded border border-outline-variant/40 px-2 py-1.5 disabled:opacity-50",children:t("cam.controls.applyRuntime")})]})]}),e.jsxs("section",{className:"col-span-12 rounded-xl border border-outline-variant/20 bg-surface-container p-4",children:[e.jsxs("div",{className:"mb-2 flex items-center gap-1 text-sm font-semibold uppercase tracking-wider",children:[e.jsx(Wt,{className:"h-3.5 w-3.5"})," ",t("cam.controls.core")]}),ce&&e.jsx("div",{className:"mb-2 rounded border border-primary/40 bg-primary/10 px-2 py-1 text-[11px] text-primary",children:t("cam.controls.pendingChanges")}),be&&e.jsx("p",{className:"mb-2 text-[11px] text-on-surface-variant",children:t("cam.controls.lockedByAe")}),e.jsxs("div",{className:"grid grid-cols-2 gap-2 text-xs",children:[e.jsx($,{label:t("cam.controls.exposure"),value:c.exposure,min:100,max:12e4,step:100,unit:"us",disabled:be,onChange:a=>{f(!0),y(r=>({...r,exposure:a}))}}),e.jsx($,{label:t("cam.controls.gain"),value:c.gain,min:1,max:24,step:.1,disabled:be,onChange:a=>{f(!0),y(r=>({...r,gain:Number(a.toFixed(1))}))}}),e.jsx($,{label:t("cam.controls.digitalGain"),value:c.digitalGain,min:1,max:8,step:.1,disabled:be||!Na,onChange:a=>{f(!0),y(r=>({...r,digitalGain:Number(a.toFixed(1))}))}}),e.jsxs("label",{className:"block",children:[t("cam.controls.noiseReductionMode"),e.jsx("select",{value:c.noiseReductionMode,onChange:a=>{f(!0),y(r=>({...r,noiseReductionMode:a.target.value}))},className:"mt-1 w-full rounded border border-outline-variant/30 bg-surface-container-low px-2 py-1.5",children:kt.map(a=>e.jsx("option",{value:a,children:t(`cam.controls.nr.${a}`)},a))})]}),e.jsx($,{label:t("cam.controls.contrast"),value:c.contrast,min:0,max:2,step:.1,onChange:a=>{f(!0),y(r=>({...r,contrast:Number(a.toFixed(1))}))}}),e.jsx($,{label:t("cam.controls.brightness"),value:c.brightness,min:-1,max:1,step:.1,onChange:a=>{f(!0),y(r=>({...r,brightness:Number(a.toFixed(1))}))}}),e.jsx($,{label:t("cam.controls.saturation"),value:c.saturation,min:0,max:2,step:.1,onChange:a=>{f(!0),y(r=>({...r,saturation:Number(a.toFixed(1))}))}}),e.jsx($,{label:t("cam.controls.sharpness"),value:c.sharpness,min:0,max:2,step:.1,onChange:a=>{f(!0),y(r=>({...r,sharpness:Number(a.toFixed(1))}))}})]}),!Na&&e.jsxs("p",{className:"mt-2 text-[11px] text-on-surface-variant",children:[t("cam.controls.digitalGainReadOnly"),": ",e.jsx("span",{className:"font-mono",children:Number(((Ha=n==null?void 0:n.info)==null?void 0:Ha.actual_digital_gain)??c.digitalGain).toFixed(2)})]}),e.jsx("div",{className:"mt-2",children:e.jsx("button",{type:"button",disabled:!ce,onClick:()=>void lt(),className:"w-full rounded border border-outline-variant/40 px-2 py-1.5 disabled:opacity-50",children:t("cam.controls.applySettings")})})]}),e.jsxs("section",{className:"col-span-12 rounded-xl border border-outline-variant/20 bg-surface-container p-4",children:[e.jsx("div",{className:"mb-2 text-sm font-semibold uppercase tracking-wider",children:t("cam.controls.mode")}),e.jsxs("div",{className:"grid grid-cols-2 gap-2 text-xs",children:[e.jsxs("label",{className:"block",children:[t("cam.controls.autoExposure"),e.jsxs("select",{value:c.autoExposure?"auto":"manual",onChange:a=>{y(r=>({...r,autoExposure:a.target.value==="auto"})),f(!0)},className:"mt-1 w-full rounded border border-outline-variant/30 bg-surface-container-low px-2 py-1.5",children:[e.jsx("option",{value:"auto",children:t("cam.controls.auto")}),e.jsx("option",{value:"manual",children:t("cam.controls.manual")})]})]}),e.jsxs("label",{className:"block",children:[t("cam.controls.colorMode"),e.jsxs("select",{value:c.colorMode,onChange:a=>{y(r=>({...r,colorMode:a.target.value})),f(!0)},className:"mt-1 w-full rounded border border-outline-variant/30 bg-surface-container-low px-2 py-1.5",children:[e.jsx("option",{value:"color",children:t("cam.controls.color")}),e.jsx("option",{value:"mono",children:t("cam.controls.mono")})]})]}),e.jsxs("label",{className:"block",children:[t("cam.controls.whiteBalance"),e.jsx("select",{value:c.whiteBalanceMode,onChange:a=>{y(r=>({...r,whiteBalanceMode:a.target.value})),f(!0)},className:"mt-1 w-full rounded border border-outline-variant/30 bg-surface-container-low px-2 py-1.5",children:St.map(a=>e.jsx("option",{value:a,children:t(`cam.controls.wb.${a}`)},a))})]}),e.jsxs("label",{className:"block",children:[t("cam.controls.aeFlicker"),e.jsxs("select",{value:c.aeFlickerMode,onChange:a=>{y(r=>({...r,aeFlickerMode:a.target.value})),f(!0)},className:"mt-1 w-full rounded border border-outline-variant/30 bg-surface-container-low px-2 py-1.5",children:[e.jsx("option",{value:"off",children:t("cam.controls.off")}),e.jsx("option",{value:"50hz",children:"50 Hz"}),e.jsx("option",{value:"60hz",children:"60 Hz"})]})]}),e.jsxs("label",{className:"block",children:[t("cam.controls.maxAeFrame"),e.jsx("input",{type:"number",min:1e4,max:1e7,step:1e4,value:c.autoExposureMaxUs,onChange:a=>{y(r=>({...r,autoExposureMaxUs:j(parseInt(a.target.value,10)||2e6,1e4,1e7)})),f(!0)},className:"mt-1 w-full rounded border border-outline-variant/30 bg-surface-container-low px-2 py-1.5"})]}),e.jsx($,{label:"R Gain",value:c.whiteBalanceGainR,min:.1,max:3,step:.1,disabled:!qe,onChange:a=>{y(r=>({...r,whiteBalanceGainR:Number(a.toFixed(1))})),f(!0)}}),e.jsx($,{label:"B Gain",value:c.whiteBalanceGainB,min:.1,max:3,step:.1,disabled:!qe,onChange:a=>{y(r=>({...r,whiteBalanceGainB:Number(a.toFixed(1))})),f(!0)}})]}),!qe&&e.jsx("p",{className:"mt-2 text-[11px] text-on-surface-variant",children:t("cam.controls.lockedByWb")}),e.jsx("div",{className:"mt-2",children:e.jsx("button",{type:"button",onClick:()=>void dt(),className:"w-full rounded border border-outline-variant/40 px-2 py-1.5",children:t("cam.controls.applyMode")})})]})]})]})]}),(Re||V)&&e.jsxs("div",{className:"fixed bottom-4 right-4 z-40 max-w-md space-y-2 text-xs",children:[Re&&e.jsx("div",{className:"rounded border border-error/40 bg-error-container/20 px-3 py-2 text-on-error-container",children:Re}),V&&e.jsx("div",{className:"rounded border border-primary/30 bg-primary/10 px-3 py-2 text-on-surface",children:V})]})]})}Ft.createRoot(document.getElementById("root")).render(e.jsx($t.StrictMode,{children:e.jsx(Tt,{children:e.jsx(At,{children:e.jsx(Yt,{})})})}));
diff --git a/web/static/analysis-lab/assets/http-ChPtkS1w.js b/web/static/analysis-lab/assets/http-VZMNcsmS.js
similarity index 96%
rename from web/static/analysis-lab/assets/http-ChPtkS1w.js
rename to web/static/analysis-lab/assets/http-VZMNcsmS.js
index cca1d58..cb65497 100644
--- a/web/static/analysis-lab/assets/http-ChPtkS1w.js
+++ b/web/static/analysis-lab/assets/http-VZMNcsmS.js
@@ -1,4 +1,4 @@
-import{c as u}from"./index-CutgeBjy.js";import{r as o,j as f}from"./client-D1ZVDB-N.js";/**
+import{c as u}from"./index-C78KOEFu.js";import{r as o,j as f}from"./client-D1ZVDB-N.js";/**
* @license lucide-react v0.460.0 - ISC
*
* This source code is licensed under the ISC license.
diff --git a/web/static/analysis-lab/assets/index-C78KOEFu.js b/web/static/analysis-lab/assets/index-C78KOEFu.js
new file mode 100644
index 0000000..269abd1
--- /dev/null
+++ b/web/static/analysis-lab/assets/index-C78KOEFu.js
@@ -0,0 +1,26 @@
+import{r as a,j as v}from"./client-D1ZVDB-N.js";/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const f=e=>e.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase(),u=(...e)=>e.filter((s,t,o)=>!!s&&s.trim()!==""&&o.indexOf(s)===t).join(" ").trim();/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */var w={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const S=a.forwardRef(({color:e="currentColor",size:s=24,strokeWidth:t=2,absoluteStrokeWidth:o,className:l="",children:r,iconNode:m,...n},c)=>a.createElement("svg",{ref:c,...w,width:s,height:s,stroke:e,strokeWidth:o?Number(t)*24/Number(s):t,className:u("lucide",l),...n},[...m.map(([i,d])=>a.createElement(i,d)),...Array.isArray(r)?r:[r]]));/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const P=(e,s)=>{const t=a.forwardRef(({className:o,...l},r)=>a.createElement(S,{ref:r,iconNode:s,className:u(`lucide-${f(e)}`,o),...l}));return t.displayName=`${e}`,t};/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const A=P("Trash2",[["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6",key:"4alrt4"}],["path",{d:"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2",key:"v07s0e"}],["line",{x1:"10",x2:"10",y1:"11",y2:"17",key:"1uufr5"}],["line",{x1:"14",x2:"14",y1:"11",y2:"17",key:"xtxkd"}]]),k={"app.title":"OGScope Plate Solve Console","nav.lab":"Lab","nav.labImage":"Image solve","nav.labVideo":"Video solve","delete.uploadCascade":"Delete {n} linked experiment record(s) as well?","lab.solveCurrentFrame":"Solve current frame (file)","lab.solveFileStart":"Start continuous file solve","lab.solveFileStop":"Stop continuous file solve","lab.cameraPreviewLoading":"Connecting to shared preview…","lab.solveCameraFrame":"Solve live camera frame","lab.solveCameraStart":"Start camera solve","lab.solveCameraStop":"Stop camera solve","lab.videoPreviewFailed":"This video format may be unsupported by the browser. Try MP4 (H.264) or WebM.","lab.previewModeFile":"Pool file","lab.previewModeCamera":"Device camera","lab.videoLiveIntro":"Shares the same camera as Camera Debug — preview and solve live frames here without opening debug. Both pages can run together.","lab.videoMjpegHint":"MJPEG slots are full: close live previews on other pages or retry later (default ~4; tune OGSCOPE_STREAM_MAX_MJPEG_CLIENTS in systemd).","lab.cameraStreamStatusFail":"Could not read MJPEG slot status. Please retry shortly.","lab.cameraSnapshotName":"ogscope_camera_live","lab.metric.probRaw":"Raw Prob","lab.systemLoad":"System load","results.saveBatchAll":"Save all to records","nav.pool":"Assets","nav.history":"Records","nav.cameraDebug":"Camera Debug","nav.home":"Home","nav.systemAdmin":"Back to System Admin","lang.zh":"中文","lang.en":"EN","sidebar.assets":"Uploaded assets","sidebar.upload":"Upload","sidebar.refresh":"Refresh","sidebar.debugCaptures":"Debug console media","sidebar.assetTypeImage":"Image","sidebar.assetTypeVideo":"Video","sidebar.debugEmpty":"No debug files","sidebar.importToPool":"Import to pool","sidebar.importToPoolWithTranscode":"Transcode & Import","sidebar.importToPoolDirect":"Import Directly","sidebar.flowPreparing":"Preparing...","sidebar.flowImportingDebug":"Importing debug capture...","sidebar.flowDownloadingPool":"Downloading imported file...","sidebar.flowWritingBuffer":"Writing to transcoder buffer...","sidebar.flowLoadingTranscoder":"Loading transcoder...","sidebar.flowTranscoding":"Transcoding AVI -> MP4...","sidebar.flowPackaging":"Packaging output...","sidebar.flowUploadingMp4":"Uploading MP4...","sidebar.flowReplacing":"Replacing and removing original AVI...","sidebar.flowNoTranscode":"No transcode required, imported directly.","sidebar.flowDone":"Completed","sidebar.flowDoneMsg":"Done: {name}","sidebar.flowFailed":"Failed","sidebar.debugPage":"Page {cur} / {total}","sidebar.batchPresets":"Batch presets","sidebar.batchHint":"Check presets, then use Batch solve to compare multiple param sets.","lab.selectOrUpload":"Pick an uploaded or imported asset from the left","lab.selectOrUploadVideo":"Pick a pool video to preview, or use the button above for the live camera frame.","lab.file":"File","lab.source":"Source","lab.layers":"Layers","lab.layer.matched":"Matched","lab.layer.pattern":"Pattern","lab.layer.all":"All centroids","lab.layer.rejected":"Rejected (red cross)","lab.previewConfidence":"Confidence","lab.grid":"Grid","lab.zoomIn":"Zoom in","lab.zoomOut":"Zoom out","lab.zoomReset":"Reset","lab.resolution":"Resolution","lab.fwhm":"FWHM","lab.starsDetected":"Stars detected","lab.meta.title":"Capture & file info","lab.meta.noSidecar":"No sidecar (not from debug capture)","lab.meta.partial":"No detailed sidecar; file info only.","lab.solveSection":"Solve","lab.imageSection":"Image","lab.metric.solveMs":"Time","lab.metric.solveComputeMs":"Solve compute","lab.metric.solveComputeHelp":"Server-side Tetra3 + star extraction only (no network).","lab.metric.solveRoundTripMs":"End-to-end","lab.metric.solveRoundTripHelp":"From request start to UI updated: network + JSON + render.","lab.metric.backendTotalMs":"Backend total","lab.metric.openDecodeMs":"Open/decode","lab.metric.preprocessMs":"Preprocess","lab.metric.extractMs":"Extract","lab.metric.solveOnlyMs":"Solve match","lab.metric.probHelp":"Tetra3 `Prob` is false-positive probability (lower is better). We use (1−Prob)×100% when representable; for extremely small Prob, IEEE doubles round to 100%, so we map −log10(Prob) to ~70–100% for differentiation.","lab.metric.probRawHelp":"Raw Tetra3 Prob (e.g. log-likelihood); compare with the normalized line above.","lab.metric.radec":"RA / Dec","lab.metric.matches":"Matches","lab.metric.rmse":"RMSE","lab.metric.prob":"Prob.","lab.metric.status":"Status","lab.gateNextMs":"Retry in ~{ms} ms","lab.gate.skipRecording":"Recording is active: the camera is writing video, so live-frame solve is paused. Stop recording and try again.","lab.gate.skipBusyInFlight":"The previous solve is still running; please wait. If this repeats often, increase the realtime solve interval.","lab.gate.skipInterval":"Solve requests are arriving faster than allowed; the server is throttling. Wait for the countdown and retry.","lab.gate.skipBusyFallback":"Realtime solve could not start ({detail}).","lab.centroidQualityTitle":"Centroid quality","lab.centroidQualityMetrics":"In {in} → kept {out}; removed dense {dense}, collinear {line}","meta.exposure":"Exposure","meta.gain":"Gain","meta.fps":"FPS","meta.sensor":"Sensor","meta.colorMode":"Color","meta.outputResolution":"Output size","meta.fileTime":"File time","meta.fileSize":"File size","results.viewRaw":"Raw JSON","results.hideRaw":"Hide","results.solveHistoryTitle":"Solve history","results.historySingle":"Single frame","results.historyBatch":"Batch ({count} runs)","params.title":"Solve parameters","params.blockSolveIntro":"Plate-solve (Tetra3): FOV, timeout, and coarse sky hints. FOV should match your lens.","params.centroid":"Star detection","params.blockCentroidIntro":"Star detection: threshold, blob area, and local background window for centroids.","params.fov":"FOV estimate (°)","params.fovHelp":"Horizontal field of view for lost-in-space solve.","params.fovErr":"FOV max error (°)","params.fovErrHelp":"Search range around estimated FOV.","params.timeout":"Timeout (ms)","params.timeoutHelp":"Max wait time per solve.","params.solveIntervalMs":"Realtime solve interval (ms)","params.solveIntervalMsHelp":"Desired interval is adjustable, but backend clamps it into a safe range.","params.solveIntervalIndependent":"Solve cadence is independent from sensor and preview frame rates.","params.solveIntervalBound":"Backend bounds: {min}-{max} ms, effective now: {effective} ms","params.centroidRejectionLevel":"Centroid rejection (1–5)","params.centroidRejectionLevelHelp":"1 keeps more stars; 5 aggressively rejects dense clusters and collinear false detections; default 3.","params.centroidRejectionScale":"Conservative ← → Aggressive","params.solveProfile":"Solve profile","params.solveProfileHelp":"Speed/Balanced/Robust tune timeout, centroid thresholds, and matching star count together.","params.solveProfileSpeed":"Speed first","params.solveProfileBalanced":"Balanced","params.solveProfileRobust":"Robust first","params.ra":"RA hint (°)","params.raHelp":"Approximate right ascension in degrees.","params.dec":"Dec hint (°)","params.decHelp":"Approximate declination in degrees.","params.maxSide":"Max long side before extract (px)","params.maxSideHelp":"Downscale long edge for faster centroid extraction.","params.detailLevelFull":"Include full Tetra3 raw block (larger payload, for debugging only).","params.largeScaleBg":"Large-scale background flattening","params.largeScaleBgHelp":"Before centroiding, estimate a low-frequency background on a downscaled image and correct uneven illumination (e.g. corner glow). Off by default to match legacy behavior.","params.sigma":"σ threshold","params.sigmaHelp":"Multiplier over background noise for star candidates.","params.maxArea":"max_area","params.maxAreaHelp":"Max connected component area in pixels.","params.minArea":"min_area","params.minAreaHelp":"Min connected component area in pixels.","params.filtsize":"filtsize (odd)","params.filtsizeHelp":"Local filter window size, must be odd.","btn.solveOne":"Solve once","btn.solveBatch":"Batch solve (presets)","btn.applyPresets":"Apply preset to form","btn.savePreset":"Save","placeholder.newPreset":"New preset name","pool.title":"Server asset pool","pool.col.name":"Filename","pool.col.source":"Source","pool.col.size":"Size","pool.col.time":"Modified","pool.delete":"Delete","history.title":"Experiment records","history.intro":"Saved solve snapshots from the Lab. After a solve, use Save to records in the Lab main panel (Result comparison), or Save on each batch result card. Search by filename or preset; export JSON/CSV for backup.","history.search":"Search…","history.searchBtn":"Search","history.exportJson":"Export JSON","history.exportCsv":"Export CSV","history.total":"Total {n}","history.preset":"Preset","history.metrics":"Metrics","history.detail":"Details","history.collapse":"Collapse","history.prev":"Prev","history.next":"Next","history.delete":"Delete","delete.uploadFirst":'Delete "{name}" from the asset pool?',"delete.uploadSecond":"This cannot be undone. Confirm again?","delete.experimentFirst":"Delete this experiment record?","delete.experimentSecond":"This cannot be undone. Confirm again?","results.title":"Results","results.saveCurrent":"Save to records","results.saveRow":"Save","results.expand":"Expand","results.collapseJson":"Collapse","err.selectFile":"Select a file","err.selectPresets":"Select at least one preset","common.placeholder":"—","lab.transcode.title":"AVI requires transcoding","lab.transcode.desc":"This video is AVI. For browser preview and continuous solving, transcode it locally to MP4 and upload replacement. The original AVI on server will be removed after success.","lab.transcode.button":"Transcode and Upload Replacement","lab.transcode.loading":"Loading transcoder...","lab.transcode.writingBuffer":"Writing to transcoder buffer...","lab.transcode.running":"Transcoding AVI -> MP4...","lab.transcode.packaging":"Packaging transcoded output...","lab.transcode.uploading":"Uploading transcoded output...","lab.transcode.done":"Transcode and replacement completed.","lab.transcode.failed":"Transcode or upload failed. Please retry.","lab.transcode.fetchFailed":"Failed to fetch AVI from server.","sys.shell.subtitle":"system debug console","sys.shell.nav.overview":"Overview","sys.shell.nav.network":"Network","sys.shell.nav.camera":"Camera Debug","sys.shell.nav.analysis":"Analysis Console","sys.shell.nav.sensors":"Sensors","sys.shell.nav.power":"Power","sys.shell.nav.hmi":"HMI","sys.shell.nav.config":"Config","sys.shell.workbench":"WORKBENCH / System workspace","sys.shell.node":"Node","sys.shell.top.overview":"System Status","sys.shell.top.network":"Network & WiFi","sys.shell.top.sensors":"Sensors","sys.shell.top.power":"Power Management","sys.shell.top.hmi":"HMI","sys.shell.top.config":"Config","sys.overview.breadcrumb.console":"Console","sys.overview.breadcrumb.module":"System Overview","sys.overview.title":"System Overview","sys.overview.subtitle":"Live system telemetry snapshot","sys.overview.metric.cpu":"CPU Usage","sys.overview.metric.mem":"Memory Usage","sys.overview.metric.temp":"Core Temperature","sys.overview.metric.wifi":"WiFi Link","sys.overview.metric.uptime":"Uptime","sys.overview.metric.load":"Load (1m)","sys.overview.metric.storage":"Storage","sys.overview.linkActive":"Link Active","sys.overview.tempState":"status: nominal","sys.overview.wifiSummary":"WiFi Summary","sys.overview.iface":"Interface","sys.overview.signal":"Signal","sys.overview.quality":"Quality","sys.overview.storageComingSoon":"Coming soon","sys.logs.title":"System Logs","sys.logs.kernel":"Kernel","sys.logs.liveToggle":"Live pull","sys.logs.refresh":"Refresh","sys.logs.liveOffHint":"Live pull is off (default)","sys.logs.loading":"Loading logs...","sys.logs.empty":"No logs","sys.placeholder.breadcrumb":"Console / Reserved Module","sys.placeholder.block":"PLANNED BLOCK","sys.placeholder.desc":"Shell layout is reserved and ready for real data integration.","sys.placeholder.status":"STATUS: RESERVED / Structured placeholder page.","sys.placeholder.sensors.title":"Sensors Console","sys.placeholder.sensors.desc":"Unified monitor for IMU, environment, power rails, and timeline sampling.","sys.placeholder.sensors.block1":"Realtime chart area","sys.placeholder.sensors.block2":"Device health cards","sys.placeholder.sensors.block3":"Threshold alerts","sys.sensors.title":"Sensors Console","sys.sensors.desc":"Visual compass (magnetometer) and gyro rate gauges; backed by dev debug APIs.","sys.sensors.mag.section":"Magnetometer (AK09911 / I²C)","sys.sensors.mag.note":"Defaults: bus 1, address 12 (0x0C with CAD to GND). No app-level GPIO mapping is required; enable I²C in firmware and load i2c-dev.","sys.sensors.mag.bus":"I²C bus","sys.sensors.mag.addr":"7-bit address (decimal, 12 = 0x0C)","sys.sensors.mag.i2cdetect":"Run i2cdetect","sys.sensors.mag.btnSelftest":"Run self-test","sys.sensors.mag.btnProbe":"Probe all buses","sys.sensors.mag.btnCalStart":"Start heading calibration","sys.sensors.mag.btnCalCommit":"Save & lock heading","sys.sensors.mag.btnCalReset":"Reset to auto mode","sys.sensors.mag.btnCalStatus":"Calibration status","sys.sensors.mag.running":"Running…","sys.sensors.mpu.section":"IMU (MPU-6050 / I²C)","sys.sensors.mpu.note":"Defaults: bus 1, address 104 (0x68 with AD0 low). Includes gyro angular-rate sampling (MPU integrates accel + gyro).","sys.sensors.mpu.addr":"7-bit address (decimal, 104 = 0x68)","sys.sensors.mpu.btnSelftest":"Run MPU self-test","sys.sensors.mpu.running":"Running…","sys.sensors.gyro.title":"Gyroscope (MPU-6050)","sys.sensors.gyro.subtitle":"Uses the same bus/address as above; one IMU read: 3-axis gyro, accel-derived roll/pitch, and a 3D board hint.","sys.sensors.gyro.btn":"Read angular rate","sys.sensors.gyro.loading":"Sampling…","sys.sensors.gyro.dps":"Angular rate","sys.sensors.gyro.raw":"Raw","sys.sensors.gyro.unitDps":"°/s, ±250 °/s range, default sensitivity","sys.sensors.gyro.unitRaw":"signed 16-bit","sys.sensors.gyro.hint":"Click “Read angular rate” to sample accel (0x3B) and gyro (0x43). Roll/pitch use gravity; ωz is yaw rate, not absolute heading.","sys.sensors.gyro.errUnknown":"Sample failed","sys.sensors.gyro.live":"Live update (~2 Hz)","sys.sensors.gyro.barsTitle":"Angular rate bars (±250 °/s full scale)","sys.sensors.gyro.rawBlock":"Raw register values (16-bit)","sys.sensors.gyro.att3dTitle":"3D attitude hint","sys.sensors.gyro.att3dDesc":"Roll/pitch from accelerometer (gravity); ωz is yaw rate (not magnetic heading). Body XYZ triad is fixed to the PCB and rotates in 3D.","sys.sensors.gyro.axisXLabel":"+X","sys.sensors.gyro.axisYLabel":"+Y","sys.sensors.gyro.axisZLabel":"+Z","sys.sensors.gyro.att3dBodyAxes":"Body frame on the PCB: +X amber, +Y sky, +Z violet (normal); short dashes mark negative directions; the whole frame follows roll/pitch.","sys.sensors.gyro.att3dRefTitle":"Orthogonal axes (fixed view)","sys.sensors.gyro.att3dRefDesc":"Same colors as left; use this to read XYZ directions at a glance.","sys.sensors.gyro.roll":"Roll","sys.sensors.gyro.pitch":"Pitch","sys.sensors.gyro.yawRate":"Yaw rate","sys.sensors.compass.tapeCaption":"Heading tape · center = current readout","sys.sensors.compass.title":"Magnetometer · North compass","sys.sensors.compass.desc":"Horizontal-plane heading for “roughly north” debugging; precise alignment needs calibration and a fixed mount definition.","sys.sensors.compass.btn":"Refresh compass","sys.sensors.compass.loading":"Measuring…","sys.sensors.compass.live":"Live update","sys.sensors.compass.headingLabel":"Horizontal heading","sys.sensors.compass.headingHint":"From atan2(Hx, Hy), degrees","sys.sensors.compass.cardN":"N","sys.sensors.compass.cardE":"E","sys.sensors.compass.cardS":"S","sys.sensors.compass.cardW":"W","sys.sensors.compass.err":"Sample failed","sys.sensors.compass.footnote":"The red tip tracks the horizontal XY field direction; it should move as you rotate the board. Use soft/hard-iron calibration for tight north lock.","sys.sensors.jsonToggle":"Raw JSON (troubleshooting)","sys.hmi.title":"HMI · SPI display","sys.hmi.desc":"ST7796 via hardware plane (default 320×320). Requires OGSCOPE_DISPLAY_ENABLED=true and SPI enabled (/dev/spidev0.0).","sys.hmi.status.section":"Display & hardware plane","sys.hmi.status.refresh":"Refresh","sys.hmi.status.displayEnabled":"DISPLAY_ENABLED","sys.hmi.status.spidev":"/dev/spidev0.0","sys.hmi.status.yes":"present","sys.hmi.status.no":"missing","sys.hmi.status.resolution":"Resolution / DC","sys.hmi.status.driver":"Driver handle","sys.hmi.status.open":"open","sys.hmi.status.closed":"closed","sys.hmi.status.screenOutput":"Logical output","sys.hmi.status.on":"on","sys.hmi.status.off":"off","sys.hmi.status.lastPattern":"Last pattern","sys.hmi.status.lastError":"Last error","sys.hmi.actions.section":"Output to panel","sys.hmi.actions.hint":"Uses hardware plane device.command → hmi; large frames use a longer RPC timeout.","sys.hmi.actions.smoke":"Test pattern (text + frame)","sys.hmi.actions.colorbars":"Color bars","sys.hmi.actions.fill":"Fill RGB","sys.hmi.actions.screenOn":"Enable panel output","sys.hmi.actions.screenOff":"Pause panel output","sys.hmi.actions.release":"Release driver (close SPI/GPIO)","sys.hmi.rawJson":"Raw JSON response","sys.placeholder.hmi.title":"HMI Console","sys.placeholder.hmi.desc":"Debug workspace for SPI display, key matrix, backlight, and UI replay.","sys.placeholder.hmi.block1":"Display preview","sys.placeholder.hmi.block2":"Input event stream","sys.placeholder.hmi.block3":"Mode controls","sys.placeholder.power.title":"Power Console","sys.placeholder.power.desc":"Debug workspace for battery, charging, power profile, and thermal policy.","sys.placeholder.power.block1":"Power dashboard","sys.placeholder.power.block2":"Power policy table","sys.placeholder.power.block3":"Recovery actions","cam.title":"Camera Debug Console","cam.subtitle":"Migrated to unified admin UI with key optimizations preserved","cam.btn.system":"System Admin","cam.btn.analysis":"Analysis Console","cam.btn.home":"Home","cam.btn.start":"Start Preview","cam.btn.starting":"Starting...","cam.btn.stop":"Stop Preview","cam.btn.stopping":"Stopping...","cam.btn.capture":"Capture","cam.btn.capturing":"Capturing...","cam.btn.recordStart":"Start Recording","cam.btn.recordStop":"Stop Recording","cam.btn.recordBusy":"Switching recording...","cam.btn.refresh":"Refresh Status","cam.preview.title":"Live Preview","cam.preview.state":"State","cam.preview.mode":"Exposure Mode","cam.preview.emptyTitle":"Preview is not started","cam.preview.emptyDesc":"Start the camera preview stream with the button below.","cam.state.streaming":"streaming","cam.state.idle":"idle","cam.state.rec":"REC","cam.stats.requestFps":"Request FPS","cam.hint.mjpegSingleStream":"MJPEG concurrent streams are limited (default ~2). Beyond the cap, extra tabs may contend or get busy—reduce simultaneous live previews.","cam.stats.frameFps":"Display FPS","cam.stats.fpsMeasureNote":"Pixel deltas on a 32×32 sample over ~1s. Multiple previews, slow links, or a stream cap below the sensor target often read much lower.","cam.err.streamBusy":"Preview unavailable: MJPEG slot is already in use","cam.err.streamBusyHint":"Close the live preview in other tabs or in the Analysis console, then try again.","cam.err.streamProbeFailed":"Could not open the preview stream","cam.err.streamProbeDetail":"Check network and camera service, or retry in a moment.","cam.stats.targetFps":"Camera capture target FPS","cam.stats.streamPacingFps":"Shared stream / MJPEG cap FPS","cam.stats.streamPacingHint":"Matches OGSCOPE_SHARED_PREVIEW_FPS: shared grabber pacing and per-stream MJPEG min frame spacing.","cam.stats.captureFps":"Actual capture FPS","cam.stats.encodeMs":"Average JPEG encode","cam.stats.encoder":"Preview encoder","cam.stats.driver":"Camera driver","cam.stats.metadata":"Metadata","cam.stats.consumers":"Preview/analysis/record consumers","cam.stats.cameraMemory":"Process memory/swap","cam.stats.longExposureThrottle":"Auto exposure is about {exposure} ms, so long exposure is limiting FPS; exposure is not shortened to preserve analysis quality.","cam.stats.uptime":"Stream Uptime","cam.controls.title":"Controls","cam.controls.core":"Core Settings","cam.controls.fps":"FPS","cam.controls.sensorFps":"Sensor target FPS","cam.controls.previewFps":"Preview target FPS","cam.controls.resolution":"Resolution","cam.controls.applyRes":"Apply resolution","cam.controls.sampling":"Sampling mode","cam.controls.exposure":"Exposure","cam.controls.gain":"Analog Gain","cam.controls.digitalGain":"Digital Gain","cam.controls.noiseReduction":"Noise Reduction","cam.controls.noiseReductionMode":"Noise Reduction","cam.controls.digitalGainReadOnly":"Digital gain is read-only on this driver","cam.controls.aeFlicker":"AE Flicker","cam.controls.maxAeFrame":"Max AE frame (us)","cam.controls.contrast":"Contrast","cam.controls.brightness":"Brightness","cam.controls.saturation":"Saturation","cam.controls.sharpness":"Sharpness","cam.controls.applySettings":"Apply All Settings","cam.controls.applyAll":"Save Parameters","cam.controls.applyRuntime":"Save Runtime Settings","cam.controls.mode":"Mode Controls","cam.controls.autoExposure":"Exposure Mode","cam.controls.whiteBalance":"White Balance","cam.controls.colorMode":"Color Mode","cam.controls.auto":"Auto","cam.controls.manual":"Manual","cam.controls.night":"Night","cam.controls.off":"Off","cam.controls.nr.off":"Off","cam.controls.nr.fast":"Fast","cam.controls.nr.high_quality":"High quality","cam.controls.wb.auto":"Auto","cam.controls.wb.daylight":"Daylight","cam.controls.wb.cloudy":"Cloudy","cam.controls.wb.tungsten":"Tungsten","cam.controls.wb.fluorescent":"Fluorescent","cam.controls.wb.indoor":"Indoor","cam.controls.wb.manual":"Manual","cam.controls.wb.night":"Night","cam.controls.color":"Color","cam.controls.mono":"Mono","cam.controls.applyAe":"Apply Exposure Mode","cam.controls.applyWb":"Apply White Balance","cam.controls.applyColor":"Apply Color Mode","cam.controls.applyMode":"Apply Mode Settings","cam.controls.lockedByAe":"Exposure and gain are locked while auto exposure is enabled.","cam.controls.lockedByWb":"R/B gains are locked unless white balance is in manual mode.","cam.controls.pendingChanges":"You have unapplied parameter changes","cam.controls.tools":"Tools","cam.controls.nightPreset":"Night Preset","cam.controls.nightOn":"Enable Night Mode","cam.controls.nightOff":"Disable Night Mode","cam.controls.backup":"Backup Settings","cam.controls.restore":"Restore Settings","cam.controls.reset":"Reset Camera","cam.quick.title":"Quick Presets & Smart Tuning","cam.quick.daylight":"Daylight","cam.quick.night":"Night","cam.quick.nightPreset":"Apply Night Preset","cam.quick.deep-sky":"Deep Sky","cam.quick.planetary":"Planetary","cam.quick.autoAdjust":"Auto Adjust","cam.quick.nightHint":"Night preset is one-click tuning; night mode toggle is runtime on/off control.","cam.system.title":"System Monitor","cam.system.status":"Camera Ready","cam.system.stream":"Stream","cam.system.sensor":"Sensor","cam.system.quality":"Image Quality","cam.presets.title":"Preset Management","cam.presets.name":"Preset Name","cam.presets.desc":"Preset Description","cam.presets.save":"Save Preset","cam.presets.empty":"No presets","cam.presets.noDesc":"No description","cam.presets.apply":"Apply","cam.presets.delete":"Delete","cam.files.title":"File Management","cam.files.refresh":"Refresh File List","cam.files.empty":"No files","cam.files.download":"Download","cam.files.info":"Details","cam.files.delete":"Delete","cam.files.loadingInfo":"Loading file details...","cam.files.prev":"Prev","cam.files.next":"Next","cam.files.page":"Page {current}/{total}","cam.files.closeDetail":"Close details","cam.files.size":"Size","cam.files.type":"Type","cam.files.modified":"Modified","cam.hist.title":"Histogram","cam.hist.enabled":"Enable histogram","cam.hist.luminance":"Luminance","cam.hist.over":"Over-exposure","cam.hist.expand":"Expand Histogram","cam.hist.collapse":"Collapse Histogram","cam.notice.previewStart":"Preview started","cam.notice.previewStop":"Preview stopped","cam.notice.captureSaved":"Capture saved: {name}","cam.notice.recordStart":"Recording started: {name}","cam.notice.recordStop":"Recording stopped","cam.notice.fpsApplied":"FPS updated to {fps}","cam.notice.resApplied":"Resolution updated to {w}x{h}","cam.notice.samplingApplied":"Sampling mode updated: {mode}","cam.notice.settingsApplied":"Settings applied","cam.notice.aeApplied":"Exposure mode applied","cam.notice.wbApplied":"White balance applied","cam.notice.colorApplied":"Color mode applied","cam.notice.modeApplied":"Mode settings applied","cam.notice.runtimeApplied":"Runtime settings applied","cam.notice.rotationApplied":"Rotation set to {value}°","cam.mirror.hint":"Use with rotation when the camera is mounted inverted; preview, plate solve, and polar guide share one coordinate frame.","cam.mirror.horizontal":"Horizontal mirror","cam.mirror.vertical":"Vertical mirror","cam.notice.mirrorApplied":"Mirror settings updated","cam.notice.nightOn":"Night mode enabled","cam.notice.nightOff":"Night mode disabled","cam.notice.nightPreset":"Night preset applied","cam.notice.reset":"Camera settings reset","cam.notice.backup":"Settings backed up","cam.notice.restore":"Settings restored","cam.notice.presetSaved":"Preset saved: {name}","cam.notice.presetApplied":"Preset applied: {name}","cam.notice.presetDeleted":"Preset deleted: {name}","cam.notice.download":"Download started: {name}","cam.notice.downloadWithSidecar":"Downloaded: {name} (with sidecar {sidecar})","cam.notice.fileDeleted":"File deleted: {name}","cam.notice.quickPresetApplied":"Quick preset applied: {preset}","cam.notice.needPreview":"Start preview first","cam.notice.needManualForAutoAdjust":"Switch to manual exposure before auto-adjust","cam.notice.autoAdjusted":"Auto-adjust completed and applied","cam.confirm.deletePreset":"Delete preset {name}?","cam.confirm.deleteFile":"Delete file {name}?","sys.sensors.mag.calStatusPrefix":"Calibration mode:","sys.sensors.mag.calModeAuto":"Auto (not locked)","sys.sensors.mag.calModeRecording":"Recording (rotate device)","sys.sensors.mag.calModeLocked":"Locked (saved heading)","sys.sensors.mag.calSamplesPrefix":"Recorded samples:","sys.sensors.mag.calRecordingHint":"Tip: keep level and rotate slowly; save after 10+ samples.","sys.sensors.mag.calLockedHint":"Locked heading params:"},C={"app.title":"OGScope 星空解算控制台","nav.lab":"解算台","nav.labImage":"图片解算","nav.labVideo":"视频解算","nav.pool":"素材池","nav.history":"实验记录","nav.cameraDebug":"相机调试控制台","nav.home":"首页","nav.systemAdmin":"返回系统后台","lang.zh":"中文","lang.en":"EN","sidebar.assets":"自行上传素材","sidebar.upload":"上传文件","sidebar.refresh":"刷新列表","sidebar.debugCaptures":"调试控制台素材","sidebar.assetTypeImage":"图片","sidebar.assetTypeVideo":"视频","sidebar.debugEmpty":"暂无调试文件","sidebar.importToPool":"导入到素材池","sidebar.importToPoolWithTranscode":"转码并导入素材池","sidebar.importToPoolDirect":"直接导入素材池","sidebar.flowPreparing":"准备中…","sidebar.flowImportingDebug":"正在导入调试素材…","sidebar.flowDownloadingPool":"正在下载已导入文件…","sidebar.flowWritingBuffer":"正在写入转码缓冲区…","sidebar.flowLoadingTranscoder":"正在加载转码器…","sidebar.flowTranscoding":"正在转码 AVI -> MP4…","sidebar.flowPackaging":"正在封装转码结果…","sidebar.flowUploadingMp4":"正在上传 MP4…","sidebar.flowReplacing":"正在替换并清理原 AVI…","sidebar.flowNoTranscode":"该文件无需转码,已直接导入。","sidebar.flowDone":"流程完成","sidebar.flowDoneMsg":"已完成:{name}","sidebar.flowFailed":"流程失败","sidebar.debugPage":"第 {cur} / {total} 页","sidebar.batchPresets":"批量预设","sidebar.batchHint":"勾选后点击「批量解算」可一次用多组参数对比结果。","lab.selectOrUpload":"从左侧选择自行上传或已导入的素材","lab.selectOrUploadVideo":"从左侧选择视频素材预览;或使用上方按钮解算设备相机实时帧。","lab.file":"文件","lab.source":"来源","lab.layers":"叠加层","lab.layer.matched":"匹配星","lab.layer.pattern":"图案星","lab.layer.all":"全部质心","lab.layer.rejected":"已剔除(红叉)","lab.previewConfidence":"置信度","lab.grid":"网格","lab.zoomIn":"放大","lab.zoomOut":"缩小","lab.zoomReset":"复位","lab.resolution":"分辨率","lab.fwhm":"FWHM","lab.starsDetected":"检测星点","lab.meta.title":"拍摄与文件信息","lab.meta.noSidecar":"无侧车信息(非调试采集或仅本地上传)","lab.meta.partial":"暂无侧车详细字段,仅显示文件信息。","lab.solveSection":"解算","lab.imageSection":"图像","lab.metric.solveMs":"用时","lab.metric.solveComputeMs":"解算计算用时","lab.metric.solveComputeHelp":"服务端 Tetra3 与提星等纯计算耗时(与网络无关)。","lab.metric.solveRoundTripMs":"全链路用时","lab.metric.solveRoundTripHelp":"从本页发起请求到收到结果并完成界面刷新的总耗时,含网络往返与浏览器渲染。","lab.metric.backendTotalMs":"后端总用时","lab.metric.openDecodeMs":"读取/解码","lab.metric.preprocessMs":"预处理","lab.metric.extractMs":"提星","lab.metric.solveOnlyMs":"匹配解算","lab.metric.probHelp":"Tetra3 的 Prob 为假阳性概率(越低越可信)。一般使用 (1−Prob)×100%;当 Prob 极小时浮点数会舍入为 100%,此时用 −log₁₀(Prob) 映射到约 70–100% 以便区分。","lab.metric.probRawHelp":"Tetra3 返回的原始 Prob 字段,可能为对数似然等内部量;与上一行换算后的百分比对照查看即可。","lab.metric.radec":"RA / Dec","lab.metric.matches":"匹配","lab.metric.rmse":"RMSE","lab.metric.prob":"置信","lab.metric.status":"状态","lab.gateNextMs":"约 {ms} ms 后可重试","lab.gate.skipRecording":"录制中:相机正写入录像,暂无法从相机取帧解算;请先停止录制后再试。","lab.gate.skipBusyInFlight":"上一帧解算仍在处理中,请稍候。若频繁出现,可适当增大「实时解算间隔」。","lab.gate.skipInterval":"解算请求过于频繁,已按后端节流排队。","lab.gate.skipBusyFallback":"实时解算暂时无法开始({detail})。","lab.centroidQualityTitle":"质心质量","lab.centroidQualityMetrics":"输入 {in} → 保留 {out};剔除过密 {dense},共线 {line}","meta.exposure":"曝光","meta.gain":"增益","meta.fps":"帧率","meta.sensor":"传感器","meta.colorMode":"色彩","meta.outputResolution":"输出分辨率","meta.fileTime":"文件时间","meta.fileSize":"文件大小","results.viewRaw":"原始 JSON","results.hideRaw":"收起","results.solveHistoryTitle":"解算历史","results.historySingle":"单帧","results.historyBatch":"批量 {count} 组","params.title":"解算参数","params.blockSolveIntro":"以下为板块求解(Tetra3)搜索天区、超时与粗略指向提示;FOV 需与镜头视场大致一致。","params.centroid":"提星","params.blockCentroidIntro":"以下为星点检测:阈值、连通域面积与局部背景窗口,用于从图像中提取星点质心。","params.fov":"FOV 估计 (°)","params.fovHelp":"水平视场角估计值,用于 lost-in-space 解算。","params.fovErr":"FOV 允许误差 (°)","params.fovErrHelp":"允许 Tetra3 在估计 FOV 附近的搜索范围。","params.timeout":"超时 (ms)","params.timeoutHelp":"单次解算最长等待时间。","params.solveIntervalMs":"实时解算间隔 (ms)","params.solveIntervalMsHelp":"期望间隔可调整,但会被后端限制在安全范围内。","params.solveIntervalIndependent":"解算间隔独立于相机采集和预览帧率。","params.solveIntervalBound":"后端限制范围:{min}-{max} ms,当前生效:{effective} ms","params.centroidRejectionLevel":"质心剔除强度 (1–5)","params.centroidRejectionLevelHelp":"1 最保守保留更多星点,5 更激进剔除过密与共线假星;默认 3。","params.centroidRejectionScale":"保守 ← → 激进","params.solveProfile":"解算档位","params.solveProfileHelp":"速度/平衡/稳健三档会同时调整超时、提星阈值与参与匹配星点数。","params.solveProfileSpeed":"速度优先","params.solveProfileBalanced":"平衡","params.solveProfileRobust":"稳健优先","params.ra":"RA 提示 (°)","params.raHelp":"大致天球赤经,缩小搜索范围(度)。","params.dec":"Dec 提示 (°)","params.decHelp":"大致天球赤纬(度)。","params.maxSide":"提星前长边上界 (px)","params.maxSideHelp":"降采样长边上限,大图可加速提星。","params.detailLevelFull":"包含完整 Tetra3 原始结果(体积略大,仅调试时开启)","params.largeScaleBg":"大尺度背景减除","params.largeScaleBgHelp":"在提星前用低分辨率平滑估计并校正大尺度亮度不均,可减轻角部光晕导致的假星;默认关闭以保持与过往行为一致。","params.sigma":"σ(阈值倍数)","params.sigmaHelp":"高于背景噪声倍数的区域视为星点候选。","params.maxArea":"max_area","params.maxAreaHelp":"连通域最大像素面积。","params.minArea":"min_area","params.minAreaHelp":"连通域最小像素面积。","params.filtsize":"filtsize(奇数)","params.filtsizeHelp":"局部背景滤波窗口边长,须为奇数。","btn.solveOne":"单张解算","btn.solveBatch":"批量解算(勾选预设)","btn.applyPresets":"应用预设到表单","btn.savePreset":"保存","placeholder.newPreset":"新预设名称","pool.title":"服务器素材池","pool.col.name":"文件名","pool.col.source":"来源","pool.col.size":"大小","pool.col.time":"修改时间","pool.delete":"删除","history.title":"实验记录","history.intro":"此处展示你在解算台完成解算后手动保存的快照。用法:在「解算台」主栏「结果对比」中,单张解算后点「保存当前到实验记录」,或批量解算后在某张结果卡片上点「保存记录」。本页可按文件名或预设名搜索,支持导出 JSON/CSV 备份。","history.search":"搜索…","history.searchBtn":"搜索","history.exportJson":"导出 JSON","history.exportCsv":"导出 CSV","history.total":"共 {n} 条","history.preset":"预设","history.metrics":"指标","history.detail":"详情","history.collapse":"收起","history.prev":"上一页","history.next":"下一页","history.delete":"删除","delete.uploadFirst":"确定要从素材池删除「{name}」吗?","delete.uploadSecond":"此操作不可恢复,再次确认删除?","delete.experimentFirst":"确定要删除这条实验记录吗?","delete.experimentSecond":"此操作不可恢复,再次确认删除?","results.title":"结果对比","results.saveCurrent":"保存当前到实验记录","results.saveRow":"保存记录","results.expand":"展开","results.collapseJson":"收起","err.selectFile":"请选择素材","err.selectPresets":"请勾选至少一个预设","common.placeholder":"—","delete.uploadCascade":"该素材有 {n} 条实验记录,是否一并删除?","lab.solveCurrentFrame":"解算当前帧(文件)","lab.solveFileStart":"开始文件连续解算","lab.solveFileStop":"停止文件连续解算","lab.cameraPreviewLoading":"正在连接共享预览…","lab.solveCameraFrame":"解算相机当前帧","lab.solveCameraStart":"开始相机解算","lab.solveCameraStop":"停止相机解算","lab.videoPreviewFailed":"该视频格式可能不受浏览器支持,建议使用 MP4(H.264) 或 WebM。","lab.previewModeFile":"素材文件","lab.previewModeCamera":"设备相机","lab.stopCameraPreview":"停止预览","lab.videoLiveIntro":"与调试控制台共用同一相机;此处可预览并解算实时帧,无需单独打开调试页。两页可同时使用。","lab.videoMjpegHint":"MJPEG 同时连接数已满:请关闭其它页面的实时预览或稍后重试(默认允许约 4 路,可在 systemd 调整 OGSCOPE_STREAM_MAX_MJPEG_CLIENTS)。","lab.cameraStreamStatusFail":"无法读取视频流名额状态,请稍后重试。","lab.cameraSnapshotName":"ogscope_camera_live","lab.metric.probRaw":"原始 Prob","lab.systemLoad":"系统负载","results.saveBatchAll":"保存全部到实验记录","lab.transcode.title":"AVI 文件需先转码","lab.transcode.desc":"当前视频为 AVI。为保证浏览器可预览与连续解算,请先在本地转码为 MP4 并上传替换。上传成功后将自动删除服务器上的原 AVI。","lab.transcode.button":"转码并上传替换","lab.transcode.loading":"正在加载转码器…","lab.transcode.writingBuffer":"正在写入转码缓冲区…","lab.transcode.running":"正在转码 AVI -> MP4…","lab.transcode.packaging":"正在封装转码结果…","lab.transcode.uploading":"正在上传转码结果…","lab.transcode.done":"转码并替换完成,可继续预览与解算。","lab.transcode.failed":"转码或上传失败,请重试。","lab.transcode.fetchFailed":"无法读取服务器上的 AVI 文件。","sys.shell.subtitle":"系统调试控制台","sys.shell.nav.overview":"总览","sys.shell.nav.network":"网络","sys.shell.nav.camera":"相机调试","sys.shell.nav.analysis":"寻星控制台","sys.shell.nav.sensors":"传感器","sys.shell.nav.power":"电源","sys.shell.nav.hmi":"人机交互","sys.shell.nav.config":"配置管理","sys.shell.workbench":"WORKBENCH / 系统工作台","sys.shell.node":"节点","sys.shell.top.overview":"系统状态","sys.shell.top.network":"网络与 WiFi","sys.shell.top.sensors":"传感器","sys.shell.top.power":"电源管理","sys.shell.top.hmi":"人机交互","sys.shell.top.config":"配置管理","sys.overview.breadcrumb.console":"控制台","sys.overview.breadcrumb.module":"系统总览","sys.overview.title":"系统总览","sys.overview.subtitle":"实时系统运行健康状况","sys.overview.metric.cpu":"CPU 使用率","sys.overview.metric.mem":"内存占用","sys.overview.metric.temp":"核心温度","sys.overview.metric.wifi":"WiFi 链路","sys.overview.metric.uptime":"运行时长","sys.overview.metric.load":"1 分钟负载","sys.overview.metric.storage":"存储","sys.overview.linkActive":"链路在线","sys.overview.tempState":"状态: 正常","sys.overview.wifiSummary":"无线网络摘要","sys.overview.iface":"接口","sys.overview.signal":"信号","sys.overview.quality":"质量","sys.overview.storageComingSoon":"即将接入","sys.logs.title":"系统日志","sys.logs.kernel":"内核","sys.logs.liveToggle":"实时拉取","sys.logs.refresh":"刷新","sys.logs.liveOffHint":"实时拉取关闭(默认)","sys.logs.loading":"日志加载中...","sys.logs.empty":"暂无日志","sys.placeholder.breadcrumb":"控制台 / 预留模块","sys.placeholder.block":"预留模块","sys.placeholder.desc":"模块壳层已预留,后续将按统一组件体系接入真实数据。","sys.placeholder.status":"STATUS: RESERVED / 当前为结构化占位页面。","sys.placeholder.sensors.title":"传感器诊断台","sys.placeholder.sensors.desc":"用于 IMU、温湿度、电流电压、姿态与时序采样的统一监控面板。","sys.placeholder.sensors.block1":"实时曲线区","sys.placeholder.sensors.block2":"设备健康卡","sys.placeholder.sensors.block3":"阈值告警区","sys.sensors.title":"传感器诊断台","sys.sensors.desc":"磁力计罗盘指北与 MPU-6050 陀螺仪角速度可视化;底层仍为开发者调试 API。","sys.sensors.mag.section":"磁力计(AK09911 / I²C)","sys.sensors.mag.note":"默认总线 1、地址 12(0x0C,CAD 接 GND)。无需在应用层“定义 GPIO”;请确保固件已启用 I²C 且已加载 i2c-dev。","sys.sensors.mag.bus":"I²C 总线号","sys.sensors.mag.addr":"7-bit 地址(十进制,12=0x0C)","sys.sensors.mag.i2cdetect":"附带运行 i2cdetect","sys.sensors.mag.btnSelftest":"运行自检","sys.sensors.mag.btnProbe":"扫描各总线","sys.sensors.mag.btnCalStart":"开始方向校准","sys.sensors.mag.btnCalCommit":"保存并锁定方向","sys.sensors.mag.btnCalReset":"重置到自动模式","sys.sensors.mag.btnCalStatus":"查看校准状态","sys.sensors.mag.running":"执行中…","sys.sensors.mpu.section":"IMU(MPU-6050 / I²C)","sys.sensors.mpu.note":"默认总线 1、地址 104(0x68,AD0 接 GND)。含陀螺仪角速度采样(MPU 内集成加速度计与陀螺仪)。","sys.sensors.mpu.addr":"7-bit 地址(十进制,104=0x68)","sys.sensors.mpu.btnSelftest":"运行 MPU 自检","sys.sensors.mpu.running":"执行中…","sys.sensors.gyro.title":"陀螺仪(MPU-6050)","sys.sensors.gyro.subtitle":"使用上方同一总线/地址;一次读取加速度与陀螺仪,估算横滚/俯仰并显示 3D 板卡示意。","sys.sensors.gyro.btn":"读取角速度","sys.sensors.gyro.loading":"采样中…","sys.sensors.gyro.dps":"角速度","sys.sensors.gyro.raw":"原始值","sys.sensors.gyro.unitDps":"°/s,±250°/s 量程、默认灵敏度","sys.sensors.gyro.unitRaw":"16-bit 有符号","sys.sensors.gyro.hint":"点击「读取角速度」从 IMU 采样(加速度 0x3B + 陀螺仪 0x43)。横滚/俯仰来自重力;ωz 为绕竖直轴角速度,并非绝对航向。","sys.sensors.gyro.errUnknown":"采样失败","sys.sensors.gyro.live":"连续刷新(约 2 Hz)","sys.sensors.gyro.barsTitle":"角速度条(相对 ±250°/s 满偏)","sys.sensors.gyro.rawBlock":"寄存器原始值(16-bit)","sys.sensors.gyro.att3dTitle":"3D 姿态示意","sys.sensors.gyro.att3dDesc":"横滚/俯仰由加速度计重力估算;ωz 为陀螺仪绕竖直轴角速度(非磁航向)。机体系三轴与板卡固连,随姿态在三维空间中转动。","sys.sensors.gyro.axisXLabel":"+X","sys.sensors.gyro.axisYLabel":"+Y","sys.sensors.gyro.axisZLabel":"+Z","sys.sensors.gyro.att3dBodyAxes":"左侧为与电路板固连的机体系:琥珀 +X、天蓝 +Y、紫 +Z(法向);短划为负向参考,整体随横滚/俯仰旋转。","sys.sensors.gyro.att3dRefTitle":"正交轴参考(固定视角)","sys.sensors.gyro.att3dRefDesc":"与左侧同色对应,便于辨认三轴空间关系。","sys.sensors.gyro.roll":"横滚","sys.sensors.gyro.pitch":"俯仰","sys.sensors.gyro.yawRate":"偏航角速度","sys.sensors.compass.tapeCaption":"水平航向带 · 中心为当前读数","sys.sensors.compass.title":"磁力计 · 指北罗盘","sys.sensors.compass.desc":"根据水平面磁场分量估算方向角,用于调试「是否大致指向北方」;精对准需校准与固定安装姿态。","sys.sensors.compass.btn":"刷新罗盘","sys.sensors.compass.loading":"测量中…","sys.sensors.compass.live":"连续刷新","sys.sensors.compass.headingLabel":"水平航向角","sys.sensors.compass.headingHint":"由 atan2(Hx, Hy) 得到,单位度","sys.sensors.compass.cardN":"北","sys.sensors.compass.cardE":"东","sys.sensors.compass.cardS":"南","sys.sensors.compass.cardW":"西","sys.sensors.compass.err":"采样失败","sys.sensors.compass.footnote":"红色针尖表示 XY 平面内磁场水平分量方向;转动设备时指针应随之变化。精对准需软铁/硬铁校准。","sys.sensors.jsonToggle":"展开原始 JSON(排障用)","sys.hmi.title":"人机交互 · SPI 显示","sys.hmi.desc":"通过硬件平面驱动 ST7796(默认 320×320);需 OGSCOPE_DISPLAY_ENABLED=true 且系统已启用 SPI(/dev/spidev0.0)。","sys.hmi.status.section":"显示与硬件平面状态","sys.hmi.status.refresh":"刷新状态","sys.hmi.status.displayEnabled":"DISPLAY_ENABLED","sys.hmi.status.spidev":"/dev/spidev0.0","sys.hmi.status.yes":"存在","sys.hmi.status.no":"不存在","sys.hmi.status.resolution":"分辨率 / DC","sys.hmi.status.driver":"驱动句柄","sys.hmi.status.open":"已打开","sys.hmi.status.closed":"未打开","sys.hmi.status.screenOutput":"逻辑输出","sys.hmi.status.on":"开","sys.hmi.status.off":"关","sys.hmi.status.lastPattern":"上次图案","sys.hmi.status.lastError":"上次错误","sys.hmi.actions.section":"输出到屏幕","sys.hmi.actions.hint":"以下为硬件平面 device.command → hmi;大屏刷新使用较长 RPC 超时。","sys.hmi.actions.smoke":"测试画面(文字+边框)","sys.hmi.actions.colorbars":"彩条","sys.hmi.actions.fill":"填充 RGB","sys.hmi.actions.screenOn":"允许屏幕输出","sys.hmi.actions.screenOff":"暂停屏幕输出","sys.hmi.actions.release":"释放驱动(关闭 SPI/GPIO)","sys.hmi.rawJson":"原始 JSON 响应","sys.placeholder.hmi.title":"人机交互台","sys.placeholder.hmi.desc":"用于 SPI 显示、按键矩阵、背光/对比度与界面回放的调试工作区。","sys.placeholder.hmi.block1":"显示预览区","sys.placeholder.hmi.block2":"输入事件流","sys.placeholder.hmi.block3":"参数与模式控制","sys.placeholder.power.title":"电源与功耗台","sys.placeholder.power.desc":"用于电池、充电、瞬时功耗与热管理策略联动调试。","sys.placeholder.power.block1":"功耗看板","sys.placeholder.power.block2":"电源策略表","sys.placeholder.power.block3":"恢复与保护动作","cam.title":"相机调试控制台","cam.subtitle":"迁移到统一后台架构,保留关键性能优化","cam.btn.system":"系统后台","cam.btn.analysis":"寻星控制台","cam.btn.home":"首页","cam.btn.start":"启动预览","cam.btn.starting":"启动中...","cam.btn.stop":"停止预览","cam.btn.stopping":"停止中...","cam.btn.capture":"拍摄","cam.btn.capturing":"拍摄中...","cam.btn.recordStart":"开始录制","cam.btn.recordStop":"停止录制","cam.btn.recordBusy":"录制切换中...","cam.btn.refresh":"刷新状态","cam.preview.title":"实时预览","cam.preview.state":"状态","cam.preview.mode":"曝光模式","cam.preview.emptyTitle":"预览尚未启动","cam.preview.emptyDesc":"点击下方按钮启动相机预览流。","cam.state.streaming":"流已启动","cam.state.idle":"未启动","cam.state.rec":"录制中","cam.stats.requestFps":"请求 FPS","cam.hint.mjpegSingleStream":"MJPEG 同时连接数有限(默认约两路);超出上限时多标签页会争用或返回忙,请减少同时打开的实时预览。","cam.stats.frameFps":"画面 FPS","cam.stats.fpsMeasureNote":"统计最近约 1 秒内在 32×32 采样上的像素变化次数;多路同时预览、网络较慢、或下方「共享流上限」低于相机目标时,读数常明显偏低。","cam.err.streamBusy":"无法连接预览:MJPEG 名额已被占用","cam.err.streamBusyHint":"请关闭其它标签页或解算台中的设备实时预览,或稍后再试。","cam.err.streamProbeFailed":"无法建立预览流","cam.err.streamProbeDetail":"请检查网络与相机服务状态,或稍后重试。","cam.stats.targetFps":"相机采集目标 FPS","cam.stats.streamPacingFps":"共享流/MJPEG 上限 FPS","cam.stats.streamPacingHint":"与进程环境 OGSCOPE_SHARED_PREVIEW_FPS 一致,约束共享抓帧节奏与每路 MJPEG 的最小帧间隔。","cam.stats.captureFps":"实际采集 FPS","cam.stats.encodeMs":"JPEG 平均编码","cam.stats.encoder":"预览编码器","cam.stats.driver":"相机驱动","cam.stats.metadata":"元数据","cam.stats.consumers":"预览/分析/录像消费者","cam.stats.cameraMemory":"进程内存/Swap","cam.stats.longExposureThrottle":"自动曝光约 {exposure} ms,实际帧率受长曝光限制;为保证分析效果未强制缩短曝光。","cam.stats.uptime":"流运行时长","cam.controls.title":"参数控制","cam.controls.core":"核心参数","cam.controls.fps":"帧率","cam.controls.sensorFps":"传感器目标 FPS","cam.controls.previewFps":"预览目标 FPS","cam.controls.resolution":"分辨率","cam.controls.applyRes":"应用分辨率","cam.controls.sampling":"采样模式","cam.controls.exposure":"曝光","cam.controls.gain":"模拟增益","cam.controls.digitalGain":"数字增益","cam.controls.noiseReduction":"降噪级别","cam.controls.noiseReductionMode":"降噪模式","cam.controls.digitalGainReadOnly":"当前驱动仅上报数字增益,不支持手动设置","cam.controls.aeFlicker":"AE 防闪烁","cam.controls.maxAeFrame":"最长自动曝光帧周期(us)","cam.controls.contrast":"对比度","cam.controls.brightness":"亮度","cam.controls.saturation":"饱和度","cam.controls.sharpness":"锐度","cam.controls.applySettings":"应用全部设置","cam.controls.applyAll":"保存参数","cam.controls.applyRuntime":"保存运行参数","cam.controls.mode":"模式控制","cam.controls.autoExposure":"曝光模式","cam.controls.whiteBalance":"白平衡","cam.controls.colorMode":"颜色模式","cam.controls.auto":"自动","cam.controls.manual":"手动","cam.controls.night":"夜间","cam.controls.off":"关闭","cam.controls.nr.off":"关闭","cam.controls.nr.fast":"快速","cam.controls.nr.high_quality":"高质量","cam.controls.wb.auto":"自动","cam.controls.wb.daylight":"日光","cam.controls.wb.cloudy":"阴天","cam.controls.wb.tungsten":"钨丝灯","cam.controls.wb.fluorescent":"荧光灯","cam.controls.wb.indoor":"室内","cam.controls.wb.manual":"手动","cam.controls.wb.night":"夜间","cam.controls.color":"彩色","cam.controls.mono":"黑白","cam.controls.applyAe":"应用曝光模式","cam.controls.applyWb":"应用白平衡","cam.controls.applyColor":"应用颜色模式","cam.controls.applyMode":"应用模式设置","cam.controls.lockedByAe":"自动曝光开启时,曝光与增益参数锁定。","cam.controls.lockedByWb":"当前非手动白平衡模式,R/B 增益参数锁定。","cam.controls.pendingChanges":"有未应用的参数修改","cam.controls.tools":"工具","cam.controls.nightPreset":"夜间预设","cam.controls.nightOn":"开启夜间模式","cam.controls.nightOff":"关闭夜间模式","cam.controls.backup":"备份设置","cam.controls.restore":"恢复设置","cam.controls.reset":"重置相机","cam.quick.title":"快速预设与智能调参","cam.quick.daylight":"白天模式","cam.quick.night":"夜间模式","cam.quick.nightPreset":"应用夜间预设","cam.quick.deep-sky":"深空模式","cam.quick.planetary":"行星模式","cam.quick.autoAdjust":"智能调整","cam.quick.nightHint":"夜间预设用于一键切换夜拍参数;夜间模式开关用于实时启停红外/夜景模式。","cam.system.title":"系统监控","cam.system.status":"相机就绪","cam.system.stream":"流状态","cam.system.sensor":"传感器","cam.system.quality":"图像质量","cam.presets.title":"预设管理","cam.presets.name":"预设名称","cam.presets.desc":"预设描述","cam.presets.save":"保存预设","cam.presets.empty":"暂无预设","cam.presets.noDesc":"无描述","cam.presets.apply":"应用","cam.presets.delete":"删除","cam.files.title":"文件管理","cam.files.refresh":"刷新文件列表","cam.files.empty":"暂无文件","cam.files.download":"下载","cam.files.info":"详情","cam.files.delete":"删除","cam.files.loadingInfo":"正在读取文件详情...","cam.files.prev":"上一页","cam.files.next":"下一页","cam.files.page":"第 {current}/{total} 页","cam.files.closeDetail":"关闭详情","cam.files.size":"文件大小","cam.files.type":"文件类型","cam.files.modified":"修改时间","cam.hist.title":"直方图","cam.hist.enabled":"启用直方图","cam.hist.luminance":"亮度通道","cam.hist.over":"过曝警告","cam.hist.expand":"展开直方图","cam.hist.collapse":"收起直方图","cam.notice.previewStart":"预览已启动","cam.notice.previewStop":"预览已停止","cam.notice.captureSaved":"拍摄完成: {name}","cam.notice.recordStart":"开始录制: {name}","cam.notice.recordStop":"录制已停止","cam.notice.fpsApplied":"帧率已设置为 {fps}","cam.notice.resApplied":"分辨率已设置为 {w}x{h}","cam.notice.samplingApplied":"采样模式已设置为 {mode}","cam.notice.settingsApplied":"参数已应用","cam.notice.aeApplied":"曝光模式已应用","cam.notice.wbApplied":"白平衡已应用","cam.notice.colorApplied":"颜色模式已应用","cam.notice.modeApplied":"模式设置已应用","cam.notice.runtimeApplied":"运行参数已应用","cam.notice.rotationApplied":"旋转已设置为 {value}°","cam.mirror.hint":"相机倒装时可配合旋转使用;镜像后预览与星点解算、极轴引导同坐标系。","cam.mirror.horizontal":"水平镜像","cam.mirror.vertical":"垂直镜像","cam.notice.mirrorApplied":"镜像设置已更新","cam.notice.nightOn":"夜间模式已开启","cam.notice.nightOff":"夜间模式已关闭","cam.notice.nightPreset":"夜间预设已应用","cam.notice.reset":"相机设置已重置","cam.notice.backup":"设置已备份","cam.notice.restore":"设置已恢复","cam.notice.presetSaved":"预设已保存: {name}","cam.notice.presetApplied":"预设已应用: {name}","cam.notice.presetDeleted":"预设已删除: {name}","cam.notice.download":"已开始下载: {name}","cam.notice.downloadWithSidecar":"已下载: {name}(含侧车 {sidecar})","cam.notice.fileDeleted":"文件已删除: {name}","cam.notice.quickPresetApplied":"已应用快速预设: {preset}","cam.notice.needPreview":"请先启动预览","cam.notice.needManualForAutoAdjust":"请先切换为手动曝光再进行智能调整","cam.notice.autoAdjusted":"智能调整已完成并生效","cam.confirm.deletePreset":"确定删除预设 {name} 吗?","cam.confirm.deleteFile":"确定删除文件 {name} 吗?","sys.sensors.mag.calStatusPrefix":"校准状态:","sys.sensors.mag.calModeAuto":"自动模式(未锁定)","sys.sensors.mag.calModeRecording":"录制中(请旋转设备)","sys.sensors.mag.calModeLocked":"已锁定(使用保存方向)","sys.sensors.mag.calSamplesPrefix":"录制样本:","sys.sensors.mag.calRecordingHint":"操作提示:保持水平,缓慢旋转设备,建议累计 10+ 样本后保存。","sys.sensors.mag.calLockedHint":"已锁定方向参数:"},g=a.createContext(null),p={zh:C,en:k},b="ogscope.analysis.locale";function y(){const e=window.localStorage.getItem(b);return e==="zh"||e==="en"?e:(navigator.language||"zh").toLowerCase().startsWith("en")?"en":"zh"}function R({children:e}){const[s,t]=a.useState(y),[o,l]=a.useState(p[y()]);a.useEffect(()=>{l(p[s]),window.localStorage.setItem(b,s),document.documentElement.lang=s==="en"?"en":"zh-CN"},[s]);const r=a.useMemo(()=>(n,c)=>{let i=o[n]??n;if(c)for(const[d,h]of Object.entries(c))i=i.replace(new RegExp(`\\{${d}\\}`,"g"),String(h));return i},[o]),m=a.useMemo(()=>({locale:s,setLocale:t,t:r}),[s,r]);return v.jsx(g.Provider,{value:m,children:e})}function x(){const e=a.useContext(g);if(!e)throw new Error("useI18n must be used within I18nProvider");return e}export{R as I,A as T,P as c,x as u};
diff --git a/web/static/analysis-lab/assets/index-CutgeBjy.js b/web/static/analysis-lab/assets/index-CutgeBjy.js
deleted file mode 100644
index b90e05a..0000000
--- a/web/static/analysis-lab/assets/index-CutgeBjy.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import{r as a,j as v}from"./client-D1ZVDB-N.js";/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const f=e=>e.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase(),u=(...e)=>e.filter((s,t,o)=>!!s&&s.trim()!==""&&o.indexOf(s)===t).join(" ").trim();/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */var w={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const S=a.forwardRef(({color:e="currentColor",size:s=24,strokeWidth:t=2,absoluteStrokeWidth:o,className:l="",children:r,iconNode:m,...n},c)=>a.createElement("svg",{ref:c,...w,width:s,height:s,stroke:e,strokeWidth:o?Number(t)*24/Number(s):t,className:u("lucide",l),...n},[...m.map(([i,d])=>a.createElement(i,d)),...Array.isArray(r)?r:[r]]));/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const P=(e,s)=>{const t=a.forwardRef(({className:o,...l},r)=>a.createElement(S,{ref:r,iconNode:s,className:u(`lucide-${f(e)}`,o),...l}));return t.displayName=`${e}`,t};/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const R=P("Trash2",[["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6",key:"4alrt4"}],["path",{d:"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2",key:"v07s0e"}],["line",{x1:"10",x2:"10",y1:"11",y2:"17",key:"1uufr5"}],["line",{x1:"14",x2:"14",y1:"11",y2:"17",key:"xtxkd"}]]),k={"app.title":"OGScope Plate Solve Console","nav.lab":"Lab","nav.labImage":"Image solve","nav.labVideo":"Video solve","delete.uploadCascade":"Delete {n} linked experiment record(s) as well?","lab.solveCurrentFrame":"Solve current frame (file)","lab.solveFileStart":"Start continuous file solve","lab.solveFileStop":"Stop continuous file solve","lab.cameraPreviewLoading":"Connecting to shared preview…","lab.solveCameraFrame":"Solve live camera frame","lab.solveCameraStart":"Start camera solve","lab.solveCameraStop":"Stop camera solve","lab.videoPreviewFailed":"This video format may be unsupported by the browser. Try MP4 (H.264) or WebM.","lab.previewModeFile":"Pool file","lab.previewModeCamera":"Device camera","lab.videoLiveIntro":"Shares the same camera as Camera Debug — preview and solve live frames here without opening debug. Both pages can run together.","lab.videoMjpegHint":"MJPEG slots are full: close live previews on other pages or retry later (default ~4; tune OGSCOPE_STREAM_MAX_MJPEG_CLIENTS in systemd).","lab.cameraStreamStatusFail":"Could not read MJPEG slot status. Please retry shortly.","lab.cameraSnapshotName":"ogscope_camera_live","lab.metric.probRaw":"Raw Prob","lab.systemLoad":"System load","results.saveBatchAll":"Save all to records","nav.pool":"Assets","nav.history":"Records","nav.cameraDebug":"Camera Debug","nav.home":"Home","nav.systemAdmin":"Back to System Admin","lang.zh":"中文","lang.en":"EN","sidebar.assets":"Uploaded assets","sidebar.upload":"Upload","sidebar.refresh":"Refresh","sidebar.debugCaptures":"Debug console media","sidebar.assetTypeImage":"Image","sidebar.assetTypeVideo":"Video","sidebar.debugEmpty":"No debug files","sidebar.importToPool":"Import to pool","sidebar.importToPoolWithTranscode":"Transcode & Import","sidebar.importToPoolDirect":"Import Directly","sidebar.flowPreparing":"Preparing...","sidebar.flowImportingDebug":"Importing debug capture...","sidebar.flowDownloadingPool":"Downloading imported file...","sidebar.flowWritingBuffer":"Writing to transcoder buffer...","sidebar.flowLoadingTranscoder":"Loading transcoder...","sidebar.flowTranscoding":"Transcoding AVI -> MP4...","sidebar.flowPackaging":"Packaging output...","sidebar.flowUploadingMp4":"Uploading MP4...","sidebar.flowReplacing":"Replacing and removing original AVI...","sidebar.flowNoTranscode":"No transcode required, imported directly.","sidebar.flowDone":"Completed","sidebar.flowDoneMsg":"Done: {name}","sidebar.flowFailed":"Failed","sidebar.debugPage":"Page {cur} / {total}","sidebar.batchPresets":"Batch presets","sidebar.batchHint":"Check presets, then use Batch solve to compare multiple param sets.","lab.selectOrUpload":"Pick an uploaded or imported asset from the left","lab.selectOrUploadVideo":"Pick a pool video to preview, or use the button above for the live camera frame.","lab.file":"File","lab.source":"Source","lab.layers":"Layers","lab.layer.matched":"Matched","lab.layer.pattern":"Pattern","lab.layer.all":"All centroids","lab.layer.rejected":"Rejected (red cross)","lab.previewConfidence":"Confidence","lab.grid":"Grid","lab.zoomIn":"Zoom in","lab.zoomOut":"Zoom out","lab.zoomReset":"Reset","lab.resolution":"Resolution","lab.fwhm":"FWHM","lab.starsDetected":"Stars detected","lab.meta.title":"Capture & file info","lab.meta.noSidecar":"No sidecar (not from debug capture)","lab.meta.partial":"No detailed sidecar; file info only.","lab.solveSection":"Solve","lab.imageSection":"Image","lab.metric.solveMs":"Time","lab.metric.solveComputeMs":"Solve compute","lab.metric.solveComputeHelp":"Server-side Tetra3 + star extraction only (no network).","lab.metric.solveRoundTripMs":"End-to-end","lab.metric.solveRoundTripHelp":"From request start to UI updated: network + JSON + render.","lab.metric.backendTotalMs":"Backend total","lab.metric.openDecodeMs":"Open/decode","lab.metric.preprocessMs":"Preprocess","lab.metric.extractMs":"Extract","lab.metric.solveOnlyMs":"Solve match","lab.metric.probHelp":"Tetra3 `Prob` is false-positive probability (lower is better). We use (1−Prob)×100% when representable; for extremely small Prob, IEEE doubles round to 100%, so we map −log10(Prob) to ~70–100% for differentiation.","lab.metric.probRawHelp":"Raw Tetra3 Prob (e.g. log-likelihood); compare with the normalized line above.","lab.metric.radec":"RA / Dec","lab.metric.matches":"Matches","lab.metric.rmse":"RMSE","lab.metric.prob":"Prob.","lab.metric.status":"Status","lab.gateNextMs":"Retry in ~{ms} ms","lab.gate.skipRecording":"Recording is active: the camera is writing video, so live-frame solve is paused. Stop recording and try again.","lab.gate.skipBusyInFlight":"The previous solve is still running; please wait. If this repeats often, increase the realtime solve interval.","lab.gate.skipInterval":"Solve requests are arriving faster than allowed; the server is throttling. Wait for the countdown and retry.","lab.gate.skipBusyFallback":"Realtime solve could not start ({detail}).","lab.centroidQualityTitle":"Centroid quality","lab.centroidQualityMetrics":"In {in} → kept {out}; removed dense {dense}, collinear {line}","meta.exposure":"Exposure","meta.gain":"Gain","meta.fps":"FPS","meta.sensor":"Sensor","meta.colorMode":"Color","meta.outputResolution":"Output size","meta.fileTime":"File time","meta.fileSize":"File size","results.viewRaw":"Raw JSON","results.hideRaw":"Hide","results.solveHistoryTitle":"Solve history","results.historySingle":"Single frame","results.historyBatch":"Batch ({count} runs)","params.title":"Solve parameters","params.blockSolveIntro":"Plate-solve (Tetra3): FOV, timeout, and coarse sky hints. FOV should match your lens.","params.centroid":"Star detection","params.blockCentroidIntro":"Star detection: threshold, blob area, and local background window for centroids.","params.fov":"FOV estimate (°)","params.fovHelp":"Horizontal field of view for lost-in-space solve.","params.fovErr":"FOV max error (°)","params.fovErrHelp":"Search range around estimated FOV.","params.timeout":"Timeout (ms)","params.timeoutHelp":"Max wait time per solve.","params.solveIntervalMs":"Realtime solve interval (ms)","params.solveIntervalMsHelp":"Desired interval is adjustable, but backend clamps it into a safe range.","params.solveIntervalBound":"Backend bounds: {min}-{max} ms, effective now: {effective} ms","params.centroidRejectionLevel":"Centroid rejection (1–5)","params.centroidRejectionLevelHelp":"1 keeps more stars; 5 aggressively rejects dense clusters and collinear false detections; default 3.","params.centroidRejectionScale":"Conservative ← → Aggressive","params.solveProfile":"Solve profile","params.solveProfileHelp":"Speed/Balanced/Robust tune timeout, centroid thresholds, and matching star count together.","params.solveProfileSpeed":"Speed first","params.solveProfileBalanced":"Balanced","params.solveProfileRobust":"Robust first","params.ra":"RA hint (°)","params.raHelp":"Approximate right ascension in degrees.","params.dec":"Dec hint (°)","params.decHelp":"Approximate declination in degrees.","params.maxSide":"Max long side before extract (px)","params.maxSideHelp":"Downscale long edge for faster centroid extraction.","params.detailLevelFull":"Include full Tetra3 raw block (larger payload, for debugging only).","params.largeScaleBg":"Large-scale background flattening","params.largeScaleBgHelp":"Before centroiding, estimate a low-frequency background on a downscaled image and correct uneven illumination (e.g. corner glow). Off by default to match legacy behavior.","params.sigma":"σ threshold","params.sigmaHelp":"Multiplier over background noise for star candidates.","params.maxArea":"max_area","params.maxAreaHelp":"Max connected component area in pixels.","params.minArea":"min_area","params.minAreaHelp":"Min connected component area in pixels.","params.filtsize":"filtsize (odd)","params.filtsizeHelp":"Local filter window size, must be odd.","btn.solveOne":"Solve once","btn.solveBatch":"Batch solve (presets)","btn.applyPresets":"Apply preset to form","btn.savePreset":"Save","placeholder.newPreset":"New preset name","pool.title":"Server asset pool","pool.col.name":"Filename","pool.col.source":"Source","pool.col.size":"Size","pool.col.time":"Modified","pool.delete":"Delete","history.title":"Experiment records","history.intro":"Saved solve snapshots from the Lab. After a solve, use Save to records in the Lab main panel (Result comparison), or Save on each batch result card. Search by filename or preset; export JSON/CSV for backup.","history.search":"Search…","history.searchBtn":"Search","history.exportJson":"Export JSON","history.exportCsv":"Export CSV","history.total":"Total {n}","history.preset":"Preset","history.metrics":"Metrics","history.detail":"Details","history.collapse":"Collapse","history.prev":"Prev","history.next":"Next","history.delete":"Delete","delete.uploadFirst":'Delete "{name}" from the asset pool?',"delete.uploadSecond":"This cannot be undone. Confirm again?","delete.experimentFirst":"Delete this experiment record?","delete.experimentSecond":"This cannot be undone. Confirm again?","results.title":"Results","results.saveCurrent":"Save to records","results.saveRow":"Save","results.expand":"Expand","results.collapseJson":"Collapse","err.selectFile":"Select a file","err.selectPresets":"Select at least one preset","common.placeholder":"—","lab.transcode.title":"AVI requires transcoding","lab.transcode.desc":"This video is AVI. For browser preview and continuous solving, transcode it locally to MP4 and upload replacement. The original AVI on server will be removed after success.","lab.transcode.button":"Transcode and Upload Replacement","lab.transcode.loading":"Loading transcoder...","lab.transcode.writingBuffer":"Writing to transcoder buffer...","lab.transcode.running":"Transcoding AVI -> MP4...","lab.transcode.packaging":"Packaging transcoded output...","lab.transcode.uploading":"Uploading transcoded output...","lab.transcode.done":"Transcode and replacement completed.","lab.transcode.failed":"Transcode or upload failed. Please retry.","lab.transcode.fetchFailed":"Failed to fetch AVI from server.","sys.shell.subtitle":"system debug console","sys.shell.nav.overview":"Overview","sys.shell.nav.network":"Network","sys.shell.nav.camera":"Camera Debug","sys.shell.nav.analysis":"Analysis Console","sys.shell.nav.sensors":"Sensors","sys.shell.nav.power":"Power","sys.shell.nav.hmi":"HMI","sys.shell.nav.config":"Config","sys.shell.workbench":"WORKBENCH / System workspace","sys.shell.node":"Node","sys.shell.top.overview":"System Status","sys.shell.top.network":"Network & WiFi","sys.shell.top.sensors":"Sensors","sys.shell.top.power":"Power Management","sys.shell.top.hmi":"HMI","sys.shell.top.config":"Config","sys.overview.breadcrumb.console":"Console","sys.overview.breadcrumb.module":"System Overview","sys.overview.title":"System Overview","sys.overview.subtitle":"Live system telemetry snapshot","sys.overview.metric.cpu":"CPU Usage","sys.overview.metric.mem":"Memory Usage","sys.overview.metric.temp":"Core Temperature","sys.overview.metric.wifi":"WiFi Link","sys.overview.metric.uptime":"Uptime","sys.overview.metric.load":"Load (1m)","sys.overview.metric.storage":"Storage","sys.overview.linkActive":"Link Active","sys.overview.tempState":"status: nominal","sys.overview.wifiSummary":"WiFi Summary","sys.overview.iface":"Interface","sys.overview.signal":"Signal","sys.overview.quality":"Quality","sys.overview.storageComingSoon":"Coming soon","sys.logs.title":"System Logs","sys.logs.kernel":"Kernel","sys.logs.liveToggle":"Live pull","sys.logs.refresh":"Refresh","sys.logs.liveOffHint":"Live pull is off (default)","sys.logs.loading":"Loading logs...","sys.logs.empty":"No logs","sys.placeholder.breadcrumb":"Console / Reserved Module","sys.placeholder.block":"PLANNED BLOCK","sys.placeholder.desc":"Shell layout is reserved and ready for real data integration.","sys.placeholder.status":"STATUS: RESERVED / Structured placeholder page.","sys.placeholder.sensors.title":"Sensors Console","sys.placeholder.sensors.desc":"Unified monitor for IMU, environment, power rails, and timeline sampling.","sys.placeholder.sensors.block1":"Realtime chart area","sys.placeholder.sensors.block2":"Device health cards","sys.placeholder.sensors.block3":"Threshold alerts","sys.sensors.title":"Sensors Console","sys.sensors.desc":"Visual compass (magnetometer) and gyro rate gauges; backed by dev debug APIs.","sys.sensors.mag.section":"Magnetometer (AK09911 / I²C)","sys.sensors.mag.note":"Defaults: bus 1, address 12 (0x0C with CAD to GND). No app-level GPIO mapping is required; enable I²C in firmware and load i2c-dev.","sys.sensors.mag.bus":"I²C bus","sys.sensors.mag.addr":"7-bit address (decimal, 12 = 0x0C)","sys.sensors.mag.i2cdetect":"Run i2cdetect","sys.sensors.mag.btnSelftest":"Run self-test","sys.sensors.mag.btnProbe":"Probe all buses","sys.sensors.mag.btnCalStart":"Start heading calibration","sys.sensors.mag.btnCalCommit":"Save & lock heading","sys.sensors.mag.btnCalReset":"Reset to auto mode","sys.sensors.mag.btnCalStatus":"Calibration status","sys.sensors.mag.running":"Running…","sys.sensors.mpu.section":"IMU (MPU-6050 / I²C)","sys.sensors.mpu.note":"Defaults: bus 1, address 104 (0x68 with AD0 low). Includes gyro angular-rate sampling (MPU integrates accel + gyro).","sys.sensors.mpu.addr":"7-bit address (decimal, 104 = 0x68)","sys.sensors.mpu.btnSelftest":"Run MPU self-test","sys.sensors.mpu.running":"Running…","sys.sensors.gyro.title":"Gyroscope (MPU-6050)","sys.sensors.gyro.subtitle":"Uses the same bus/address as above; one IMU read: 3-axis gyro, accel-derived roll/pitch, and a 3D board hint.","sys.sensors.gyro.btn":"Read angular rate","sys.sensors.gyro.loading":"Sampling…","sys.sensors.gyro.dps":"Angular rate","sys.sensors.gyro.raw":"Raw","sys.sensors.gyro.unitDps":"°/s, ±250 °/s range, default sensitivity","sys.sensors.gyro.unitRaw":"signed 16-bit","sys.sensors.gyro.hint":"Click “Read angular rate” to sample accel (0x3B) and gyro (0x43). Roll/pitch use gravity; ωz is yaw rate, not absolute heading.","sys.sensors.gyro.errUnknown":"Sample failed","sys.sensors.gyro.live":"Live update (~2 Hz)","sys.sensors.gyro.barsTitle":"Angular rate bars (±250 °/s full scale)","sys.sensors.gyro.rawBlock":"Raw register values (16-bit)","sys.sensors.gyro.att3dTitle":"3D attitude hint","sys.sensors.gyro.att3dDesc":"Roll/pitch from accelerometer (gravity); ωz is yaw rate (not magnetic heading). Body XYZ triad is fixed to the PCB and rotates in 3D.","sys.sensors.gyro.axisXLabel":"+X","sys.sensors.gyro.axisYLabel":"+Y","sys.sensors.gyro.axisZLabel":"+Z","sys.sensors.gyro.att3dBodyAxes":"Body frame on the PCB: +X amber, +Y sky, +Z violet (normal); short dashes mark negative directions; the whole frame follows roll/pitch.","sys.sensors.gyro.att3dRefTitle":"Orthogonal axes (fixed view)","sys.sensors.gyro.att3dRefDesc":"Same colors as left; use this to read XYZ directions at a glance.","sys.sensors.gyro.roll":"Roll","sys.sensors.gyro.pitch":"Pitch","sys.sensors.gyro.yawRate":"Yaw rate","sys.sensors.compass.tapeCaption":"Heading tape · center = current readout","sys.sensors.compass.title":"Magnetometer · North compass","sys.sensors.compass.desc":"Horizontal-plane heading for “roughly north” debugging; precise alignment needs calibration and a fixed mount definition.","sys.sensors.compass.btn":"Refresh compass","sys.sensors.compass.loading":"Measuring…","sys.sensors.compass.live":"Live update","sys.sensors.compass.headingLabel":"Horizontal heading","sys.sensors.compass.headingHint":"From atan2(Hx, Hy), degrees","sys.sensors.compass.cardN":"N","sys.sensors.compass.cardE":"E","sys.sensors.compass.cardS":"S","sys.sensors.compass.cardW":"W","sys.sensors.compass.err":"Sample failed","sys.sensors.compass.footnote":"The red tip tracks the horizontal XY field direction; it should move as you rotate the board. Use soft/hard-iron calibration for tight north lock.","sys.sensors.jsonToggle":"Raw JSON (troubleshooting)","sys.hmi.title":"HMI · SPI display","sys.hmi.desc":"ST7796 via hardware plane (default 320×320). Requires OGSCOPE_DISPLAY_ENABLED=true and SPI enabled (/dev/spidev0.0).","sys.hmi.status.section":"Display & hardware plane","sys.hmi.status.refresh":"Refresh","sys.hmi.status.displayEnabled":"DISPLAY_ENABLED","sys.hmi.status.spidev":"/dev/spidev0.0","sys.hmi.status.yes":"present","sys.hmi.status.no":"missing","sys.hmi.status.resolution":"Resolution / DC","sys.hmi.status.driver":"Driver handle","sys.hmi.status.open":"open","sys.hmi.status.closed":"closed","sys.hmi.status.screenOutput":"Logical output","sys.hmi.status.on":"on","sys.hmi.status.off":"off","sys.hmi.status.lastPattern":"Last pattern","sys.hmi.status.lastError":"Last error","sys.hmi.actions.section":"Output to panel","sys.hmi.actions.hint":"Uses hardware plane device.command → hmi; large frames use a longer RPC timeout.","sys.hmi.actions.smoke":"Test pattern (text + frame)","sys.hmi.actions.colorbars":"Color bars","sys.hmi.actions.fill":"Fill RGB","sys.hmi.actions.screenOn":"Enable panel output","sys.hmi.actions.screenOff":"Pause panel output","sys.hmi.actions.release":"Release driver (close SPI/GPIO)","sys.hmi.rawJson":"Raw JSON response","sys.placeholder.hmi.title":"HMI Console","sys.placeholder.hmi.desc":"Debug workspace for SPI display, key matrix, backlight, and UI replay.","sys.placeholder.hmi.block1":"Display preview","sys.placeholder.hmi.block2":"Input event stream","sys.placeholder.hmi.block3":"Mode controls","sys.placeholder.power.title":"Power Console","sys.placeholder.power.desc":"Debug workspace for battery, charging, power profile, and thermal policy.","sys.placeholder.power.block1":"Power dashboard","sys.placeholder.power.block2":"Power policy table","sys.placeholder.power.block3":"Recovery actions","cam.title":"Camera Debug Console","cam.subtitle":"Migrated to unified admin UI with key optimizations preserved","cam.btn.system":"System Admin","cam.btn.analysis":"Analysis Console","cam.btn.home":"Home","cam.btn.start":"Start Preview","cam.btn.starting":"Starting...","cam.btn.stop":"Stop Preview","cam.btn.stopping":"Stopping...","cam.btn.capture":"Capture","cam.btn.capturing":"Capturing...","cam.btn.recordStart":"Start Recording","cam.btn.recordStop":"Stop Recording","cam.btn.recordBusy":"Switching recording...","cam.btn.refresh":"Refresh Status","cam.preview.title":"Live Preview","cam.preview.state":"State","cam.preview.mode":"Exposure Mode","cam.preview.emptyTitle":"Preview is not started","cam.preview.emptyDesc":"Start the camera preview stream with the button below.","cam.state.streaming":"streaming","cam.state.idle":"idle","cam.state.rec":"REC","cam.stats.requestFps":"Request FPS","cam.hint.mjpegSingleStream":"MJPEG concurrent streams are limited (default ~2). Beyond the cap, extra tabs may contend or get busy—reduce simultaneous live previews.","cam.stats.frameFps":"Display FPS","cam.stats.fpsMeasureNote":"Pixel deltas on a 32×32 sample over ~1s. Multiple previews, slow links, or a stream cap below the sensor target often read much lower.","cam.err.streamBusy":"Preview unavailable: MJPEG slot is already in use","cam.err.streamBusyHint":"Close the live preview in other tabs or in the Analysis console, then try again.","cam.err.streamProbeFailed":"Could not open the preview stream","cam.err.streamProbeDetail":"Check network and camera service, or retry in a moment.","cam.stats.targetFps":"Camera capture target FPS","cam.stats.streamPacingFps":"Shared stream / MJPEG cap FPS","cam.stats.streamPacingHint":"Matches OGSCOPE_SHARED_PREVIEW_FPS: shared grabber pacing and per-stream MJPEG min frame spacing.","cam.stats.uptime":"Stream Uptime","cam.controls.title":"Controls","cam.controls.core":"Core Settings","cam.controls.fps":"FPS","cam.controls.resolution":"Resolution","cam.controls.applyRes":"Apply resolution","cam.controls.sampling":"Sampling mode","cam.controls.exposure":"Exposure","cam.controls.gain":"Analog Gain","cam.controls.digitalGain":"Digital Gain","cam.controls.noiseReduction":"Noise Reduction","cam.controls.contrast":"Contrast","cam.controls.brightness":"Brightness","cam.controls.saturation":"Saturation","cam.controls.sharpness":"Sharpness","cam.controls.applySettings":"Apply All Settings","cam.controls.applyAll":"Save Parameters","cam.controls.applyRuntime":"Save Runtime Settings","cam.controls.mode":"Mode Controls","cam.controls.autoExposure":"Exposure Mode","cam.controls.whiteBalance":"White Balance","cam.controls.colorMode":"Color Mode","cam.controls.auto":"Auto","cam.controls.manual":"Manual","cam.controls.night":"Night","cam.controls.color":"Color","cam.controls.mono":"Mono","cam.controls.applyAe":"Apply Exposure Mode","cam.controls.applyWb":"Apply White Balance","cam.controls.applyColor":"Apply Color Mode","cam.controls.applyMode":"Apply Mode Settings","cam.controls.lockedByAe":"Exposure and gain are locked while auto exposure is enabled.","cam.controls.lockedByWb":"R/B gains are locked unless white balance is in manual mode.","cam.controls.pendingChanges":"You have unapplied parameter changes","cam.controls.tools":"Tools","cam.controls.nightPreset":"Night Preset","cam.controls.nightOn":"Enable Night Mode","cam.controls.nightOff":"Disable Night Mode","cam.controls.backup":"Backup Settings","cam.controls.restore":"Restore Settings","cam.controls.reset":"Reset Camera","cam.quick.title":"Quick Presets & Smart Tuning","cam.quick.daylight":"Daylight","cam.quick.night":"Night","cam.quick.nightPreset":"Apply Night Preset","cam.quick.deep-sky":"Deep Sky","cam.quick.planetary":"Planetary","cam.quick.autoAdjust":"Auto Adjust","cam.quick.nightHint":"Night preset is one-click tuning; night mode toggle is runtime on/off control.","cam.system.title":"System Monitor","cam.system.status":"Camera Ready","cam.system.stream":"Stream","cam.system.sensor":"Sensor","cam.system.quality":"Image Quality","cam.presets.title":"Preset Management","cam.presets.name":"Preset Name","cam.presets.desc":"Preset Description","cam.presets.save":"Save Preset","cam.presets.empty":"No presets","cam.presets.noDesc":"No description","cam.presets.apply":"Apply","cam.presets.delete":"Delete","cam.files.title":"File Management","cam.files.refresh":"Refresh File List","cam.files.empty":"No files","cam.files.download":"Download","cam.files.info":"Details","cam.files.delete":"Delete","cam.files.loadingInfo":"Loading file details...","cam.files.prev":"Prev","cam.files.next":"Next","cam.files.page":"Page {current}/{total}","cam.files.closeDetail":"Close details","cam.files.size":"Size","cam.files.type":"Type","cam.files.modified":"Modified","cam.hist.title":"Histogram","cam.hist.enabled":"Enable histogram","cam.hist.luminance":"Luminance","cam.hist.over":"Over-exposure","cam.hist.expand":"Expand Histogram","cam.hist.collapse":"Collapse Histogram","cam.notice.previewStart":"Preview started","cam.notice.previewStop":"Preview stopped","cam.notice.captureSaved":"Capture saved: {name}","cam.notice.recordStart":"Recording started: {name}","cam.notice.recordStop":"Recording stopped","cam.notice.fpsApplied":"FPS updated to {fps}","cam.notice.resApplied":"Resolution updated to {w}x{h}","cam.notice.samplingApplied":"Sampling mode updated: {mode}","cam.notice.settingsApplied":"Settings applied","cam.notice.aeApplied":"Exposure mode applied","cam.notice.wbApplied":"White balance applied","cam.notice.colorApplied":"Color mode applied","cam.notice.modeApplied":"Mode settings applied","cam.notice.runtimeApplied":"Runtime settings applied","cam.notice.rotationApplied":"Rotation set to {value}°","cam.mirror.hint":"Use with rotation when the camera is mounted inverted; preview, plate solve, and polar guide share one coordinate frame.","cam.mirror.horizontal":"Horizontal mirror","cam.mirror.vertical":"Vertical mirror","cam.notice.mirrorApplied":"Mirror settings updated","cam.notice.nightOn":"Night mode enabled","cam.notice.nightOff":"Night mode disabled","cam.notice.nightPreset":"Night preset applied","cam.notice.reset":"Camera settings reset","cam.notice.backup":"Settings backed up","cam.notice.restore":"Settings restored","cam.notice.presetSaved":"Preset saved: {name}","cam.notice.presetApplied":"Preset applied: {name}","cam.notice.presetDeleted":"Preset deleted: {name}","cam.notice.download":"Download started: {name}","cam.notice.downloadWithSidecar":"Downloaded: {name} (with sidecar {sidecar})","cam.notice.fileDeleted":"File deleted: {name}","cam.notice.quickPresetApplied":"Quick preset applied: {preset}","cam.notice.needPreview":"Start preview first","cam.notice.needManualForAutoAdjust":"Switch to manual exposure before auto-adjust","cam.notice.autoAdjusted":"Auto-adjust completed and applied","cam.confirm.deletePreset":"Delete preset {name}?","cam.confirm.deleteFile":"Delete file {name}?","sys.sensors.mag.calStatusPrefix":"Calibration mode:","sys.sensors.mag.calModeAuto":"Auto (not locked)","sys.sensors.mag.calModeRecording":"Recording (rotate device)","sys.sensors.mag.calModeLocked":"Locked (saved heading)","sys.sensors.mag.calSamplesPrefix":"Recorded samples:","sys.sensors.mag.calRecordingHint":"Tip: keep level and rotate slowly; save after 10+ samples.","sys.sensors.mag.calLockedHint":"Locked heading params:"},C={"app.title":"OGScope 星空解算控制台","nav.lab":"解算台","nav.labImage":"图片解算","nav.labVideo":"视频解算","nav.pool":"素材池","nav.history":"实验记录","nav.cameraDebug":"相机调试控制台","nav.home":"首页","nav.systemAdmin":"返回系统后台","lang.zh":"中文","lang.en":"EN","sidebar.assets":"自行上传素材","sidebar.upload":"上传文件","sidebar.refresh":"刷新列表","sidebar.debugCaptures":"调试控制台素材","sidebar.assetTypeImage":"图片","sidebar.assetTypeVideo":"视频","sidebar.debugEmpty":"暂无调试文件","sidebar.importToPool":"导入到素材池","sidebar.importToPoolWithTranscode":"转码并导入素材池","sidebar.importToPoolDirect":"直接导入素材池","sidebar.flowPreparing":"准备中…","sidebar.flowImportingDebug":"正在导入调试素材…","sidebar.flowDownloadingPool":"正在下载已导入文件…","sidebar.flowWritingBuffer":"正在写入转码缓冲区…","sidebar.flowLoadingTranscoder":"正在加载转码器…","sidebar.flowTranscoding":"正在转码 AVI -> MP4…","sidebar.flowPackaging":"正在封装转码结果…","sidebar.flowUploadingMp4":"正在上传 MP4…","sidebar.flowReplacing":"正在替换并清理原 AVI…","sidebar.flowNoTranscode":"该文件无需转码,已直接导入。","sidebar.flowDone":"流程完成","sidebar.flowDoneMsg":"已完成:{name}","sidebar.flowFailed":"流程失败","sidebar.debugPage":"第 {cur} / {total} 页","sidebar.batchPresets":"批量预设","sidebar.batchHint":"勾选后点击「批量解算」可一次用多组参数对比结果。","lab.selectOrUpload":"从左侧选择自行上传或已导入的素材","lab.selectOrUploadVideo":"从左侧选择视频素材预览;或使用上方按钮解算设备相机实时帧。","lab.file":"文件","lab.source":"来源","lab.layers":"叠加层","lab.layer.matched":"匹配星","lab.layer.pattern":"图案星","lab.layer.all":"全部质心","lab.layer.rejected":"已剔除(红叉)","lab.previewConfidence":"置信度","lab.grid":"网格","lab.zoomIn":"放大","lab.zoomOut":"缩小","lab.zoomReset":"复位","lab.resolution":"分辨率","lab.fwhm":"FWHM","lab.starsDetected":"检测星点","lab.meta.title":"拍摄与文件信息","lab.meta.noSidecar":"无侧车信息(非调试采集或仅本地上传)","lab.meta.partial":"暂无侧车详细字段,仅显示文件信息。","lab.solveSection":"解算","lab.imageSection":"图像","lab.metric.solveMs":"用时","lab.metric.solveComputeMs":"解算计算用时","lab.metric.solveComputeHelp":"服务端 Tetra3 与提星等纯计算耗时(与网络无关)。","lab.metric.solveRoundTripMs":"全链路用时","lab.metric.solveRoundTripHelp":"从本页发起请求到收到结果并完成界面刷新的总耗时,含网络往返与浏览器渲染。","lab.metric.backendTotalMs":"后端总用时","lab.metric.openDecodeMs":"读取/解码","lab.metric.preprocessMs":"预处理","lab.metric.extractMs":"提星","lab.metric.solveOnlyMs":"匹配解算","lab.metric.probHelp":"Tetra3 的 Prob 为假阳性概率(越低越可信)。一般使用 (1−Prob)×100%;当 Prob 极小时浮点数会舍入为 100%,此时用 −log₁₀(Prob) 映射到约 70–100% 以便区分。","lab.metric.probRawHelp":"Tetra3 返回的原始 Prob 字段,可能为对数似然等内部量;与上一行换算后的百分比对照查看即可。","lab.metric.radec":"RA / Dec","lab.metric.matches":"匹配","lab.metric.rmse":"RMSE","lab.metric.prob":"置信","lab.metric.status":"状态","lab.gateNextMs":"约 {ms} ms 后可重试","lab.gate.skipRecording":"录制中:相机正写入录像,暂无法从相机取帧解算;请先停止录制后再试。","lab.gate.skipBusyInFlight":"上一帧解算仍在处理中,请稍候。若频繁出现,可适当增大「实时解算间隔」。","lab.gate.skipInterval":"解算请求过于频繁,已按后端节流排队。","lab.gate.skipBusyFallback":"实时解算暂时无法开始({detail})。","lab.centroidQualityTitle":"质心质量","lab.centroidQualityMetrics":"输入 {in} → 保留 {out};剔除过密 {dense},共线 {line}","meta.exposure":"曝光","meta.gain":"增益","meta.fps":"帧率","meta.sensor":"传感器","meta.colorMode":"色彩","meta.outputResolution":"输出分辨率","meta.fileTime":"文件时间","meta.fileSize":"文件大小","results.viewRaw":"原始 JSON","results.hideRaw":"收起","results.solveHistoryTitle":"解算历史","results.historySingle":"单帧","results.historyBatch":"批量 {count} 组","params.title":"解算参数","params.blockSolveIntro":"以下为板块求解(Tetra3)搜索天区、超时与粗略指向提示;FOV 需与镜头视场大致一致。","params.centroid":"提星","params.blockCentroidIntro":"以下为星点检测:阈值、连通域面积与局部背景窗口,用于从图像中提取星点质心。","params.fov":"FOV 估计 (°)","params.fovHelp":"水平视场角估计值,用于 lost-in-space 解算。","params.fovErr":"FOV 允许误差 (°)","params.fovErrHelp":"允许 Tetra3 在估计 FOV 附近的搜索范围。","params.timeout":"超时 (ms)","params.timeoutHelp":"单次解算最长等待时间。","params.solveIntervalMs":"实时解算间隔 (ms)","params.solveIntervalMsHelp":"期望间隔可调整,但会被后端限制在安全范围内。","params.solveIntervalBound":"后端限制范围:{min}-{max} ms,当前生效:{effective} ms","params.centroidRejectionLevel":"质心剔除强度 (1–5)","params.centroidRejectionLevelHelp":"1 最保守保留更多星点,5 更激进剔除过密与共线假星;默认 3。","params.centroidRejectionScale":"保守 ← → 激进","params.solveProfile":"解算档位","params.solveProfileHelp":"速度/平衡/稳健三档会同时调整超时、提星阈值与参与匹配星点数。","params.solveProfileSpeed":"速度优先","params.solveProfileBalanced":"平衡","params.solveProfileRobust":"稳健优先","params.ra":"RA 提示 (°)","params.raHelp":"大致天球赤经,缩小搜索范围(度)。","params.dec":"Dec 提示 (°)","params.decHelp":"大致天球赤纬(度)。","params.maxSide":"提星前长边上界 (px)","params.maxSideHelp":"降采样长边上限,大图可加速提星。","params.detailLevelFull":"包含完整 Tetra3 原始结果(体积略大,仅调试时开启)","params.largeScaleBg":"大尺度背景减除","params.largeScaleBgHelp":"在提星前用低分辨率平滑估计并校正大尺度亮度不均,可减轻角部光晕导致的假星;默认关闭以保持与过往行为一致。","params.sigma":"σ(阈值倍数)","params.sigmaHelp":"高于背景噪声倍数的区域视为星点候选。","params.maxArea":"max_area","params.maxAreaHelp":"连通域最大像素面积。","params.minArea":"min_area","params.minAreaHelp":"连通域最小像素面积。","params.filtsize":"filtsize(奇数)","params.filtsizeHelp":"局部背景滤波窗口边长,须为奇数。","btn.solveOne":"单张解算","btn.solveBatch":"批量解算(勾选预设)","btn.applyPresets":"应用预设到表单","btn.savePreset":"保存","placeholder.newPreset":"新预设名称","pool.title":"服务器素材池","pool.col.name":"文件名","pool.col.source":"来源","pool.col.size":"大小","pool.col.time":"修改时间","pool.delete":"删除","history.title":"实验记录","history.intro":"此处展示你在解算台完成解算后手动保存的快照。用法:在「解算台」主栏「结果对比」中,单张解算后点「保存当前到实验记录」,或批量解算后在某张结果卡片上点「保存记录」。本页可按文件名或预设名搜索,支持导出 JSON/CSV 备份。","history.search":"搜索…","history.searchBtn":"搜索","history.exportJson":"导出 JSON","history.exportCsv":"导出 CSV","history.total":"共 {n} 条","history.preset":"预设","history.metrics":"指标","history.detail":"详情","history.collapse":"收起","history.prev":"上一页","history.next":"下一页","history.delete":"删除","delete.uploadFirst":"确定要从素材池删除「{name}」吗?","delete.uploadSecond":"此操作不可恢复,再次确认删除?","delete.experimentFirst":"确定要删除这条实验记录吗?","delete.experimentSecond":"此操作不可恢复,再次确认删除?","results.title":"结果对比","results.saveCurrent":"保存当前到实验记录","results.saveRow":"保存记录","results.expand":"展开","results.collapseJson":"收起","err.selectFile":"请选择素材","err.selectPresets":"请勾选至少一个预设","common.placeholder":"—","delete.uploadCascade":"该素材有 {n} 条实验记录,是否一并删除?","lab.solveCurrentFrame":"解算当前帧(文件)","lab.solveFileStart":"开始文件连续解算","lab.solveFileStop":"停止文件连续解算","lab.cameraPreviewLoading":"正在连接共享预览…","lab.solveCameraFrame":"解算相机当前帧","lab.solveCameraStart":"开始相机解算","lab.solveCameraStop":"停止相机解算","lab.videoPreviewFailed":"该视频格式可能不受浏览器支持,建议使用 MP4(H.264) 或 WebM。","lab.previewModeFile":"素材文件","lab.previewModeCamera":"设备相机","lab.stopCameraPreview":"停止预览","lab.videoLiveIntro":"与调试控制台共用同一相机;此处可预览并解算实时帧,无需单独打开调试页。两页可同时使用。","lab.videoMjpegHint":"MJPEG 同时连接数已满:请关闭其它页面的实时预览或稍后重试(默认允许约 4 路,可在 systemd 调整 OGSCOPE_STREAM_MAX_MJPEG_CLIENTS)。","lab.cameraStreamStatusFail":"无法读取视频流名额状态,请稍后重试。","lab.cameraSnapshotName":"ogscope_camera_live","lab.metric.probRaw":"原始 Prob","lab.systemLoad":"系统负载","results.saveBatchAll":"保存全部到实验记录","lab.transcode.title":"AVI 文件需先转码","lab.transcode.desc":"当前视频为 AVI。为保证浏览器可预览与连续解算,请先在本地转码为 MP4 并上传替换。上传成功后将自动删除服务器上的原 AVI。","lab.transcode.button":"转码并上传替换","lab.transcode.loading":"正在加载转码器…","lab.transcode.writingBuffer":"正在写入转码缓冲区…","lab.transcode.running":"正在转码 AVI -> MP4…","lab.transcode.packaging":"正在封装转码结果…","lab.transcode.uploading":"正在上传转码结果…","lab.transcode.done":"转码并替换完成,可继续预览与解算。","lab.transcode.failed":"转码或上传失败,请重试。","lab.transcode.fetchFailed":"无法读取服务器上的 AVI 文件。","sys.shell.subtitle":"系统调试控制台","sys.shell.nav.overview":"总览","sys.shell.nav.network":"网络","sys.shell.nav.camera":"相机调试","sys.shell.nav.analysis":"寻星控制台","sys.shell.nav.sensors":"传感器","sys.shell.nav.power":"电源","sys.shell.nav.hmi":"人机交互","sys.shell.nav.config":"配置管理","sys.shell.workbench":"WORKBENCH / 系统工作台","sys.shell.node":"节点","sys.shell.top.overview":"系统状态","sys.shell.top.network":"网络与 WiFi","sys.shell.top.sensors":"传感器","sys.shell.top.power":"电源管理","sys.shell.top.hmi":"人机交互","sys.shell.top.config":"配置管理","sys.overview.breadcrumb.console":"控制台","sys.overview.breadcrumb.module":"系统总览","sys.overview.title":"系统总览","sys.overview.subtitle":"实时系统运行健康状况","sys.overview.metric.cpu":"CPU 使用率","sys.overview.metric.mem":"内存占用","sys.overview.metric.temp":"核心温度","sys.overview.metric.wifi":"WiFi 链路","sys.overview.metric.uptime":"运行时长","sys.overview.metric.load":"1 分钟负载","sys.overview.metric.storage":"存储","sys.overview.linkActive":"链路在线","sys.overview.tempState":"状态: 正常","sys.overview.wifiSummary":"无线网络摘要","sys.overview.iface":"接口","sys.overview.signal":"信号","sys.overview.quality":"质量","sys.overview.storageComingSoon":"即将接入","sys.logs.title":"系统日志","sys.logs.kernel":"内核","sys.logs.liveToggle":"实时拉取","sys.logs.refresh":"刷新","sys.logs.liveOffHint":"实时拉取关闭(默认)","sys.logs.loading":"日志加载中...","sys.logs.empty":"暂无日志","sys.placeholder.breadcrumb":"控制台 / 预留模块","sys.placeholder.block":"预留模块","sys.placeholder.desc":"模块壳层已预留,后续将按统一组件体系接入真实数据。","sys.placeholder.status":"STATUS: RESERVED / 当前为结构化占位页面。","sys.placeholder.sensors.title":"传感器诊断台","sys.placeholder.sensors.desc":"用于 IMU、温湿度、电流电压、姿态与时序采样的统一监控面板。","sys.placeholder.sensors.block1":"实时曲线区","sys.placeholder.sensors.block2":"设备健康卡","sys.placeholder.sensors.block3":"阈值告警区","sys.sensors.title":"传感器诊断台","sys.sensors.desc":"磁力计罗盘指北与 MPU-6050 陀螺仪角速度可视化;底层仍为开发者调试 API。","sys.sensors.mag.section":"磁力计(AK09911 / I²C)","sys.sensors.mag.note":"默认总线 1、地址 12(0x0C,CAD 接 GND)。无需在应用层“定义 GPIO”;请确保固件已启用 I²C 且已加载 i2c-dev。","sys.sensors.mag.bus":"I²C 总线号","sys.sensors.mag.addr":"7-bit 地址(十进制,12=0x0C)","sys.sensors.mag.i2cdetect":"附带运行 i2cdetect","sys.sensors.mag.btnSelftest":"运行自检","sys.sensors.mag.btnProbe":"扫描各总线","sys.sensors.mag.btnCalStart":"开始方向校准","sys.sensors.mag.btnCalCommit":"保存并锁定方向","sys.sensors.mag.btnCalReset":"重置到自动模式","sys.sensors.mag.btnCalStatus":"查看校准状态","sys.sensors.mag.running":"执行中…","sys.sensors.mpu.section":"IMU(MPU-6050 / I²C)","sys.sensors.mpu.note":"默认总线 1、地址 104(0x68,AD0 接 GND)。含陀螺仪角速度采样(MPU 内集成加速度计与陀螺仪)。","sys.sensors.mpu.addr":"7-bit 地址(十进制,104=0x68)","sys.sensors.mpu.btnSelftest":"运行 MPU 自检","sys.sensors.mpu.running":"执行中…","sys.sensors.gyro.title":"陀螺仪(MPU-6050)","sys.sensors.gyro.subtitle":"使用上方同一总线/地址;一次读取加速度与陀螺仪,估算横滚/俯仰并显示 3D 板卡示意。","sys.sensors.gyro.btn":"读取角速度","sys.sensors.gyro.loading":"采样中…","sys.sensors.gyro.dps":"角速度","sys.sensors.gyro.raw":"原始值","sys.sensors.gyro.unitDps":"°/s,±250°/s 量程、默认灵敏度","sys.sensors.gyro.unitRaw":"16-bit 有符号","sys.sensors.gyro.hint":"点击「读取角速度」从 IMU 采样(加速度 0x3B + 陀螺仪 0x43)。横滚/俯仰来自重力;ωz 为绕竖直轴角速度,并非绝对航向。","sys.sensors.gyro.errUnknown":"采样失败","sys.sensors.gyro.live":"连续刷新(约 2 Hz)","sys.sensors.gyro.barsTitle":"角速度条(相对 ±250°/s 满偏)","sys.sensors.gyro.rawBlock":"寄存器原始值(16-bit)","sys.sensors.gyro.att3dTitle":"3D 姿态示意","sys.sensors.gyro.att3dDesc":"横滚/俯仰由加速度计重力估算;ωz 为陀螺仪绕竖直轴角速度(非磁航向)。机体系三轴与板卡固连,随姿态在三维空间中转动。","sys.sensors.gyro.axisXLabel":"+X","sys.sensors.gyro.axisYLabel":"+Y","sys.sensors.gyro.axisZLabel":"+Z","sys.sensors.gyro.att3dBodyAxes":"左侧为与电路板固连的机体系:琥珀 +X、天蓝 +Y、紫 +Z(法向);短划为负向参考,整体随横滚/俯仰旋转。","sys.sensors.gyro.att3dRefTitle":"正交轴参考(固定视角)","sys.sensors.gyro.att3dRefDesc":"与左侧同色对应,便于辨认三轴空间关系。","sys.sensors.gyro.roll":"横滚","sys.sensors.gyro.pitch":"俯仰","sys.sensors.gyro.yawRate":"偏航角速度","sys.sensors.compass.tapeCaption":"水平航向带 · 中心为当前读数","sys.sensors.compass.title":"磁力计 · 指北罗盘","sys.sensors.compass.desc":"根据水平面磁场分量估算方向角,用于调试「是否大致指向北方」;精对准需校准与固定安装姿态。","sys.sensors.compass.btn":"刷新罗盘","sys.sensors.compass.loading":"测量中…","sys.sensors.compass.live":"连续刷新","sys.sensors.compass.headingLabel":"水平航向角","sys.sensors.compass.headingHint":"由 atan2(Hx, Hy) 得到,单位度","sys.sensors.compass.cardN":"北","sys.sensors.compass.cardE":"东","sys.sensors.compass.cardS":"南","sys.sensors.compass.cardW":"西","sys.sensors.compass.err":"采样失败","sys.sensors.compass.footnote":"红色针尖表示 XY 平面内磁场水平分量方向;转动设备时指针应随之变化。精对准需软铁/硬铁校准。","sys.sensors.jsonToggle":"展开原始 JSON(排障用)","sys.hmi.title":"人机交互 · SPI 显示","sys.hmi.desc":"通过硬件平面驱动 ST7796(默认 320×320);需 OGSCOPE_DISPLAY_ENABLED=true 且系统已启用 SPI(/dev/spidev0.0)。","sys.hmi.status.section":"显示与硬件平面状态","sys.hmi.status.refresh":"刷新状态","sys.hmi.status.displayEnabled":"DISPLAY_ENABLED","sys.hmi.status.spidev":"/dev/spidev0.0","sys.hmi.status.yes":"存在","sys.hmi.status.no":"不存在","sys.hmi.status.resolution":"分辨率 / DC","sys.hmi.status.driver":"驱动句柄","sys.hmi.status.open":"已打开","sys.hmi.status.closed":"未打开","sys.hmi.status.screenOutput":"逻辑输出","sys.hmi.status.on":"开","sys.hmi.status.off":"关","sys.hmi.status.lastPattern":"上次图案","sys.hmi.status.lastError":"上次错误","sys.hmi.actions.section":"输出到屏幕","sys.hmi.actions.hint":"以下为硬件平面 device.command → hmi;大屏刷新使用较长 RPC 超时。","sys.hmi.actions.smoke":"测试画面(文字+边框)","sys.hmi.actions.colorbars":"彩条","sys.hmi.actions.fill":"填充 RGB","sys.hmi.actions.screenOn":"允许屏幕输出","sys.hmi.actions.screenOff":"暂停屏幕输出","sys.hmi.actions.release":"释放驱动(关闭 SPI/GPIO)","sys.hmi.rawJson":"原始 JSON 响应","sys.placeholder.hmi.title":"人机交互台","sys.placeholder.hmi.desc":"用于 SPI 显示、按键矩阵、背光/对比度与界面回放的调试工作区。","sys.placeholder.hmi.block1":"显示预览区","sys.placeholder.hmi.block2":"输入事件流","sys.placeholder.hmi.block3":"参数与模式控制","sys.placeholder.power.title":"电源与功耗台","sys.placeholder.power.desc":"用于电池、充电、瞬时功耗与热管理策略联动调试。","sys.placeholder.power.block1":"功耗看板","sys.placeholder.power.block2":"电源策略表","sys.placeholder.power.block3":"恢复与保护动作","cam.title":"相机调试控制台","cam.subtitle":"迁移到统一后台架构,保留关键性能优化","cam.btn.system":"系统后台","cam.btn.analysis":"寻星控制台","cam.btn.home":"首页","cam.btn.start":"启动预览","cam.btn.starting":"启动中...","cam.btn.stop":"停止预览","cam.btn.stopping":"停止中...","cam.btn.capture":"拍摄","cam.btn.capturing":"拍摄中...","cam.btn.recordStart":"开始录制","cam.btn.recordStop":"停止录制","cam.btn.recordBusy":"录制切换中...","cam.btn.refresh":"刷新状态","cam.preview.title":"实时预览","cam.preview.state":"状态","cam.preview.mode":"曝光模式","cam.preview.emptyTitle":"预览尚未启动","cam.preview.emptyDesc":"点击下方按钮启动相机预览流。","cam.state.streaming":"流已启动","cam.state.idle":"未启动","cam.state.rec":"录制中","cam.stats.requestFps":"请求 FPS","cam.hint.mjpegSingleStream":"MJPEG 同时连接数有限(默认约两路);超出上限时多标签页会争用或返回忙,请减少同时打开的实时预览。","cam.stats.frameFps":"画面 FPS","cam.stats.fpsMeasureNote":"统计最近约 1 秒内在 32×32 采样上的像素变化次数;多路同时预览、网络较慢、或下方「共享流上限」低于相机目标时,读数常明显偏低。","cam.err.streamBusy":"无法连接预览:MJPEG 名额已被占用","cam.err.streamBusyHint":"请关闭其它标签页或解算台中的设备实时预览,或稍后再试。","cam.err.streamProbeFailed":"无法建立预览流","cam.err.streamProbeDetail":"请检查网络与相机服务状态,或稍后重试。","cam.stats.targetFps":"相机采集目标 FPS","cam.stats.streamPacingFps":"共享流/MJPEG 上限 FPS","cam.stats.streamPacingHint":"与进程环境 OGSCOPE_SHARED_PREVIEW_FPS 一致,约束共享抓帧节奏与每路 MJPEG 的最小帧间隔。","cam.stats.uptime":"流运行时长","cam.controls.title":"参数控制","cam.controls.core":"核心参数","cam.controls.fps":"帧率","cam.controls.resolution":"分辨率","cam.controls.applyRes":"应用分辨率","cam.controls.sampling":"采样模式","cam.controls.exposure":"曝光","cam.controls.gain":"模拟增益","cam.controls.digitalGain":"数字增益","cam.controls.noiseReduction":"降噪级别","cam.controls.contrast":"对比度","cam.controls.brightness":"亮度","cam.controls.saturation":"饱和度","cam.controls.sharpness":"锐度","cam.controls.applySettings":"应用全部设置","cam.controls.applyAll":"保存参数","cam.controls.applyRuntime":"保存运行参数","cam.controls.mode":"模式控制","cam.controls.autoExposure":"曝光模式","cam.controls.whiteBalance":"白平衡","cam.controls.colorMode":"颜色模式","cam.controls.auto":"自动","cam.controls.manual":"手动","cam.controls.night":"夜间","cam.controls.color":"彩色","cam.controls.mono":"黑白","cam.controls.applyAe":"应用曝光模式","cam.controls.applyWb":"应用白平衡","cam.controls.applyColor":"应用颜色模式","cam.controls.applyMode":"应用模式设置","cam.controls.lockedByAe":"自动曝光开启时,曝光与增益参数锁定。","cam.controls.lockedByWb":"当前非手动白平衡模式,R/B 增益参数锁定。","cam.controls.pendingChanges":"有未应用的参数修改","cam.controls.tools":"工具","cam.controls.nightPreset":"夜间预设","cam.controls.nightOn":"开启夜间模式","cam.controls.nightOff":"关闭夜间模式","cam.controls.backup":"备份设置","cam.controls.restore":"恢复设置","cam.controls.reset":"重置相机","cam.quick.title":"快速预设与智能调参","cam.quick.daylight":"白天模式","cam.quick.night":"夜间模式","cam.quick.nightPreset":"应用夜间预设","cam.quick.deep-sky":"深空模式","cam.quick.planetary":"行星模式","cam.quick.autoAdjust":"智能调整","cam.quick.nightHint":"夜间预设用于一键切换夜拍参数;夜间模式开关用于实时启停红外/夜景模式。","cam.system.title":"系统监控","cam.system.status":"相机就绪","cam.system.stream":"流状态","cam.system.sensor":"传感器","cam.system.quality":"图像质量","cam.presets.title":"预设管理","cam.presets.name":"预设名称","cam.presets.desc":"预设描述","cam.presets.save":"保存预设","cam.presets.empty":"暂无预设","cam.presets.noDesc":"无描述","cam.presets.apply":"应用","cam.presets.delete":"删除","cam.files.title":"文件管理","cam.files.refresh":"刷新文件列表","cam.files.empty":"暂无文件","cam.files.download":"下载","cam.files.info":"详情","cam.files.delete":"删除","cam.files.loadingInfo":"正在读取文件详情...","cam.files.prev":"上一页","cam.files.next":"下一页","cam.files.page":"第 {current}/{total} 页","cam.files.closeDetail":"关闭详情","cam.files.size":"文件大小","cam.files.type":"文件类型","cam.files.modified":"修改时间","cam.hist.title":"直方图","cam.hist.enabled":"启用直方图","cam.hist.luminance":"亮度通道","cam.hist.over":"过曝警告","cam.hist.expand":"展开直方图","cam.hist.collapse":"收起直方图","cam.notice.previewStart":"预览已启动","cam.notice.previewStop":"预览已停止","cam.notice.captureSaved":"拍摄完成: {name}","cam.notice.recordStart":"开始录制: {name}","cam.notice.recordStop":"录制已停止","cam.notice.fpsApplied":"帧率已设置为 {fps}","cam.notice.resApplied":"分辨率已设置为 {w}x{h}","cam.notice.samplingApplied":"采样模式已设置为 {mode}","cam.notice.settingsApplied":"参数已应用","cam.notice.aeApplied":"曝光模式已应用","cam.notice.wbApplied":"白平衡已应用","cam.notice.colorApplied":"颜色模式已应用","cam.notice.modeApplied":"模式设置已应用","cam.notice.runtimeApplied":"运行参数已应用","cam.notice.rotationApplied":"旋转已设置为 {value}°","cam.mirror.hint":"相机倒装时可配合旋转使用;镜像后预览与星点解算、极轴引导同坐标系。","cam.mirror.horizontal":"水平镜像","cam.mirror.vertical":"垂直镜像","cam.notice.mirrorApplied":"镜像设置已更新","cam.notice.nightOn":"夜间模式已开启","cam.notice.nightOff":"夜间模式已关闭","cam.notice.nightPreset":"夜间预设已应用","cam.notice.reset":"相机设置已重置","cam.notice.backup":"设置已备份","cam.notice.restore":"设置已恢复","cam.notice.presetSaved":"预设已保存: {name}","cam.notice.presetApplied":"预设已应用: {name}","cam.notice.presetDeleted":"预设已删除: {name}","cam.notice.download":"已开始下载: {name}","cam.notice.downloadWithSidecar":"已下载: {name}(含侧车 {sidecar})","cam.notice.fileDeleted":"文件已删除: {name}","cam.notice.quickPresetApplied":"已应用快速预设: {preset}","cam.notice.needPreview":"请先启动预览","cam.notice.needManualForAutoAdjust":"请先切换为手动曝光再进行智能调整","cam.notice.autoAdjusted":"智能调整已完成并生效","cam.confirm.deletePreset":"确定删除预设 {name} 吗?","cam.confirm.deleteFile":"确定删除文件 {name} 吗?","sys.sensors.mag.calStatusPrefix":"校准状态:","sys.sensors.mag.calModeAuto":"自动模式(未锁定)","sys.sensors.mag.calModeRecording":"录制中(请旋转设备)","sys.sensors.mag.calModeLocked":"已锁定(使用保存方向)","sys.sensors.mag.calSamplesPrefix":"录制样本:","sys.sensors.mag.calRecordingHint":"操作提示:保持水平,缓慢旋转设备,建议累计 10+ 样本后保存。","sys.sensors.mag.calLockedHint":"已锁定方向参数:"},g=a.createContext(null),p={zh:C,en:k},b="ogscope.analysis.locale";function y(){const e=window.localStorage.getItem(b);return e==="zh"||e==="en"?e:(navigator.language||"zh").toLowerCase().startsWith("en")?"en":"zh"}function A({children:e}){const[s,t]=a.useState(y),[o,l]=a.useState(p[y()]);a.useEffect(()=>{l(p[s]),window.localStorage.setItem(b,s),document.documentElement.lang=s==="en"?"en":"zh-CN"},[s]);const r=a.useMemo(()=>(n,c)=>{let i=o[n]??n;if(c)for(const[d,h]of Object.entries(c))i=i.replace(new RegExp(`\\{${d}\\}`,"g"),String(h));return i},[o]),m=a.useMemo(()=>({locale:s,setLocale:t,t:r}),[s,r]);return v.jsx(g.Provider,{value:m,children:e})}function x(){const e=a.useContext(g);if(!e)throw new Error("useI18n must be used within I18nProvider");return e}export{A as I,R as T,P as c,x as u};
diff --git a/web/static/analysis-lab/assets/refresh-cw-BkMjDReH.js b/web/static/analysis-lab/assets/refresh-cw-gseCLMRP.js
similarity index 91%
rename from web/static/analysis-lab/assets/refresh-cw-BkMjDReH.js
rename to web/static/analysis-lab/assets/refresh-cw-gseCLMRP.js
index 7a080db..0ea1c11 100644
--- a/web/static/analysis-lab/assets/refresh-cw-BkMjDReH.js
+++ b/web/static/analysis-lab/assets/refresh-cw-gseCLMRP.js
@@ -1,4 +1,4 @@
-import{c as e}from"./index-CutgeBjy.js";/**
+import{c as e}from"./index-C78KOEFu.js";/**
* @license lucide-react v0.460.0 - ISC
*
* This source code is licensed under the ISC license.
diff --git a/web/static/analysis-lab/assets/system-B6miqBWl.js b/web/static/analysis-lab/assets/system-B6miqBWl.js
deleted file mode 100644
index 3ce1222..0000000
--- a/web/static/analysis-lab/assets/system-B6miqBWl.js
+++ /dev/null
@@ -1,78 +0,0 @@
-import{j as e,r as a,a as $e,R as Me}from"./client-D1ZVDB-N.js";import{u as pe,C as Ee,r as K,a as U,S as Pe,b as Le}from"./http-ChPtkS1w.js";import{c as q,u as J,T as Re,I as Te}from"./index-CutgeBjy.js";import{R as he}from"./refresh-cw-BkMjDReH.js";/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const me=q("Activity",[["path",{d:"M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2",key:"169zse"}]]);/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Oe=q("Bolt",[["path",{d:"M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z",key:"yt0hxn"}],["circle",{cx:"12",cy:"12",r:"4",key:"4exip2"}]]);/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Ae=q("BookOpen",[["path",{d:"M12 7v14",key:"1akyts"}],["path",{d:"M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z",key:"ruj8y"}]]);/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Se=q("Cpu",[["rect",{width:"16",height:"16",x:"4",y:"4",rx:"2",key:"14l7u7"}],["rect",{width:"6",height:"6",x:"9",y:"9",rx:"1",key:"5aljv4"}],["path",{d:"M15 2v2",key:"13l42r"}],["path",{d:"M15 20v2",key:"15mkzm"}],["path",{d:"M2 15h2",key:"1gxd5l"}],["path",{d:"M2 9h2",key:"1bbxkp"}],["path",{d:"M20 15h2",key:"19e6y8"}],["path",{d:"M20 9h2",key:"19tzq7"}],["path",{d:"M9 2v2",key:"165o2o"}],["path",{d:"M9 20v2",key:"i2bqo8"}]]);/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Fe=q("HardDrive",[["line",{x1:"22",x2:"2",y1:"12",y2:"12",key:"1y58io"}],["path",{d:"M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z",key:"oot6mr"}],["line",{x1:"6",x2:"6.01",y1:"16",y2:"16",key:"sgf278"}],["line",{x1:"10",x2:"10.01",y1:"16",y2:"16",key:"1l4acy"}]]);/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Ie=q("LayoutDashboard",[["rect",{width:"7",height:"9",x:"3",y:"3",rx:"1",key:"10lvy0"}],["rect",{width:"7",height:"5",x:"14",y:"3",rx:"1",key:"16une8"}],["rect",{width:"7",height:"9",x:"14",y:"12",rx:"1",key:"1hutg5"}],["rect",{width:"7",height:"5",x:"3",y:"16",rx:"1",key:"ldoo1y"}]]);/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const ze=q("Network",[["rect",{x:"16",y:"16",width:"6",height:"6",rx:"1",key:"4q2zg0"}],["rect",{x:"2",y:"16",width:"6",height:"6",rx:"1",key:"8cvhb9"}],["rect",{x:"9",y:"2",width:"6",height:"6",rx:"1",key:"1egb70"}],["path",{d:"M5 16v-3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v3",key:"1jsf9p"}],["path",{d:"M12 12V8",key:"2874zd"}]]);/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const De=q("Plus",[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"M12 5v14",key:"s699le"}]]);/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const _e=q("Search",[["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}],["path",{d:"m21 21-4.3-4.3",key:"1qie3q"}]]);/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const qe=q("Settings",[["path",{d:"M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z",key:"1qme2f"}],["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}]]);/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const ge=q("Sparkles",[["path",{d:"M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z",key:"4pj2yx"}],["path",{d:"M20 3v4",key:"1olli1"}],["path",{d:"M22 5h-4",key:"1gvqau"}],["path",{d:"M4 17v2",key:"vumght"}],["path",{d:"M5 18H3",key:"zchphs"}]]);/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const He=q("Thermometer",[["path",{d:"M14 4v10.54a4 4 0 1 1-4 0V4a2 2 0 0 1 4 0Z",key:"17jzev"}]]);/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Ue=q("Touchpad",[["rect",{width:"20",height:"16",x:"2",y:"4",rx:"2",key:"18n3k1"}],["path",{d:"M2 14h20",key:"myj16y"}],["path",{d:"M12 20v-6",key:"1rm09r"}]]);/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Be=q("TriangleAlert",[["path",{d:"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3",key:"wmoenq"}],["path",{d:"M12 9v4",key:"juzpu7"}],["path",{d:"M12 17h.01",key:"p32p05"}]]);/**
- * @license lucide-react v0.460.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const fe=q("Wifi",[["path",{d:"M12 20h.01",key:"zekei9"}],["path",{d:"M2 8.82a15 15 0 0 1 20 0",key:"dnpr2z"}],["path",{d:"M5 12.859a10 10 0 0 1 14 0",key:"1x1e6c"}],["path",{d:"M8.5 16.429a5 5 0 0 1 7 0",key:"1bycff"}]]),V=s=>`flex items-center gap-3 rounded-lg px-3 py-2.5 font-headline text-sm tracking-tight transition-colors ${s?"border-r-2 border-primary bg-white/5 font-semibold text-primary":"text-on-surface-variant hover:bg-white/5 hover:text-on-surface"}`;function We({route:s,allowNetworkRoute:o,onRouteChange:n,children:t}){const{t:c,locale:d,setLocale:N}=J(),{info:m}=pe(),f=(m==null?void 0:m.cpu_usage)!=null?Number(m.cpu_usage).toFixed(1):"—",y=(m==null?void 0:m.memory_usage)!=null?Number(m.memory_usage).toFixed(1):"—",g=(m==null?void 0:m.temperature)!=null?Number(m.temperature).toFixed(1):"—",S=(m==null?void 0:m.wifi_quality)!=null&&!Number.isNaN(Number(m.wifi_quality))?`${Number(m.wifi_quality).toFixed(0)}%`:"—",v={overview:c("sys.shell.top.overview"),network:c("sys.shell.top.network"),sensors:c("sys.shell.top.sensors"),hmi:c("sys.shell.top.hmi"),power:c("sys.shell.top.power"),config:c("sys.shell.top.config")},p=V(!1),$=(M,R)=>{const _=window.open(M,R);_&&_.focus()};return e.jsxs("div",{className:"flex h-full min-h-0 flex-col bg-background text-on-surface md:flex-row",children:[e.jsxs("aside",{className:"glass-panel z-50 flex w-full shrink-0 flex-col border-b border-outline-variant/20 bg-surface-container-low/80 backdrop-blur-xl md:fixed md:left-0 md:top-0 md:h-full md:w-64 md:border-b-0 md:border-r md:border-white/5",children:[e.jsxs("div",{className:"p-5",children:[e.jsxs("div",{className:"mb-8 flex items-center gap-3",children:[e.jsx("div",{className:"primary-gradient flex h-10 w-10 items-center justify-center rounded-lg shadow-lg",children:e.jsx(ge,{className:"h-5 w-5 text-on-primary-container"})}),e.jsxs("div",{children:[e.jsx("h1",{className:"font-headline text-lg font-bold tracking-widest text-primary",children:"OGScope"}),e.jsx("p",{className:"font-mono text-[10px] uppercase tracking-widest text-on-surface-variant",children:c("sys.shell.subtitle")})]})]}),e.jsxs("nav",{className:"flex flex-col gap-0.5",children:[e.jsxs("button",{type:"button",className:V(s==="overview"),onClick:()=>n("overview"),children:[e.jsx(Ie,{className:"h-4 w-4 shrink-0"}),e.jsx("span",{children:c("sys.shell.nav.overview")})]}),o&&e.jsxs("button",{type:"button",className:V(s==="network"),onClick:()=>n("network"),children:[e.jsx(ze,{className:"h-4 w-4 shrink-0"}),e.jsx("span",{children:c("sys.shell.nav.network")})]}),e.jsxs("a",{href:"/debug/camera",className:p,onClick:M=>{M.preventDefault(),$("/debug/camera","ogscopeCameraConsole")},children:[e.jsx(Ee,{className:"h-4 w-4 shrink-0"}),e.jsx("span",{children:c("sys.shell.nav.camera")})]}),e.jsxs("a",{href:"/debug/analysis",className:p,onClick:M=>{M.preventDefault(),$("/debug/analysis","ogscopeAnalysisConsole")},children:[e.jsx(ge,{className:"h-4 w-4 shrink-0"}),e.jsx("span",{children:c("sys.shell.nav.analysis")})]}),e.jsxs("button",{type:"button",className:V(s==="sensors"),onClick:()=>n("sensors"),children:[e.jsx(me,{className:"h-4 w-4 shrink-0"}),e.jsx("span",{children:c("sys.shell.nav.sensors")})]}),e.jsxs("button",{type:"button",className:V(s==="power"),onClick:()=>n("power"),children:[e.jsx(Oe,{className:"h-4 w-4 shrink-0"}),e.jsx("span",{children:c("sys.shell.nav.power")})]}),e.jsxs("button",{type:"button",className:V(s==="hmi"),onClick:()=>n("hmi"),children:[e.jsx(Ue,{className:"h-4 w-4 shrink-0"}),e.jsx("span",{children:c("sys.shell.nav.hmi")})]}),e.jsxs("button",{type:"button",className:V(s==="config"),onClick:()=>n("config"),children:[e.jsx(qe,{className:"h-4 w-4 shrink-0"}),e.jsx("span",{children:c("sys.shell.nav.config")})]})]})]}),e.jsx("div",{className:"mt-auto hidden p-5 md:block",children:e.jsxs("div",{className:"rounded-xl border border-white/5 bg-surface-container-low p-3",children:[e.jsx("p",{className:"truncate text-xs font-semibold text-on-surface",children:c("sys.shell.workbench")}),e.jsxs("p",{className:"font-mono text-[10px] text-on-surface-variant",children:[c("sys.shell.node"),": OGSCOPE_PI_ZERO_2W"]})]})})]}),e.jsxs("div",{className:"flex min-h-0 min-w-0 flex-1 flex-col md:ml-64",children:[e.jsxs("header",{className:"sticky top-0 z-40 flex h-14 shrink-0 items-center justify-between border-b border-white/5 bg-neutral-950/80 px-4 backdrop-blur-md md:px-8",children:[e.jsx("div",{className:"flex min-w-0 items-center gap-3",children:e.jsx("span",{className:"hidden truncate border-b-2 border-primary pb-0.5 font-mono text-xs uppercase tracking-wider text-primary sm:inline",children:v[s]})}),e.jsxs("div",{className:"flex flex-wrap items-center justify-end gap-3 sm:gap-4",children:[e.jsxs("div",{className:"mr-2 flex gap-1 text-[10px]",children:[e.jsx("button",{type:"button",className:`rounded px-2 py-0.5 ${d==="zh"?"bg-primary-container text-on-primary-container":"text-on-surface-variant"}`,onClick:()=>N("zh"),children:c("lang.zh")}),e.jsx("button",{type:"button",className:`rounded px-2 py-0.5 ${d==="en"?"bg-primary-container text-on-primary-container":"text-on-surface-variant"}`,onClick:()=>N("en"),children:c("lang.en")})]}),e.jsxs("div",{className:"flex flex-wrap items-center justify-end gap-3 font-mono text-[10px] uppercase tracking-wider text-on-surface-variant sm:gap-4",children:[e.jsxs("span",{className:"flex items-center gap-1 text-primary",children:[e.jsx(Se,{className:"h-3.5 w-3.5"})," CPU ",f,"%"]}),e.jsxs("span",{className:"flex items-center gap-1",children:[e.jsx(me,{className:"h-3.5 w-3.5"})," MEM ",y,"%"]}),e.jsxs("span",{className:"flex items-center gap-1",children:[e.jsx("span",{className:"text-xs",children:"°C"})," ",g]}),e.jsxs("span",{className:"flex items-center gap-1 text-secondary",children:[e.jsx(fe,{className:"h-3.5 w-3.5"})," ",S]})]})]})]}),e.jsx("main",{className:"og-scrollbar min-h-0 flex-1 overflow-auto p-4 md:p-6",children:t})]})]})}async function Ke(){return await K("/api/dev/system/hardware-plane/status",{cache:"no-store"})}async function Xe(s){return await K("/api/dev/system/hardware-plane/command",{method:"POST",body:JSON.stringify(s)})}async function Ge(s){var t;const o=new URLSearchParams;s!=null&&s.service&&o.set("service",s.service),o.set("since_seconds",String(s.sinceSeconds)),o.set("limit",String(s.limit)),(t=s==null?void 0:s.levels)!=null&&t.length&&o.set("levels",s.levels.join(","));const n=o.toString();return await K(`/api/dev/debug/logs/systemd${n?`?${n}`:""}`,{cache:"no-store"})}function Ze(s){const o=Math.max(0,parseInt(String(s??0),10)||0),n=Math.floor(o/86400),t=Math.floor(o%86400/3600),c=Math.floor(o%3600/60);return n>0?`${n}d ${t}h`:t>0?`${t}h ${c}m`:`${c}m`}function de(s,o=1){return s==null||Number.isNaN(Number(s))?"—":Number(s).toFixed(o)}function ye(s){return s==="ERROR"?"text-error":s==="WARN"?"text-amber-300":"text-primary"}function Je(s){if(!s)return"--:--:--";const o=new Date(s);return Number.isNaN(o.getTime())?"--:--:--":o.toLocaleTimeString()}function Ye(){const{t:n}=J(),{info:t,error:c}=pe(),[d,N]=a.useState(!1),[m,f]=a.useState(["INFO","WARN","ERROR"]),[y,g]=a.useState([]),[S,v]=a.useState(null),[p,$]=a.useState(!1),[M,R]=a.useState(!0),_=a.useRef(null),E=a.useRef(new Set),h=de(t==null?void 0:t.cpu_usage),T=de(t==null?void 0:t.memory_usage),O=de(t==null?void 0:t.temperature),j=(t==null?void 0:t.load_average_1m)!=null?String(t.load_average_1m):"—",C=(t==null?void 0:t.wifi_quality)!=null&&!Number.isNaN(Number(t.wifi_quality))?`${Number(t.wifi_quality).toFixed(0)}%`:"—",w=(t==null?void 0:t.wifi_signal_dbm)!=null&&!Number.isNaN(Number(t.wifi_signal_dbm))?`${Number(t.wifi_signal_dbm).toFixed(0)} dBm`:"—",P=async()=>{if(m.length===0){g([]);return}$(!0);try{v(null);const i=await Ge({service:"ogscope",sinceSeconds:1200,limit:240,levels:m});g(k=>{const L=new Set(k.map(I=>`${I.ts??""}::${I.level}::${I.source}::${I.message}`)),A=[...k];for(const I of i.items){const B=`${I.ts??""}::${I.level}::${I.source}::${I.message}`;L.has(B)||(L.add(B),A.push(I))}return A.length<=300?A:A.slice(A.length-300)})}catch(i){v(i instanceof Error?i.message:String(i))}finally{$(!1)}};a.useEffect(()=>{if(!d)return;P();const i=window.setInterval(()=>{document.hidden||P()},4e3);return()=>window.clearInterval(i)},[d,m.join(",")]),a.useEffect(()=>{const i=_.current;if(!i)return;const k=()=>{const L=i.scrollHeight-i.scrollTop-i.clientHeight;R(L<=24)};return k(),i.addEventListener("scroll",k),()=>i.removeEventListener("scroll",k)},[]),a.useEffect(()=>{if(!_.current||y.length===0)return;const k=new Set(y.map(A=>`${A.ts??""}::${A.level}::${A.source}::${A.message}`));let L=!1;k.forEach(A=>{E.current.has(A)||(L=!0)}),E.current=k,M&&L&&requestAnimationFrame(()=>{_.current&&(_.current.scrollTop=_.current.scrollHeight)})},[y,M]);const z=a.useMemo(()=>new Set(m),[m]);return e.jsxs("div",{className:"mx-auto max-w-7xl space-y-6",children:[e.jsxs("header",{className:"mb-1",children:[e.jsxs("div",{className:"flex items-center gap-2 text-[10px] uppercase tracking-[0.14em] text-on-surface-variant",children:[e.jsx("span",{children:n("sys.overview.breadcrumb.console")}),e.jsx("span",{children:"/"}),e.jsx("span",{className:"text-primary",children:n("sys.overview.breadcrumb.module")})]}),e.jsx("h2",{className:"mt-1 font-headline text-3xl font-black tracking-tight",children:n("sys.overview.title")}),e.jsx("p",{className:"text-sm text-on-surface-variant",children:n("sys.overview.subtitle")})]}),c&&e.jsx("div",{className:"rounded-lg border border-error/40 bg-error-container/20 px-3 py-2 text-sm text-on-error-container",children:c}),e.jsxs("section",{className:"grid grid-cols-12 gap-4",children:[e.jsxs("article",{className:"col-span-12 rounded-xl border border-outline-variant/20 bg-surface-container p-4 md:col-span-3",children:[e.jsxs("div",{className:"mb-3 flex items-center justify-between text-[10px] uppercase tracking-wider text-on-surface-variant",children:[e.jsx("span",{children:n("sys.overview.metric.cpu")}),e.jsx(Se,{className:"h-4 w-4 text-primary"})]}),e.jsxs("div",{className:"text-3xl font-bold text-on-surface",children:[h,"%"]}),e.jsx("div",{className:"mt-3 h-1.5 w-full overflow-hidden rounded bg-surface-container-high",children:e.jsx("div",{className:"h-full bg-primary",style:{width:`${Math.min(Number(h)||0,100)}%`}})})]}),e.jsxs("article",{className:"col-span-12 rounded-xl border border-outline-variant/20 bg-surface-container p-4 md:col-span-3",children:[e.jsxs("div",{className:"mb-3 flex items-center justify-between text-[10px] uppercase tracking-wider text-on-surface-variant",children:[e.jsx("span",{children:n("sys.overview.metric.mem")}),e.jsx(me,{className:"h-4 w-4 text-secondary"})]}),e.jsxs("div",{className:"text-3xl font-bold text-on-surface",children:[T,"%"]}),e.jsx("div",{className:"mt-3 h-1.5 w-full overflow-hidden rounded bg-surface-container-high",children:e.jsx("div",{className:"h-full bg-secondary",style:{width:`${Math.min(Number(T)||0,100)}%`}})})]}),e.jsxs("article",{className:"col-span-12 rounded-xl border border-outline-variant/20 bg-surface-container p-4 md:col-span-3",children:[e.jsxs("div",{className:"mb-3 flex items-center justify-between text-[10px] uppercase tracking-wider text-on-surface-variant",children:[e.jsx("span",{children:n("sys.overview.metric.temp")}),e.jsx(He,{className:"h-4 w-4 text-primary"})]}),e.jsxs("div",{className:"text-3xl font-bold text-on-surface",children:[O,"°C"]}),e.jsx("div",{className:"mt-3 text-xs text-on-surface-variant",children:n("sys.overview.tempState")})]}),e.jsxs("article",{className:"col-span-12 rounded-xl border border-outline-variant/20 bg-surface-container p-4 md:col-span-3",children:[e.jsxs("div",{className:"mb-3 flex items-center justify-between text-[10px] uppercase tracking-wider text-on-surface-variant",children:[e.jsx("span",{children:n("sys.overview.metric.wifi")}),e.jsx(fe,{className:"h-4 w-4 text-primary"})]}),e.jsx("div",{className:"text-3xl font-bold text-on-surface",children:C}),e.jsx("div",{className:"mt-3 text-xs text-on-surface-variant",children:w})]})]}),e.jsxs("section",{className:"grid grid-cols-12 gap-4",children:[e.jsxs("article",{className:"col-span-12 rounded-xl border border-white/5 bg-surface-container-low p-6 lg:col-span-8",children:[e.jsxs("div",{className:"mb-6 flex items-center justify-between",children:[e.jsxs("div",{children:[e.jsx("h3",{className:"font-headline text-lg font-bold",children:n("sys.overview.wifiSummary")}),e.jsxs("p",{className:"text-xs text-on-surface-variant",children:[n("sys.overview.iface"),": ",String((t==null?void 0:t.wifi_interface)??"wlan0")]})]}),e.jsx("span",{className:"rounded border border-primary/30 bg-primary/10 px-2 py-1 text-[10px] uppercase tracking-widest text-primary",children:n("sys.overview.linkActive")})]}),e.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[e.jsxs("div",{className:"rounded-lg border border-outline-variant/20 bg-surface-container p-4",children:[e.jsx("p",{className:"text-[10px] uppercase tracking-widest text-on-surface-variant",children:n("sys.overview.signal")}),e.jsx("p",{className:"mt-1 font-mono text-2xl font-bold",children:w})]}),e.jsxs("div",{className:"rounded-lg border border-outline-variant/20 bg-surface-container p-4",children:[e.jsx("p",{className:"text-[10px] uppercase tracking-widest text-on-surface-variant",children:n("sys.overview.quality")}),e.jsx("p",{className:"mt-1 font-mono text-2xl font-bold",children:C})]})]})]}),e.jsxs("aside",{className:"col-span-12 space-y-4 lg:col-span-4",children:[e.jsxs("div",{className:"rounded-xl border border-outline-variant/20 bg-surface-container p-4",children:[e.jsx("p",{className:"text-[10px] uppercase tracking-widest text-on-surface-variant",children:n("sys.overview.metric.uptime")}),e.jsx("p",{className:"mt-1 text-xl font-bold",children:Ze(t==null?void 0:t.uptime_seconds)})]}),e.jsxs("div",{className:"rounded-xl border border-outline-variant/20 bg-surface-container p-4",children:[e.jsx("p",{className:"text-[10px] uppercase tracking-widest text-on-surface-variant",children:n("sys.overview.metric.load")}),e.jsx("p",{className:"mt-1 text-xl font-bold",children:j})]}),e.jsxs("div",{className:"rounded-xl border border-outline-variant/20 bg-surface-container p-4",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("p",{className:"text-[10px] uppercase tracking-widest text-on-surface-variant",children:n("sys.overview.metric.storage")}),e.jsx(Fe,{className:"h-4 w-4 text-on-surface-variant"})]}),e.jsx("p",{className:"mt-2 text-sm text-on-surface-variant",children:n("sys.overview.storageComingSoon")})]})]})]}),e.jsxs("section",{className:"rounded-xl border border-outline-variant/20 bg-surface-container-lowest p-4",children:[e.jsxs("div",{className:"mb-3 flex flex-wrap items-center justify-between gap-3 border-b border-outline-variant/20 pb-2",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("span",{className:"text-[10px] uppercase tracking-widest text-primary",children:n("sys.logs.title")}),e.jsxs("span",{className:"text-[10px] text-on-surface-variant",children:[n("sys.logs.kernel"),": ",String((t==null?void 0:t.os)??"—")]})]}),e.jsxs("div",{className:"flex items-center gap-2 text-xs",children:[e.jsxs("label",{className:"inline-flex items-center gap-2 text-on-surface-variant",children:[e.jsx("input",{type:"checkbox",checked:d,onChange:i=>N(i.target.checked)}),n("sys.logs.liveToggle")]}),e.jsx("button",{type:"button",className:"rounded border border-outline-variant/40 px-2 py-1 text-on-surface-variant hover:border-primary hover:text-on-surface",onClick:()=>void P(),children:e.jsxs("span",{className:"inline-flex items-center gap-1",children:[e.jsx(he,{className:"h-3.5 w-3.5"})," ",n("sys.logs.refresh")]})})]})]}),e.jsxs("div",{className:"mb-2 flex flex-wrap items-center gap-2 text-xs",children:[["INFO","WARN","ERROR"].map(i=>e.jsxs("label",{className:"inline-flex items-center gap-1.5 text-on-surface-variant",children:[e.jsx("input",{type:"checkbox",checked:z.has(i),onChange:k=>{f(L=>k.target.checked?Array.from(new Set([...L,i])):L.filter(A=>A!==i))}}),e.jsx("span",{className:ye(i),children:i})]},i)),!d&&e.jsxs("span",{className:"inline-flex items-center gap-1 rounded border border-outline-variant/30 px-2 py-0.5 text-[11px] text-on-surface-variant",children:[e.jsx(Be,{className:"h-3.5 w-3.5"})," ",n("sys.logs.liveOffHint")]})]}),S&&e.jsx("div",{className:"mb-2 rounded border border-error/40 bg-error-container/20 px-2 py-1 text-xs text-on-error-container",children:S}),e.jsxs("div",{ref:_,className:"og-scrollbar max-h-72 space-y-1 overflow-auto font-mono text-[11px] text-on-surface-variant",children:[p&&y.length===0&&e.jsx("div",{children:n("sys.logs.loading")}),!p&&y.length===0&&e.jsx("div",{children:n("sys.logs.empty")}),y.map((i,k)=>e.jsxs("div",{className:"flex items-start gap-2",children:[e.jsxs("span",{className:"shrink-0 text-primary",children:["[",Je(i.ts),"]"]}),e.jsx("span",{className:`shrink-0 ${ye(i.level)}`,children:i.level}),e.jsx("span",{className:"shrink-0 text-on-surface/80",children:i.source}),e.jsx("span",{className:"min-w-0 break-words text-on-surface",children:i.message})]},`${i.ts||"ts"}-${k}`))]})]})]})}function ie(){return typeof window.OGSCOPE_HTTP_PORT=="number"?window.OGSCOPE_HTTP_PORT:8e3}function Ve(s){const o=Math.max(0,parseInt(String(s??0),10)||0),n=Math.floor(o/86400),t=Math.floor(o%86400/3600),c=Math.floor(o%3600/60);return n>0?`${n}天 ${t}小时`:t>0?`${t}小时 ${c}分`:`${c}分`}function Qe(){const{info:s,error:o}=pe(),[n,t]=a.useState("加载中..."),[c,d]=a.useState(""),[N,m]=a.useState(""),[f,y]=a.useState([]),[g,S]=a.useState(!1),[v,p]=a.useState([]),[$,M]=a.useState(!1),[R,_]=a.useState(""),[E,h]=a.useState(""),[T,O]=a.useState(!1),[j,C]=a.useState(""),[w,P]=a.useState(`http://192.168.4.1:${ie()}`),[z,i]=a.useState("OGScope_xxxx"),[k,L]=a.useState(null),[A,I]=a.useState("—"),B=a.useCallback(r=>{const x=r.mode||"unknown",b=r.active_connection||"-",H=r.wireless_interface||"wlan0",D=r.ap_ipv4||"-",Z=r.configured?"是":"否",Y=r.message?`,消息: ${r.message}`:"";r.ap_url_hint&&P(r.ap_url_hint),r.ap_ssid&&i(r.ap_ssid);const te=ie();if(r.mdns_hostname_hint){const oe=`http://${r.mdns_hostname_hint}:${te}/debug`;L(oe),I(oe)}else if(r.device_id_suffix){const oe=`http://${`ogscope-${r.device_id_suffix}.local`}:${te}/debug`;L(oe),I(oe)}else L(null),I("未提供");t(`模式: ${x} | 活动连接: ${b} | 接口: ${H} | AP地址: ${D} | 已配置: ${Z}${Y}`)},[]),X=a.useCallback(async()=>{const r=await K("/api/network/wifi",{cache:"no-store"});B(r)},[B]);a.useEffect(()=>{X().catch(r=>t(`获取状态失败: ${r.message}`))},[X]);const Q=async r=>{t(`正在切换到 ${r.toUpperCase()}...`);const x=await K("/api/network/wifi",{method:"POST",body:JSON.stringify({mode:r})});B(x)},ae=async()=>{S(!0),m("扫描中..."),y([]);try{const r=await K("/api/network/wifi/scan",{cache:"no-store"}),x=r.networks||[],b=r.hint?` ${r.hint}`:"";y(x),m(`扫描到 ${x.length} 个网络${b}`.trim())}catch(r){m(`扫描失败: ${r instanceof Error?r.message:String(r)}`)}finally{S(!1)}},ee=async r=>{const x=window.prompt(`输入密码: ${r}`,"");if(x!==null){d(""),t("正在连接..."),O(!0);try{const b=await K("/api/network/wifi/sta/connect",{method:"POST",body:JSON.stringify({ssid:r,password:x||null})});B(b);const H=b.mode||"unknown";d(H==="sta"?`连接成功,当前连接: ${b.active_connection||"—"}`:`连接请求已提交,当前模式: ${H},连接: ${b.active_connection||"—"}`),window.setTimeout(()=>void X().catch(()=>{}),2500)}catch(b){t(`连接失败: ${b instanceof Error?b.message:String(b)}`),d(`错误详情: ${b instanceof Error?b.message:String(b)}`)}finally{O(!1)}}},se=async()=>{const r=R.trim();if(!r){window.alert("请输入 SSID");return}d(""),t("正在连接..."),O(!0);try{const x=await K("/api/network/wifi/sta/connect",{method:"POST",body:JSON.stringify({ssid:r,password:E||null})});B(x);const b=x.mode||"unknown";d(b==="sta"?`连接成功,当前连接: ${x.active_connection||"—"}`:`连接请求已提交,当前模式: ${b},连接: ${x.active_connection||"—"}`),window.setTimeout(()=>void X().catch(()=>{}),2500)}catch(x){t(`连接失败: ${x instanceof Error?x.message:String(x)}`),d(`错误详情: ${x instanceof Error?x.message:String(x)}`)}finally{O(!1)}},ne=async()=>{M(!0);try{const r=await K("/api/network/wifi/profiles",{cache:"no-store"});p(r.profiles||[])}catch(r){p([]),window.alert(r instanceof Error?r.message:String(r))}finally{M(!1)}},u=async r=>{try{await K("/api/network/wifi/profile/activate",{method:"POST",body:JSON.stringify({connection_name:r})}),t("已发送激活请求")}catch(x){window.alert(x instanceof Error?x.message:String(x))}};async function F(r,x){const b=`http://${r}:${x}/health`;try{const H=new AbortController,D=window.setTimeout(()=>H.abort(),700),Z=await fetch(b,{signal:H.signal,mode:"cors"});if(window.clearTimeout(D),!Z.ok)return null;const Y=await Z.json();if(Y&&Y.status==="healthy")return`http://${r}:${x}`}catch{}return null}const G=async()=>{const r=ie();C("扫描中...");const x=["192.168.0","192.168.1","192.168.31","10.0.0"];for(const b of x){const H=[];for(let D=1;D<255;D++)H.push(`${b}.${D}`);for(let D=0;DF(ce,r)))).find(Boolean);if(te){C(`已找到设备: ${te}`),window.location.href=`${te}/debug`;return}}}C("未找到设备")},l=a.useMemo(()=>{if(!s)return[];const r=s.wifi_quality!=null&&!Number.isNaN(Number(s.wifi_quality))?`${Number(s.wifi_quality).toFixed(1)}%`:"—",x=s.wifi_signal_dbm!=null&&!Number.isNaN(Number(s.wifi_signal_dbm))?`${Number(s.wifi_signal_dbm).toFixed(0)} dBm (${String(s.wifi_interface??"?")})`:"—";return[["平台",String(s.platform??"—")],["系统",String(s.os??"—")],["CPU 占用",`${Number(s.cpu_usage??0).toFixed(1)}%`],["内存占用",`${Number(s.memory_usage??0).toFixed(1)}%`],["CPU 温度",`${Number(s.temperature??0).toFixed(1)} °C`],["运行时长",Ve(s.uptime_seconds)],["1 分钟负载",String(s.load_average_1m??"—")],["WiFi 质量",r],["WiFi 信号",x]]},[s]);return e.jsxs("div",{className:"mx-auto max-w-7xl space-y-6",children:[e.jsxs("header",{children:[e.jsxs("div",{className:"flex items-center gap-2 text-[10px] uppercase tracking-[0.14em] text-on-surface-variant",children:[e.jsx("span",{children:"Console"}),e.jsx("span",{children:"/"}),e.jsx("span",{className:"text-primary",children:"System_Network_Debug"})]}),e.jsx("h2",{className:"mt-1 font-headline text-3xl font-black tracking-tight",children:"NETWORK TERMINAL"}),e.jsx("p",{className:"text-sm text-on-surface-variant",children:"WiFi 管理、模式切换、发现与恢复工具"})]}),o&&e.jsx("div",{className:"rounded-lg border border-error/40 bg-error-container/20 px-3 py-2 text-sm text-on-error-container",children:o}),e.jsxs("section",{className:"grid grid-cols-12 gap-6",children:[e.jsxs("div",{className:"col-span-12 space-y-6 lg:col-span-8",children:[e.jsxs("div",{className:"rounded-xl border border-outline-variant/20 bg-surface-container",children:[e.jsxs("div",{className:"flex items-center justify-between border-b border-outline-variant/20 bg-surface-container-high px-4 py-3",children:[e.jsxs("div",{className:"flex items-center gap-2 text-xs font-semibold uppercase tracking-wider text-primary",children:[e.jsx(fe,{className:"h-4 w-4"}),"WiFi Control"]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("button",{type:"button",className:"rounded border border-outline-variant/40 px-2 py-1 text-xs hover:border-primary",onClick:()=>void Q("sta").catch(r=>t(String(r))),children:"STA"}),e.jsx("button",{type:"button",className:"rounded border border-outline-variant/40 px-2 py-1 text-xs hover:border-primary",onClick:()=>void Q("ap").catch(r=>t(String(r))),children:"AP"}),e.jsx("button",{type:"button",className:"rounded border border-outline-variant/40 p-1.5 hover:border-primary",onClick:()=>void X().catch(r=>t(String(r))),children:e.jsx(he,{className:"h-3.5 w-3.5"})})]})]}),e.jsxs("div",{className:"space-y-3 p-4",children:[e.jsx("p",{className:"rounded border border-outline-variant/20 bg-surface-container-low p-3 font-mono text-xs text-on-surface",children:n}),e.jsxs("div",{className:"grid gap-2 text-xs text-on-surface-variant md:grid-cols-2",children:[e.jsxs("p",{children:["AP 地址:",e.jsx("span",{className:"font-mono text-on-surface",children:w})]}),e.jsxs("p",{children:["mDNS:"," ",k?e.jsx("a",{className:"font-mono text-primary underline",href:k,children:A}):e.jsx("span",{className:"font-mono text-on-surface",children:A})]})]})]})]}),e.jsxs("div",{className:"rounded-xl border border-outline-variant/20 bg-surface-container p-4",children:[e.jsxs("div",{className:"mb-3 flex items-center justify-between",children:[e.jsx("h3",{className:"text-sm font-semibold uppercase tracking-wider",children:"Scan Results"}),e.jsx("button",{type:"button",disabled:g,className:"rounded bg-primary-container px-3 py-1.5 text-xs font-medium text-on-primary-container disabled:opacity-50",onClick:()=>void ae(),children:e.jsxs("span",{className:"inline-flex items-center gap-1",children:[e.jsx(_e,{className:"h-3.5 w-3.5"})," 扫描 WiFi"]})})]}),e.jsx("p",{className:"mb-3 text-xs text-on-surface-variant",children:N||"由设备端 NetworkManager 执行扫描"}),e.jsx("div",{className:"overflow-x-auto",children:e.jsxs("table",{className:"w-full text-left text-sm",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"border-b border-outline-variant/30 text-xs uppercase tracking-wider text-on-surface-variant",children:[e.jsx("th",{className:"p-2",children:"SSID"}),e.jsx("th",{className:"p-2",children:"信号"}),e.jsx("th",{className:"p-2",children:"安全"}),e.jsx("th",{className:"p-2 text-right",children:"操作"})]})}),e.jsx("tbody",{children:f.map(r=>e.jsxs("tr",{className:"border-b border-outline-variant/10",children:[e.jsx("td",{className:"p-2 font-mono",children:r.ssid}),e.jsx("td",{className:"p-2",children:r.signal??"—"}),e.jsx("td",{className:"p-2",children:r.security??"—"}),e.jsx("td",{className:"p-2 text-right",children:e.jsx("button",{type:"button",disabled:T,className:"rounded border border-primary/40 px-2 py-1 text-xs text-primary disabled:opacity-50",onClick:()=>void ee(r.ssid),children:"Connect"})})]},r.ssid))})]})})]}),e.jsxs("div",{className:"rounded-xl border border-outline-variant/20 bg-surface-container p-4",children:[e.jsx("h3",{className:"mb-3 text-sm font-semibold uppercase tracking-wider",children:"Known Networks"}),e.jsx("button",{type:"button",disabled:$,className:"rounded border border-outline-variant/40 px-3 py-1.5 text-xs disabled:opacity-50",onClick:()=>void ne(),children:"刷新已保存网络"}),e.jsxs("table",{className:"mt-3 w-full text-left text-sm",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"border-b border-outline-variant/30 text-xs uppercase tracking-wider text-on-surface-variant",children:[e.jsx("th",{className:"p-2",children:"连接名"}),e.jsx("th",{className:"p-2",children:"SSID"}),e.jsx("th",{className:"p-2",children:"自动连接"}),e.jsx("th",{className:"p-2 text-right",children:"操作"})]})}),e.jsx("tbody",{children:v.map(r=>e.jsxs("tr",{className:"border-b border-outline-variant/10",children:[e.jsx("td",{className:"p-2",children:r.connection_name}),e.jsx("td",{className:"p-2",children:r.ssid}),e.jsx("td",{className:"p-2",children:r.autoconnect?"是":"否"}),e.jsx("td",{className:"p-2 text-right",children:e.jsx("button",{type:"button",className:"rounded border border-outline-variant/40 px-2 py-1 text-xs hover:border-primary",onClick:()=>void u(r.connection_name),children:"Activate"})})]},r.connection_name))})]})]})]}),e.jsxs("aside",{className:"col-span-12 space-y-6 lg:col-span-4",children:[e.jsxs("div",{className:"rounded-xl border border-outline-variant/20 bg-surface-container p-4",children:[e.jsx("h3",{className:"mb-3 text-sm font-semibold uppercase tracking-wider",children:"System Monitor"}),e.jsx("div",{className:"grid gap-2",children:l.map(([r,x])=>e.jsxs("div",{className:"flex justify-between border-b border-outline-variant/10 pb-1 text-xs",children:[e.jsx("span",{className:"text-on-surface-variant",children:r}),e.jsx("span",{className:"font-mono text-on-surface",children:x})]},r))})]}),e.jsxs("div",{className:"rounded-xl border border-outline-variant/20 bg-surface-container p-4",children:[e.jsx("h3",{className:"mb-3 text-sm font-semibold uppercase tracking-wider",children:"Manual Connect"}),e.jsxs("div",{className:"space-y-2",children:[e.jsxs("label",{className:"block text-xs text-on-surface-variant",children:["SSID",e.jsx("input",{className:"mt-1 w-full rounded border border-outline-variant/40 bg-surface-container-low px-2 py-1.5 text-sm",value:R,onChange:r=>_(r.target.value)})]}),e.jsxs("label",{className:"block text-xs text-on-surface-variant",children:["Password",e.jsx("input",{type:"password",className:"mt-1 w-full rounded border border-outline-variant/40 bg-surface-container-low px-2 py-1.5 text-sm",value:E,onChange:r=>h(r.target.value)})]}),e.jsx("button",{type:"button",disabled:T,className:"w-full rounded bg-primary-container px-4 py-2 text-sm font-medium text-on-primary-container disabled:opacity-50",onClick:()=>void se(),children:"连接并切换 STA"})]}),e.jsx("p",{className:"mt-2 text-xs text-on-surface-variant",children:c})]}),e.jsxs("div",{className:"rounded-xl border border-outline-variant/20 bg-surface-container p-4",children:[e.jsx("h3",{className:"mb-2 text-sm font-semibold uppercase tracking-wider",children:"WiFi 引导"}),e.jsxs("ol",{className:"list-decimal space-y-1 pl-4 text-xs text-on-surface-variant",children:[e.jsxs("li",{children:["连接热点 ",e.jsx("strong",{children:z}),",密码 ",e.jsx("code",{children:"ogscopeadmin"})]}),e.jsxs("li",{children:["浏览器打开 ",e.jsxs("span",{className:"font-mono",children:["http://192.168.4.1:",ie()]})]}),e.jsx("li",{children:"扫描 WiFi 或手动填写 SSID 连接"})]})]}),e.jsxs("div",{className:"rounded-xl border border-outline-variant/20 bg-surface-container p-4",children:[e.jsx("h3",{className:"mb-2 text-sm font-semibold uppercase tracking-wider",children:"Find Device"}),e.jsx("p",{className:"mb-3 text-xs text-on-surface-variant",children:"扫描常见网段并探测 /health"}),e.jsx("button",{type:"button",className:"w-full rounded border border-outline-variant/40 px-3 py-2 text-sm hover:border-primary",onClick:()=>void G(),children:"扫描局域网"}),e.jsx("p",{className:"mt-2 text-xs text-on-surface-variant",children:j})]})]})]})]})}function es(s){var t,c;const o=(c=(t=s==null?void 0:s.data)==null?void 0:t.services)==null?void 0:c.hmi;if(!o||typeof o!="object")return null;const n=o.display;return!n||typeof n!="object"?null:n}function ss(s){try{return JSON.stringify(s,null,2)}catch{return String(s)}}function ts(){var j,C;const{t:s}=J(),[o,n]=a.useState(null),[t,c]=a.useState(null),[d,N]=a.useState(!1),[m,f]=a.useState(null),[y,g]=a.useState(null),[S,v]=a.useState(40),[p,$]=a.useState(80),[M,R]=a.useState(200),_=a.useCallback(async()=>{try{c(null);const w=await Ke();n(w)}catch(w){n(null),c(w instanceof Error?w.message:String(w))}},[]);a.useEffect(()=>{_()},[_]);const E=a.useCallback(async(w,P={})=>{var z;N(!0),g(null);try{const i=await Xe({target:"hmi",action:w,payload:P,timeout_ms:8e3});if(f(i),!i.success){const A=((z=i.error)==null?void 0:z.message)??"RPC failed";g(A);return}const k=i.data,L=k==null?void 0:k.result;L&&L.accepted===!1&&L.message?g(L.message):g(null),await _()}catch(i){f(null),g(i instanceof Error?i.message:String(i))}finally{N(!1)}},[_]),h=es(o),T=(C=(j=o==null?void 0:o.data)==null?void 0:j.services)==null?void 0:C.hmi,O=typeof(T==null?void 0:T.screen_on)=="boolean"?T.screen_on:void 0;return e.jsxs("div",{className:"mx-auto max-w-6xl space-y-6",children:[e.jsxs("header",{children:[e.jsx("div",{className:"text-[10px] uppercase tracking-[0.14em] text-on-surface-variant",children:s("sys.placeholder.breadcrumb")}),e.jsx("h2",{className:"mt-1 font-headline text-3xl font-black tracking-tight",children:s("sys.hmi.title")}),e.jsx("p",{className:"text-sm text-on-surface-variant",children:s("sys.hmi.desc")})]}),e.jsxs("section",{className:"rounded-xl border border-outline-variant/20 bg-surface-container-low p-4",children:[e.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-3",children:[e.jsx("p",{className:"text-xs font-medium text-on-surface",children:s("sys.hmi.status.section")}),e.jsx("button",{type:"button",className:"rounded-lg border border-outline-variant/40 bg-surface-container px-3 py-1.5 text-xs font-medium text-on-surface hover:bg-surface-container-high",onClick:()=>void _(),disabled:d,children:s("sys.hmi.status.refresh")})]}),t?e.jsx("p",{className:"mt-2 text-sm text-error",children:t}):e.jsxs("dl",{className:"mt-3 grid gap-2 font-mono text-[11px] text-on-surface-variant sm:grid-cols-2",children:[e.jsxs("div",{children:[e.jsx("dt",{className:"text-on-surface-variant",children:s("sys.hmi.status.displayEnabled")}),e.jsx("dd",{className:"text-on-surface",children:(h==null?void 0:h.enabled)===!0?"true":"false"})]}),e.jsxs("div",{children:[e.jsx("dt",{children:s("sys.hmi.status.spidev")}),e.jsx("dd",{className:h!=null&&h.spidev_present?"text-primary":"text-error",children:h!=null&&h.spidev_present?s("sys.hmi.status.yes"):s("sys.hmi.status.no")})]}),e.jsxs("div",{children:[e.jsx("dt",{children:s("sys.hmi.status.resolution")}),e.jsxs("dd",{children:[(h==null?void 0:h.width)??"—"," × ",(h==null?void 0:h.height)??"—"," · DC GPIO ",(h==null?void 0:h.dc_pin)??"—"]})]}),e.jsxs("div",{children:[e.jsx("dt",{children:s("sys.hmi.status.driver")}),e.jsx("dd",{children:h!=null&&h.driver_open?s("sys.hmi.status.open"):s("sys.hmi.status.closed")})]}),e.jsxs("div",{children:[e.jsx("dt",{children:s("sys.hmi.status.screenOutput")}),e.jsx("dd",{children:O===void 0?"—":s(O?"sys.hmi.status.on":"sys.hmi.status.off")})]}),e.jsxs("div",{className:"sm:col-span-2",children:[e.jsx("dt",{children:s("sys.hmi.status.lastPattern")}),e.jsx("dd",{children:(h==null?void 0:h.last_pattern)??"—"})]}),h!=null&&h.last_error?e.jsxs("div",{className:"sm:col-span-2",children:[e.jsx("dt",{children:s("sys.hmi.status.lastError")}),e.jsx("dd",{className:"text-error",children:h.last_error})]}):null]})]}),e.jsxs("section",{className:"rounded-xl border border-outline-variant/20 bg-surface-container-low p-4",children:[e.jsx("p",{className:"text-xs font-medium text-on-surface",children:s("sys.hmi.actions.section")}),e.jsx("p",{className:"mt-1 text-[11px] text-on-surface-variant",children:s("sys.hmi.actions.hint")}),e.jsxs("div",{className:"mt-4 flex flex-wrap gap-2",children:[e.jsx("button",{type:"button",className:"rounded-lg bg-primary px-3 py-2 text-xs font-semibold text-on-primary hover:opacity-90 disabled:opacity-50",disabled:d,onClick:()=>void E("display.test_pattern",{pattern:"smoke"}),children:s("sys.hmi.actions.smoke")}),e.jsx("button",{type:"button",className:"rounded-lg border border-outline-variant/40 bg-surface-container px-3 py-2 text-xs font-medium text-on-surface hover:bg-surface-container-high disabled:opacity-50",disabled:d,onClick:()=>void E("display.test_pattern",{pattern:"colorbars"}),children:s("sys.hmi.actions.colorbars")})]}),e.jsxs("div",{className:"mt-6 flex flex-wrap items-end gap-3",children:[e.jsxs("label",{className:"flex flex-col gap-1 text-[11px] text-on-surface-variant",children:["R",e.jsx("input",{type:"number",min:0,max:255,className:"w-20 rounded border border-outline-variant/40 bg-surface-container px-2 py-1 font-mono text-sm text-on-surface",value:S,onChange:w=>v(Number(w.target.value))})]}),e.jsxs("label",{className:"flex flex-col gap-1 text-[11px] text-on-surface-variant",children:["G",e.jsx("input",{type:"number",min:0,max:255,className:"w-20 rounded border border-outline-variant/40 bg-surface-container px-2 py-1 font-mono text-sm text-on-surface",value:p,onChange:w=>$(Number(w.target.value))})]}),e.jsxs("label",{className:"flex flex-col gap-1 text-[11px] text-on-surface-variant",children:["B",e.jsx("input",{type:"number",min:0,max:255,className:"w-20 rounded border border-outline-variant/40 bg-surface-container px-2 py-1 font-mono text-sm text-on-surface",value:M,onChange:w=>R(Number(w.target.value))})]}),e.jsx("button",{type:"button",className:"rounded-lg border border-outline-variant/40 bg-surface-container px-3 py-2 text-xs font-medium text-on-surface hover:bg-surface-container-high disabled:opacity-50",disabled:d,onClick:()=>void E("display.test_pattern",{pattern:"fill",r:S,g:p,b:M}),children:s("sys.hmi.actions.fill")})]}),e.jsxs("div",{className:"mt-6 flex flex-wrap gap-2 border-t border-outline-variant/20 pt-4",children:[e.jsx("button",{type:"button",className:"rounded-lg border border-outline-variant/40 px-3 py-2 text-xs text-on-surface hover:bg-surface-container-high disabled:opacity-50",disabled:d,onClick:()=>void E("screen.set",{on:!0}),children:s("sys.hmi.actions.screenOn")}),e.jsx("button",{type:"button",className:"rounded-lg border border-outline-variant/40 px-3 py-2 text-xs text-on-surface hover:bg-surface-container-high disabled:opacity-50",disabled:d,onClick:()=>void E("screen.set",{on:!1}),children:s("sys.hmi.actions.screenOff")}),e.jsx("button",{type:"button",className:"rounded-lg border border-outline-variant/40 px-3 py-2 text-xs text-on-surface hover:bg-surface-container-high disabled:opacity-50",disabled:d,onClick:()=>void E("display.release"),children:s("sys.hmi.actions.release")})]}),y?e.jsx("p",{className:"mt-3 text-sm text-error",children:y}):null,e.jsxs("details",{className:"mt-4",children:[e.jsx("summary",{className:"cursor-pointer text-[11px] text-on-surface-variant",children:s("sys.hmi.rawJson")}),e.jsx("pre",{className:"mt-2 max-h-64 overflow-auto rounded border border-outline-variant/30 bg-surface-container p-2 font-mono text-[10px] text-on-surface",children:m?ss(m):"—"})]})]})]})}const rs={sensors:{titleKey:"sys.placeholder.sensors.title",descKey:"sys.placeholder.sensors.desc",blocks:["sys.placeholder.sensors.block1","sys.placeholder.sensors.block2","sys.placeholder.sensors.block3"]},hmi:{titleKey:"sys.placeholder.hmi.title",descKey:"sys.placeholder.hmi.desc",blocks:["sys.placeholder.hmi.block1","sys.placeholder.hmi.block2","sys.placeholder.hmi.block3"]},power:{titleKey:"sys.placeholder.power.title",descKey:"sys.placeholder.power.desc",blocks:["sys.placeholder.power.block1","sys.placeholder.power.block2","sys.placeholder.power.block3"]}};function ve({scope:s}){const{t:o}=J(),n=rs[s];return e.jsxs("div",{className:"mx-auto max-w-6xl space-y-6",children:[e.jsxs("header",{children:[e.jsx("div",{className:"text-[10px] uppercase tracking-[0.14em] text-on-surface-variant",children:o("sys.placeholder.breadcrumb")}),e.jsx("h2",{className:"mt-1 font-headline text-3xl font-black tracking-tight",children:o(n.titleKey)}),e.jsx("p",{className:"text-sm text-on-surface-variant",children:o(n.descKey)})]}),e.jsx("section",{className:"grid grid-cols-12 gap-4",children:n.blocks.map(t=>e.jsxs("article",{className:"col-span-12 rounded-xl border border-dashed border-outline-variant/40 bg-surface-container/60 p-5 md:col-span-4",children:[e.jsx("p",{className:"text-[10px] uppercase tracking-widest text-primary",children:o("sys.placeholder.block")}),e.jsx("h3",{className:"mt-2 text-lg font-semibold",children:o(t)}),e.jsx("p",{className:"mt-2 text-sm text-on-surface-variant",children:o("sys.placeholder.desc")})]},t))}),e.jsx("section",{className:"rounded-xl border border-outline-variant/20 bg-surface-container-low p-4",children:e.jsx("p",{className:"font-mono text-xs text-on-surface-variant",children:o("sys.placeholder.status")})})]})}const W=62,re=4;function as(s){const{rollDeg:o,pitchDeg:n,yawRateDps:t}=s,{t:c}=J(),d=`rotateX(${-n}deg) rotateY(${o}deg)`;return e.jsxs("div",{className:"mt-5 rounded-xl border border-teal-500/25 bg-black/25 p-4 ring-1 ring-teal-500/10",children:[e.jsx("p",{className:"text-center text-[11px] font-semibold text-teal-200/95",children:c("sys.sensors.gyro.att3dTitle")}),e.jsx("p",{className:"mx-auto mt-1 max-w-xl text-center text-[10px] leading-relaxed text-on-surface-variant",children:c("sys.sensors.gyro.att3dDesc")}),e.jsxs("div",{className:"mt-4 flex flex-col gap-6 xl:flex-row xl:items-start xl:justify-between xl:gap-8",children:[e.jsxs("div",{className:"flex min-w-0 flex-1 flex-col items-center gap-4",children:[e.jsx("div",{className:"flex shrink-0 items-center justify-center py-2",style:{perspective:"820px"},children:e.jsxs("div",{className:"relative h-[188px] w-[220px]",style:{transformStyle:"preserve-3d",transform:d},children:[e.jsxs("div",{className:"absolute left-[10px] top-[18px] h-[152px] w-[200px] rounded-2xl border-2 border-teal-400/75 bg-gradient-to-br from-slate-600/90 via-slate-800/95 to-slate-950 shadow-[0_20px_50px_rgba(0,0,0,0.55),inset_0_1px_0_rgba(255,255,255,0.08)] transition-transform duration-150 ease-out",style:{transformStyle:"preserve-3d"},children:[e.jsx("div",{className:"pointer-events-none absolute inset-x-0 top-2 flex justify-center",children:e.jsx("span",{className:"rounded bg-black/35 px-2 py-0.5 text-[9px] font-bold uppercase tracking-[0.2em] text-teal-100",children:"TOP"})}),e.jsx("div",{className:"absolute bottom-2 left-2 font-mono text-[9px] text-slate-400",children:"MPU-6050"})]}),e.jsxs("div",{className:"pointer-events-none absolute left-[110px] top-[94px] h-0 w-0",style:{transformStyle:"preserve-3d"},children:[e.jsx("div",{className:"absolute rounded-full bg-white/90 shadow-[0_0_6px_rgba(255,255,255,0.6)]",style:{width:7,height:7,left:-3.5,top:-3.5,transform:"translateZ(0.5px)"}}),e.jsx("div",{className:"absolute bg-amber-400 shadow-md ring-1 ring-amber-200/35",style:{width:W,height:re,left:0,top:-re/2,transformOrigin:"0 50%"}}),e.jsx("span",{className:"absolute whitespace-nowrap font-mono text-[10px] font-bold text-amber-200",style:{left:W+4,top:-8},children:c("sys.sensors.gyro.axisXLabel")}),e.jsx("div",{className:"absolute bg-sky-400 shadow-md ring-1 ring-sky-200/30",style:{width:W,height:re,left:0,top:-re/2,transformOrigin:"0 50%",transform:"rotateZ(90deg)"}}),e.jsx("span",{className:"absolute whitespace-nowrap font-mono text-[10px] font-bold text-sky-200",style:{left:-6,top:-W-16},children:c("sys.sensors.gyro.axisYLabel")}),e.jsx("div",{className:"absolute bg-violet-400 shadow-md ring-1 ring-violet-200/35",style:{width:W,height:re,left:0,top:-re/2,transformOrigin:"0 50%",transform:"rotateY(-90deg)"}}),e.jsx("span",{className:"absolute whitespace-nowrap font-mono text-[10px] font-bold text-violet-200",style:{left:W*.35,top:-W*.45,transform:"translateZ(28px)"},children:c("sys.sensors.gyro.axisZLabel")}),e.jsx("div",{className:"absolute bg-amber-900/55",style:{width:W*.45,height:2,left:-W*.45,top:-1,transformOrigin:"100% 50%"}}),e.jsx("div",{className:"absolute bg-sky-900/50",style:{width:W*.45,height:2,left:0,top:-1,transformOrigin:"0 50%",transform:`rotateZ(90deg) translateX(${-W*.45}px)`}}),e.jsx("div",{className:"absolute bg-violet-900/45",style:{width:W*.4,height:2,left:0,top:-1,transformOrigin:"0 50%",transform:`rotateY(-90deg) translateX(${-W*.4}px)`}})]})]})}),e.jsx("p",{className:"max-w-md text-center text-[10px] leading-snug text-on-surface-variant",children:c("sys.sensors.gyro.att3dBodyAxes")})]}),e.jsxs("div",{className:"flex w-full max-w-[220px] shrink-0 flex-col items-center gap-2 self-center xl:self-start",children:[e.jsx("p",{className:"text-center text-[10px] font-medium text-on-surface-variant",children:c("sys.sensors.gyro.att3dRefTitle")}),e.jsx("div",{className:"flex items-center justify-center rounded-lg border border-outline-variant/30 bg-slate-950/50 px-4 py-5 ring-1 ring-white/5",children:e.jsx("div",{style:{perspective:"280px"},children:e.jsx("div",{className:"relative h-[100px] w-[100px]",style:{transformStyle:"preserve-3d",transform:"rotateX(58deg) rotateZ(-42deg)"},children:e.jsxs("div",{className:"absolute left-1/2 top-1/2 h-0 w-0",style:{transformStyle:"preserve-3d"},children:[e.jsx("div",{className:"absolute bg-amber-400/95",style:{width:44,height:3,left:0,top:-1.5,transformOrigin:"0 50%"}}),e.jsx("div",{className:"absolute bg-sky-400/95",style:{width:44,height:3,left:0,top:-1.5,transformOrigin:"0 50%",transform:"rotateZ(90deg)"}}),e.jsx("div",{className:"absolute bg-violet-400/95",style:{width:44,height:3,left:0,top:-1.5,transformOrigin:"0 50%",transform:"rotateY(-90deg)"}}),e.jsx("div",{className:"absolute rounded-full bg-white/80",style:{width:5,height:5,left:-2.5,top:-2.5}})]})})})}),e.jsx("p",{className:"text-center text-[9px] leading-relaxed text-on-surface-variant/85",children:c("sys.sensors.gyro.att3dRefDesc")})]}),e.jsxs("div",{className:"grid w-full min-w-[200px] max-w-md grid-cols-3 gap-3 font-mono text-[11px] lg:max-w-lg xl:max-w-[340px]",children:[e.jsxs("div",{className:"rounded-lg border border-outline-variant/30 bg-surface-container/80 px-2 py-2 text-center",children:[e.jsx("p",{className:"text-[9px] uppercase tracking-wider text-on-surface-variant",children:c("sys.sensors.gyro.roll")}),e.jsxs("p",{className:"mt-1 text-lg font-bold tabular-nums text-teal-200",children:[o.toFixed(1),"°"]})]}),e.jsxs("div",{className:"rounded-lg border border-outline-variant/30 bg-surface-container/80 px-2 py-2 text-center",children:[e.jsx("p",{className:"text-[9px] uppercase tracking-wider text-on-surface-variant",children:c("sys.sensors.gyro.pitch")}),e.jsxs("p",{className:"mt-1 text-lg font-bold tabular-nums text-teal-200",children:[n.toFixed(1),"°"]})]}),e.jsxs("div",{className:"rounded-lg border border-outline-variant/30 bg-surface-container/80 px-2 py-2 text-center",children:[e.jsx("p",{className:"text-[9px] uppercase tracking-wider text-on-surface-variant",children:c("sys.sensors.gyro.yawRate")}),e.jsx("p",{className:"mt-1 text-lg font-bold tabular-nums text-amber-200/95",children:t.toFixed(2)}),e.jsx("p",{className:"text-[9px] text-on-surface-variant",children:"°/s"})]})]})]})]})}const xe=250;function ue(s){const{label:o,value:n,maxAbs:t,unit:c}=s,d=Math.max(-1,Math.min(1,n/t)),N=d>=0?50:50+d*50,m=Math.abs(d)*50;return e.jsxs("div",{className:"space-y-1",children:[e.jsxs("div",{className:"flex items-baseline justify-between gap-2",children:[e.jsx("span",{className:"text-[11px] font-medium text-on-surface",children:o}),e.jsxs("span",{className:"font-mono text-xs tabular-nums text-sky-100",children:[n.toFixed(2),e.jsxs("span",{className:"text-on-surface-variant",children:[" ",c]})]})]}),e.jsxs("div",{className:"relative h-5 w-full overflow-hidden rounded-md bg-slate-900/80 ring-1 ring-slate-600/50",children:[e.jsx("div",{className:"absolute left-1/2 top-0 z-10 h-full w-px -translate-x-px bg-slate-500/90"}),e.jsx("div",{className:"absolute top-1 h-3 rounded-sm bg-gradient-to-r from-emerald-600 to-sky-500 shadow-sm",style:{left:`${N}%`,width:`${Math.max(m,d===0?0:.8)}%`}})]})]})}function ns(s){const{bus:o,addr:n}=s,{t}=J(),[c,d]=a.useState(!1),[N,m]=a.useState(null),[f,y]=a.useState(null),[g,S]=a.useState(null),[v,p]=a.useState(null),[$,M]=a.useState(null),[R,_]=a.useState(null),[E,h]=a.useState(!1),T=a.useRef(null),O=a.useCallback(async()=>{var C;d(!0),m(null);try{const w=new URLSearchParams({bus:String(o),addr:String(n)}),P=await U(`/api/debug/sensors/mpu6050/imu-sample?${w.toString()}`);if(!P.success){y(null),S(null),p(null),M(null),_(null),m(((C=P.sample)==null?void 0:C.error)||P.error||t("sys.sensors.gyro.errUnknown"));return}y(P.gyro_dps??null),S(P.gyro_raw??null),p(P.tilt_deg??null),M(P.yaw_rate_dps??null),_(P.accel_g??null)}catch(w){y(null),S(null),p(null),M(null),_(null),m(w instanceof Error?w.message:String(w))}finally{d(!1)}},[n,o,t]);a.useEffect(()=>{if(!E){T.current&&(clearInterval(T.current),T.current=null);return}return T.current=setInterval(()=>void O(),500),()=>{T.current&&clearInterval(T.current)}},[E,O]);const j=v!=null&&$!=null&&Number.isFinite(v.roll)&&Number.isFinite(v.pitch);return e.jsxs("div",{className:"mt-6 rounded-xl border border-emerald-600/35 bg-gradient-to-br from-slate-900/40 to-surface-container/70 p-4",children:[e.jsxs("div",{className:"flex flex-wrap items-start justify-between gap-3",children:[e.jsxs("div",{children:[e.jsx("p",{className:"text-sm font-semibold text-on-surface",children:t("sys.sensors.gyro.title")}),e.jsx("p",{className:"mt-1 max-w-xl text-[11px] leading-snug text-on-surface-variant",children:t("sys.sensors.gyro.subtitle")})]}),e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsxs("label",{className:"flex cursor-pointer items-center gap-1.5 text-[11px] text-on-surface-variant",children:[e.jsx("input",{type:"checkbox",checked:E,onChange:C=>h(C.target.checked)}),t("sys.sensors.gyro.live")]}),e.jsx("button",{type:"button",disabled:c,className:"rounded-lg bg-emerald-800/90 px-3 py-1.5 text-xs font-medium text-emerald-50 hover:bg-emerald-700 disabled:opacity-50",onClick:()=>void O(),children:t(c?"sys.sensors.gyro.loading":"sys.sensors.gyro.btn")})]})]}),j&&e.jsx(as,{rollDeg:v.roll,pitchDeg:v.pitch,yawRateDps:$}),f&&e.jsxs("div",{className:"mt-5 grid gap-4 md:grid-cols-3",children:[e.jsxs("div",{className:"rounded-lg border border-emerald-500/20 bg-black/20 px-3 py-3 text-center md:col-span-1",children:[e.jsx("p",{className:"text-[10px] text-on-surface-variant",children:"ωx"}),e.jsx("p",{className:"font-mono text-3xl font-bold tabular-nums text-emerald-200",children:f.x.toFixed(2)}),e.jsx("p",{className:"text-[10px] text-on-surface-variant",children:"°/s"})]}),e.jsxs("div",{className:"rounded-lg border border-emerald-500/20 bg-black/20 px-3 py-3 text-center md:col-span-1",children:[e.jsx("p",{className:"text-[10px] text-on-surface-variant",children:"ωy"}),e.jsx("p",{className:"font-mono text-3xl font-bold tabular-nums text-emerald-200",children:f.y.toFixed(2)}),e.jsx("p",{className:"text-[10px] text-on-surface-variant",children:"°/s"})]}),e.jsxs("div",{className:"rounded-lg border border-emerald-500/20 bg-black/20 px-3 py-3 text-center md:col-span-1",children:[e.jsx("p",{className:"text-[10px] text-on-surface-variant",children:"ωz"}),e.jsx("p",{className:"font-mono text-3xl font-bold tabular-nums text-emerald-200",children:f.z.toFixed(2)}),e.jsx("p",{className:"text-[10px] text-on-surface-variant",children:"°/s"})]})]}),f&&e.jsxs("div",{className:"mt-5 space-y-4",children:[e.jsx("p",{className:"text-[10px] font-medium uppercase tracking-wider text-on-surface-variant",children:t("sys.sensors.gyro.barsTitle")}),e.jsx(ue,{label:"X",value:f.x,maxAbs:xe,unit:"°/s"}),e.jsx(ue,{label:"Y",value:f.y,maxAbs:xe,unit:"°/s"}),e.jsx(ue,{label:"Z",value:f.z,maxAbs:xe,unit:"°/s"})]}),R&&e.jsxs("p",{className:"mt-3 text-center font-mono text-[10px] text-on-surface-variant",children:["g — X:",R.x.toFixed(3)," Y:",R.y.toFixed(3)," Z:",R.z.toFixed(3)]}),g&&e.jsxs("div",{className:"mt-4 rounded border border-outline-variant/25 bg-surface-container/50 px-3 py-2",children:[e.jsx("p",{className:"text-[10px] text-on-surface-variant",children:t("sys.sensors.gyro.rawBlock")}),e.jsxs("p",{className:"mt-1 font-mono text-[11px] tabular-nums text-on-surface",children:["raw X=",g.x," · Y=",g.y," · Z=",g.z]})]}),!f&&!N&&!c&&e.jsx("p",{className:"mt-4 text-[11px] text-on-surface-variant",children:t("sys.sensors.gyro.hint")}),N&&e.jsx("p",{className:"mt-3 rounded border border-amber-500/40 bg-amber-500/10 px-2 py-1.5 font-mono text-[11px] text-amber-100",children:N})]})}const be=3.4,Ce=7,os=3;function ls(s){const o=(s%360+360)%360;return o<1||o>359?"cardN":Math.abs(o-90)<1?"cardE":Math.abs(o-180)<1?"cardS":Math.abs(o-270)<1?"cardW":null}function is(){const s=[],o=Ce*360;for(let n=0;n<=o;n+=5){const t=n%360,c=n%30===0,d=!c&&n%10===0,N=ls(t),m=!N&&c&&t%90!==0?t:void 0;s.push({x:n*be,deg:n,h:c?"maj":d?"mid":"min",degLabel:m,cardKey:N??void 0})}return s}const cs=is(),je=Ce*360*be;function ds(s,o){const n=(s%360+360)%360;let c=(o%360+360)%360-n;return c=(c+180)%360-180,s+c}function xs(s){const{bus:o,addr:n}=s,{t}=J(),[c,d]=a.useState(!1),[N,m]=a.useState(null),[f,y]=a.useState(null),[g,S]=a.useState(null),[v,p]=a.useState(!1),$=a.useRef(null),M=a.useRef(0),R=a.useRef(null),[_,E]=a.useState(320);a.useEffect(()=>{const j=R.current;if(!j)return;const C=new ResizeObserver(()=>{E(Math.max(200,j.clientWidth))});return C.observe(j),E(Math.max(200,j.clientWidth)),()=>C.disconnect()},[]),a.useEffect(()=>{y(null),S(null),m(null)},[o,n]);const h=a.useCallback(async()=>{const j=++M.current;d(!0);try{const C=new URLSearchParams({bus:String(o),addr:String(n)}),w=await U(`/api/debug/sensors/magnetometer/sample?${C.toString()}`);if(j!==M.current)return;if(!w.success){y(null),S(null),m(w.error||t("sys.sensors.compass.err"));return}m(null);const P=w.heading_deg??null;P!=null&&y(z=>z==null?os*360+P:ds(z,P)),S(w.field_ut??null)}catch(C){if(j!==M.current)return;y(null),S(null),m(C instanceof Error?C.message:String(C))}finally{j===M.current&&d(!1)}},[n,o,t]);a.useEffect(()=>{if(!v){$.current&&(clearInterval($.current),$.current=null);return}return $.current=setInterval(()=>{h()},850),()=>{$.current&&clearInterval($.current)}},[v,h]);const T=a.useMemo(()=>{if(f==null)return 0;const j=f*be;return _/2-j},[f,_]),O=f!=null?(f%360+360)%360:null;return e.jsxs("div",{className:"rounded-xl border border-sky-500/30 bg-gradient-to-b from-slate-900/90 via-slate-900/70 to-surface-container/90 p-4",children:[e.jsxs("div",{className:"flex flex-wrap items-start justify-between gap-3",children:[e.jsxs("div",{children:[e.jsx("p",{className:"text-sm font-semibold text-on-surface",children:t("sys.sensors.compass.title")}),e.jsx("p",{className:"mt-1 max-w-xl text-[11px] leading-snug text-on-surface-variant",children:t("sys.sensors.compass.desc")})]}),e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsxs("label",{className:"flex cursor-pointer items-center gap-1.5 text-[11px] text-on-surface-variant",children:[e.jsx("input",{type:"checkbox",checked:v,onChange:j=>p(j.target.checked)}),t("sys.sensors.compass.live")]}),e.jsx("button",{type:"button",disabled:c,className:"rounded-lg bg-sky-700/80 px-3 py-1.5 text-xs font-medium text-white hover:bg-sky-600 disabled:opacity-50",onClick:()=>void h(),children:t(c?"sys.sensors.compass.loading":"sys.sensors.compass.btn")})]})]}),e.jsxs("div",{className:"mt-5",children:[e.jsx("p",{className:"mb-2 text-center text-[10px] uppercase tracking-[0.2em] text-sky-300/90",children:t("sys.sensors.compass.tapeCaption")}),e.jsxs("div",{ref:R,className:"relative mx-auto h-[112px] w-full max-w-3xl overflow-hidden rounded-lg ring-1 ring-sky-500/25",style:{maskImage:"linear-gradient(90deg, transparent 0%, black 10%, black 90%, transparent 100%)",WebkitMaskImage:"linear-gradient(90deg, transparent 0%, black 10%, black 90%, transparent 100%)"},children:[e.jsx("div",{className:"pointer-events-none absolute inset-x-0 top-0 z-20 flex justify-center",children:e.jsxs("div",{className:"flex flex-col items-center",children:[e.jsx("div",{className:"h-0 w-0 border-x-[9px] border-x-transparent border-b-[12px] border-b-amber-400 drop-shadow"}),e.jsx("div",{className:"h-[88px] w-0.5 rounded-full bg-gradient-to-b from-amber-300/95 to-sky-400/40"})]})}),e.jsx("div",{className:"absolute bottom-0 left-0 top-0 will-change-transform",style:{width:je,transform:`translateX(${T}px)`,transition:"transform 0.42s cubic-bezier(0.22, 0.95, 0.28, 1)"},children:e.jsxs("div",{className:"relative h-full",style:{width:je,background:"linear-gradient(180deg, rgba(15,23,42,0.2) 0%, rgba(30,41,59,0.85) 40%, rgba(15,23,42,0.95) 100%)"},children:[e.jsx("div",{className:"absolute left-0 right-0 top-8 h-px bg-slate-600/60"}),cs.map(j=>{const C=j.h==="maj"?22:j.h==="mid"?14:8;return e.jsxs("div",{className:"absolute flex flex-col items-center",style:{left:j.x,transform:"translateX(-50%)",top:32},children:[(j.cardKey||j.degLabel!=null)&&e.jsx("span",{className:`mb-0.5 whitespace-nowrap font-mono ${j.cardKey?"text-[13px] font-bold text-sky-200":"text-[10px] font-medium text-slate-400"}`,style:{marginTop:-18},children:j.cardKey?t(`sys.sensors.compass.${j.cardKey}`):String(j.degLabel)}),e.jsx("div",{className:`w-px rounded-full ${j.h==="maj"?"bg-sky-300/90":j.h==="mid"?"bg-slate-500/85":"bg-slate-600/50"}`,style:{height:C}})]},j.deg)}),e.jsx("div",{className:"absolute bottom-6 left-0 right-0 h-px bg-slate-600/40"})]})})]})]}),e.jsxs("div",{className:"mt-5 flex flex-col gap-4 md:flex-row md:items-start md:justify-center md:gap-8",children:[e.jsxs("div",{className:"rounded-lg border border-outline-variant/30 bg-surface-container/90 px-6 py-4 text-center md:min-w-[200px]",children:[e.jsx("p",{className:"text-[10px] uppercase tracking-widest text-on-surface-variant",children:t("sys.sensors.compass.headingLabel")}),e.jsx("p",{className:"font-mono text-4xl font-bold tabular-nums text-sky-200",children:O!=null?`${O.toFixed(1)}°`:"—"}),e.jsx("p",{className:"mt-1 text-[10px] text-on-surface-variant",children:t("sys.sensors.compass.headingHint")})]}),g&&e.jsxs("div",{className:"rounded-lg border border-outline-variant/20 px-4 py-3 font-mono text-[11px] text-on-surface md:max-w-md",children:[e.jsx("p",{className:"mb-1 text-[10px] text-on-surface-variant",children:"µT (X / Y / Z)"}),e.jsxs("p",{className:"tabular-nums",children:[g.x.toFixed(2)," · ",g.y.toFixed(2)," · ",g.z.toFixed(2)]})]})]}),N&&e.jsx("p",{className:"mt-3 rounded border border-amber-500/40 bg-amber-500/10 px-3 py-2 font-mono text-[11px] text-amber-100",children:N}),e.jsx("p",{className:"mt-3 text-[10px] leading-relaxed text-on-surface-variant/90",children:t("sys.sensors.compass.footnote")})]})}function Ne(s){try{return JSON.stringify(s,null,2)}catch{return String(s)}}function us(){const{t:s}=J(),[o,n]=a.useState(1),[t,c]=a.useState(12),[d,N]=a.useState(!0),[m,f]=a.useState(!1),[y,g]=a.useState(null),[S,v]=a.useState(null),[p,$]=a.useState(null),[M,R]=a.useState(null),[_,E]=a.useState(1),[h,T]=a.useState(104),[O,j]=a.useState(!0),[C,w]=a.useState(!1),[P,z]=a.useState(null),[i,k]=a.useState(null),L=a.useCallback(async()=>{f(!0),g(null);try{const u=new URLSearchParams({bus:String(o),addr:String(t),i2cdetect:d?"true":"false"}),F=await U(`/api/debug/sensors/magnetometer/selftest?${u.toString()}`);v(F)}catch(u){v(null),g(u instanceof Error?u.message:String(u))}finally{f(!1)}},[t,o,d]),A=a.useCallback(async()=>{f(!0),g(null);try{const u=new URLSearchParams({addr:String(t)}),F=await U(`/api/debug/sensors/magnetometer/probe-buses?${u.toString()}`);v(F)}catch(u){v(null),g(u instanceof Error?u.message:String(u))}finally{f(!1)}},[t]),I=a.useCallback(async()=>{f(!0),g(null);try{const u=new URLSearchParams({bus:String(o),addr:String(t)}),F=await U(`/api/debug/sensors/magnetometer/calibration/start?${u.toString()}`,{method:"POST"});v(F),R("已开始方向校准,请缓慢旋转设备 5-15 秒。");const G=await U(`/api/debug/sensors/magnetometer/calibration/status?${u.toString()}`);$(G)}catch(u){v(null),g(u instanceof Error?u.message:String(u))}finally{f(!1)}},[t,o]),B=a.useCallback(async()=>{f(!0),g(null);try{const u=new URLSearchParams({bus:String(o),addr:String(t)}),F=await U(`/api/debug/sensors/magnetometer/calibration/commit?${u.toString()}`,{method:"POST"});v(F),R("已保存并锁定方向校准。");const G=await U(`/api/debug/sensors/magnetometer/calibration/status?${u.toString()}`);$(G)}catch(u){v(null),g(u instanceof Error?u.message:String(u))}finally{f(!1)}},[t,o]),X=a.useCallback(async()=>{f(!0),g(null);try{const u=new URLSearchParams({bus:String(o),addr:String(t)}),F=await U(`/api/debug/sensors/magnetometer/calibration/reset?${u.toString()}`,{method:"POST"});v(F),R("已重置到自动模式。");const G=await U(`/api/debug/sensors/magnetometer/calibration/status?${u.toString()}`);$(G)}catch(u){v(null),g(u instanceof Error?u.message:String(u))}finally{f(!1)}},[t,o]),Q=a.useCallback(async()=>{f(!0),g(null);try{const u=new URLSearchParams({bus:String(o),addr:String(t)}),F=await U(`/api/debug/sensors/magnetometer/calibration/status?${u.toString()}`);v(F),$(F)}catch(u){v(null),g(u instanceof Error?u.message:String(u))}finally{f(!1)}},[t,o]);a.useEffect(()=>{const u=new URLSearchParams({bus:String(o),addr:String(t)});U(`/api/debug/sensors/magnetometer/calibration/status?${u.toString()}`).then(F=>$(F)).catch(()=>{})},[t,o]),a.useEffect(()=>{if((p==null?void 0:p.mode)!=="recording")return;const u=setInterval(()=>{const F=new URLSearchParams({bus:String(o),addr:String(t)});U(`/api/debug/sensors/magnetometer/calibration/status?${F.toString()}`).then(G=>$(G)).catch(()=>{})},900);return()=>clearInterval(u)},[p==null?void 0:p.mode,o,t]);const ae=a.useMemo(()=>{const u=(p==null?void 0:p.mode)??"auto";return s(u==="recording"?"sys.sensors.mag.calModeRecording":u==="locked"?"sys.sensors.mag.calModeLocked":"sys.sensors.mag.calModeAuto")},[p==null?void 0:p.mode,s]),ee=Number((p==null?void 0:p.samples)??0),se=(p==null?void 0:p.mode)==="recording"&&ee>=10,ne=a.useCallback(async()=>{w(!0),z(null);try{const u=new URLSearchParams({bus:String(_),addr:String(h),i2cdetect:O?"true":"false"}),F=await U(`/api/debug/sensors/mpu6050/selftest?${u.toString()}`);k(F)}catch(u){k(null),z(u instanceof Error?u.message:String(u))}finally{w(!1)}},[h,_,O]);return e.jsxs("div",{className:"mx-auto max-w-6xl space-y-6",children:[e.jsxs("header",{children:[e.jsx("div",{className:"text-[10px] uppercase tracking-[0.14em] text-on-surface-variant",children:s("sys.placeholder.breadcrumb")}),e.jsx("h2",{className:"mt-1 font-headline text-3xl font-black tracking-tight",children:s("sys.sensors.title")}),e.jsx("p",{className:"text-sm text-on-surface-variant",children:s("sys.sensors.desc")})]}),e.jsxs("section",{className:"rounded-xl border border-outline-variant/20 bg-surface-container-low p-4",children:[e.jsx("p",{className:"text-xs font-medium text-on-surface",children:s("sys.sensors.mag.section")}),e.jsx("p",{className:"mt-1 text-[11px] text-on-surface-variant",children:s("sys.sensors.mag.note")}),e.jsxs("div",{className:"mt-4 flex flex-wrap items-end gap-4",children:[e.jsxs("label",{className:"flex flex-col gap-1 text-[11px] text-on-surface-variant",children:[s("sys.sensors.mag.bus"),e.jsx("input",{type:"number",min:0,max:32,className:"w-24 rounded border border-outline-variant/40 bg-surface-container px-2 py-1 font-mono text-sm text-on-surface",value:o,onChange:u=>n(Number(u.target.value))})]}),e.jsxs("label",{className:"flex flex-col gap-1 text-[11px] text-on-surface-variant",children:[s("sys.sensors.mag.addr"),e.jsx("input",{type:"number",min:1,max:127,className:"w-24 rounded border border-outline-variant/40 bg-surface-container px-2 py-1 font-mono text-sm text-on-surface",value:t,onChange:u=>c(Number(u.target.value))})]}),e.jsxs("label",{className:"flex items-center gap-2 text-[11px] text-on-surface-variant",children:[e.jsx("input",{type:"checkbox",checked:d,onChange:u=>N(u.target.checked)}),s("sys.sensors.mag.i2cdetect")]})]}),e.jsx(xs,{bus:o,addr:t}),e.jsxs("div",{className:"mt-6 flex flex-wrap gap-2",children:[e.jsx("button",{type:"button",disabled:m,className:"rounded-lg bg-primary px-4 py-2 text-sm font-medium text-on-primary hover:opacity-90 disabled:opacity-50",onClick:()=>void L(),children:s(m?"sys.sensors.mag.running":"sys.sensors.mag.btnSelftest")}),e.jsx("button",{type:"button",disabled:m,className:"rounded-lg border border-outline-variant/40 px-4 py-2 text-sm text-on-surface hover:bg-surface-container/80 disabled:opacity-50",onClick:()=>void A(),children:s("sys.sensors.mag.btnProbe")})]}),e.jsxs("div",{className:"mt-3 flex flex-wrap gap-2",children:[e.jsx("button",{type:"button",disabled:m||(p==null?void 0:p.mode)==="recording",className:"rounded-lg border border-amber-400/40 px-3 py-1.5 text-xs text-on-surface hover:bg-amber-500/10 disabled:opacity-50",onClick:()=>void I(),children:s("sys.sensors.mag.btnCalStart")}),e.jsx("button",{type:"button",disabled:m||!se,className:"rounded-lg border border-emerald-400/40 px-3 py-1.5 text-xs text-on-surface hover:bg-emerald-500/10 disabled:opacity-50",onClick:()=>void B(),children:s("sys.sensors.mag.btnCalCommit")}),e.jsx("button",{type:"button",disabled:m,className:"rounded-lg border border-rose-400/40 px-3 py-1.5 text-xs text-on-surface hover:bg-rose-500/10 disabled:opacity-50",onClick:()=>void X(),children:s("sys.sensors.mag.btnCalReset")}),e.jsx("button",{type:"button",disabled:m,className:"rounded-lg border border-outline-variant/40 px-3 py-1.5 text-xs text-on-surface hover:bg-surface-container/80 disabled:opacity-50",onClick:()=>void Q(),children:s("sys.sensors.mag.btnCalStatus")})]}),e.jsxs("div",{className:"mt-3 rounded-lg border border-outline-variant/30 bg-surface-container/60 px-3 py-2 text-[11px] text-on-surface",children:[e.jsxs("p",{className:"font-medium",children:[s("sys.sensors.mag.calStatusPrefix")," ",ae]}),e.jsxs("p",{className:"mt-1 text-on-surface-variant",children:[s("sys.sensors.mag.calSamplesPrefix")," ",ee,(p==null?void 0:p.mode)==="recording"?" / 10+":""]}),(p==null?void 0:p.span_xyz)&&e.jsxs("p",{className:"mt-1 font-mono text-[10px] text-on-surface-variant",children:["span xyz: ",Number(p.span_xyz.x??0).toFixed(1)," /"," ",Number(p.span_xyz.y??0).toFixed(1)," /"," ",Number(p.span_xyz.z??0).toFixed(1)]}),(p==null?void 0:p.mode)==="recording"&&e.jsx("p",{className:"mt-1 text-amber-200",children:s("sys.sensors.mag.calRecordingHint")}),(p==null?void 0:p.mode)==="locked"&&p.locked&&e.jsxs("p",{className:"mt-1 text-emerald-200",children:[s("sys.sensors.mag.calLockedHint")," axes=",String(p.locked.axes_pair??"xy")]}),M&&e.jsx("p",{className:"mt-1 text-sky-200",children:M})]}),y&&e.jsx("p",{className:"mt-3 rounded border border-red-500/40 bg-red-500/10 px-3 py-2 font-mono text-xs text-red-200",children:y}),S&&e.jsxs("details",{className:"mt-4 rounded-lg border border-outline-variant/30 bg-surface-container/50",children:[e.jsx("summary",{className:"cursor-pointer px-3 py-2 text-[11px] text-on-surface-variant",children:s("sys.sensors.jsonToggle")}),e.jsx("pre",{className:"max-h-[320px] overflow-auto border-t border-outline-variant/20 p-3 font-mono text-[11px] leading-relaxed text-on-surface",children:Ne(S)})]})]}),e.jsxs("section",{className:"rounded-xl border border-outline-variant/20 bg-surface-container-low p-4",children:[e.jsx("p",{className:"text-xs font-medium text-on-surface",children:s("sys.sensors.mpu.section")}),e.jsx("p",{className:"mt-1 text-[11px] text-on-surface-variant",children:s("sys.sensors.mpu.note")}),e.jsxs("div",{className:"mt-4 flex flex-wrap items-end gap-4",children:[e.jsxs("label",{className:"flex flex-col gap-1 text-[11px] text-on-surface-variant",children:[s("sys.sensors.mag.bus"),e.jsx("input",{type:"number",min:0,max:32,className:"w-24 rounded border border-outline-variant/40 bg-surface-container px-2 py-1 font-mono text-sm text-on-surface",value:_,onChange:u=>E(Number(u.target.value))})]}),e.jsxs("label",{className:"flex flex-col gap-1 text-[11px] text-on-surface-variant",children:[s("sys.sensors.mpu.addr"),e.jsx("input",{type:"number",min:1,max:127,className:"w-24 rounded border border-outline-variant/40 bg-surface-container px-2 py-1 font-mono text-sm text-on-surface",value:h,onChange:u=>T(Number(u.target.value))})]}),e.jsxs("label",{className:"flex items-center gap-2 text-[11px] text-on-surface-variant",children:[e.jsx("input",{type:"checkbox",checked:O,onChange:u=>j(u.target.checked)}),s("sys.sensors.mag.i2cdetect")]})]}),e.jsx(ns,{bus:_,addr:h}),e.jsx("div",{className:"mt-4 flex flex-wrap gap-2",children:e.jsx("button",{type:"button",disabled:C,className:"rounded-lg bg-primary px-4 py-2 text-sm font-medium text-on-primary hover:opacity-90 disabled:opacity-50",onClick:()=>void ne(),children:s(C?"sys.sensors.mpu.running":"sys.sensors.mpu.btnSelftest")})}),P&&e.jsx("p",{className:"mt-3 rounded border border-red-500/40 bg-red-500/10 px-3 py-2 font-mono text-xs text-red-200",children:P}),i&&e.jsxs("details",{className:"mt-4 rounded-lg border border-outline-variant/30 bg-surface-container/50",children:[e.jsx("summary",{className:"cursor-pointer px-3 py-2 text-[11px] text-on-surface-variant",children:s("sys.sensors.jsonToggle")}),e.jsx("pre",{className:"max-h-[320px] overflow-auto border-t border-outline-variant/20 p-3 font-mono text-[11px] leading-relaxed text-on-surface",children:Ne(i)})]})]})]})}const ms={ogscope:{zh:"主配置 ogscope.env",en:"Primary ogscope.env"},network:{zh:"网络 network.env",en:"Network network.env"}};function ps(s){const o=[];let n=0;return s.split(/\r?\n/).forEach((c,d)=>{const N=c.trim();if(!N||N.startsWith("#"))return;const m=N.startsWith("export ")?N.slice(7):N,f=m.indexOf("=");if(f<=0){n+=1;return}const y=m.slice(0,f).trim();if(!y){n+=1;return}const g=m.slice(f+1);o.push({id:`env-${d}-${y}`,key:y,value:g})}),{entries:o,unsupportedLines:n}}function we(s){const o=s.map(n=>({key:n.key.trim(),value:n.value})).filter(n=>n.key.length>0).map(n=>`${n.key}=${n.value}`);return o.length>0?`${o.join(`
-`)}
-`:""}function le(s,o){return s==="both"?!0:s===o}function hs(){const{locale:s}=J(),[o,n]=a.useState([]),[t,c]=a.useState(null),[d,N]=a.useState(""),[m,f]=a.useState(""),[y,g]=a.useState("form"),[S,v]=a.useState([]),[p,$]=a.useState(0),[M,R]=a.useState(!1),[_,E]=a.useState(""),[h,T]=a.useState(""),[O,j]=a.useState(""),[C,w]=a.useState(!0),[P,z]=a.useState(""),i=s==="zh",k=a.useMemo(()=>o.find(l=>l.file_id===d)??null,[d,o]),L=a.useMemo(()=>{const l=new Map;for(const r of(t==null?void 0:t.sections)??[])for(const x of r.entries)l.set(x.key.toUpperCase(),x);for(const r of(t==null?void 0:t.network_only)??[])l.set(r.key.toUpperCase(),r);return l},[t]),A=a.useMemo(()=>{if(!d)return[];const l=new Set(S.map(x=>x.key.trim().toUpperCase()).filter(Boolean)),r=[];for(const x of(t==null?void 0:t.sections)??[])for(const b of x.entries)le(b.scope,d)&&(l.has(b.key.toUpperCase())||r.push(b));for(const x of(t==null?void 0:t.network_only)??[])le(x.scope,d)&&(l.has(x.key.toUpperCase())||r.push(x));return r.sort((x,b)=>x.key.localeCompare(b.key))},[d,t,S]),I=a.useMemo(()=>{const l=O.trim().toLowerCase();return l?S.filter(r=>{const x=r.key.toLowerCase(),b=L.get(r.key.trim().toUpperCase());return`${x} ${r.value} ${(b==null?void 0:b.zh)??""} ${(b==null?void 0:b.en)??""}`.toLowerCase().includes(l)}):S},[S,L,O]),B=l=>{const r=ps(l);v(r.entries),$(r.unsupportedLines)},X=async()=>{var l,r;R(!0),E("");try{const[x,b]=await Promise.all([K("/api/dev/system/config/files",{cache:"no-store"}),K("/api/dev/system/config/catalog",{cache:"no-store"})]);n(x.files??[]),c(b);const H=((r=(l=x.files)==null?void 0:l[0])==null?void 0:r.file_id)??"";N(D=>{var Z;return D&&((Z=x.files)!=null&&Z.some(Y=>Y.file_id===D))?D:H})}catch(x){E(x instanceof Error?x.message:String(x))}finally{R(!1)}},Q=async()=>{if(!d)return;if(y==="form"&&p>0){E(i?"当前文件包含无法表单化的行,请切换到「原始文本」模式编辑后再保存。":"This file has lines not supported by form mode. Switch to Raw mode before saving.");return}const l=y==="form"?we(S):m;R(!0),E(""),T("");try{const r=await K("/api/dev/system/config/files",{method:"POST",body:JSON.stringify({file_id:d,content:l})});T(i?`保存成功:${r.message??d};请重启 ogscope 服务使配置生效`:`Saved: ${r.message??d}; restart ogscope to apply changes`),await X()}catch(r){E(r instanceof Error?r.message:String(r))}finally{R(!1)}};a.useEffect(()=>{X()},[]),a.useEffect(()=>{if(!k){f(""),v([]),$(0),z("");return}const l=k.content??"";f(l),B(l),z("")},[k]);const ae=()=>{v(l=>[...l,{id:`env-new-${Date.now()}-${l.length}`,key:"",value:""}])},ee=()=>{const l=A.find(r=>r.key===P);l&&(v(r=>[...r,{id:`env-catalog-${Date.now()}-${l.key}`,key:l.key,value:l.default??""}]),z(""))},se=(l,r)=>{v(x=>x.map(b=>b.id===l?{...b,...r}:b))},ne=l=>{v(r=>r.filter(x=>x.id!==l))},u=l=>{const r=L.get(l.trim().toUpperCase());return r?i?r.zh:r.en:i?"暂无释义(可查阅 deploy/*.env.example)":"No hint (see deploy/*.env.example)"},F=l=>{const r=L.get(l.trim().toUpperCase());return r!=null&&r.default?i?`默认:${r.default}`:`Default: ${r.default}`:""},G=l=>{const r=ms[l];return r?i?r.zh:r.en:l};return e.jsxs("div",{className:"mx-auto max-w-7xl space-y-6",children:[e.jsxs("header",{children:[e.jsxs("div",{className:"flex items-center gap-2 text-[10px] uppercase tracking-[0.14em] text-on-surface-variant",children:[e.jsx("span",{children:"Console"}),e.jsx("span",{children:"/"}),e.jsx("span",{className:"text-primary",children:i?"配置管理":"Config Manager"})]}),e.jsx("h2",{className:"mt-1 font-headline text-3xl font-black tracking-tight",children:i?"环境配置管理":"Environment Config Manager"}),e.jsx("p",{className:"text-sm text-on-surface-variant",children:i?"编辑 /etc/ogscope 下的 ogscope.env 与 network.env。保存后请重启 ogscope 服务;配置项说明来自服务端目录 API。":"Edit ogscope.env and network.env under /etc/ogscope. Restart ogscope after saving; hints come from the server catalog API."})]}),_&&e.jsx("div",{className:"rounded-lg border border-error/40 bg-error-container/20 px-3 py-2 text-sm text-on-error-container",children:_}),h&&e.jsx("div",{className:"rounded-lg border border-primary/30 bg-primary/10 px-3 py-2 text-sm",children:h}),e.jsxs("section",{className:"grid grid-cols-12 gap-4",children:[e.jsxs("aside",{className:"col-span-12 space-y-2 rounded-xl border border-outline-variant/20 bg-surface-container p-3 lg:col-span-3",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("p",{className:"text-xs uppercase tracking-wider text-on-surface-variant",children:i?"配置文件":"Config Files"}),e.jsx("button",{type:"button",onClick:()=>void X(),disabled:M,children:e.jsxs("span",{className:"inline-flex items-center gap-1 text-xs",children:[e.jsx(he,{className:"h-3.5 w-3.5"})," ",i?"刷新":"Refresh"]})})]}),o.map(l=>e.jsxs("button",{type:"button",onClick:()=>N(l.file_id),className:`w-full rounded-lg border px-3 py-2 text-left text-sm ${l.file_id===d?"border-primary bg-primary/10 text-on-surface":"border-outline-variant/30 bg-surface-container-low text-on-surface-variant"}`,children:[e.jsx("div",{className:"font-medium",children:G(l.file_id)}),e.jsx("div",{className:"mt-1 truncate font-mono text-[11px]",children:l.path})]},l.file_id)),(t==null?void 0:t.env_files)&&e.jsxs("div",{className:"mt-3 rounded border border-outline-variant/20 bg-surface-container-low px-2 py-2 text-[11px] text-on-surface-variant",children:[e.jsx("p",{className:"mb-1 font-medium text-on-surface",children:i?"配置路径":"Config paths"}),Object.entries(t.env_files).map(([l,r])=>e.jsxs("p",{className:"font-mono",children:[l,": ",r]},l))]})]}),e.jsxs("div",{className:"col-span-12 rounded-xl border border-outline-variant/20 bg-surface-container p-4 lg:col-span-9",children:[!k&&e.jsx("p",{className:"text-sm text-on-surface-variant",children:i?"暂无可编辑配置文件":"No editable config files."}),k&&e.jsxs("div",{className:"space-y-3",children:[e.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-2",children:[e.jsxs("div",{children:[e.jsx("p",{className:"font-mono text-xs text-on-surface",children:k.path}),e.jsxs("p",{className:"text-xs text-on-surface-variant",children:[i?"可写":"Writable",": ",String(k.writable)," · ",i?"存在":"Exists",":"," ",String(k.exists)]})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("button",{type:"button",onClick:()=>{y==="raw"&&B(m),g("form")},className:`rounded px-2 py-1 text-xs ${y==="form"?"bg-primary-container text-on-primary-container":"border border-outline-variant/30 text-on-surface-variant"}`,children:i?"表单模式":"Form"}),e.jsx("button",{type:"button",onClick:()=>{y==="form"&&f(we(S)),g("raw")},className:`rounded px-2 py-1 text-xs ${y==="raw"?"bg-primary-container text-on-primary-container":"border border-outline-variant/30 text-on-surface-variant"}`,children:i?"原始文本":"Raw"})]}),e.jsx("button",{type:"button",onClick:()=>void Q(),disabled:M||!k.writable,children:e.jsxs("span",{className:"inline-flex items-center gap-1",children:[e.jsx(Pe,{className:"h-3.5 w-3.5"}),i?"保存并提示重启":"Save"]})})]}),k.error&&e.jsx("div",{className:"rounded border border-error/40 bg-error-container/20 px-2 py-1 text-xs text-on-error-container",children:k.error}),y==="form"?e.jsxs("div",{className:"space-y-3",children:[p>0&&e.jsx("div",{className:"rounded border border-warning/40 bg-warning/10 px-2 py-1 text-xs text-on-surface",children:i?`检测到 ${p} 行无法转换为键值表单。请切到「原始文本」模式处理。`:`${p} line(s) cannot be represented in key-value form. Use Raw mode.`}),e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsxs("div",{className:"relative min-w-[200px] flex-1",children:[e.jsx(_e,{className:"pointer-events-none absolute left-2 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-on-surface-variant"}),e.jsx("input",{className:"w-full rounded border border-outline-variant/30 bg-surface-container-low py-1.5 pl-8 pr-2 text-xs outline-none focus:border-primary",placeholder:i?"搜索键名、值或释义…":"Search keys, values, or hints…",value:O,onChange:l=>j(l.target.value)})]}),e.jsxs("select",{className:"max-w-xs flex-1 rounded border border-outline-variant/30 bg-surface-container-low px-2 py-1.5 text-xs outline-none focus:border-primary",value:P,onChange:l=>z(l.target.value),children:[e.jsx("option",{value:"",children:i?"从目录添加配置项…":"Add from catalog…"}),A.map(l=>e.jsx("option",{value:l.key,children:l.key},l.key))]}),e.jsx("button",{type:"button",disabled:!P,onClick:ee,className:"rounded border border-outline-variant/30 px-2 py-1.5 text-xs disabled:opacity-50",children:i?"添加":"Add"})]}),e.jsxs("div",{className:"max-h-[420px] overflow-auto pr-1",children:[e.jsxs("table",{className:"w-full border-separate border-spacing-y-2",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"text-left text-[11px] uppercase tracking-wide text-on-surface-variant",children:[e.jsx("th",{children:i?"配置项":"Key"}),e.jsx("th",{children:i?"值":"Value"}),e.jsx("th",{children:i?"释义":"Meaning"}),e.jsx("th",{children:i?"操作":"Action"})]})}),e.jsx("tbody",{children:I.map(l=>e.jsxs("tr",{children:[e.jsx("td",{className:"pr-2 align-top",children:e.jsx("input",{className:"w-full rounded border border-outline-variant/30 bg-surface-container-low px-2 py-1 font-mono text-xs outline-none focus:border-primary",placeholder:"OGSCOPE_PORT",value:l.key,onChange:r=>se(l.id,{key:r.target.value})})}),e.jsx("td",{className:"pr-2 align-top",children:e.jsx("input",{className:"w-full rounded border border-outline-variant/30 bg-surface-container-low px-2 py-1 font-mono text-xs outline-none focus:border-primary",placeholder:i?"变量值":"Value",value:l.value,onChange:r=>se(l.id,{value:r.target.value})})}),e.jsxs("td",{className:"pr-2 align-top text-xs text-on-surface-variant",children:[e.jsx("p",{children:u(l.key)}),F(l.key)&&e.jsx("p",{className:"mt-0.5 font-mono text-[10px] text-on-surface-variant/80",children:F(l.key)})]}),e.jsx("td",{className:"align-top",children:e.jsx("button",{type:"button",onClick:()=>ne(l.id),children:e.jsx(Re,{className:"h-3.5 w-3.5 text-on-surface-variant"})})})]},l.id))})]}),S.length===0&&e.jsx("div",{className:"rounded border border-outline-variant/20 bg-surface-container-low px-2 py-2 text-xs text-on-surface-variant",children:i?"当前没有可编辑变量,可从目录添加或手动新增。":"No variables yet. Add from catalog or manually."}),S.length>0&&I.length===0&&e.jsx("div",{className:"rounded border border-outline-variant/20 bg-surface-container-low px-2 py-2 text-xs text-on-surface-variant",children:i?"无匹配项,请调整搜索条件。":"No matches for the current search."})]}),e.jsx("div",{className:"flex flex-wrap gap-2",children:e.jsx("button",{type:"button",onClick:ae,children:e.jsxs("span",{className:"inline-flex items-center gap-1 text-xs",children:[e.jsx(De,{className:"h-3.5 w-3.5"}),i?"空白行":"Blank row"]})})}),e.jsxs("details",{open:C,onToggle:l=>w(l.target.open),className:"rounded border border-outline-variant/20 bg-surface-container-low px-3 py-2 text-xs text-on-surface-variant",children:[e.jsx("summary",{className:"cursor-pointer font-medium text-on-surface",children:e.jsxs("span",{className:"inline-flex items-center gap-1",children:[e.jsx(Ae,{className:"h-3.5 w-3.5"}),i?"配置目录(按模块)":"Config catalog (by section)"]})}),e.jsxs("div",{className:"mt-3 space-y-4",children:[((t==null?void 0:t.sections)??[]).map(l=>{const r=l.entries.filter(x=>d?le(x.scope,d):!0);return r.length===0?null:e.jsxs("div",{children:[e.jsx("p",{className:"mb-1 font-medium text-on-surface",children:i?l.title_zh:l.title_en}),e.jsx("div",{className:"space-y-1",children:r.map(x=>e.jsxs("p",{children:[e.jsx("span",{className:"font-mono text-[11px] text-on-surface",children:x.key}),x.default!=null&&x.default!==""&&e.jsxs("span",{className:"ml-1 font-mono text-[10px] text-on-surface-variant/80",children:["(= ",x.default,")"]})," — ",e.jsx("span",{children:i?x.zh:x.en})]},x.key))})]},l.id)}),((t==null?void 0:t.network_only)??[]).filter(l=>d?le(l.scope,d):!0).length>0&&e.jsxs("div",{children:[e.jsx("p",{className:"mb-1 font-medium text-on-surface",children:i?"仅 network.env / 脚本":"network.env / scripts only"}),e.jsx("div",{className:"space-y-1",children:((t==null?void 0:t.network_only)??[]).filter(l=>d?le(l.scope,d):!0).map(l=>e.jsxs("p",{children:[e.jsx("span",{className:"font-mono text-[11px] text-on-surface",children:l.key})," — ",e.jsx("span",{children:i?l.zh:l.en})]},l.key))})]})]})]})]}):e.jsx("textarea",{className:"h-[460px] w-full rounded-lg border border-outline-variant/30 bg-neutral-950 p-3 font-mono text-xs text-on-surface outline-none focus:border-primary",spellCheck:!1,value:m,onChange:l=>f(l.target.value)})]})]})]})]})}const fs=new Set(["overview","network","sensors","hmi","power","config"]);function ke(){const s=window.location.hash.replace(/^#\/?/,"").trim().toLowerCase();return fs.has(s)?s:"overview"}function bs(s){window.location.hash=`/${s}`}function gs(){const[s,o]=a.useState(()=>ke()),[n,t]=a.useState(!0);a.useEffect(()=>{const d=()=>o(ke());return window.addEventListener("hashchange",d),()=>window.removeEventListener("hashchange",d)},[]),a.useEffect(()=>{(async()=>{var d;try{const N=await fetch("/api",{cache:"no-store"});if(!N.ok)return;const m=await N.json();t(!!((d=m.endpoints)!=null&&d.network))}catch{}})()},[]);const c=a.useMemo(()=>s==="network"?n?e.jsx(Qe,{}):e.jsx(ve,{scope:"network"}):s==="sensors"?e.jsx(us,{}):s==="hmi"?e.jsx(ts,{}):s==="config"?e.jsx(hs,{}):s==="power"?e.jsx(ve,{scope:"power"}):e.jsx(Ye,{}),[n,s]);return e.jsx(We,{route:s,allowNetworkRoute:n,onRouteChange:d=>{d==="network"&&!n||d!==s&&bs(d)},children:c})}$e.createRoot(document.getElementById("root")).render(e.jsx(Me.StrictMode,{children:e.jsx(Te,{children:e.jsx(Le,{children:e.jsx(gs,{})})})}));
diff --git a/web/static/analysis-lab/assets/system-Z3O5uO1l.js b/web/static/analysis-lab/assets/system-Z3O5uO1l.js
new file mode 100644
index 0000000..9ce3790
--- /dev/null
+++ b/web/static/analysis-lab/assets/system-Z3O5uO1l.js
@@ -0,0 +1,78 @@
+import{j as e,r as a,a as $e,R as Me}from"./client-D1ZVDB-N.js";import{u as pe,C as Ee,r as W,a as q,S as Pe,b as Le}from"./http-VZMNcsmS.js";import{c as H,u as Z,T as Re,I as Te}from"./index-C78KOEFu.js";import{R as he}from"./refresh-cw-gseCLMRP.js";/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const me=H("Activity",[["path",{d:"M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2",key:"169zse"}]]);/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const Oe=H("Bolt",[["path",{d:"M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z",key:"yt0hxn"}],["circle",{cx:"12",cy:"12",r:"4",key:"4exip2"}]]);/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const Ae=H("BookOpen",[["path",{d:"M12 7v14",key:"1akyts"}],["path",{d:"M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z",key:"ruj8y"}]]);/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const Se=H("Cpu",[["rect",{width:"16",height:"16",x:"4",y:"4",rx:"2",key:"14l7u7"}],["rect",{width:"6",height:"6",x:"9",y:"9",rx:"1",key:"5aljv4"}],["path",{d:"M15 2v2",key:"13l42r"}],["path",{d:"M15 20v2",key:"15mkzm"}],["path",{d:"M2 15h2",key:"1gxd5l"}],["path",{d:"M2 9h2",key:"1bbxkp"}],["path",{d:"M20 15h2",key:"19e6y8"}],["path",{d:"M20 9h2",key:"19tzq7"}],["path",{d:"M9 2v2",key:"165o2o"}],["path",{d:"M9 20v2",key:"i2bqo8"}]]);/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const Fe=H("HardDrive",[["line",{x1:"22",x2:"2",y1:"12",y2:"12",key:"1y58io"}],["path",{d:"M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z",key:"oot6mr"}],["line",{x1:"6",x2:"6.01",y1:"16",y2:"16",key:"sgf278"}],["line",{x1:"10",x2:"10.01",y1:"16",y2:"16",key:"1l4acy"}]]);/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const Ie=H("LayoutDashboard",[["rect",{width:"7",height:"9",x:"3",y:"3",rx:"1",key:"10lvy0"}],["rect",{width:"7",height:"5",x:"14",y:"3",rx:"1",key:"16une8"}],["rect",{width:"7",height:"9",x:"14",y:"12",rx:"1",key:"1hutg5"}],["rect",{width:"7",height:"5",x:"3",y:"16",rx:"1",key:"ldoo1y"}]]);/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const ze=H("Network",[["rect",{x:"16",y:"16",width:"6",height:"6",rx:"1",key:"4q2zg0"}],["rect",{x:"2",y:"16",width:"6",height:"6",rx:"1",key:"8cvhb9"}],["rect",{x:"9",y:"2",width:"6",height:"6",rx:"1",key:"1egb70"}],["path",{d:"M5 16v-3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v3",key:"1jsf9p"}],["path",{d:"M12 12V8",key:"2874zd"}]]);/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const De=H("Plus",[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"M12 5v14",key:"s699le"}]]);/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const _e=H("Search",[["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}],["path",{d:"m21 21-4.3-4.3",key:"1qie3q"}]]);/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const He=H("Settings",[["path",{d:"M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z",key:"1qme2f"}],["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}]]);/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const ge=H("Sparkles",[["path",{d:"M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z",key:"4pj2yx"}],["path",{d:"M20 3v4",key:"1olli1"}],["path",{d:"M22 5h-4",key:"1gvqau"}],["path",{d:"M4 17v2",key:"vumght"}],["path",{d:"M5 18H3",key:"zchphs"}]]);/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const qe=H("Thermometer",[["path",{d:"M14 4v10.54a4 4 0 1 1-4 0V4a2 2 0 0 1 4 0Z",key:"17jzev"}]]);/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const Ue=H("Touchpad",[["rect",{width:"20",height:"16",x:"2",y:"4",rx:"2",key:"18n3k1"}],["path",{d:"M2 14h20",key:"myj16y"}],["path",{d:"M12 20v-6",key:"1rm09r"}]]);/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const Be=H("TriangleAlert",[["path",{d:"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3",key:"wmoenq"}],["path",{d:"M12 9v4",key:"juzpu7"}],["path",{d:"M12 17h.01",key:"p32p05"}]]);/**
+ * @license lucide-react v0.460.0 - ISC
+ *
+ * This source code is licensed under the ISC license.
+ * See the LICENSE file in the root directory of this source tree.
+ */const fe=H("Wifi",[["path",{d:"M12 20h.01",key:"zekei9"}],["path",{d:"M2 8.82a15 15 0 0 1 20 0",key:"dnpr2z"}],["path",{d:"M5 12.859a10 10 0 0 1 14 0",key:"1x1e6c"}],["path",{d:"M8.5 16.429a5 5 0 0 1 7 0",key:"1bycff"}]]),V=t=>`flex items-center gap-3 rounded-lg px-3 py-2.5 font-headline text-sm tracking-tight transition-colors ${t?"border-r-2 border-primary bg-white/5 font-semibold text-primary":"text-on-surface-variant hover:bg-white/5 hover:text-on-surface"}`;function We({route:t,allowNetworkRoute:o,onRouteChange:n,children:r}){const{t:c,locale:d,setLocale:j}=Z(),{info:u}=pe(),f=(u==null?void 0:u.cpu_usage)!=null?Number(u.cpu_usage).toFixed(1):"—",g=(u==null?void 0:u.memory_usage)!=null?Number(u.memory_usage).toFixed(1):"—",b=(u==null?void 0:u.temperature)!=null?Number(u.temperature).toFixed(1):"—",k=(u==null?void 0:u.wifi_quality)!=null&&!Number.isNaN(Number(u.wifi_quality))?`${Number(u.wifi_quality).toFixed(0)}%`:"—",y={overview:c("sys.shell.top.overview"),network:c("sys.shell.top.network"),sensors:c("sys.shell.top.sensors"),hmi:c("sys.shell.top.hmi"),power:c("sys.shell.top.power"),config:c("sys.shell.top.config")},m=V(!1),$=(M,R)=>{const S=window.open(M,R);S&&S.focus()};return e.jsxs("div",{className:"flex h-full min-h-0 flex-col bg-background text-on-surface md:flex-row",children:[e.jsxs("aside",{className:"glass-panel z-50 flex w-full shrink-0 flex-col border-b border-outline-variant/20 bg-surface-container-low/80 backdrop-blur-xl md:fixed md:left-0 md:top-0 md:h-full md:w-64 md:border-b-0 md:border-r md:border-white/5",children:[e.jsxs("div",{className:"p-5",children:[e.jsxs("div",{className:"mb-8 flex items-center gap-3",children:[e.jsx("div",{className:"primary-gradient flex h-10 w-10 items-center justify-center rounded-lg shadow-lg",children:e.jsx(ge,{className:"h-5 w-5 text-on-primary-container"})}),e.jsxs("div",{children:[e.jsx("h1",{className:"font-headline text-lg font-bold tracking-widest text-primary",children:"OGScope"}),e.jsx("p",{className:"font-mono text-[10px] uppercase tracking-widest text-on-surface-variant",children:c("sys.shell.subtitle")})]})]}),e.jsxs("nav",{className:"flex flex-col gap-0.5",children:[e.jsxs("button",{type:"button",className:V(t==="overview"),onClick:()=>n("overview"),children:[e.jsx(Ie,{className:"h-4 w-4 shrink-0"}),e.jsx("span",{children:c("sys.shell.nav.overview")})]}),o&&e.jsxs("button",{type:"button",className:V(t==="network"),onClick:()=>n("network"),children:[e.jsx(ze,{className:"h-4 w-4 shrink-0"}),e.jsx("span",{children:c("sys.shell.nav.network")})]}),e.jsxs("a",{href:"/debug/camera",className:m,onClick:M=>{M.preventDefault(),$("/debug/camera","ogscopeCameraConsole")},children:[e.jsx(Ee,{className:"h-4 w-4 shrink-0"}),e.jsx("span",{children:c("sys.shell.nav.camera")})]}),e.jsxs("a",{href:"/debug/analysis",className:m,onClick:M=>{M.preventDefault(),$("/debug/analysis","ogscopeAnalysisConsole")},children:[e.jsx(ge,{className:"h-4 w-4 shrink-0"}),e.jsx("span",{children:c("sys.shell.nav.analysis")})]}),e.jsxs("button",{type:"button",className:V(t==="sensors"),onClick:()=>n("sensors"),children:[e.jsx(me,{className:"h-4 w-4 shrink-0"}),e.jsx("span",{children:c("sys.shell.nav.sensors")})]}),e.jsxs("button",{type:"button",className:V(t==="power"),onClick:()=>n("power"),children:[e.jsx(Oe,{className:"h-4 w-4 shrink-0"}),e.jsx("span",{children:c("sys.shell.nav.power")})]}),e.jsxs("button",{type:"button",className:V(t==="hmi"),onClick:()=>n("hmi"),children:[e.jsx(Ue,{className:"h-4 w-4 shrink-0"}),e.jsx("span",{children:c("sys.shell.nav.hmi")})]}),e.jsxs("button",{type:"button",className:V(t==="config"),onClick:()=>n("config"),children:[e.jsx(He,{className:"h-4 w-4 shrink-0"}),e.jsx("span",{children:c("sys.shell.nav.config")})]})]})]}),e.jsx("div",{className:"mt-auto hidden p-5 md:block",children:e.jsxs("div",{className:"rounded-xl border border-white/5 bg-surface-container-low p-3",children:[e.jsx("p",{className:"truncate text-xs font-semibold text-on-surface",children:c("sys.shell.workbench")}),e.jsxs("p",{className:"font-mono text-[10px] text-on-surface-variant",children:[c("sys.shell.node"),": OGSCOPE_PI_ZERO_2W"]})]})})]}),e.jsxs("div",{className:"flex min-h-0 min-w-0 flex-1 flex-col md:ml-64",children:[e.jsxs("header",{className:"sticky top-0 z-40 flex h-14 shrink-0 items-center justify-between border-b border-white/5 bg-neutral-950/80 px-4 backdrop-blur-md md:px-8",children:[e.jsx("div",{className:"flex min-w-0 items-center gap-3",children:e.jsx("span",{className:"hidden truncate border-b-2 border-primary pb-0.5 font-mono text-xs uppercase tracking-wider text-primary sm:inline",children:y[t]})}),e.jsxs("div",{className:"flex flex-wrap items-center justify-end gap-3 sm:gap-4",children:[e.jsxs("div",{className:"mr-2 flex gap-1 text-[10px]",children:[e.jsx("button",{type:"button",className:`rounded px-2 py-0.5 ${d==="zh"?"bg-primary-container text-on-primary-container":"text-on-surface-variant"}`,onClick:()=>j("zh"),children:c("lang.zh")}),e.jsx("button",{type:"button",className:`rounded px-2 py-0.5 ${d==="en"?"bg-primary-container text-on-primary-container":"text-on-surface-variant"}`,onClick:()=>j("en"),children:c("lang.en")})]}),e.jsxs("div",{className:"flex flex-wrap items-center justify-end gap-3 font-mono text-[10px] uppercase tracking-wider text-on-surface-variant sm:gap-4",children:[e.jsxs("span",{className:"flex items-center gap-1 text-primary",children:[e.jsx(Se,{className:"h-3.5 w-3.5"})," CPU ",f,"%"]}),e.jsxs("span",{className:"flex items-center gap-1",children:[e.jsx(me,{className:"h-3.5 w-3.5"})," MEM ",g,"%"]}),e.jsxs("span",{className:"flex items-center gap-1",children:[e.jsx("span",{className:"text-xs",children:"°C"})," ",b]}),e.jsxs("span",{className:"flex items-center gap-1 text-secondary",children:[e.jsx(fe,{className:"h-3.5 w-3.5"})," ",k]})]})]})]}),e.jsx("main",{className:"og-scrollbar min-h-0 flex-1 overflow-auto p-4 md:p-6",children:r})]})]})}async function Ke(){return await W("/api/dev/system/hardware-plane/status",{cache:"no-store"})}async function Xe(t){return await W("/api/dev/system/hardware-plane/command",{method:"POST",body:JSON.stringify(t)})}async function Ge(t){var r;const o=new URLSearchParams;t!=null&&t.service&&o.set("service",t.service),o.set("since_seconds",String(t.sinceSeconds)),o.set("limit",String(t.limit)),(r=t==null?void 0:t.levels)!=null&&r.length&&o.set("levels",t.levels.join(","));const n=o.toString();return await W(`/api/dev/debug/logs/systemd${n?`?${n}`:""}`,{cache:"no-store"})}function Ze(t){const o=Math.max(0,parseInt(String(t??0),10)||0),n=Math.floor(o/86400),r=Math.floor(o%86400/3600),c=Math.floor(o%3600/60);return n>0?`${n}d ${r}h`:r>0?`${r}h ${c}m`:`${c}m`}function de(t,o=1){return t==null||Number.isNaN(Number(t))?"—":Number(t).toFixed(o)}function ye(t){return t==="ERROR"?"text-error":t==="WARN"?"text-amber-300":"text-primary"}function Je(t){if(!t)return"--:--:--";const o=new Date(t);return Number.isNaN(o.getTime())?"--:--:--":o.toLocaleTimeString()}function Ye(){const{t:n}=Z(),{info:r,error:c}=pe(),[d,j]=a.useState(!1),[u,f]=a.useState(["INFO","WARN","ERROR"]),[g,b]=a.useState([]),[k,y]=a.useState(null),[m,$]=a.useState(!1),[M,R]=a.useState(!0),S=a.useRef(null),E=a.useRef(new Set),h=de(r==null?void 0:r.cpu_usage),T=de(r==null?void 0:r.memory_usage),O=de(r==null?void 0:r.temperature),v=(r==null?void 0:r.load_average_1m)!=null?String(r.load_average_1m):"—",_=(r==null?void 0:r.wifi_quality)!=null&&!Number.isNaN(Number(r.wifi_quality))?`${Number(r.wifi_quality).toFixed(0)}%`:"—",N=(r==null?void 0:r.wifi_signal_dbm)!=null&&!Number.isNaN(Number(r.wifi_signal_dbm))?`${Number(r.wifi_signal_dbm).toFixed(0)} dBm`:"—",P=async()=>{if(u.length===0){b([]);return}$(!0);try{y(null);const i=await Ge({service:"ogscope",sinceSeconds:1200,limit:240,levels:u});b(w=>{const L=new Set(w.map(I=>`${I.ts??""}::${I.level}::${I.source}::${I.message}`)),A=[...w];for(const I of i.items){const U=`${I.ts??""}::${I.level}::${I.source}::${I.message}`;L.has(U)||(L.add(U),A.push(I))}return A.length<=300?A:A.slice(A.length-300)})}catch(i){y(i instanceof Error?i.message:String(i))}finally{$(!1)}};a.useEffect(()=>{if(!d)return;P();const i=window.setInterval(()=>{document.hidden||P()},4e3);return()=>window.clearInterval(i)},[d,u.join(",")]),a.useEffect(()=>{const i=S.current;if(!i)return;const w=()=>{const L=i.scrollHeight-i.scrollTop-i.clientHeight;R(L<=24)};return w(),i.addEventListener("scroll",w),()=>i.removeEventListener("scroll",w)},[]),a.useEffect(()=>{if(!S.current||g.length===0)return;const w=new Set(g.map(A=>`${A.ts??""}::${A.level}::${A.source}::${A.message}`));let L=!1;w.forEach(A=>{E.current.has(A)||(L=!0)}),E.current=w,M&&L&&requestAnimationFrame(()=>{S.current&&(S.current.scrollTop=S.current.scrollHeight)})},[g,M]);const z=a.useMemo(()=>new Set(u),[u]);return e.jsxs("div",{className:"mx-auto max-w-7xl space-y-6",children:[e.jsxs("header",{className:"mb-1",children:[e.jsxs("div",{className:"flex items-center gap-2 text-[10px] uppercase tracking-[0.14em] text-on-surface-variant",children:[e.jsx("span",{children:n("sys.overview.breadcrumb.console")}),e.jsx("span",{children:"/"}),e.jsx("span",{className:"text-primary",children:n("sys.overview.breadcrumb.module")})]}),e.jsx("h2",{className:"mt-1 font-headline text-3xl font-black tracking-tight",children:n("sys.overview.title")}),e.jsx("p",{className:"text-sm text-on-surface-variant",children:n("sys.overview.subtitle")})]}),c&&e.jsx("div",{className:"rounded-lg border border-error/40 bg-error-container/20 px-3 py-2 text-sm text-on-error-container",children:c}),e.jsxs("section",{className:"grid grid-cols-12 gap-4",children:[e.jsxs("article",{className:"col-span-12 rounded-xl border border-outline-variant/20 bg-surface-container p-4 md:col-span-3",children:[e.jsxs("div",{className:"mb-3 flex items-center justify-between text-[10px] uppercase tracking-wider text-on-surface-variant",children:[e.jsx("span",{children:n("sys.overview.metric.cpu")}),e.jsx(Se,{className:"h-4 w-4 text-primary"})]}),e.jsxs("div",{className:"text-3xl font-bold text-on-surface",children:[h,"%"]}),e.jsx("div",{className:"mt-3 h-1.5 w-full overflow-hidden rounded bg-surface-container-high",children:e.jsx("div",{className:"h-full bg-primary",style:{width:`${Math.min(Number(h)||0,100)}%`}})})]}),e.jsxs("article",{className:"col-span-12 rounded-xl border border-outline-variant/20 bg-surface-container p-4 md:col-span-3",children:[e.jsxs("div",{className:"mb-3 flex items-center justify-between text-[10px] uppercase tracking-wider text-on-surface-variant",children:[e.jsx("span",{children:n("sys.overview.metric.mem")}),e.jsx(me,{className:"h-4 w-4 text-secondary"})]}),e.jsxs("div",{className:"text-3xl font-bold text-on-surface",children:[T,"%"]}),e.jsx("div",{className:"mt-3 h-1.5 w-full overflow-hidden rounded bg-surface-container-high",children:e.jsx("div",{className:"h-full bg-secondary",style:{width:`${Math.min(Number(T)||0,100)}%`}})})]}),e.jsxs("article",{className:"col-span-12 rounded-xl border border-outline-variant/20 bg-surface-container p-4 md:col-span-3",children:[e.jsxs("div",{className:"mb-3 flex items-center justify-between text-[10px] uppercase tracking-wider text-on-surface-variant",children:[e.jsx("span",{children:n("sys.overview.metric.temp")}),e.jsx(qe,{className:"h-4 w-4 text-primary"})]}),e.jsxs("div",{className:"text-3xl font-bold text-on-surface",children:[O,"°C"]}),e.jsx("div",{className:"mt-3 text-xs text-on-surface-variant",children:n("sys.overview.tempState")})]}),e.jsxs("article",{className:"col-span-12 rounded-xl border border-outline-variant/20 bg-surface-container p-4 md:col-span-3",children:[e.jsxs("div",{className:"mb-3 flex items-center justify-between text-[10px] uppercase tracking-wider text-on-surface-variant",children:[e.jsx("span",{children:n("sys.overview.metric.wifi")}),e.jsx(fe,{className:"h-4 w-4 text-primary"})]}),e.jsx("div",{className:"text-3xl font-bold text-on-surface",children:_}),e.jsx("div",{className:"mt-3 text-xs text-on-surface-variant",children:N})]})]}),e.jsxs("section",{className:"grid grid-cols-12 gap-4",children:[e.jsxs("article",{className:"col-span-12 rounded-xl border border-white/5 bg-surface-container-low p-6 lg:col-span-8",children:[e.jsxs("div",{className:"mb-6 flex items-center justify-between",children:[e.jsxs("div",{children:[e.jsx("h3",{className:"font-headline text-lg font-bold",children:n("sys.overview.wifiSummary")}),e.jsxs("p",{className:"text-xs text-on-surface-variant",children:[n("sys.overview.iface"),": ",String((r==null?void 0:r.wifi_interface)??"wlan0")]})]}),e.jsx("span",{className:"rounded border border-primary/30 bg-primary/10 px-2 py-1 text-[10px] uppercase tracking-widest text-primary",children:n("sys.overview.linkActive")})]}),e.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[e.jsxs("div",{className:"rounded-lg border border-outline-variant/20 bg-surface-container p-4",children:[e.jsx("p",{className:"text-[10px] uppercase tracking-widest text-on-surface-variant",children:n("sys.overview.signal")}),e.jsx("p",{className:"mt-1 font-mono text-2xl font-bold",children:N})]}),e.jsxs("div",{className:"rounded-lg border border-outline-variant/20 bg-surface-container p-4",children:[e.jsx("p",{className:"text-[10px] uppercase tracking-widest text-on-surface-variant",children:n("sys.overview.quality")}),e.jsx("p",{className:"mt-1 font-mono text-2xl font-bold",children:_})]})]})]}),e.jsxs("aside",{className:"col-span-12 space-y-4 lg:col-span-4",children:[e.jsxs("div",{className:"rounded-xl border border-outline-variant/20 bg-surface-container p-4",children:[e.jsx("p",{className:"text-[10px] uppercase tracking-widest text-on-surface-variant",children:n("sys.overview.metric.uptime")}),e.jsx("p",{className:"mt-1 text-xl font-bold",children:Ze(r==null?void 0:r.uptime_seconds)})]}),e.jsxs("div",{className:"rounded-xl border border-outline-variant/20 bg-surface-container p-4",children:[e.jsx("p",{className:"text-[10px] uppercase tracking-widest text-on-surface-variant",children:n("sys.overview.metric.load")}),e.jsx("p",{className:"mt-1 text-xl font-bold",children:v})]}),e.jsxs("div",{className:"rounded-xl border border-outline-variant/20 bg-surface-container p-4",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("p",{className:"text-[10px] uppercase tracking-widest text-on-surface-variant",children:n("sys.overview.metric.storage")}),e.jsx(Fe,{className:"h-4 w-4 text-on-surface-variant"})]}),e.jsx("p",{className:"mt-2 text-sm text-on-surface-variant",children:n("sys.overview.storageComingSoon")})]})]})]}),e.jsxs("section",{className:"rounded-xl border border-outline-variant/20 bg-surface-container-lowest p-4",children:[e.jsxs("div",{className:"mb-3 flex flex-wrap items-center justify-between gap-3 border-b border-outline-variant/20 pb-2",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("span",{className:"text-[10px] uppercase tracking-widest text-primary",children:n("sys.logs.title")}),e.jsxs("span",{className:"text-[10px] text-on-surface-variant",children:[n("sys.logs.kernel"),": ",String((r==null?void 0:r.os)??"—")]})]}),e.jsxs("div",{className:"flex items-center gap-2 text-xs",children:[e.jsxs("label",{className:"inline-flex items-center gap-2 text-on-surface-variant",children:[e.jsx("input",{type:"checkbox",checked:d,onChange:i=>j(i.target.checked)}),n("sys.logs.liveToggle")]}),e.jsx("button",{type:"button",className:"rounded border border-outline-variant/40 px-2 py-1 text-on-surface-variant hover:border-primary hover:text-on-surface",onClick:()=>void P(),children:e.jsxs("span",{className:"inline-flex items-center gap-1",children:[e.jsx(he,{className:"h-3.5 w-3.5"})," ",n("sys.logs.refresh")]})})]})]}),e.jsxs("div",{className:"mb-2 flex flex-wrap items-center gap-2 text-xs",children:[["INFO","WARN","ERROR"].map(i=>e.jsxs("label",{className:"inline-flex items-center gap-1.5 text-on-surface-variant",children:[e.jsx("input",{type:"checkbox",checked:z.has(i),onChange:w=>{f(L=>w.target.checked?Array.from(new Set([...L,i])):L.filter(A=>A!==i))}}),e.jsx("span",{className:ye(i),children:i})]},i)),!d&&e.jsxs("span",{className:"inline-flex items-center gap-1 rounded border border-outline-variant/30 px-2 py-0.5 text-[11px] text-on-surface-variant",children:[e.jsx(Be,{className:"h-3.5 w-3.5"})," ",n("sys.logs.liveOffHint")]})]}),k&&e.jsx("div",{className:"mb-2 rounded border border-error/40 bg-error-container/20 px-2 py-1 text-xs text-on-error-container",children:k}),e.jsxs("div",{ref:S,className:"og-scrollbar max-h-72 space-y-1 overflow-auto font-mono text-[11px] text-on-surface-variant",children:[m&&g.length===0&&e.jsx("div",{children:n("sys.logs.loading")}),!m&&g.length===0&&e.jsx("div",{children:n("sys.logs.empty")}),g.map((i,w)=>e.jsxs("div",{className:"flex items-start gap-2",children:[e.jsxs("span",{className:"shrink-0 text-primary",children:["[",Je(i.ts),"]"]}),e.jsx("span",{className:`shrink-0 ${ye(i.level)}`,children:i.level}),e.jsx("span",{className:"shrink-0 text-on-surface/80",children:i.source}),e.jsx("span",{className:"min-w-0 break-words text-on-surface",children:i.message})]},`${i.ts||"ts"}-${w}`))]})]})]})}function le(){return typeof window.OGSCOPE_HTTP_PORT=="number"?window.OGSCOPE_HTTP_PORT:8e3}function Ve(t){const o=Math.max(0,parseInt(String(t??0),10)||0),n=Math.floor(o/86400),r=Math.floor(o%86400/3600),c=Math.floor(o%3600/60);return n>0?`${n}天 ${r}小时`:r>0?`${r}小时 ${c}分`:`${c}分`}function Qe(){const{info:t,error:o}=pe(),[n,r]=a.useState("加载中..."),[c,d]=a.useState(""),[j,u]=a.useState(""),[f,g]=a.useState([]),[b,k]=a.useState(!1),[y,m]=a.useState([]),[$,M]=a.useState(!1),[R,S]=a.useState(""),[E,h]=a.useState(""),[T,O]=a.useState(!1),[v,_]=a.useState(""),[N,P]=a.useState(`http://192.168.4.1:${le()}`),[z,i]=a.useState("OGScope_xxxx"),[w,L]=a.useState(null),[A,I]=a.useState("—"),U=a.useCallback(s=>{const l=s.mode||"unknown",p=s.active_connection||"-",C=s.wireless_interface||"wlan0",D=s.ap_ipv4||"-",G=s.configured?"是":"否",J=s.message?`,消息: ${s.message}`:"";s.ap_url_hint&&P(s.ap_url_hint),s.ap_ssid&&i(s.ap_ssid);const Y=le();if(s.mdns_hostname_hint){const ne=`http://${s.mdns_hostname_hint}:${Y}/debug`;L(ne),I(ne)}else if(s.device_id_suffix){const ne=`http://${`ogscope-${s.device_id_suffix}.local`}:${Y}/debug`;L(ne),I(ne)}else L(null),I("未提供");r(`模式: ${l} | 活动连接: ${p} | 接口: ${C} | AP地址: ${D} | 已配置: ${G}${J}`)},[]),K=a.useCallback(async()=>{const s=await W("/api/network/wifi",{cache:"no-store"});U(s)},[U]);a.useEffect(()=>{K().catch(s=>r(`获取状态失败: ${s.message}`))},[K]);const Q=async s=>{r(`正在切换到 ${s.toUpperCase()}...`);const l=await W("/api/network/wifi",{method:"POST",body:JSON.stringify({mode:s})});U(l)},re=async()=>{k(!0),u("扫描中..."),g([]);try{const s=await W("/api/network/wifi/scan",{cache:"no-store"}),l=s.networks||[],p=s.hint?` ${s.hint}`:"";g(l),u(`扫描到 ${l.length} 个网络${p}`.trim())}catch(s){u(`扫描失败: ${s instanceof Error?s.message:String(s)}`)}finally{k(!1)}},ee=async s=>{const l=window.prompt(`输入密码: ${s}`,"");if(l!==null){d(""),r("正在连接..."),O(!0);try{const p=await W("/api/network/wifi/sta/connect",{method:"POST",body:JSON.stringify({ssid:s,password:l||null})});U(p);const C=p.mode||"unknown";d(C==="sta"?`连接成功,当前连接: ${p.active_connection||"—"}`:`连接请求已提交,当前模式: ${C},连接: ${p.active_connection||"—"}`),window.setTimeout(()=>void K().catch(()=>{}),2500)}catch(p){r(`连接失败: ${p instanceof Error?p.message:String(p)}`),d(`错误详情: ${p instanceof Error?p.message:String(p)}`)}finally{O(!1)}}},se=async()=>{const s=R.trim();if(!s){window.alert("请输入 SSID");return}d(""),r("正在连接..."),O(!0);try{const l=await W("/api/network/wifi/sta/connect",{method:"POST",body:JSON.stringify({ssid:s,password:E||null})});U(l);const p=l.mode||"unknown";d(p==="sta"?`连接成功,当前连接: ${l.active_connection||"—"}`:`连接请求已提交,当前模式: ${p},连接: ${l.active_connection||"—"}`),window.setTimeout(()=>void K().catch(()=>{}),2500)}catch(l){r(`连接失败: ${l instanceof Error?l.message:String(l)}`),d(`错误详情: ${l instanceof Error?l.message:String(l)}`)}finally{O(!1)}},ae=async()=>{M(!0);try{const s=await W("/api/network/wifi/profiles",{cache:"no-store"});m(s.profiles||[])}catch(s){m([]),window.alert(s instanceof Error?s.message:String(s))}finally{M(!1)}},x=async s=>{try{await W("/api/network/wifi/profile/activate",{method:"POST",body:JSON.stringify({connection_name:s})}),r("已发送激活请求")}catch(l){window.alert(l instanceof Error?l.message:String(l))}};async function F(s,l){const p=`http://${s}:${l}/health`;try{const C=new AbortController,D=window.setTimeout(()=>C.abort(),700),G=await fetch(p,{signal:C.signal,mode:"cors"});if(window.clearTimeout(D),!G.ok)return null;const J=await G.json();if(J&&J.status==="healthy")return`http://${s}:${l}`}catch{}return null}const X=async()=>{const s=le();_("扫描中...");const l=["192.168.0","192.168.1","192.168.31","10.0.0"];for(const p of l){const C=[];for(let D=1;D<255;D++)C.push(`${p}.${D}`);for(let D=0;DF(ce,s)))).find(Boolean);if(Y){_(`已找到设备: ${Y}`),window.location.href=`${Y}/debug`;return}}}_("未找到设备")},ie=a.useMemo(()=>{if(!t)return[];const s=t.wifi_quality!=null&&!Number.isNaN(Number(t.wifi_quality))?`${Number(t.wifi_quality).toFixed(1)}%`:"—",l=t.wifi_signal_dbm!=null&&!Number.isNaN(Number(t.wifi_signal_dbm))?`${Number(t.wifi_signal_dbm).toFixed(0)} dBm (${String(t.wifi_interface??"?")})`:"—";return[["平台",String(t.platform??"—")],["系统",String(t.os??"—")],["CPU 占用",`${Number(t.cpu_usage??0).toFixed(1)}%`],["内存占用",`${Number(t.memory_usage??0).toFixed(1)}%`],["CPU 温度",`${Number(t.temperature??0).toFixed(1)} °C`],["运行时长",Ve(t.uptime_seconds)],["1 分钟负载",String(t.load_average_1m??"—")],["WiFi 质量",s],["WiFi 信号",l]]},[t]);return e.jsxs("div",{className:"mx-auto max-w-7xl space-y-6",children:[e.jsxs("header",{children:[e.jsxs("div",{className:"flex items-center gap-2 text-[10px] uppercase tracking-[0.14em] text-on-surface-variant",children:[e.jsx("span",{children:"Console"}),e.jsx("span",{children:"/"}),e.jsx("span",{className:"text-primary",children:"System_Network_Debug"})]}),e.jsx("h2",{className:"mt-1 font-headline text-3xl font-black tracking-tight",children:"NETWORK TERMINAL"}),e.jsx("p",{className:"text-sm text-on-surface-variant",children:"WiFi 管理、模式切换、发现与恢复工具"})]}),o&&e.jsx("div",{className:"rounded-lg border border-error/40 bg-error-container/20 px-3 py-2 text-sm text-on-error-container",children:o}),e.jsxs("section",{className:"grid grid-cols-12 gap-6",children:[e.jsxs("div",{className:"col-span-12 space-y-6 lg:col-span-8",children:[e.jsxs("div",{className:"rounded-xl border border-outline-variant/20 bg-surface-container",children:[e.jsxs("div",{className:"flex items-center justify-between border-b border-outline-variant/20 bg-surface-container-high px-4 py-3",children:[e.jsxs("div",{className:"flex items-center gap-2 text-xs font-semibold uppercase tracking-wider text-primary",children:[e.jsx(fe,{className:"h-4 w-4"}),"WiFi Control"]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("button",{type:"button",className:"rounded border border-outline-variant/40 px-2 py-1 text-xs hover:border-primary",onClick:()=>void Q("sta").catch(s=>r(String(s))),children:"STA"}),e.jsx("button",{type:"button",className:"rounded border border-outline-variant/40 px-2 py-1 text-xs hover:border-primary",onClick:()=>void Q("ap").catch(s=>r(String(s))),children:"AP"}),e.jsx("button",{type:"button",className:"rounded border border-outline-variant/40 p-1.5 hover:border-primary",onClick:()=>void K().catch(s=>r(String(s))),children:e.jsx(he,{className:"h-3.5 w-3.5"})})]})]}),e.jsxs("div",{className:"space-y-3 p-4",children:[e.jsx("p",{className:"rounded border border-outline-variant/20 bg-surface-container-low p-3 font-mono text-xs text-on-surface",children:n}),e.jsxs("div",{className:"grid gap-2 text-xs text-on-surface-variant md:grid-cols-2",children:[e.jsxs("p",{children:["AP 地址:",e.jsx("span",{className:"font-mono text-on-surface",children:N})]}),e.jsxs("p",{children:["mDNS:"," ",w?e.jsx("a",{className:"font-mono text-primary underline",href:w,children:A}):e.jsx("span",{className:"font-mono text-on-surface",children:A})]})]})]})]}),e.jsxs("div",{className:"rounded-xl border border-outline-variant/20 bg-surface-container p-4",children:[e.jsxs("div",{className:"mb-3 flex items-center justify-between",children:[e.jsx("h3",{className:"text-sm font-semibold uppercase tracking-wider",children:"Scan Results"}),e.jsx("button",{type:"button",disabled:b,className:"rounded bg-primary-container px-3 py-1.5 text-xs font-medium text-on-primary-container disabled:opacity-50",onClick:()=>void re(),children:e.jsxs("span",{className:"inline-flex items-center gap-1",children:[e.jsx(_e,{className:"h-3.5 w-3.5"})," 扫描 WiFi"]})})]}),e.jsx("p",{className:"mb-3 text-xs text-on-surface-variant",children:j||"由设备端 NetworkManager 执行扫描"}),e.jsx("div",{className:"overflow-x-auto",children:e.jsxs("table",{className:"w-full text-left text-sm",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"border-b border-outline-variant/30 text-xs uppercase tracking-wider text-on-surface-variant",children:[e.jsx("th",{className:"p-2",children:"SSID"}),e.jsx("th",{className:"p-2",children:"信号"}),e.jsx("th",{className:"p-2",children:"安全"}),e.jsx("th",{className:"p-2 text-right",children:"操作"})]})}),e.jsx("tbody",{children:f.map(s=>e.jsxs("tr",{className:"border-b border-outline-variant/10",children:[e.jsx("td",{className:"p-2 font-mono",children:s.ssid}),e.jsx("td",{className:"p-2",children:s.signal??"—"}),e.jsx("td",{className:"p-2",children:s.security??"—"}),e.jsx("td",{className:"p-2 text-right",children:e.jsx("button",{type:"button",disabled:T,className:"rounded border border-primary/40 px-2 py-1 text-xs text-primary disabled:opacity-50",onClick:()=>void ee(s.ssid),children:"Connect"})})]},s.ssid))})]})})]}),e.jsxs("div",{className:"rounded-xl border border-outline-variant/20 bg-surface-container p-4",children:[e.jsx("h3",{className:"mb-3 text-sm font-semibold uppercase tracking-wider",children:"Known Networks"}),e.jsx("button",{type:"button",disabled:$,className:"rounded border border-outline-variant/40 px-3 py-1.5 text-xs disabled:opacity-50",onClick:()=>void ae(),children:"刷新已保存网络"}),e.jsxs("table",{className:"mt-3 w-full text-left text-sm",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"border-b border-outline-variant/30 text-xs uppercase tracking-wider text-on-surface-variant",children:[e.jsx("th",{className:"p-2",children:"连接名"}),e.jsx("th",{className:"p-2",children:"SSID"}),e.jsx("th",{className:"p-2",children:"自动连接"}),e.jsx("th",{className:"p-2 text-right",children:"操作"})]})}),e.jsx("tbody",{children:y.map(s=>e.jsxs("tr",{className:"border-b border-outline-variant/10",children:[e.jsx("td",{className:"p-2",children:s.connection_name}),e.jsx("td",{className:"p-2",children:s.ssid}),e.jsx("td",{className:"p-2",children:s.autoconnect?"是":"否"}),e.jsx("td",{className:"p-2 text-right",children:e.jsx("button",{type:"button",className:"rounded border border-outline-variant/40 px-2 py-1 text-xs hover:border-primary",onClick:()=>void x(s.connection_name),children:"Activate"})})]},s.connection_name))})]})]})]}),e.jsxs("aside",{className:"col-span-12 space-y-6 lg:col-span-4",children:[e.jsxs("div",{className:"rounded-xl border border-outline-variant/20 bg-surface-container p-4",children:[e.jsx("h3",{className:"mb-3 text-sm font-semibold uppercase tracking-wider",children:"System Monitor"}),e.jsx("div",{className:"grid gap-2",children:ie.map(([s,l])=>e.jsxs("div",{className:"flex justify-between border-b border-outline-variant/10 pb-1 text-xs",children:[e.jsx("span",{className:"text-on-surface-variant",children:s}),e.jsx("span",{className:"font-mono text-on-surface",children:l})]},s))})]}),e.jsxs("div",{className:"rounded-xl border border-outline-variant/20 bg-surface-container p-4",children:[e.jsx("h3",{className:"mb-3 text-sm font-semibold uppercase tracking-wider",children:"Manual Connect"}),e.jsxs("div",{className:"space-y-2",children:[e.jsxs("label",{className:"block text-xs text-on-surface-variant",children:["SSID",e.jsx("input",{className:"mt-1 w-full rounded border border-outline-variant/40 bg-surface-container-low px-2 py-1.5 text-sm",value:R,onChange:s=>S(s.target.value)})]}),e.jsxs("label",{className:"block text-xs text-on-surface-variant",children:["Password",e.jsx("input",{type:"password",className:"mt-1 w-full rounded border border-outline-variant/40 bg-surface-container-low px-2 py-1.5 text-sm",value:E,onChange:s=>h(s.target.value)})]}),e.jsx("button",{type:"button",disabled:T,className:"w-full rounded bg-primary-container px-4 py-2 text-sm font-medium text-on-primary-container disabled:opacity-50",onClick:()=>void se(),children:"连接并切换 STA"})]}),e.jsx("p",{className:"mt-2 text-xs text-on-surface-variant",children:c})]}),e.jsxs("div",{className:"rounded-xl border border-outline-variant/20 bg-surface-container p-4",children:[e.jsx("h3",{className:"mb-2 text-sm font-semibold uppercase tracking-wider",children:"WiFi 引导"}),e.jsxs("ol",{className:"list-decimal space-y-1 pl-4 text-xs text-on-surface-variant",children:[e.jsxs("li",{children:["连接热点 ",e.jsx("strong",{children:z}),",密码 ",e.jsx("code",{children:"ogscopeadmin"})]}),e.jsxs("li",{children:["浏览器打开 ",e.jsxs("span",{className:"font-mono",children:["http://192.168.4.1:",le()]})]}),e.jsx("li",{children:"扫描 WiFi 或手动填写 SSID 连接"})]})]}),e.jsxs("div",{className:"rounded-xl border border-outline-variant/20 bg-surface-container p-4",children:[e.jsx("h3",{className:"mb-2 text-sm font-semibold uppercase tracking-wider",children:"Find Device"}),e.jsx("p",{className:"mb-3 text-xs text-on-surface-variant",children:"扫描常见网段并探测 /health"}),e.jsx("button",{type:"button",className:"w-full rounded border border-outline-variant/40 px-3 py-2 text-sm hover:border-primary",onClick:()=>void X(),children:"扫描局域网"}),e.jsx("p",{className:"mt-2 text-xs text-on-surface-variant",children:v})]})]})]})]})}function es(t){var r,c;const o=(c=(r=t==null?void 0:t.data)==null?void 0:r.services)==null?void 0:c.hmi;if(!o||typeof o!="object")return null;const n=o.display;return!n||typeof n!="object"?null:n}function ss(t){try{return JSON.stringify(t,null,2)}catch{return String(t)}}function ts(){var v,_;const{t}=Z(),[o,n]=a.useState(null),[r,c]=a.useState(null),[d,j]=a.useState(!1),[u,f]=a.useState(null),[g,b]=a.useState(null),[k,y]=a.useState(40),[m,$]=a.useState(80),[M,R]=a.useState(200),S=a.useCallback(async()=>{try{c(null);const N=await Ke();n(N)}catch(N){n(null),c(N instanceof Error?N.message:String(N))}},[]);a.useEffect(()=>{S()},[S]);const E=a.useCallback(async(N,P={})=>{var z;j(!0),b(null);try{const i=await Xe({target:"hmi",action:N,payload:P,timeout_ms:8e3});if(f(i),!i.success){const A=((z=i.error)==null?void 0:z.message)??"RPC failed";b(A);return}const w=i.data,L=w==null?void 0:w.result;L&&L.accepted===!1&&L.message?b(L.message):b(null),await S()}catch(i){f(null),b(i instanceof Error?i.message:String(i))}finally{j(!1)}},[S]),h=es(o),T=(_=(v=o==null?void 0:o.data)==null?void 0:v.services)==null?void 0:_.hmi,O=typeof(T==null?void 0:T.screen_on)=="boolean"?T.screen_on:void 0;return e.jsxs("div",{className:"mx-auto max-w-6xl space-y-6",children:[e.jsxs("header",{children:[e.jsx("div",{className:"text-[10px] uppercase tracking-[0.14em] text-on-surface-variant",children:t("sys.placeholder.breadcrumb")}),e.jsx("h2",{className:"mt-1 font-headline text-3xl font-black tracking-tight",children:t("sys.hmi.title")}),e.jsx("p",{className:"text-sm text-on-surface-variant",children:t("sys.hmi.desc")})]}),e.jsxs("section",{className:"rounded-xl border border-outline-variant/20 bg-surface-container-low p-4",children:[e.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-3",children:[e.jsx("p",{className:"text-xs font-medium text-on-surface",children:t("sys.hmi.status.section")}),e.jsx("button",{type:"button",className:"rounded-lg border border-outline-variant/40 bg-surface-container px-3 py-1.5 text-xs font-medium text-on-surface hover:bg-surface-container-high",onClick:()=>void S(),disabled:d,children:t("sys.hmi.status.refresh")})]}),r?e.jsx("p",{className:"mt-2 text-sm text-error",children:r}):e.jsxs("dl",{className:"mt-3 grid gap-2 font-mono text-[11px] text-on-surface-variant sm:grid-cols-2",children:[e.jsxs("div",{children:[e.jsx("dt",{className:"text-on-surface-variant",children:t("sys.hmi.status.displayEnabled")}),e.jsx("dd",{className:"text-on-surface",children:(h==null?void 0:h.enabled)===!0?"true":"false"})]}),e.jsxs("div",{children:[e.jsx("dt",{children:t("sys.hmi.status.spidev")}),e.jsx("dd",{className:h!=null&&h.spidev_present?"text-primary":"text-error",children:h!=null&&h.spidev_present?t("sys.hmi.status.yes"):t("sys.hmi.status.no")})]}),e.jsxs("div",{children:[e.jsx("dt",{children:t("sys.hmi.status.resolution")}),e.jsxs("dd",{children:[(h==null?void 0:h.width)??"—"," × ",(h==null?void 0:h.height)??"—"," · DC GPIO ",(h==null?void 0:h.dc_pin)??"—"]})]}),e.jsxs("div",{children:[e.jsx("dt",{children:t("sys.hmi.status.driver")}),e.jsx("dd",{children:h!=null&&h.driver_open?t("sys.hmi.status.open"):t("sys.hmi.status.closed")})]}),e.jsxs("div",{children:[e.jsx("dt",{children:t("sys.hmi.status.screenOutput")}),e.jsx("dd",{children:O===void 0?"—":t(O?"sys.hmi.status.on":"sys.hmi.status.off")})]}),e.jsxs("div",{className:"sm:col-span-2",children:[e.jsx("dt",{children:t("sys.hmi.status.lastPattern")}),e.jsx("dd",{children:(h==null?void 0:h.last_pattern)??"—"})]}),h!=null&&h.last_error?e.jsxs("div",{className:"sm:col-span-2",children:[e.jsx("dt",{children:t("sys.hmi.status.lastError")}),e.jsx("dd",{className:"text-error",children:h.last_error})]}):null]})]}),e.jsxs("section",{className:"rounded-xl border border-outline-variant/20 bg-surface-container-low p-4",children:[e.jsx("p",{className:"text-xs font-medium text-on-surface",children:t("sys.hmi.actions.section")}),e.jsx("p",{className:"mt-1 text-[11px] text-on-surface-variant",children:t("sys.hmi.actions.hint")}),e.jsxs("div",{className:"mt-4 flex flex-wrap gap-2",children:[e.jsx("button",{type:"button",className:"rounded-lg bg-primary px-3 py-2 text-xs font-semibold text-on-primary hover:opacity-90 disabled:opacity-50",disabled:d,onClick:()=>void E("display.test_pattern",{pattern:"smoke"}),children:t("sys.hmi.actions.smoke")}),e.jsx("button",{type:"button",className:"rounded-lg border border-outline-variant/40 bg-surface-container px-3 py-2 text-xs font-medium text-on-surface hover:bg-surface-container-high disabled:opacity-50",disabled:d,onClick:()=>void E("display.test_pattern",{pattern:"colorbars"}),children:t("sys.hmi.actions.colorbars")})]}),e.jsxs("div",{className:"mt-6 flex flex-wrap items-end gap-3",children:[e.jsxs("label",{className:"flex flex-col gap-1 text-[11px] text-on-surface-variant",children:["R",e.jsx("input",{type:"number",min:0,max:255,className:"w-20 rounded border border-outline-variant/40 bg-surface-container px-2 py-1 font-mono text-sm text-on-surface",value:k,onChange:N=>y(Number(N.target.value))})]}),e.jsxs("label",{className:"flex flex-col gap-1 text-[11px] text-on-surface-variant",children:["G",e.jsx("input",{type:"number",min:0,max:255,className:"w-20 rounded border border-outline-variant/40 bg-surface-container px-2 py-1 font-mono text-sm text-on-surface",value:m,onChange:N=>$(Number(N.target.value))})]}),e.jsxs("label",{className:"flex flex-col gap-1 text-[11px] text-on-surface-variant",children:["B",e.jsx("input",{type:"number",min:0,max:255,className:"w-20 rounded border border-outline-variant/40 bg-surface-container px-2 py-1 font-mono text-sm text-on-surface",value:M,onChange:N=>R(Number(N.target.value))})]}),e.jsx("button",{type:"button",className:"rounded-lg border border-outline-variant/40 bg-surface-container px-3 py-2 text-xs font-medium text-on-surface hover:bg-surface-container-high disabled:opacity-50",disabled:d,onClick:()=>void E("display.test_pattern",{pattern:"fill",r:k,g:m,b:M}),children:t("sys.hmi.actions.fill")})]}),e.jsxs("div",{className:"mt-6 flex flex-wrap gap-2 border-t border-outline-variant/20 pt-4",children:[e.jsx("button",{type:"button",className:"rounded-lg border border-outline-variant/40 px-3 py-2 text-xs text-on-surface hover:bg-surface-container-high disabled:opacity-50",disabled:d,onClick:()=>void E("screen.set",{on:!0}),children:t("sys.hmi.actions.screenOn")}),e.jsx("button",{type:"button",className:"rounded-lg border border-outline-variant/40 px-3 py-2 text-xs text-on-surface hover:bg-surface-container-high disabled:opacity-50",disabled:d,onClick:()=>void E("screen.set",{on:!1}),children:t("sys.hmi.actions.screenOff")}),e.jsx("button",{type:"button",className:"rounded-lg border border-outline-variant/40 px-3 py-2 text-xs text-on-surface hover:bg-surface-container-high disabled:opacity-50",disabled:d,onClick:()=>void E("display.release"),children:t("sys.hmi.actions.release")})]}),g?e.jsx("p",{className:"mt-3 text-sm text-error",children:g}):null,e.jsxs("details",{className:"mt-4",children:[e.jsx("summary",{className:"cursor-pointer text-[11px] text-on-surface-variant",children:t("sys.hmi.rawJson")}),e.jsx("pre",{className:"mt-2 max-h-64 overflow-auto rounded border border-outline-variant/30 bg-surface-container p-2 font-mono text-[10px] text-on-surface",children:u?ss(u):"—"})]})]})]})}const rs={sensors:{titleKey:"sys.placeholder.sensors.title",descKey:"sys.placeholder.sensors.desc",blocks:["sys.placeholder.sensors.block1","sys.placeholder.sensors.block2","sys.placeholder.sensors.block3"]},hmi:{titleKey:"sys.placeholder.hmi.title",descKey:"sys.placeholder.hmi.desc",blocks:["sys.placeholder.hmi.block1","sys.placeholder.hmi.block2","sys.placeholder.hmi.block3"]},power:{titleKey:"sys.placeholder.power.title",descKey:"sys.placeholder.power.desc",blocks:["sys.placeholder.power.block1","sys.placeholder.power.block2","sys.placeholder.power.block3"]}};function ve({scope:t}){const{t:o}=Z(),n=rs[t];return e.jsxs("div",{className:"mx-auto max-w-6xl space-y-6",children:[e.jsxs("header",{children:[e.jsx("div",{className:"text-[10px] uppercase tracking-[0.14em] text-on-surface-variant",children:o("sys.placeholder.breadcrumb")}),e.jsx("h2",{className:"mt-1 font-headline text-3xl font-black tracking-tight",children:o(n.titleKey)}),e.jsx("p",{className:"text-sm text-on-surface-variant",children:o(n.descKey)})]}),e.jsx("section",{className:"grid grid-cols-12 gap-4",children:n.blocks.map(r=>e.jsxs("article",{className:"col-span-12 rounded-xl border border-dashed border-outline-variant/40 bg-surface-container/60 p-5 md:col-span-4",children:[e.jsx("p",{className:"text-[10px] uppercase tracking-widest text-primary",children:o("sys.placeholder.block")}),e.jsx("h3",{className:"mt-2 text-lg font-semibold",children:o(r)}),e.jsx("p",{className:"mt-2 text-sm text-on-surface-variant",children:o("sys.placeholder.desc")})]},r))}),e.jsx("section",{className:"rounded-xl border border-outline-variant/20 bg-surface-container-low p-4",children:e.jsx("p",{className:"font-mono text-xs text-on-surface-variant",children:o("sys.placeholder.status")})})]})}const B=62,te=4;function as(t){const{rollDeg:o,pitchDeg:n,yawRateDps:r}=t,{t:c}=Z(),d=`rotateX(${-n}deg) rotateY(${o}deg)`;return e.jsxs("div",{className:"mt-5 rounded-xl border border-teal-500/25 bg-black/25 p-4 ring-1 ring-teal-500/10",children:[e.jsx("p",{className:"text-center text-[11px] font-semibold text-teal-200/95",children:c("sys.sensors.gyro.att3dTitle")}),e.jsx("p",{className:"mx-auto mt-1 max-w-xl text-center text-[10px] leading-relaxed text-on-surface-variant",children:c("sys.sensors.gyro.att3dDesc")}),e.jsxs("div",{className:"mt-4 flex flex-col gap-6 xl:flex-row xl:items-start xl:justify-between xl:gap-8",children:[e.jsxs("div",{className:"flex min-w-0 flex-1 flex-col items-center gap-4",children:[e.jsx("div",{className:"flex shrink-0 items-center justify-center py-2",style:{perspective:"820px"},children:e.jsxs("div",{className:"relative h-[188px] w-[220px]",style:{transformStyle:"preserve-3d",transform:d},children:[e.jsxs("div",{className:"absolute left-[10px] top-[18px] h-[152px] w-[200px] rounded-2xl border-2 border-teal-400/75 bg-gradient-to-br from-slate-600/90 via-slate-800/95 to-slate-950 shadow-[0_20px_50px_rgba(0,0,0,0.55),inset_0_1px_0_rgba(255,255,255,0.08)] transition-transform duration-150 ease-out",style:{transformStyle:"preserve-3d"},children:[e.jsx("div",{className:"pointer-events-none absolute inset-x-0 top-2 flex justify-center",children:e.jsx("span",{className:"rounded bg-black/35 px-2 py-0.5 text-[9px] font-bold uppercase tracking-[0.2em] text-teal-100",children:"TOP"})}),e.jsx("div",{className:"absolute bottom-2 left-2 font-mono text-[9px] text-slate-400",children:"MPU-6050"})]}),e.jsxs("div",{className:"pointer-events-none absolute left-[110px] top-[94px] h-0 w-0",style:{transformStyle:"preserve-3d"},children:[e.jsx("div",{className:"absolute rounded-full bg-white/90 shadow-[0_0_6px_rgba(255,255,255,0.6)]",style:{width:7,height:7,left:-3.5,top:-3.5,transform:"translateZ(0.5px)"}}),e.jsx("div",{className:"absolute bg-amber-400 shadow-md ring-1 ring-amber-200/35",style:{width:B,height:te,left:0,top:-te/2,transformOrigin:"0 50%"}}),e.jsx("span",{className:"absolute whitespace-nowrap font-mono text-[10px] font-bold text-amber-200",style:{left:B+4,top:-8},children:c("sys.sensors.gyro.axisXLabel")}),e.jsx("div",{className:"absolute bg-sky-400 shadow-md ring-1 ring-sky-200/30",style:{width:B,height:te,left:0,top:-te/2,transformOrigin:"0 50%",transform:"rotateZ(90deg)"}}),e.jsx("span",{className:"absolute whitespace-nowrap font-mono text-[10px] font-bold text-sky-200",style:{left:-6,top:-B-16},children:c("sys.sensors.gyro.axisYLabel")}),e.jsx("div",{className:"absolute bg-violet-400 shadow-md ring-1 ring-violet-200/35",style:{width:B,height:te,left:0,top:-te/2,transformOrigin:"0 50%",transform:"rotateY(-90deg)"}}),e.jsx("span",{className:"absolute whitespace-nowrap font-mono text-[10px] font-bold text-violet-200",style:{left:B*.35,top:-B*.45,transform:"translateZ(28px)"},children:c("sys.sensors.gyro.axisZLabel")}),e.jsx("div",{className:"absolute bg-amber-900/55",style:{width:B*.45,height:2,left:-B*.45,top:-1,transformOrigin:"100% 50%"}}),e.jsx("div",{className:"absolute bg-sky-900/50",style:{width:B*.45,height:2,left:0,top:-1,transformOrigin:"0 50%",transform:`rotateZ(90deg) translateX(${-B*.45}px)`}}),e.jsx("div",{className:"absolute bg-violet-900/45",style:{width:B*.4,height:2,left:0,top:-1,transformOrigin:"0 50%",transform:`rotateY(-90deg) translateX(${-B*.4}px)`}})]})]})}),e.jsx("p",{className:"max-w-md text-center text-[10px] leading-snug text-on-surface-variant",children:c("sys.sensors.gyro.att3dBodyAxes")})]}),e.jsxs("div",{className:"flex w-full max-w-[220px] shrink-0 flex-col items-center gap-2 self-center xl:self-start",children:[e.jsx("p",{className:"text-center text-[10px] font-medium text-on-surface-variant",children:c("sys.sensors.gyro.att3dRefTitle")}),e.jsx("div",{className:"flex items-center justify-center rounded-lg border border-outline-variant/30 bg-slate-950/50 px-4 py-5 ring-1 ring-white/5",children:e.jsx("div",{style:{perspective:"280px"},children:e.jsx("div",{className:"relative h-[100px] w-[100px]",style:{transformStyle:"preserve-3d",transform:"rotateX(58deg) rotateZ(-42deg)"},children:e.jsxs("div",{className:"absolute left-1/2 top-1/2 h-0 w-0",style:{transformStyle:"preserve-3d"},children:[e.jsx("div",{className:"absolute bg-amber-400/95",style:{width:44,height:3,left:0,top:-1.5,transformOrigin:"0 50%"}}),e.jsx("div",{className:"absolute bg-sky-400/95",style:{width:44,height:3,left:0,top:-1.5,transformOrigin:"0 50%",transform:"rotateZ(90deg)"}}),e.jsx("div",{className:"absolute bg-violet-400/95",style:{width:44,height:3,left:0,top:-1.5,transformOrigin:"0 50%",transform:"rotateY(-90deg)"}}),e.jsx("div",{className:"absolute rounded-full bg-white/80",style:{width:5,height:5,left:-2.5,top:-2.5}})]})})})}),e.jsx("p",{className:"text-center text-[9px] leading-relaxed text-on-surface-variant/85",children:c("sys.sensors.gyro.att3dRefDesc")})]}),e.jsxs("div",{className:"grid w-full min-w-[200px] max-w-md grid-cols-3 gap-3 font-mono text-[11px] lg:max-w-lg xl:max-w-[340px]",children:[e.jsxs("div",{className:"rounded-lg border border-outline-variant/30 bg-surface-container/80 px-2 py-2 text-center",children:[e.jsx("p",{className:"text-[9px] uppercase tracking-wider text-on-surface-variant",children:c("sys.sensors.gyro.roll")}),e.jsxs("p",{className:"mt-1 text-lg font-bold tabular-nums text-teal-200",children:[o.toFixed(1),"°"]})]}),e.jsxs("div",{className:"rounded-lg border border-outline-variant/30 bg-surface-container/80 px-2 py-2 text-center",children:[e.jsx("p",{className:"text-[9px] uppercase tracking-wider text-on-surface-variant",children:c("sys.sensors.gyro.pitch")}),e.jsxs("p",{className:"mt-1 text-lg font-bold tabular-nums text-teal-200",children:[n.toFixed(1),"°"]})]}),e.jsxs("div",{className:"rounded-lg border border-outline-variant/30 bg-surface-container/80 px-2 py-2 text-center",children:[e.jsx("p",{className:"text-[9px] uppercase tracking-wider text-on-surface-variant",children:c("sys.sensors.gyro.yawRate")}),e.jsx("p",{className:"mt-1 text-lg font-bold tabular-nums text-amber-200/95",children:r.toFixed(2)}),e.jsx("p",{className:"text-[9px] text-on-surface-variant",children:"°/s"})]})]})]})]})}const xe=250;function ue(t){const{label:o,value:n,maxAbs:r,unit:c}=t,d=Math.max(-1,Math.min(1,n/r)),j=d>=0?50:50+d*50,u=Math.abs(d)*50;return e.jsxs("div",{className:"space-y-1",children:[e.jsxs("div",{className:"flex items-baseline justify-between gap-2",children:[e.jsx("span",{className:"text-[11px] font-medium text-on-surface",children:o}),e.jsxs("span",{className:"font-mono text-xs tabular-nums text-sky-100",children:[n.toFixed(2),e.jsxs("span",{className:"text-on-surface-variant",children:[" ",c]})]})]}),e.jsxs("div",{className:"relative h-5 w-full overflow-hidden rounded-md bg-slate-900/80 ring-1 ring-slate-600/50",children:[e.jsx("div",{className:"absolute left-1/2 top-0 z-10 h-full w-px -translate-x-px bg-slate-500/90"}),e.jsx("div",{className:"absolute top-1 h-3 rounded-sm bg-gradient-to-r from-emerald-600 to-sky-500 shadow-sm",style:{left:`${j}%`,width:`${Math.max(u,d===0?0:.8)}%`}})]})]})}function ns(t){const{bus:o,addr:n}=t,{t:r}=Z(),[c,d]=a.useState(!1),[j,u]=a.useState(null),[f,g]=a.useState(null),[b,k]=a.useState(null),[y,m]=a.useState(null),[$,M]=a.useState(null),[R,S]=a.useState(null),[E,h]=a.useState(!1),T=a.useRef(null),O=a.useCallback(async()=>{var _;d(!0),u(null);try{const N=new URLSearchParams({bus:String(o),addr:String(n)}),P=await q(`/api/debug/sensors/mpu6050/imu-sample?${N.toString()}`);if(!P.success){g(null),k(null),m(null),M(null),S(null),u(((_=P.sample)==null?void 0:_.error)||P.error||r("sys.sensors.gyro.errUnknown"));return}g(P.gyro_dps??null),k(P.gyro_raw??null),m(P.tilt_deg??null),M(P.yaw_rate_dps??null),S(P.accel_g??null)}catch(N){g(null),k(null),m(null),M(null),S(null),u(N instanceof Error?N.message:String(N))}finally{d(!1)}},[n,o,r]);a.useEffect(()=>{if(!E){T.current&&(clearInterval(T.current),T.current=null);return}return T.current=setInterval(()=>void O(),500),()=>{T.current&&clearInterval(T.current)}},[E,O]);const v=y!=null&&$!=null&&Number.isFinite(y.roll)&&Number.isFinite(y.pitch);return e.jsxs("div",{className:"mt-6 rounded-xl border border-emerald-600/35 bg-gradient-to-br from-slate-900/40 to-surface-container/70 p-4",children:[e.jsxs("div",{className:"flex flex-wrap items-start justify-between gap-3",children:[e.jsxs("div",{children:[e.jsx("p",{className:"text-sm font-semibold text-on-surface",children:r("sys.sensors.gyro.title")}),e.jsx("p",{className:"mt-1 max-w-xl text-[11px] leading-snug text-on-surface-variant",children:r("sys.sensors.gyro.subtitle")})]}),e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsxs("label",{className:"flex cursor-pointer items-center gap-1.5 text-[11px] text-on-surface-variant",children:[e.jsx("input",{type:"checkbox",checked:E,onChange:_=>h(_.target.checked)}),r("sys.sensors.gyro.live")]}),e.jsx("button",{type:"button",disabled:c,className:"rounded-lg bg-emerald-800/90 px-3 py-1.5 text-xs font-medium text-emerald-50 hover:bg-emerald-700 disabled:opacity-50",onClick:()=>void O(),children:r(c?"sys.sensors.gyro.loading":"sys.sensors.gyro.btn")})]})]}),v&&e.jsx(as,{rollDeg:y.roll,pitchDeg:y.pitch,yawRateDps:$}),f&&e.jsxs("div",{className:"mt-5 grid gap-4 md:grid-cols-3",children:[e.jsxs("div",{className:"rounded-lg border border-emerald-500/20 bg-black/20 px-3 py-3 text-center md:col-span-1",children:[e.jsx("p",{className:"text-[10px] text-on-surface-variant",children:"ωx"}),e.jsx("p",{className:"font-mono text-3xl font-bold tabular-nums text-emerald-200",children:f.x.toFixed(2)}),e.jsx("p",{className:"text-[10px] text-on-surface-variant",children:"°/s"})]}),e.jsxs("div",{className:"rounded-lg border border-emerald-500/20 bg-black/20 px-3 py-3 text-center md:col-span-1",children:[e.jsx("p",{className:"text-[10px] text-on-surface-variant",children:"ωy"}),e.jsx("p",{className:"font-mono text-3xl font-bold tabular-nums text-emerald-200",children:f.y.toFixed(2)}),e.jsx("p",{className:"text-[10px] text-on-surface-variant",children:"°/s"})]}),e.jsxs("div",{className:"rounded-lg border border-emerald-500/20 bg-black/20 px-3 py-3 text-center md:col-span-1",children:[e.jsx("p",{className:"text-[10px] text-on-surface-variant",children:"ωz"}),e.jsx("p",{className:"font-mono text-3xl font-bold tabular-nums text-emerald-200",children:f.z.toFixed(2)}),e.jsx("p",{className:"text-[10px] text-on-surface-variant",children:"°/s"})]})]}),f&&e.jsxs("div",{className:"mt-5 space-y-4",children:[e.jsx("p",{className:"text-[10px] font-medium uppercase tracking-wider text-on-surface-variant",children:r("sys.sensors.gyro.barsTitle")}),e.jsx(ue,{label:"X",value:f.x,maxAbs:xe,unit:"°/s"}),e.jsx(ue,{label:"Y",value:f.y,maxAbs:xe,unit:"°/s"}),e.jsx(ue,{label:"Z",value:f.z,maxAbs:xe,unit:"°/s"})]}),R&&e.jsxs("p",{className:"mt-3 text-center font-mono text-[10px] text-on-surface-variant",children:["g — X:",R.x.toFixed(3)," Y:",R.y.toFixed(3)," Z:",R.z.toFixed(3)]}),b&&e.jsxs("div",{className:"mt-4 rounded border border-outline-variant/25 bg-surface-container/50 px-3 py-2",children:[e.jsx("p",{className:"text-[10px] text-on-surface-variant",children:r("sys.sensors.gyro.rawBlock")}),e.jsxs("p",{className:"mt-1 font-mono text-[11px] tabular-nums text-on-surface",children:["raw X=",b.x," · Y=",b.y," · Z=",b.z]})]}),!f&&!j&&!c&&e.jsx("p",{className:"mt-4 text-[11px] text-on-surface-variant",children:r("sys.sensors.gyro.hint")}),j&&e.jsx("p",{className:"mt-3 rounded border border-amber-500/40 bg-amber-500/10 px-2 py-1.5 font-mono text-[11px] text-amber-100",children:j})]})}const be=3.4,Ce=7,os=3;function ls(t){const o=(t%360+360)%360;return o<1||o>359?"cardN":Math.abs(o-90)<1?"cardE":Math.abs(o-180)<1?"cardS":Math.abs(o-270)<1?"cardW":null}function is(){const t=[],o=Ce*360;for(let n=0;n<=o;n+=5){const r=n%360,c=n%30===0,d=!c&&n%10===0,j=ls(r),u=!j&&c&&r%90!==0?r:void 0;t.push({x:n*be,deg:n,h:c?"maj":d?"mid":"min",degLabel:u,cardKey:j??void 0})}return t}const cs=is(),je=Ce*360*be;function ds(t,o){const n=(t%360+360)%360;let c=(o%360+360)%360-n;return c=(c+180)%360-180,t+c}function xs(t){const{bus:o,addr:n}=t,{t:r}=Z(),[c,d]=a.useState(!1),[j,u]=a.useState(null),[f,g]=a.useState(null),[b,k]=a.useState(null),[y,m]=a.useState(!1),$=a.useRef(null),M=a.useRef(0),R=a.useRef(null),[S,E]=a.useState(320);a.useEffect(()=>{const v=R.current;if(!v)return;const _=new ResizeObserver(()=>{E(Math.max(200,v.clientWidth))});return _.observe(v),E(Math.max(200,v.clientWidth)),()=>_.disconnect()},[]),a.useEffect(()=>{g(null),k(null),u(null)},[o,n]);const h=a.useCallback(async()=>{const v=++M.current;d(!0);try{const _=new URLSearchParams({bus:String(o),addr:String(n)}),N=await q(`/api/debug/sensors/magnetometer/sample?${_.toString()}`);if(v!==M.current)return;if(!N.success){g(null),k(null),u(N.error||r("sys.sensors.compass.err"));return}u(null);const P=N.heading_deg??null;P!=null&&g(z=>z==null?os*360+P:ds(z,P)),k(N.field_ut??null)}catch(_){if(v!==M.current)return;g(null),k(null),u(_ instanceof Error?_.message:String(_))}finally{v===M.current&&d(!1)}},[n,o,r]);a.useEffect(()=>{if(!y){$.current&&(clearInterval($.current),$.current=null);return}return $.current=setInterval(()=>{h()},850),()=>{$.current&&clearInterval($.current)}},[y,h]);const T=a.useMemo(()=>{if(f==null)return 0;const v=f*be;return S/2-v},[f,S]),O=f!=null?(f%360+360)%360:null;return e.jsxs("div",{className:"rounded-xl border border-sky-500/30 bg-gradient-to-b from-slate-900/90 via-slate-900/70 to-surface-container/90 p-4",children:[e.jsxs("div",{className:"flex flex-wrap items-start justify-between gap-3",children:[e.jsxs("div",{children:[e.jsx("p",{className:"text-sm font-semibold text-on-surface",children:r("sys.sensors.compass.title")}),e.jsx("p",{className:"mt-1 max-w-xl text-[11px] leading-snug text-on-surface-variant",children:r("sys.sensors.compass.desc")})]}),e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsxs("label",{className:"flex cursor-pointer items-center gap-1.5 text-[11px] text-on-surface-variant",children:[e.jsx("input",{type:"checkbox",checked:y,onChange:v=>m(v.target.checked)}),r("sys.sensors.compass.live")]}),e.jsx("button",{type:"button",disabled:c,className:"rounded-lg bg-sky-700/80 px-3 py-1.5 text-xs font-medium text-white hover:bg-sky-600 disabled:opacity-50",onClick:()=>void h(),children:r(c?"sys.sensors.compass.loading":"sys.sensors.compass.btn")})]})]}),e.jsxs("div",{className:"mt-5",children:[e.jsx("p",{className:"mb-2 text-center text-[10px] uppercase tracking-[0.2em] text-sky-300/90",children:r("sys.sensors.compass.tapeCaption")}),e.jsxs("div",{ref:R,className:"relative mx-auto h-[112px] w-full max-w-3xl overflow-hidden rounded-lg ring-1 ring-sky-500/25",style:{maskImage:"linear-gradient(90deg, transparent 0%, black 10%, black 90%, transparent 100%)",WebkitMaskImage:"linear-gradient(90deg, transparent 0%, black 10%, black 90%, transparent 100%)"},children:[e.jsx("div",{className:"pointer-events-none absolute inset-x-0 top-0 z-20 flex justify-center",children:e.jsxs("div",{className:"flex flex-col items-center",children:[e.jsx("div",{className:"h-0 w-0 border-x-[9px] border-x-transparent border-b-[12px] border-b-amber-400 drop-shadow"}),e.jsx("div",{className:"h-[88px] w-0.5 rounded-full bg-gradient-to-b from-amber-300/95 to-sky-400/40"})]})}),e.jsx("div",{className:"absolute bottom-0 left-0 top-0 will-change-transform",style:{width:je,transform:`translateX(${T}px)`,transition:"transform 0.42s cubic-bezier(0.22, 0.95, 0.28, 1)"},children:e.jsxs("div",{className:"relative h-full",style:{width:je,background:"linear-gradient(180deg, rgba(15,23,42,0.2) 0%, rgba(30,41,59,0.85) 40%, rgba(15,23,42,0.95) 100%)"},children:[e.jsx("div",{className:"absolute left-0 right-0 top-8 h-px bg-slate-600/60"}),cs.map(v=>{const _=v.h==="maj"?22:v.h==="mid"?14:8;return e.jsxs("div",{className:"absolute flex flex-col items-center",style:{left:v.x,transform:"translateX(-50%)",top:32},children:[(v.cardKey||v.degLabel!=null)&&e.jsx("span",{className:`mb-0.5 whitespace-nowrap font-mono ${v.cardKey?"text-[13px] font-bold text-sky-200":"text-[10px] font-medium text-slate-400"}`,style:{marginTop:-18},children:v.cardKey?r(`sys.sensors.compass.${v.cardKey}`):String(v.degLabel)}),e.jsx("div",{className:`w-px rounded-full ${v.h==="maj"?"bg-sky-300/90":v.h==="mid"?"bg-slate-500/85":"bg-slate-600/50"}`,style:{height:_}})]},v.deg)}),e.jsx("div",{className:"absolute bottom-6 left-0 right-0 h-px bg-slate-600/40"})]})})]})]}),e.jsxs("div",{className:"mt-5 flex flex-col gap-4 md:flex-row md:items-start md:justify-center md:gap-8",children:[e.jsxs("div",{className:"rounded-lg border border-outline-variant/30 bg-surface-container/90 px-6 py-4 text-center md:min-w-[200px]",children:[e.jsx("p",{className:"text-[10px] uppercase tracking-widest text-on-surface-variant",children:r("sys.sensors.compass.headingLabel")}),e.jsx("p",{className:"font-mono text-4xl font-bold tabular-nums text-sky-200",children:O!=null?`${O.toFixed(1)}°`:"—"}),e.jsx("p",{className:"mt-1 text-[10px] text-on-surface-variant",children:r("sys.sensors.compass.headingHint")})]}),b&&e.jsxs("div",{className:"rounded-lg border border-outline-variant/20 px-4 py-3 font-mono text-[11px] text-on-surface md:max-w-md",children:[e.jsx("p",{className:"mb-1 text-[10px] text-on-surface-variant",children:"µT (X / Y / Z)"}),e.jsxs("p",{className:"tabular-nums",children:[b.x.toFixed(2)," · ",b.y.toFixed(2)," · ",b.z.toFixed(2)]})]})]}),j&&e.jsx("p",{className:"mt-3 rounded border border-amber-500/40 bg-amber-500/10 px-3 py-2 font-mono text-[11px] text-amber-100",children:j}),e.jsx("p",{className:"mt-3 text-[10px] leading-relaxed text-on-surface-variant/90",children:r("sys.sensors.compass.footnote")})]})}function Ne(t){try{return JSON.stringify(t,null,2)}catch{return String(t)}}function us(){const{t}=Z(),[o,n]=a.useState(1),[r,c]=a.useState(12),[d,j]=a.useState(!0),[u,f]=a.useState(!1),[g,b]=a.useState(null),[k,y]=a.useState(null),[m,$]=a.useState(null),[M,R]=a.useState(null),[S,E]=a.useState(1),[h,T]=a.useState(104),[O,v]=a.useState(!0),[_,N]=a.useState(!1),[P,z]=a.useState(null),[i,w]=a.useState(null),L=a.useCallback(async()=>{f(!0),b(null);try{const x=new URLSearchParams({bus:String(o),addr:String(r),i2cdetect:d?"true":"false"}),F=await q(`/api/debug/sensors/magnetometer/selftest?${x.toString()}`);y(F)}catch(x){y(null),b(x instanceof Error?x.message:String(x))}finally{f(!1)}},[r,o,d]),A=a.useCallback(async()=>{f(!0),b(null);try{const x=new URLSearchParams({addr:String(r)}),F=await q(`/api/debug/sensors/magnetometer/probe-buses?${x.toString()}`);y(F)}catch(x){y(null),b(x instanceof Error?x.message:String(x))}finally{f(!1)}},[r]),I=a.useCallback(async()=>{f(!0),b(null);try{const x=new URLSearchParams({bus:String(o),addr:String(r)}),F=await q(`/api/debug/sensors/magnetometer/calibration/start?${x.toString()}`,{method:"POST"});y(F),R("已开始方向校准,请缓慢旋转设备 5-15 秒。");const X=await q(`/api/debug/sensors/magnetometer/calibration/status?${x.toString()}`);$(X)}catch(x){y(null),b(x instanceof Error?x.message:String(x))}finally{f(!1)}},[r,o]),U=a.useCallback(async()=>{f(!0),b(null);try{const x=new URLSearchParams({bus:String(o),addr:String(r)}),F=await q(`/api/debug/sensors/magnetometer/calibration/commit?${x.toString()}`,{method:"POST"});y(F),R("已保存并锁定方向校准。");const X=await q(`/api/debug/sensors/magnetometer/calibration/status?${x.toString()}`);$(X)}catch(x){y(null),b(x instanceof Error?x.message:String(x))}finally{f(!1)}},[r,o]),K=a.useCallback(async()=>{f(!0),b(null);try{const x=new URLSearchParams({bus:String(o),addr:String(r)}),F=await q(`/api/debug/sensors/magnetometer/calibration/reset?${x.toString()}`,{method:"POST"});y(F),R("已重置到自动模式。");const X=await q(`/api/debug/sensors/magnetometer/calibration/status?${x.toString()}`);$(X)}catch(x){y(null),b(x instanceof Error?x.message:String(x))}finally{f(!1)}},[r,o]),Q=a.useCallback(async()=>{f(!0),b(null);try{const x=new URLSearchParams({bus:String(o),addr:String(r)}),F=await q(`/api/debug/sensors/magnetometer/calibration/status?${x.toString()}`);y(F),$(F)}catch(x){y(null),b(x instanceof Error?x.message:String(x))}finally{f(!1)}},[r,o]);a.useEffect(()=>{const x=new URLSearchParams({bus:String(o),addr:String(r)});q(`/api/debug/sensors/magnetometer/calibration/status?${x.toString()}`).then(F=>$(F)).catch(()=>{})},[r,o]),a.useEffect(()=>{if((m==null?void 0:m.mode)!=="recording")return;const x=setInterval(()=>{const F=new URLSearchParams({bus:String(o),addr:String(r)});q(`/api/debug/sensors/magnetometer/calibration/status?${F.toString()}`).then(X=>$(X)).catch(()=>{})},900);return()=>clearInterval(x)},[m==null?void 0:m.mode,o,r]);const re=a.useMemo(()=>{const x=(m==null?void 0:m.mode)??"auto";return t(x==="recording"?"sys.sensors.mag.calModeRecording":x==="locked"?"sys.sensors.mag.calModeLocked":"sys.sensors.mag.calModeAuto")},[m==null?void 0:m.mode,t]),ee=Number((m==null?void 0:m.samples)??0),se=(m==null?void 0:m.mode)==="recording"&&ee>=10,ae=a.useCallback(async()=>{N(!0),z(null);try{const x=new URLSearchParams({bus:String(S),addr:String(h),i2cdetect:O?"true":"false"}),F=await q(`/api/debug/sensors/mpu6050/selftest?${x.toString()}`);w(F)}catch(x){w(null),z(x instanceof Error?x.message:String(x))}finally{N(!1)}},[h,S,O]);return e.jsxs("div",{className:"mx-auto max-w-6xl space-y-6",children:[e.jsxs("header",{children:[e.jsx("div",{className:"text-[10px] uppercase tracking-[0.14em] text-on-surface-variant",children:t("sys.placeholder.breadcrumb")}),e.jsx("h2",{className:"mt-1 font-headline text-3xl font-black tracking-tight",children:t("sys.sensors.title")}),e.jsx("p",{className:"text-sm text-on-surface-variant",children:t("sys.sensors.desc")})]}),e.jsxs("section",{className:"rounded-xl border border-outline-variant/20 bg-surface-container-low p-4",children:[e.jsx("p",{className:"text-xs font-medium text-on-surface",children:t("sys.sensors.mag.section")}),e.jsx("p",{className:"mt-1 text-[11px] text-on-surface-variant",children:t("sys.sensors.mag.note")}),e.jsxs("div",{className:"mt-4 flex flex-wrap items-end gap-4",children:[e.jsxs("label",{className:"flex flex-col gap-1 text-[11px] text-on-surface-variant",children:[t("sys.sensors.mag.bus"),e.jsx("input",{type:"number",min:0,max:32,className:"w-24 rounded border border-outline-variant/40 bg-surface-container px-2 py-1 font-mono text-sm text-on-surface",value:o,onChange:x=>n(Number(x.target.value))})]}),e.jsxs("label",{className:"flex flex-col gap-1 text-[11px] text-on-surface-variant",children:[t("sys.sensors.mag.addr"),e.jsx("input",{type:"number",min:1,max:127,className:"w-24 rounded border border-outline-variant/40 bg-surface-container px-2 py-1 font-mono text-sm text-on-surface",value:r,onChange:x=>c(Number(x.target.value))})]}),e.jsxs("label",{className:"flex items-center gap-2 text-[11px] text-on-surface-variant",children:[e.jsx("input",{type:"checkbox",checked:d,onChange:x=>j(x.target.checked)}),t("sys.sensors.mag.i2cdetect")]})]}),e.jsx(xs,{bus:o,addr:r}),e.jsxs("div",{className:"mt-6 flex flex-wrap gap-2",children:[e.jsx("button",{type:"button",disabled:u,className:"rounded-lg bg-primary px-4 py-2 text-sm font-medium text-on-primary hover:opacity-90 disabled:opacity-50",onClick:()=>void L(),children:t(u?"sys.sensors.mag.running":"sys.sensors.mag.btnSelftest")}),e.jsx("button",{type:"button",disabled:u,className:"rounded-lg border border-outline-variant/40 px-4 py-2 text-sm text-on-surface hover:bg-surface-container/80 disabled:opacity-50",onClick:()=>void A(),children:t("sys.sensors.mag.btnProbe")})]}),e.jsxs("div",{className:"mt-3 flex flex-wrap gap-2",children:[e.jsx("button",{type:"button",disabled:u||(m==null?void 0:m.mode)==="recording",className:"rounded-lg border border-amber-400/40 px-3 py-1.5 text-xs text-on-surface hover:bg-amber-500/10 disabled:opacity-50",onClick:()=>void I(),children:t("sys.sensors.mag.btnCalStart")}),e.jsx("button",{type:"button",disabled:u||!se,className:"rounded-lg border border-emerald-400/40 px-3 py-1.5 text-xs text-on-surface hover:bg-emerald-500/10 disabled:opacity-50",onClick:()=>void U(),children:t("sys.sensors.mag.btnCalCommit")}),e.jsx("button",{type:"button",disabled:u,className:"rounded-lg border border-rose-400/40 px-3 py-1.5 text-xs text-on-surface hover:bg-rose-500/10 disabled:opacity-50",onClick:()=>void K(),children:t("sys.sensors.mag.btnCalReset")}),e.jsx("button",{type:"button",disabled:u,className:"rounded-lg border border-outline-variant/40 px-3 py-1.5 text-xs text-on-surface hover:bg-surface-container/80 disabled:opacity-50",onClick:()=>void Q(),children:t("sys.sensors.mag.btnCalStatus")})]}),e.jsxs("div",{className:"mt-3 rounded-lg border border-outline-variant/30 bg-surface-container/60 px-3 py-2 text-[11px] text-on-surface",children:[e.jsxs("p",{className:"font-medium",children:[t("sys.sensors.mag.calStatusPrefix")," ",re]}),e.jsxs("p",{className:"mt-1 text-on-surface-variant",children:[t("sys.sensors.mag.calSamplesPrefix")," ",ee,(m==null?void 0:m.mode)==="recording"?" / 10+":""]}),(m==null?void 0:m.span_xyz)&&e.jsxs("p",{className:"mt-1 font-mono text-[10px] text-on-surface-variant",children:["span xyz: ",Number(m.span_xyz.x??0).toFixed(1)," /"," ",Number(m.span_xyz.y??0).toFixed(1)," /"," ",Number(m.span_xyz.z??0).toFixed(1)]}),(m==null?void 0:m.mode)==="recording"&&e.jsx("p",{className:"mt-1 text-amber-200",children:t("sys.sensors.mag.calRecordingHint")}),(m==null?void 0:m.mode)==="locked"&&m.locked&&e.jsxs("p",{className:"mt-1 text-emerald-200",children:[t("sys.sensors.mag.calLockedHint")," axes=",String(m.locked.axes_pair??"xy")]}),M&&e.jsx("p",{className:"mt-1 text-sky-200",children:M})]}),g&&e.jsx("p",{className:"mt-3 rounded border border-red-500/40 bg-red-500/10 px-3 py-2 font-mono text-xs text-red-200",children:g}),k&&e.jsxs("details",{className:"mt-4 rounded-lg border border-outline-variant/30 bg-surface-container/50",children:[e.jsx("summary",{className:"cursor-pointer px-3 py-2 text-[11px] text-on-surface-variant",children:t("sys.sensors.jsonToggle")}),e.jsx("pre",{className:"max-h-[320px] overflow-auto border-t border-outline-variant/20 p-3 font-mono text-[11px] leading-relaxed text-on-surface",children:Ne(k)})]})]}),e.jsxs("section",{className:"rounded-xl border border-outline-variant/20 bg-surface-container-low p-4",children:[e.jsx("p",{className:"text-xs font-medium text-on-surface",children:t("sys.sensors.mpu.section")}),e.jsx("p",{className:"mt-1 text-[11px] text-on-surface-variant",children:t("sys.sensors.mpu.note")}),e.jsxs("div",{className:"mt-4 flex flex-wrap items-end gap-4",children:[e.jsxs("label",{className:"flex flex-col gap-1 text-[11px] text-on-surface-variant",children:[t("sys.sensors.mag.bus"),e.jsx("input",{type:"number",min:0,max:32,className:"w-24 rounded border border-outline-variant/40 bg-surface-container px-2 py-1 font-mono text-sm text-on-surface",value:S,onChange:x=>E(Number(x.target.value))})]}),e.jsxs("label",{className:"flex flex-col gap-1 text-[11px] text-on-surface-variant",children:[t("sys.sensors.mpu.addr"),e.jsx("input",{type:"number",min:1,max:127,className:"w-24 rounded border border-outline-variant/40 bg-surface-container px-2 py-1 font-mono text-sm text-on-surface",value:h,onChange:x=>T(Number(x.target.value))})]}),e.jsxs("label",{className:"flex items-center gap-2 text-[11px] text-on-surface-variant",children:[e.jsx("input",{type:"checkbox",checked:O,onChange:x=>v(x.target.checked)}),t("sys.sensors.mag.i2cdetect")]})]}),e.jsx(ns,{bus:S,addr:h}),e.jsx("div",{className:"mt-4 flex flex-wrap gap-2",children:e.jsx("button",{type:"button",disabled:_,className:"rounded-lg bg-primary px-4 py-2 text-sm font-medium text-on-primary hover:opacity-90 disabled:opacity-50",onClick:()=>void ae(),children:t(_?"sys.sensors.mpu.running":"sys.sensors.mpu.btnSelftest")})}),P&&e.jsx("p",{className:"mt-3 rounded border border-red-500/40 bg-red-500/10 px-3 py-2 font-mono text-xs text-red-200",children:P}),i&&e.jsxs("details",{className:"mt-4 rounded-lg border border-outline-variant/30 bg-surface-container/50",children:[e.jsx("summary",{className:"cursor-pointer px-3 py-2 text-[11px] text-on-surface-variant",children:t("sys.sensors.jsonToggle")}),e.jsx("pre",{className:"max-h-[320px] overflow-auto border-t border-outline-variant/20 p-3 font-mono text-[11px] leading-relaxed text-on-surface",children:Ne(i)})]})]})]})}const ms={ogscope:{zh:"主配置 ogscope.env",en:"Primary ogscope.env"},network:{zh:"网络 network.env",en:"Network network.env"}};function ps(t){const o=[];let n=0;return t.split(/\r?\n/).forEach((c,d)=>{const j=c.trim();if(!j||j.startsWith("#"))return;const u=j.startsWith("export ")?j.slice(7):j,f=u.indexOf("=");if(f<=0){n+=1;return}const g=u.slice(0,f).trim();if(!g){n+=1;return}const b=u.slice(f+1);o.push({id:`env-${d}-${g}`,key:g,value:b})}),{entries:o,unsupportedLines:n}}function we(t){const o=t.map(n=>({key:n.key.trim(),value:n.value})).filter(n=>n.key.length>0).map(n=>`${n.key}=${n.value}`);return o.length>0?`${o.join(`
+`)}
+`:""}function oe(t,o){return t==="both"?!0:t===o}function hs(){const{locale:t}=Z(),[o,n]=a.useState([]),[r,c]=a.useState(null),[d,j]=a.useState(""),[u,f]=a.useState(""),[g,b]=a.useState("form"),[k,y]=a.useState([]),[m,$]=a.useState(0),[M,R]=a.useState(!1),[S,E]=a.useState(""),[h,T]=a.useState(""),[O,v]=a.useState(""),[_,N]=a.useState(!0),[P,z]=a.useState(""),i=t==="zh",w=a.useMemo(()=>o.find(s=>s.file_id===d)??null,[d,o]),L=a.useMemo(()=>{const s=new Map;for(const l of(r==null?void 0:r.sections)??[])for(const p of l.entries)s.set(p.key.toUpperCase(),p);for(const l of(r==null?void 0:r.network_only)??[])s.set(l.key.toUpperCase(),l);return s},[r]),A=a.useMemo(()=>{if(!d)return[];const s=new Set(k.map(p=>p.key.trim().toUpperCase()).filter(Boolean)),l=[];for(const p of(r==null?void 0:r.sections)??[])for(const C of p.entries)oe(C.scope,d)&&(s.has(C.key.toUpperCase())||l.push(C));for(const p of(r==null?void 0:r.network_only)??[])oe(p.scope,d)&&(s.has(p.key.toUpperCase())||l.push(p));return l.sort((p,C)=>p.key.localeCompare(C.key))},[d,r,k]),I=a.useMemo(()=>{const s=O.trim().toLowerCase();return s?k.filter(l=>{const p=l.key.toLowerCase(),C=L.get(l.key.trim().toUpperCase());return`${p} ${l.value} ${(C==null?void 0:C.zh)??""} ${(C==null?void 0:C.en)??""}`.toLowerCase().includes(s)}):k},[k,L,O]),U=s=>{const l=ps(s);y(l.entries),$(l.unsupportedLines)},K=async()=>{var s,l;R(!0),E("");try{const[p,C]=await Promise.all([W("/api/dev/system/config/files",{cache:"no-store"}),W("/api/dev/system/config/catalog",{cache:"no-store"})]);n(p.files??[]),c(C);const D=((l=(s=p.files)==null?void 0:s[0])==null?void 0:l.file_id)??"";j(G=>{var J;return G&&((J=p.files)!=null&&J.some(Y=>Y.file_id===G))?G:D})}catch(p){E(p instanceof Error?p.message:String(p))}finally{R(!1)}},Q=async()=>{if(!d)return;if(g==="form"&&m>0){E(i?"当前文件包含无法表单化的行,请切换到「原始文本」模式编辑后再保存。":"This file has lines not supported by form mode. Switch to Raw mode before saving.");return}const s=g==="form"?we(k):u;R(!0),E(""),T("");try{const l=await W("/api/dev/system/config/files",{method:"POST",body:JSON.stringify({file_id:d,content:s})});T(i?`保存成功:${l.message??d};请重启 ogscope 服务使配置生效`:`Saved: ${l.message??d}; restart ogscope to apply changes`),await K()}catch(l){E(l instanceof Error?l.message:String(l))}finally{R(!1)}};a.useEffect(()=>{K()},[]),a.useEffect(()=>{if(!w){f(""),y([]),$(0),z("");return}const s=w.content??"";f(s),U(s),z("")},[w]);const re=()=>{y(s=>[...s,{id:`env-new-${Date.now()}-${s.length}`,key:"",value:""}])},ee=()=>{const s=A.find(l=>l.key===P);s&&(y(l=>[...l,{id:`env-catalog-${Date.now()}-${s.key}`,key:s.key,value:s.default??""}]),z(""))},se=(s,l)=>{y(p=>p.map(C=>C.id===s?{...C,...l}:C))},ae=s=>{y(l=>l.filter(p=>p.id!==s))},x=s=>{const l=L.get(s.trim().toUpperCase());return l?i?l.zh:l.en:i?"暂无释义(可查阅 deploy/*.env.example)":"No hint (see deploy/*.env.example)"},F=s=>{const l=L.get(s.trim().toUpperCase());return l!=null&&l.default?i?`默认:${l.default}`:`Default: ${l.default}`:""},X=s=>{const l=ms[s];return l?i?l.zh:l.en:s},ie=s=>s.writable?s.writable_via_sudo&&!s.writable_direct?i?"可写(经 sudo 助手)":"Writable (via sudo helper)":s.writable_direct?i?"可写(直接)":"Writable (direct)":i?"可写":"Writable":i?"不可写:请运行 sudo ./scripts/ogscope-network-init.sh ensure-config":"Not writable: run sudo ./scripts/ogscope-network-init.sh ensure-config";return e.jsxs("div",{className:"mx-auto max-w-7xl space-y-6",children:[e.jsxs("header",{children:[e.jsxs("div",{className:"flex items-center gap-2 text-[10px] uppercase tracking-[0.14em] text-on-surface-variant",children:[e.jsx("span",{children:"Console"}),e.jsx("span",{children:"/"}),e.jsx("span",{className:"text-primary",children:i?"配置管理":"Config Manager"})]}),e.jsx("h2",{className:"mt-1 font-headline text-3xl font-black tracking-tight",children:i?"环境配置管理":"Environment Config Manager"}),e.jsx("p",{className:"text-sm text-on-surface-variant",children:i?"编辑 /etc/ogscope 下的 ogscope.env 与 network.env。保存后请重启 ogscope 服务;配置项说明来自服务端目录 API。":"Edit ogscope.env and network.env under /etc/ogscope. Restart ogscope after saving; hints come from the server catalog API."})]}),S&&e.jsx("div",{className:"rounded-lg border border-error/40 bg-error-container/20 px-3 py-2 text-sm text-on-error-container",children:S}),h&&e.jsx("div",{className:"rounded-lg border border-primary/30 bg-primary/10 px-3 py-2 text-sm",children:h}),e.jsxs("section",{className:"grid grid-cols-12 gap-4",children:[e.jsxs("aside",{className:"col-span-12 space-y-2 rounded-xl border border-outline-variant/20 bg-surface-container p-3 lg:col-span-3",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("p",{className:"text-xs uppercase tracking-wider text-on-surface-variant",children:i?"配置文件":"Config Files"}),e.jsx("button",{type:"button",onClick:()=>void K(),disabled:M,children:e.jsxs("span",{className:"inline-flex items-center gap-1 text-xs",children:[e.jsx(he,{className:"h-3.5 w-3.5"})," ",i?"刷新":"Refresh"]})})]}),o.map(s=>e.jsxs("button",{type:"button",onClick:()=>j(s.file_id),className:`w-full rounded-lg border px-3 py-2 text-left text-sm ${s.file_id===d?"border-primary bg-primary/10 text-on-surface":"border-outline-variant/30 bg-surface-container-low text-on-surface-variant"}`,children:[e.jsx("div",{className:"font-medium",children:X(s.file_id)}),e.jsx("div",{className:"mt-1 truncate font-mono text-[11px]",children:s.path})]},s.file_id)),(r==null?void 0:r.env_files)&&e.jsxs("div",{className:"mt-3 rounded border border-outline-variant/20 bg-surface-container-low px-2 py-2 text-[11px] text-on-surface-variant",children:[e.jsx("p",{className:"mb-1 font-medium text-on-surface",children:i?"配置路径":"Config paths"}),Object.entries(r.env_files).map(([s,l])=>e.jsxs("p",{className:"font-mono",children:[s,": ",l]},s))]})]}),e.jsxs("div",{className:"col-span-12 rounded-xl border border-outline-variant/20 bg-surface-container p-4 lg:col-span-9",children:[!w&&e.jsx("p",{className:"text-sm text-on-surface-variant",children:i?"暂无可编辑配置文件":"No editable config files."}),w&&e.jsxs("div",{className:"space-y-3",children:[e.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-2",children:[e.jsxs("div",{children:[e.jsx("p",{className:"font-mono text-xs text-on-surface",children:w.path}),e.jsxs("p",{className:"text-xs text-on-surface-variant",children:[ie(w)," · ",i?"存在":"Exists",": ",String(w.exists)]})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("button",{type:"button",onClick:()=>{g==="raw"&&U(u),b("form")},className:`rounded px-2 py-1 text-xs ${g==="form"?"bg-primary-container text-on-primary-container":"border border-outline-variant/30 text-on-surface-variant"}`,children:i?"表单模式":"Form"}),e.jsx("button",{type:"button",onClick:()=>{g==="form"&&f(we(k)),b("raw")},className:`rounded px-2 py-1 text-xs ${g==="raw"?"bg-primary-container text-on-primary-container":"border border-outline-variant/30 text-on-surface-variant"}`,children:i?"原始文本":"Raw"})]}),e.jsx("button",{type:"button",onClick:()=>void Q(),disabled:M||!w.writable,children:e.jsxs("span",{className:"inline-flex items-center gap-1",children:[e.jsx(Pe,{className:"h-3.5 w-3.5"}),i?"保存并提示重启":"Save"]})})]}),w.error&&e.jsx("div",{className:"rounded border border-error/40 bg-error-container/20 px-2 py-1 text-xs text-on-error-container",children:w.error}),g==="form"?e.jsxs("div",{className:"space-y-3",children:[m>0&&e.jsx("div",{className:"rounded border border-warning/40 bg-warning/10 px-2 py-1 text-xs text-on-surface",children:i?`检测到 ${m} 行无法转换为键值表单。请切到「原始文本」模式处理。`:`${m} line(s) cannot be represented in key-value form. Use Raw mode.`}),e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsxs("div",{className:"relative min-w-[200px] flex-1",children:[e.jsx(_e,{className:"pointer-events-none absolute left-2 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-on-surface-variant"}),e.jsx("input",{className:"w-full rounded border border-outline-variant/30 bg-surface-container-low py-1.5 pl-8 pr-2 text-xs outline-none focus:border-primary",placeholder:i?"搜索键名、值或释义…":"Search keys, values, or hints…",value:O,onChange:s=>v(s.target.value)})]}),e.jsxs("select",{className:"max-w-xs flex-1 rounded border border-outline-variant/30 bg-surface-container-low px-2 py-1.5 text-xs outline-none focus:border-primary",value:P,onChange:s=>z(s.target.value),children:[e.jsx("option",{value:"",children:i?"从目录添加配置项…":"Add from catalog…"}),A.map(s=>e.jsx("option",{value:s.key,children:s.key},s.key))]}),e.jsx("button",{type:"button",disabled:!P,onClick:ee,className:"rounded border border-outline-variant/30 px-2 py-1.5 text-xs disabled:opacity-50",children:i?"添加":"Add"})]}),e.jsxs("div",{className:"max-h-[420px] overflow-auto pr-1",children:[e.jsxs("table",{className:"w-full border-separate border-spacing-y-2",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"text-left text-[11px] uppercase tracking-wide text-on-surface-variant",children:[e.jsx("th",{children:i?"配置项":"Key"}),e.jsx("th",{children:i?"值":"Value"}),e.jsx("th",{children:i?"释义":"Meaning"}),e.jsx("th",{children:i?"操作":"Action"})]})}),e.jsx("tbody",{children:I.map(s=>e.jsxs("tr",{children:[e.jsx("td",{className:"pr-2 align-top",children:e.jsx("input",{className:"w-full rounded border border-outline-variant/30 bg-surface-container-low px-2 py-1 font-mono text-xs outline-none focus:border-primary",placeholder:"OGSCOPE_PORT",value:s.key,onChange:l=>se(s.id,{key:l.target.value})})}),e.jsx("td",{className:"pr-2 align-top",children:e.jsx("input",{className:"w-full rounded border border-outline-variant/30 bg-surface-container-low px-2 py-1 font-mono text-xs outline-none focus:border-primary",placeholder:i?"变量值":"Value",value:s.value,onChange:l=>se(s.id,{value:l.target.value})})}),e.jsxs("td",{className:"pr-2 align-top text-xs text-on-surface-variant",children:[e.jsx("p",{children:x(s.key)}),F(s.key)&&e.jsx("p",{className:"mt-0.5 font-mono text-[10px] text-on-surface-variant/80",children:F(s.key)})]}),e.jsx("td",{className:"align-top",children:e.jsx("button",{type:"button",onClick:()=>ae(s.id),children:e.jsx(Re,{className:"h-3.5 w-3.5 text-on-surface-variant"})})})]},s.id))})]}),k.length===0&&e.jsx("div",{className:"rounded border border-outline-variant/20 bg-surface-container-low px-2 py-2 text-xs text-on-surface-variant",children:i?"当前没有可编辑变量,可从目录添加或手动新增。":"No variables yet. Add from catalog or manually."}),k.length>0&&I.length===0&&e.jsx("div",{className:"rounded border border-outline-variant/20 bg-surface-container-low px-2 py-2 text-xs text-on-surface-variant",children:i?"无匹配项,请调整搜索条件。":"No matches for the current search."})]}),e.jsx("div",{className:"flex flex-wrap gap-2",children:e.jsx("button",{type:"button",onClick:re,children:e.jsxs("span",{className:"inline-flex items-center gap-1 text-xs",children:[e.jsx(De,{className:"h-3.5 w-3.5"}),i?"空白行":"Blank row"]})})}),e.jsxs("details",{open:_,onToggle:s=>N(s.target.open),className:"rounded border border-outline-variant/20 bg-surface-container-low px-3 py-2 text-xs text-on-surface-variant",children:[e.jsx("summary",{className:"cursor-pointer font-medium text-on-surface",children:e.jsxs("span",{className:"inline-flex items-center gap-1",children:[e.jsx(Ae,{className:"h-3.5 w-3.5"}),i?"配置目录(按模块)":"Config catalog (by section)"]})}),e.jsxs("div",{className:"mt-3 space-y-4",children:[((r==null?void 0:r.sections)??[]).map(s=>{const l=s.entries.filter(p=>d?oe(p.scope,d):!0);return l.length===0?null:e.jsxs("div",{children:[e.jsx("p",{className:"mb-1 font-medium text-on-surface",children:i?s.title_zh:s.title_en}),e.jsx("div",{className:"space-y-1",children:l.map(p=>e.jsxs("p",{children:[e.jsx("span",{className:"font-mono text-[11px] text-on-surface",children:p.key}),p.default!=null&&p.default!==""&&e.jsxs("span",{className:"ml-1 font-mono text-[10px] text-on-surface-variant/80",children:["(= ",p.default,")"]})," — ",e.jsx("span",{children:i?p.zh:p.en})]},p.key))})]},s.id)}),((r==null?void 0:r.network_only)??[]).filter(s=>d?oe(s.scope,d):!0).length>0&&e.jsxs("div",{children:[e.jsx("p",{className:"mb-1 font-medium text-on-surface",children:i?"仅 network.env / 脚本":"network.env / scripts only"}),e.jsx("div",{className:"space-y-1",children:((r==null?void 0:r.network_only)??[]).filter(s=>d?oe(s.scope,d):!0).map(s=>e.jsxs("p",{children:[e.jsx("span",{className:"font-mono text-[11px] text-on-surface",children:s.key})," — ",e.jsx("span",{children:i?s.zh:s.en})]},s.key))})]})]})]})]}):e.jsx("textarea",{className:"h-[460px] w-full rounded-lg border border-outline-variant/30 bg-neutral-950 p-3 font-mono text-xs text-on-surface outline-none focus:border-primary",spellCheck:!1,value:u,onChange:s=>f(s.target.value)})]})]})]})]})}const fs=new Set(["overview","network","sensors","hmi","power","config"]);function ke(){const t=window.location.hash.replace(/^#\/?/,"").trim().toLowerCase();return fs.has(t)?t:"overview"}function bs(t){window.location.hash=`/${t}`}function gs(){const[t,o]=a.useState(()=>ke()),[n,r]=a.useState(!0);a.useEffect(()=>{const d=()=>o(ke());return window.addEventListener("hashchange",d),()=>window.removeEventListener("hashchange",d)},[]),a.useEffect(()=>{(async()=>{var d;try{const j=await fetch("/api",{cache:"no-store"});if(!j.ok)return;const u=await j.json();r(!!((d=u.endpoints)!=null&&d.network))}catch{}})()},[]);const c=a.useMemo(()=>t==="network"?n?e.jsx(Qe,{}):e.jsx(ve,{scope:"network"}):t==="sensors"?e.jsx(us,{}):t==="hmi"?e.jsx(ts,{}):t==="config"?e.jsx(hs,{}):t==="power"?e.jsx(ve,{scope:"power"}):e.jsx(Ye,{}),[n,t]);return e.jsx(We,{route:t,allowNetworkRoute:n,onRouteChange:d=>{d==="network"&&!n||d!==t&&bs(d)},children:c})}$e.createRoot(document.getElementById("root")).render(e.jsx(Me.StrictMode,{children:e.jsx(Te,{children:e.jsx(Le,{children:e.jsx(gs,{})})})}));
diff --git a/web/static/analysis-lab/camera.html b/web/static/analysis-lab/camera.html
index aa2cae7..9fb1163 100644
--- a/web/static/analysis-lab/camera.html
+++ b/web/static/analysis-lab/camera.html
@@ -4,10 +4,10 @@
OGScope 相机调试控制台
-
+
-
-
+
+
diff --git a/web/static/analysis-lab/index.html b/web/static/analysis-lab/index.html
index 7f990f5..4180b35 100644
--- a/web/static/analysis-lab/index.html
+++ b/web/static/analysis-lab/index.html
@@ -4,10 +4,10 @@
OGScope 星空解算控制台
-
+
-
-
+
+
diff --git a/web/static/analysis-lab/system.html b/web/static/analysis-lab/system.html
index d4bf4a2..8e2ea22 100644
--- a/web/static/analysis-lab/system.html
+++ b/web/static/analysis-lab/system.html
@@ -4,11 +4,11 @@
OGScope 系统调试控制台
-
+
-
-
-
+
+
+
diff --git a/web/static/i18n/analysis.en.json b/web/static/i18n/analysis.en.json
index ed82fc1..0358472 100644
--- a/web/static/i18n/analysis.en.json
+++ b/web/static/i18n/analysis.en.json
@@ -125,6 +125,7 @@
"params.timeoutHelp": "Max wait time per solve.",
"params.solveIntervalMs": "Realtime solve interval (ms)",
"params.solveIntervalMsHelp": "Desired interval is adjustable, but backend clamps it into a safe range.",
+ "params.solveIntervalIndependent": "Solve cadence is independent from sensor and preview frame rates.",
"params.solveIntervalBound": "Backend bounds: {min}-{max} ms, effective now: {effective} ms",
"params.centroidRejectionLevel": "Centroid rejection (1–5)",
"params.centroidRejectionLevelHelp": "1 keeps more stars; 5 aggressively rejects dense clusters and collinear false detections; default 3.",
@@ -378,10 +379,20 @@
"cam.stats.targetFps": "Camera capture target FPS",
"cam.stats.streamPacingFps": "Shared stream / MJPEG cap FPS",
"cam.stats.streamPacingHint": "Matches OGSCOPE_SHARED_PREVIEW_FPS: shared grabber pacing and per-stream MJPEG min frame spacing.",
+ "cam.stats.captureFps": "Actual capture FPS",
+ "cam.stats.encodeMs": "Average JPEG encode",
+ "cam.stats.encoder": "Preview encoder",
+ "cam.stats.driver": "Camera driver",
+ "cam.stats.metadata": "Metadata",
+ "cam.stats.consumers": "Preview/analysis/record consumers",
+ "cam.stats.cameraMemory": "Process memory/swap",
+ "cam.stats.longExposureThrottle": "Auto exposure is about {exposure} ms, so long exposure is limiting FPS; exposure is not shortened to preserve analysis quality.",
"cam.stats.uptime": "Stream Uptime",
"cam.controls.title": "Controls",
"cam.controls.core": "Core Settings",
"cam.controls.fps": "FPS",
+ "cam.controls.sensorFps": "Sensor target FPS",
+ "cam.controls.previewFps": "Preview target FPS",
"cam.controls.resolution": "Resolution",
"cam.controls.applyRes": "Apply resolution",
"cam.controls.sampling": "Sampling mode",
@@ -389,6 +400,10 @@
"cam.controls.gain": "Analog Gain",
"cam.controls.digitalGain": "Digital Gain",
"cam.controls.noiseReduction": "Noise Reduction",
+ "cam.controls.noiseReductionMode": "Noise Reduction",
+ "cam.controls.digitalGainReadOnly": "Digital gain is read-only on this driver",
+ "cam.controls.aeFlicker": "AE Flicker",
+ "cam.controls.maxAeFrame": "Max AE frame (us)",
"cam.controls.contrast": "Contrast",
"cam.controls.brightness": "Brightness",
"cam.controls.saturation": "Saturation",
@@ -403,6 +418,18 @@
"cam.controls.auto": "Auto",
"cam.controls.manual": "Manual",
"cam.controls.night": "Night",
+ "cam.controls.off": "Off",
+ "cam.controls.nr.off": "Off",
+ "cam.controls.nr.fast": "Fast",
+ "cam.controls.nr.high_quality": "High quality",
+ "cam.controls.wb.auto": "Auto",
+ "cam.controls.wb.daylight": "Daylight",
+ "cam.controls.wb.cloudy": "Cloudy",
+ "cam.controls.wb.tungsten": "Tungsten",
+ "cam.controls.wb.fluorescent": "Fluorescent",
+ "cam.controls.wb.indoor": "Indoor",
+ "cam.controls.wb.manual": "Manual",
+ "cam.controls.wb.night": "Night",
"cam.controls.color": "Color",
"cam.controls.mono": "Mono",
"cam.controls.applyAe": "Apply Exposure Mode",
diff --git a/web/static/i18n/analysis.zh.json b/web/static/i18n/analysis.zh.json
index dc2d2e7..3e7bd78 100644
--- a/web/static/i18n/analysis.zh.json
+++ b/web/static/i18n/analysis.zh.json
@@ -107,6 +107,7 @@
"params.timeoutHelp": "单次解算最长等待时间。",
"params.solveIntervalMs": "实时解算间隔 (ms)",
"params.solveIntervalMsHelp": "期望间隔可调整,但会被后端限制在安全范围内。",
+ "params.solveIntervalIndependent": "解算间隔独立于相机采集和预览帧率。",
"params.solveIntervalBound": "后端限制范围:{min}-{max} ms,当前生效:{effective} ms",
"params.centroidRejectionLevel": "质心剔除强度 (1–5)",
"params.centroidRejectionLevelHelp": "1 最保守保留更多星点,5 更激进剔除过密与共线假星;默认 3。",
@@ -379,10 +380,20 @@
"cam.stats.targetFps": "相机采集目标 FPS",
"cam.stats.streamPacingFps": "共享流/MJPEG 上限 FPS",
"cam.stats.streamPacingHint": "与进程环境 OGSCOPE_SHARED_PREVIEW_FPS 一致,约束共享抓帧节奏与每路 MJPEG 的最小帧间隔。",
+ "cam.stats.captureFps": "实际采集 FPS",
+ "cam.stats.encodeMs": "JPEG 平均编码",
+ "cam.stats.encoder": "预览编码器",
+ "cam.stats.driver": "相机驱动",
+ "cam.stats.metadata": "元数据",
+ "cam.stats.consumers": "预览/分析/录像消费者",
+ "cam.stats.cameraMemory": "进程内存/Swap",
+ "cam.stats.longExposureThrottle": "自动曝光约 {exposure} ms,实际帧率受长曝光限制;为保证分析效果未强制缩短曝光。",
"cam.stats.uptime": "流运行时长",
"cam.controls.title": "参数控制",
"cam.controls.core": "核心参数",
"cam.controls.fps": "帧率",
+ "cam.controls.sensorFps": "传感器目标 FPS",
+ "cam.controls.previewFps": "预览目标 FPS",
"cam.controls.resolution": "分辨率",
"cam.controls.applyRes": "应用分辨率",
"cam.controls.sampling": "采样模式",
@@ -390,6 +401,10 @@
"cam.controls.gain": "模拟增益",
"cam.controls.digitalGain": "数字增益",
"cam.controls.noiseReduction": "降噪级别",
+ "cam.controls.noiseReductionMode": "降噪模式",
+ "cam.controls.digitalGainReadOnly": "当前驱动仅上报数字增益,不支持手动设置",
+ "cam.controls.aeFlicker": "AE 防闪烁",
+ "cam.controls.maxAeFrame": "最长自动曝光帧周期(us)",
"cam.controls.contrast": "对比度",
"cam.controls.brightness": "亮度",
"cam.controls.saturation": "饱和度",
@@ -404,6 +419,18 @@
"cam.controls.auto": "自动",
"cam.controls.manual": "手动",
"cam.controls.night": "夜间",
+ "cam.controls.off": "关闭",
+ "cam.controls.nr.off": "关闭",
+ "cam.controls.nr.fast": "快速",
+ "cam.controls.nr.high_quality": "高质量",
+ "cam.controls.wb.auto": "自动",
+ "cam.controls.wb.daylight": "日光",
+ "cam.controls.wb.cloudy": "阴天",
+ "cam.controls.wb.tungsten": "钨丝灯",
+ "cam.controls.wb.fluorescent": "荧光灯",
+ "cam.controls.wb.indoor": "室内",
+ "cam.controls.wb.manual": "手动",
+ "cam.controls.wb.night": "夜间",
"cam.controls.color": "彩色",
"cam.controls.mono": "黑白",
"cam.controls.applyAe": "应用曝光模式",