import{f as it,u as rt,i as f,a as y,b as l,w as J}from"./lit-BS7WqYd5.js";import{y as p,g as ot,j as w}from"./signals-SG45zFCj.js";(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const s of document.querySelectorAll('link[rel="modulepreload"]'))i(s);new MutationObserver(s=>{for(const r of s)if(r.type==="childList")for(const o of r.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&i(o)}).observe(document,{childList:!0,subtree:!0});function a(s){const r={};return s.integrity&&(r.integrity=s.integrity),s.referrerPolicy&&(r.referrerPolicy=s.referrerPolicy),s.crossOrigin==="use-credentials"?r.credentials="include":s.crossOrigin==="anonymous"?r.credentials="omit":r.credentials="same-origin",r}function i(s){if(s.ep)return;s.ep=!0;const r=a(s);fetch(s.href,r)}})();/**
* @license
* Copyright 2017 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
*/const x=e=>(t,a)=>{a!==void 0?a.addInitializer(()=>{customElements.define(e,t)}):customElements.define(e,t)};/**
* @license
* Copyright 2017 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
*/const nt={attribute:!0,type:String,converter:rt,reflect:!1,hasChanged:it},lt=(e=nt,t,a)=>{const{kind:i,metadata:s}=a;let r=globalThis.litPropertyMetadata.get(s);if(r===void 0&&globalThis.litPropertyMetadata.set(s,r=new Map),i==="setter"&&((e=Object.create(e)).wrapped=!0),r.set(a.name,e),i==="accessor"){const{name:o}=a;return{set(n){const d=t.get.call(this);t.set.call(this,n),this.requestUpdate(o,d,e,!0,n)},init(n){return n!==void 0&&this.C(o,void 0,e,n),n}}}if(i==="setter"){const{name:o}=a;return function(n){const d=this[o];t.call(this,n),this.requestUpdate(o,d,e,!0,n)}}throw Error("Unsupported decorator location: "+i)};function Ce(e){return(t,a)=>typeof a=="object"?lt(e,t,a):((i,s,r)=>{const o=s.hasOwnProperty(r);return s.constructor.createProperty(r,i),o?Object.getOwnPropertyDescriptor(s,r):void 0})(e,t,a)}/**
* @license
* Copyright 2017 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
*/function u(e){return Ce({...e,state:!0,attribute:!1})}/**
* @license
* Copyright 2017 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
*/const dt=(e,t,a)=>(a.configurable=!0,a.enumerable=!0,Reflect.decorate&&typeof t!="object"&&Object.defineProperty(e,t,a),a);/**
* @license
* Copyright 2017 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
*/function Ye(e,t){return(a,i,s)=>{const r=o=>o.renderRoot?.querySelector(e)??null;return dt(a,i,{get(){return r(this)}})}}var ct=Object.defineProperty,pt=Object.getOwnPropertyDescriptor,Xe=(e,t,a,i)=>{for(var s=i>1?void 0:i?pt(t,a):t,r=e.length-1,o;r>=0;r--)(o=e[r])&&(s=(i?o(t,a,s):o(s))||s);return i&&s&&ct(t,a,s),s};let ke=class extends y{constructor(){super(...arguments),this.view="scene"}navigate(e){this.dispatchEvent(new CustomEvent("navigate",{detail:e}))}render(){return l`
NV
this.navigate("scene")}>
this.navigate("apps")}>
this.navigate("inspector")}>
this.navigate("witness")}>
this.navigate("ghost-murmur")}>
this.dispatchEvent(new CustomEvent("open-settings",{bubbles:!0,composed:!0}))}>
`}};ke.styles=f`
:host {
display: flex;
flex-direction: column;
align-items: center;
padding: 10px 0;
gap: 4px;
background: var(--bg-1);
border-right: 1px solid var(--line);
}
.logo {
width: 36px; height: 36px;
border-radius: 10px;
background: linear-gradient(135deg, oklch(0.78 0.14 70) 0%, oklch(0.55 0.16 30) 100%);
display: grid; place-items: center;
color: #1a0f00;
font-weight: 700;
font-family: var(--mono);
font-size: 11px;
margin-bottom: 14px;
box-shadow: 0 4px 12px -2px oklch(0.55 0.16 30 / 0.35);
}
.btn {
width: 36px; height: 36px;
border-radius: 8px;
background: transparent;
border: 1px solid transparent;
color: var(--ink-3);
display: grid; place-items: center;
transition: all 0.15s;
position: relative;
cursor: pointer;
}
.btn:hover { color: var(--ink); background: var(--bg-2); }
.btn.active {
color: var(--ink);
background: var(--bg-3);
border-color: var(--line-2);
}
.btn.active::before {
content: ''; position: absolute; left: -10px; top: 8px; bottom: 8px;
width: 2px; background: var(--accent); border-radius: 2px;
}
.btn.ghost.active::before { background: var(--accent-3); }
.spacer { flex: 1; }
svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.8; }
`;Xe([Ce()],ke.prototype,"view",2);ke=Xe([x("nv-rail")],ke);const A=p("wasm"),Fe=p("");p(!1);p(null);const h=p(!1);p(!0);const ye=p(1),ut=p(0),Ie=p(0n),W=p(0xCAFEBABEn),K=p(1e4),Y=p(1e3),X=p(1),Q=p(!0),k=p("dark"),_=p("default"),T=p(!1),re=p(!0),he=p([0,0,0]),V=p(0),L=p(0),M=p(0),z=p(""),$=p("idle"),R=p(""),B=p(null),pe=p([]),$e=p([]),Se=p([]),ue=p([]),qe=p("rebar-walkby-01"),vt=p(""),le=p(!1),Oe=p("all"),q=p([]);function ht(e){const t=q.value.slice();for(t.push(e);t.length>200;)t.shift();q.value=t}const ve=p([]),Ve=ot(()=>A.value==="wasm"?"wasm":"ws");let Qe=null;function mt(e){Qe=e}function b(){return Qe}const P=p([]),gt=200;function c(e,t){if(le.value)return;const a=P.value.slice();for(a.push({ts:Date.now(),level:e,msg:t});a.length>gt;)a.shift();P.value=a}function bt(e){const a=pe.value.slice();a.push(e[0]),a.length>200&&a.shift();const i=$e.value.slice();i.push(e[1]),i.length>200&&i.shift();const s=Se.value.slice();s.push(e[2]),s.length>200&&s.shift(),pe.value=a,$e.value=i,Se.value=s}function ft(e){const a=ue.value.slice();for(a.push(Math.max(0,Math.min(1,e)));a.length>48;)a.shift();ue.value=a}var yt=Object.defineProperty,xt=Object.getOwnPropertyDescriptor,be=(e,t,a,i)=>{for(var s=i>1?void 0:i?xt(t,a):t,r=e.length-1,o;r>=0;r--)(o=e[r])&&(s=(i?o(t,a,s):o(s))||s);return i&&s&&yt(t,a,s),s};let j=class extends y{constructor(){super(...arguments),this.open=!1,this.mTitle="",this.mBody="",this.buttons=[],this.onModal=e=>{const t=e.detail;this.mTitle=t.title,this.mBody=t.body,this.buttons=t.buttons??[{label:"Close",variant:"primary"}],this.open=!0,this.setAttribute("open",""),requestAnimationFrame(()=>{const a=this.shadowRoot;if(!a)return;a.querySelector("input, select, textarea, button:not(.close)")?.focus()})},this.onKey=e=>{e.key==="Escape"&&this.open&&this.close()}}connectedCallback(){super.connectedCallback(),window.addEventListener("nv-modal",this.onModal),window.addEventListener("keydown",this.onKey)}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("nv-modal",this.onModal),window.removeEventListener("keydown",this.onKey)}updated(){if(!this.open)return;const e=this.shadowRoot;if(!e)return;const t=a=>{if(a.key!=="Tab")return;const i=Array.from(e.querySelectorAll("input, select, textarea, button, [href]")).filter(n=>!n.hasAttribute("disabled"));if(i.length===0)return;const s=i[0],r=i[i.length-1],o=e.activeElement??null;a.shiftKey&&o===s?(a.preventDefault(),r.focus()):!a.shiftKey&&o===r&&(a.preventDefault(),s.focus())};e.removeEventListener("keydown",t),e.addEventListener("keydown",t)}close(){this.open=!1,this.removeAttribute("open")}clickBtn(e){e.onClick?.(),this.close()}render(){return l`
${this.mTitle}
this.close()}>×
${this.buttons.map(e=>l`
this.clickBtn(e)}>${e.label}
`)}
`}};j.styles=f`
:host {
position: fixed; inset: 0;
background: rgba(0,0,0,0.55);
backdrop-filter: blur(4px);
z-index: 200;
display: grid; place-items: center;
opacity: 0; pointer-events: none;
transition: opacity 0.18s;
}
:host([open]) { opacity: 1; pointer-events: auto; }
.modal {
background: var(--bg-1);
border: 1px solid var(--line-2);
border-radius: var(--radius);
box-shadow: 0 30px 80px -20px rgba(0,0,0,0.7);
width: min(520px, 92vw);
max-height: 86vh;
display: flex; flex-direction: column;
transform: translateY(12px) scale(0.98);
transition: transform 0.22s cubic-bezier(0.2,0.7,0.3,1);
}
:host([open]) .modal { transform: translateY(0) scale(1); }
.h {
padding: 14px 16px;
border-bottom: 1px solid var(--line);
display: flex; align-items: center; justify-content: space-between;
}
.h .ttl { font-size: 14px; font-weight: 600; }
.body { padding: 16px; overflow-y: auto; font-size: 13px; color: var(--ink-2); line-height: 1.55; }
.f {
padding: 12px 16px;
border-top: 1px solid var(--line);
display: flex; gap: 8px; justify-content: flex-end;
}
button {
padding: 6px 12px;
border-radius: 8px;
font-size: 12.5px;
cursor: pointer;
font-family: inherit;
border: 1px solid var(--line);
background: var(--bg-2); color: var(--ink);
}
button.ghost { background: transparent; }
button.primary { background: var(--accent); border-color: var(--accent); color: #1a0f00; }
button.danger { background: var(--bad); border-color: var(--bad); color: #fff; }
.close {
width: 28px; height: 28px;
background: transparent; border: 1px solid var(--line);
border-radius: 6px;
color: var(--ink-2);
}
`;be([u()],j.prototype,"open",2);be([u()],j.prototype,"mTitle",2);be([u()],j.prototype,"mBody",2);be([u()],j.prototype,"buttons",2);j=be([x("nv-modal")],j);function de(e){window.dispatchEvent(new CustomEvent("nv-modal",{detail:e}))}var wt=Object.defineProperty,kt=Object.getOwnPropertyDescriptor,Ae=(e,t,a,i)=>{for(var s=i>1?void 0:i?kt(t,a):t,r=e.length-1,o;r>=0;r--)(o=e[r])&&(s=(i?o(t,a,s):o(s))||s);return i&&s&&wt(t,a,s),s};let Z=class extends y{constructor(){super(...arguments),this.visible=!1,this.msg="",this.icon="✓",this.timer=null,this.onToast=e=>{const t=e.detail;this.msg=t.msg??"Done",this.icon=t.icon??"✓",this.visible=!0,this.setAttribute("visible",""),this.timer!==null&&window.clearTimeout(this.timer),this.timer=window.setTimeout(()=>{this.visible=!1,this.removeAttribute("visible")},1800)}}connectedCallback(){super.connectedCallback(),window.addEventListener("nv-toast",this.onToast)}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("nv-toast",this.onToast)}render(){return l`${this.icon} ${this.msg} `}};Z.styles=f`
:host {
position: fixed; bottom: 24px; left: 50%;
transform: translateX(-50%) translateY(80px);
background: var(--bg-2);
border: 1px solid var(--line-2);
border-radius: var(--radius);
padding: 10px 14px;
font-size: 12.5px;
box-shadow: var(--shadow);
z-index: 100;
opacity: 0; pointer-events: none;
transition: opacity 0.2s, transform 0.2s;
display: flex; align-items: center; gap: 8px;
}
:host([visible]) {
opacity: 1;
transform: translateX(-50%) translateY(0);
pointer-events: auto;
}
.icon { color: var(--accent); }
`;Ae([u()],Z.prototype,"visible",2);Ae([u()],Z.prototype,"msg",2);Ae([u()],Z.prototype,"icon",2);Z=Ae([x("nv-toast")],Z);function C(e,t="✓"){window.dispatchEvent(new CustomEvent("nv-toast",{detail:{msg:e,icon:t}}))}var $t=Object.getOwnPropertyDescriptor,St=(e,t,a,i)=>{for(var s=i>1?void 0:i?$t(t,a):t,r=e.length-1,o;r>=0;r--)(o=e[r])&&(s=o(s)||s);return s};let Ne=class extends y{connectedCallback(){super.connectedCallback(),w(()=>{M.value,Ve.value,W.value,k.value,qe.value,h.value,this.requestUpdate()})}async toggleRun(){const e=b();e&&(h.value?(await e.pause(),h.value=!1):(await e.run(),h.value=!0))}async reset(){const e=b();e&&await e.reset()}toggleTheme(){k.value=k.value==="dark"?"light":"dark"}async openSeedModal(){const e=`0x${W.value.toString(16).toUpperCase().padStart(8,"0")}`;de({title:"Set seed",body:`Set the 32-bit hex seed for the shot-noise PRNG. Same (scene, config, seed) → byte-identical witness.
Hex seed
`,buttons:[{label:"Cancel",variant:"ghost"},{label:"Apply",variant:"primary",onClick:async()=>{const t=document.querySelector("nv-modal")?.shadowRoot?.querySelector("#seed-input");if(!t)return;const a=t.value.trim().replace(/^0x/i,""),i=BigInt("0x"+a);W.value=i,await b()?.setSeed(i),c("ok",`seed → 0x${i.toString(16).toUpperCase()}`),C(`Seed → 0x${i.toString(16).toUpperCase().slice(0,8)}`,"⟳")}}]})}openTransportSettings(){window.dispatchEvent(new CustomEvent("open-settings"))}render(){const e=W.value.toString(16).toUpperCase().padStart(8,"0");return l`
RuView /
nvsim /
${qe.value}
${M.value>0?(M.value/1e3).toFixed(2)+" kHz":"idle"}
${Ve.value}
seed: 0x${e}
window.dispatchEvent(new CustomEvent("nv-show-help"))}>
?
${k.value==="dark"?"☼":"☾"}
↺ Reset
${h.value?"❚❚ Pause":"▶ Run"}
`}};Ne.styles=f`
:host {
display: flex; align-items: center;
padding: 0 16px; gap: 12px;
background: var(--bg-1);
border-bottom: 1px solid var(--line);
z-index: 10;
}
.crumbs { display: flex; align-items: center; gap: 8px; font-size: 12.5px; color: var(--ink-3); }
.crumbs .sep { color: var(--ink-4); }
.crumbs .cur { color: var(--ink); font-weight: 500; }
.spacer { flex: 1; }
.pill {
display: inline-flex; align-items: center; gap: 6px;
padding: 5px 10px;
background: var(--bg-2); border: 1px solid var(--line);
border-radius: 999px;
font-size: 12px; color: var(--ink-2);
font-family: var(--mono); font-weight: 500;
}
.pill .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--ok); box-shadow: 0 0 6px var(--ok); animation: pulse 2s infinite; }
.pill.wasm .dot { background: var(--accent-2); box-shadow: 0 0 6px var(--accent-2); }
.pill.seed { color: var(--ink-3); cursor: pointer; }
.pill.seed:hover { border-color: var(--line-2); }
.pill.seed b { color: var(--accent); font-weight: 600; }
.pill.wasm { cursor: pointer; }
.pill.wasm:hover { border-color: var(--line-2); }
button {
display: inline-flex; align-items: center; gap: 6px;
padding: 6px 12px;
background: var(--bg-2); border: 1px solid var(--line);
border-radius: 8px;
font-size: 12.5px; font-weight: 500; color: var(--ink);
cursor: pointer;
transition: all 0.15s;
}
button:hover { border-color: var(--line-2); background: var(--bg-3); }
button.primary { background: var(--accent); border-color: var(--accent); color: #1a0f00; }
button.primary:hover { filter: brightness(1.08); }
button.ghost { background: transparent; }
`;Ne=St([x("nv-topbar")],Ne);var _t=Object.getOwnPropertyDescriptor,zt=(e,t,a,i)=>{for(var s=i>1?void 0:i?_t(t,a):t,r=e.length-1,o;r>=0;r--)(o=e[r])&&(s=o(s)||s);return s};let He=null;function xe(){He!==null&&window.clearTimeout(He),He=window.setTimeout(async()=>{const e=b();if(e)try{await e.setConfig({digitiser:{f_s_hz:K.value,f_mod_hz:Y.value},sensor:{gamma_fwhm_hz:1e6,t1_s:.005,t2_s:1e-6,t2_star_s:2e-7,contrast:.03,n_spins:1e12,shot_noise_disabled:!Q.value},dt_s:X.value*.001}),c("dbg",`config pushed · fs=${K.value} f_mod=${Y.value} dt=${X.value.toFixed(1)}ms noise=${Q.value?"on":"off"}`)}catch(t){c("warn",`config push failed: ${t.message}`)}},300)}let We=class extends y{connectedCallback(){super.connectedCallback(),w(()=>{K.value,Y.value,X.value,Q.value,h.value,this.requestUpdate()})}render(){return l`
Scene 4 sources
Magnetic primitives in the simulated environment. Drag any in the
canvas to reposition; positions persist across reloads.
rebar.steel.coil
χ=5000
heart_proxy
1e-6 A·m²
mains_60Hz
2 A · 60 Hz
door.steel
eddy
NV sensor COTS
Element Six DNV-B1 reference: 1 mm³ diamond, ~10¹² NV centers.
Floor δB ≈ 1.18 pT/√Hz per Barry 2020 §III.A.
window.dispatchEvent(new CustomEvent("nv-show-help",{detail:{section:"glossary"}}))}>What's NV?
V 1 mm³
N 1e12 NV
C 0.030
T₂* 200 ns
δB 1.18 pT/√Hz
Tunables
Live pipeline parameters. Edits debounce 300 ms then rebuild the
WASM pipeline without restarting the frame stream.
Pipeline
Forward simulator stages, left to right. Stages glow cyan while
the pipeline is running.
scene
→
B-S
→
prop
→
NV
→
ADC
→
frame
`}};We.styles=f`
:host {
display: flex; flex-direction: column; gap: 14px;
padding: 14px; overflow-y: auto;
background: var(--bg-1); border-right: 1px solid var(--line);
}
.panel {
background: var(--bg-2); border: 1px solid var(--line);
border-radius: var(--radius); padding: 12px;
}
.panel-h {
display: flex; align-items: center; justify-content: space-between;
font-size: 11px; font-weight: 600; color: var(--ink-3);
text-transform: uppercase; letter-spacing: 0.08em;
margin-bottom: 6px;
}
.panel-help {
font-size: 11.5px; color: var(--ink-3);
margin: 0 0 10px;
line-height: 1.5;
}
.help-link {
color: var(--accent-2);
cursor: pointer;
text-decoration: underline dotted;
}
.help-link:hover { color: var(--accent); }
.count {
background: var(--bg-3); color: var(--ink-2);
padding: 1px 6px; border-radius: 999px;
font-family: var(--mono); font-size: 10px;
text-transform: none; letter-spacing: 0;
}
.scene-item {
display: flex; align-items: center; gap: 10px;
padding: 8px 10px;
border-radius: var(--radius-sm);
cursor: pointer;
transition: background 0.15s;
border: 1px solid transparent;
}
.scene-item:hover { background: var(--bg-3); }
.scene-item .swatch { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.scene-item .name { font-size: 13px; flex: 1; }
.scene-item .meta { font-family: var(--mono); font-size: 10.5px; color: var(--ink-3); }
.field-row {
display: flex; align-items: center; justify-content: space-between;
padding: 6px 0; font-size: 12.5px;
border-bottom: 1px solid var(--line);
}
.field-row:last-child { border-bottom: 0; }
.field-row .lbl { color: var(--ink-3); }
.field-row .val { font-family: var(--mono); color: var(--ink); font-size: 12px; }
.slider-row { padding: 8px 0; border-bottom: 1px solid var(--line); }
.slider-row:last-child { border-bottom: 0; padding-bottom: 0; }
.slider-row .top { display: flex; justify-content: space-between; margin-bottom: 6px; font-size: 12px; }
.slider-row .top .lbl { color: var(--ink-3); }
.slider-row .top .val { font-family: var(--mono); color: var(--ink); }
input[type="range"] {
-webkit-appearance: none; appearance: none;
width: 100%; height: 4px;
background: var(--bg-3); border-radius: 2px; outline: none;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none; appearance: none;
width: 14px; height: 14px; border-radius: 50%;
background: var(--accent); cursor: pointer;
border: 2px solid var(--bg-2);
box-shadow: 0 0 0 1px var(--line-2);
}
.pipeline { display: flex; gap: 4px; align-items: center; flex-wrap: wrap; margin-top: 6px; }
.stage {
flex: 1; min-width: 50px;
padding: 4px 6px;
background: var(--bg-3); border: 1px solid var(--line);
border-radius: 6px; font-size: 9.5px; text-align: center;
color: var(--ink-2); font-family: var(--mono);
}
.stage.live { border-color: var(--accent-2); color: var(--accent-2); }
.stage-arrow { color: var(--ink-4); font-size: 10px; }
`;We=zt([x("nv-sidebar")],We);var Tt=Object.defineProperty,Mt=Object.getOwnPropertyDescriptor,se=(e,t,a,i)=>{for(var s=i>1?void 0:i?Mt(t,a):t,r=e.length-1,o;r>=0;r--)(o=e[r])&&(s=(i?o(t,a,s):o(s))||s);return i&&s&&Tt(t,a,s),s};let E=class extends y{constructor(){super(...arguments),this.zoom=1,this.layerVisible={source:!0,field:!0,label:!0},this.items=[{id:"rebar",x:740,y:240,color:"oklch(0.72 0.18 330)",name:"rebar.steel"},{id:"heart",x:220,y:180,color:"oklch(0.78 0.14 195)",name:"heart_proxy"},{id:"mains",x:180,y:380,color:"oklch(0.72 0.18 330)",name:"mains_60Hz"},{id:"door",x:800,y:470,color:"oklch(0.78 0.14 145)",name:"door.steel"}],this.dragging=null,this.selected=null,this.dragOffset={dx:0,dy:0},this.onDown=(e,t)=>{t.preventDefault(),this.dragging=e,this.selected=e;const a=this.items.find(r=>r.id===e);if(!a)return;const i=this.renderRoot.querySelector("svg");if(!i)return;const s=this.toSvg(t,i);this.dragOffset={dx:s.x-a.x,dy:s.y-a.y}},this.onPointerMove=e=>{if(!this.dragging)return;const t=this.renderRoot.querySelector("svg");if(!t)return;const a=this.toSvg(e,t);this.items=this.items.map(i=>i.id===this.dragging?{...i,x:a.x-this.dragOffset.dx,y:a.y-this.dragOffset.dy}:i)},this.onPointerUp=()=>{this.dragging&&(ve.value=this.items.map(({id:e,x:t,y:a})=>({id:e,x:t,y:a}))),this.dragging=null}}connectedCallback(){super.connectedCallback(),ve.value.length>0&&(this.items=this.items.map(e=>{const t=ve.value.find(a=>a.id===e.id);return t?{...e,x:t.x,y:t.y}:e})),w(()=>{he.value,V.value,M.value,L.value,T.value,h.value,ye.value,B.value,this.requestUpdate()}),w(()=>{const e=B.value;if(!e)return;const t=Math.sqrt(e.bPt[0]**2+e.bPt[1]**2+e.bPt[2]**2),a=Math.max(Math.abs(e.sigmaPt[0]),Math.abs(e.sigmaPt[1]),Math.abs(e.sigmaPt[2]),.001),i=t/a;Number.isFinite(i)&&(L.value=i)}),window.addEventListener("pointermove",this.onPointerMove),window.addEventListener("pointerup",this.onPointerUp)}async toggleRun(){const e=b();e&&(h.value?(await e.pause(),h.value=!1):(await e.run(),h.value=!0))}async stepFwd(){const e=b();e&&(await e.step("fwd",10),c("dbg","sim step → +1 frame"))}async stepBack(){const e=b();e&&(await e.step("back",10),c("dbg","sim step ← -1 frame"))}cycleSpeed(){const e=[.25,.5,1,2,4],t=e.indexOf(ye.value);ye.value=e[(t+1)%e.length]}zoomIn(){this.zoom=Math.min(2.5,this.zoom*1.2)}zoomOut(){this.zoom=Math.max(.5,this.zoom/1.2)}fitView(){this.zoom=1}toggleLayer(e){this.layerVisible={...this.layerVisible,[e]:!this.layerVisible[e]}}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("pointermove",this.onPointerMove),window.removeEventListener("pointerup",this.onPointerUp)}toSvg(e,t){const a=t.getBoundingClientRect(),i=(e.clientX-a.left)/a.width*1e3,s=(e.clientY-a.top)/a.height*600;return{x:i,y:s}}render(){const e=he.value,t=[e[0]*1e9,e[1]*1e9,e[2]*1e9],a=V.value*1e9,i=T.value?"":"anim",s=1e3/this.zoom,r=600/this.zoom,o=(1e3-s)/2,n=(600-r)/2;return l`
${this.layerVisible.field?this.items.map(d=>J`
`):""}
${this.layerVisible.source?this.items.map(d=>J`
this.onDown(d.id,v)}>
${this.layerVisible.label?J`${d.name} `:""}
`):""}
sensor · 〈111〉 NV
B_in: [${t[0].toFixed(2)}, ${t[1].toFixed(2)}, ${t[2].toFixed(2)}] nT
+
−
⊡
this.toggleLayer("source")}>●
this.toggleLayer("field")}>≈
this.toggleLayer("label")}>T
⏮
${h.value?"❚❚":"▶"}
⏭
${ye.value}×
FPS
${M.value>0?Math.round(M.value):"—"}
SNR
${L.value>0?L.value.toFixed(1):"—"}
`}};E.styles=f`
:host {
display: block; height: 100%; width: 100%;
background: radial-gradient(ellipse at 50% 30%, var(--bg-2) 0%, var(--bg-0) 70%);
position: relative; overflow: hidden;
border-bottom: 1px solid var(--line);
}
.grid {
position: absolute; inset: 0;
background-image:
linear-gradient(var(--grid) 1px, transparent 1px),
linear-gradient(90deg, var(--grid) 1px, transparent 1px);
background-size: 32px 32px;
pointer-events: none;
mask-image: radial-gradient(ellipse at center, black 40%, transparent 100%);
}
svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.stat-card {
background: rgba(13,17,23,0.7);
backdrop-filter: blur(8px);
border: 1px solid var(--line);
border-radius: var(--radius-sm);
padding: 8px 12px;
font-size: 11px;
min-width: 96px;
}
[data-theme="light"] .stat-card { background: rgba(255,255,255,0.85); }
.stat-card .lbl {
color: var(--ink-3);
text-transform: uppercase; font-weight: 600; letter-spacing: 0.06em; font-size: 9.5px;
}
.stat-card .val { font-family: var(--mono); font-size: 16px; font-weight: 600; margin-top: 2px; }
.stat-card .val.amber { color: var(--accent); }
.stat-card .val.cyan { color: var(--accent-2); }
.stat-card .val.mint { color: var(--accent-4); }
.scene-readout {
position: absolute; top: 14px; right: 14px;
display: flex; gap: 8px; z-index: 5;
}
.draggable { cursor: grab; transition: filter 0.15s; }
.draggable:hover { filter: brightness(1.15) drop-shadow(0 0 6px currentColor); }
.draggable.dragging { cursor: grabbing; filter: brightness(1.25) drop-shadow(0 0 10px currentColor); }
.field-line { stroke-dasharray: 4 6; }
@keyframes dash { to { stroke-dashoffset: -200; } }
.field-line.anim { animation: dash 4s linear infinite; }
@keyframes spin {
0% { transform: rotateY(0) rotateX(8deg); }
100% { transform: rotateY(360deg) rotateX(8deg); }
}
.crystal { transform-origin: center; transform-box: fill-box; }
.crystal.anim { animation: spin 12s linear infinite; }
.label {
font-family: var(--mono); font-size: 11px; fill: var(--ink-2);
pointer-events: none;
}
.scene-toolbar {
position: absolute; top: 14px; left: 14px;
display: flex; gap: 6px; z-index: 5;
background: rgba(13,17,23,0.85);
backdrop-filter: blur(8px);
border: 1px solid var(--line);
border-radius: 8px;
padding: 4px;
}
[data-theme="light"] .scene-toolbar { background: rgba(255,255,255,0.85); }
.scene-toolbar button {
width: 28px; height: 28px;
background: transparent;
border: 1px solid transparent;
border-radius: 6px;
color: var(--ink-2);
cursor: pointer;
display: grid; place-items: center;
font-size: 13px;
}
.scene-toolbar button:hover { color: var(--ink); background: var(--bg-2); }
.scene-toolbar button.on { background: var(--bg-3); color: var(--accent); border-color: var(--line-2); }
.sim-controls {
position: absolute; bottom: 14px; right: 14px;
display: flex; gap: 6px; align-items: center;
background: rgba(13,17,23,0.85);
backdrop-filter: blur(12px);
border: 1px solid var(--line-2);
border-radius: 999px;
padding: 6px 10px;
z-index: 5;
}
[data-theme="light"] .sim-controls { background: rgba(255,255,255,0.92); }
.sim-controls .play {
width: 32px; height: 32px;
background: var(--accent);
border: none;
border-radius: 50%;
color: #1a0f00;
cursor: pointer;
display: grid; place-items: center;
font-size: 13px;
}
.sim-controls .play:hover { filter: brightness(1.08); }
.sim-controls .step {
width: 26px; height: 26px;
border-radius: 6px;
background: transparent;
color: var(--ink-2);
border: 1px solid var(--line);
cursor: pointer;
font-size: 11px;
}
.sim-controls .step:hover { color: var(--ink); border-color: var(--line-2); }
.sim-controls .speed {
font-family: var(--mono); font-size: 11px;
color: var(--ink-2);
padding: 0 6px;
min-width: 36px;
text-align: center;
cursor: pointer;
}
`;se([u()],E.prototype,"zoom",2);se([u()],E.prototype,"layerVisible",2);se([u()],E.prototype,"items",2);se([u()],E.prototype,"dragging",2);se([u()],E.prototype,"selected",2);E=se([x("nv-scene")],E);var Ct=Object.defineProperty,At=Object.getOwnPropertyDescriptor,Pe=(e,t,a,i)=>{for(var s=i>1?void 0:i?At(t,a):t,r=e.length-1,o;r>=0;r--)(o=e[r])&&(s=(i?o(t,a,s):o(s))||s);return i&&s&&Ct(t,a,s),s};let ee=class extends y{constructor(){super(...arguments),this.tab="signal",this.pinTab=null,this.expanded=!1}connectedCallback(){super.connectedCallback(),w(()=>{pe.value,$e.value,Se.value,ue.value,B.value,z.value,$.value,he.value,V.value,this.requestUpdate()})}willUpdate(e){e.has("pinTab")&&this.pinTab&&this.tab!==this.pinTab&&(this.tab=this.pinTab)}async verify(){const e=b();if(e){$.value="pending",c("info","verifying witness over 256 frames…");try{const t=R.value,a=new Uint8Array(32);for(let s=0;s<32;s++)a[s]=parseInt(t.slice(s*2,s*2+2),16);const i=await e.verifyWitness(a);if(i.ok)$.value="ok",z.value=t,c("ok",`witness ${t.slice(0,16)}… matches · determinism gate ✓`);else{$.value="fail";const s=Array.from(i.actual).map(r=>r.toString(16).padStart(2,"0")).join("");z.value=s,c("err",`WITNESS MISMATCH actual=${s.slice(0,16)}…`)}}catch(t){$.value="fail",c("err",`verify failed: ${t.message}`)}}}renderHeader(){return this.expanded?l`
${{signal:"Signal inspector — live B-vector trace + frame stream",frame:"Frame inspector — MagFrame v1 fields + raw bytes",witness:"Witness panel — SHA-256 determinism gate"}[this.tab]}
${this.tab==="signal"?"Real-time recovered field-vector and frame-stream sparkline. Both update at the running pipeline's frame rate. Use the Tunables panel in the sidebar to change f_s, f_mod, dt, and shot-noise behaviour.":this.tab==="frame"?"Decoded view of the most recent MagFrame: typed fields plus the raw 60-byte little-endian binary record (magic 0xC51A_6E70).":"Re-derive the SHA-256 witness for the canonical reference scene (seed=42, N=256) right now in your browser and compare against Proof::EXPECTED_WITNESS_HEX. Same inputs → same hash, byte-for-byte, across every machine and transport."}
`:""}renderSignalTab(){const r=v=>{let m="";return v.forEach((g,G)=>{const Ee=G/Math.max(1,199)*320,De=65-g*22;m+=(G===0?"M":"L")+` ${Ee.toFixed(1)} ${De.toFixed(1)} `}),m},o=he.value,n=[o[0]*1e9,o[1]*1e9,o[2]*1e9],d=pe.value.length>0;return l`
${d?"":l`
No frames yet. Press ▶ Run in the topbar (or hit Space)
to start the live B-vector trace.
`}
B-vector trace
3-axis · nT
${J` `}
${J` `}
${J` `}
${this.expanded?l`
x: ${n[0].toFixed(3)} nT
y: ${n[1].toFixed(3)} nT
z: ${n[2].toFixed(3)} nT
|B| ${(V.value*1e9).toFixed(3)} nT
`:""}
Frame stream
live
${this.expanded?l`
frames in window: ${ue.value.length}
noise floor: ${B.value?B.value.noiseFloorPtSqrtHz.toFixed(2)+" pT/√Hz":"—"}
`:""}
`}renderFrameTab(){const e=B.value,t=e?.raw;let a="";return t&&(a=Array.from(t).map(s=>s.toString(16).padStart(2,"0")).slice(0,60).join(" ")),l`
${e?"":l`
No MagFrame to display yet. Start the pipeline (▶ Run ) to populate.
`}
MagFrame v1 fields
60 B
magic ${e?"0x"+e.magic.toString(16).toUpperCase():"—"}
version ${e?.version??"—"}
flags 0x${(e?.flags??0).toString(16).padStart(4,"0")}
sensor_id ${e?.sensorId??"—"}
t_us ${e?e.tUs.toString():"—"}
b_pT[0] ${e?e.bPt[0].toFixed(1):"—"}
b_pT[1] ${e?e.bPt[1].toFixed(1):"—"}
b_pT[2] ${e?e.bPt[2].toFixed(1):"—"}
noise_floor ${e?e.noiseFloorPtSqrtHz.toFixed(2):"—"}
temp_K ${e?e.temperatureK.toFixed(1):"—"}
Hex dump
LE
${a||"—"}
${this.expanded?l`
Layout (little-endian): magic(u32) version(u16) flags(u16) sensor_id(u16) _reserved(u16) t_us(u64) b_pt[3](f32) sigma_pt[3](f32) noise_floor(f32) temp_K(f32).
`:""}
`}renderWitnessTab(){const e=$.value,t=e==="ok"?"ok":e==="fail"?"fail":"",a=e==="pending"?"Verifying…":e==="ok"?"✓ Witness verified · determinism gate":e==="fail"?"✗ Witness mismatch · audit required":"Verify witness",i=R.value&&z.value&&R.value===z.value;return l`
${this.expanded?l`
Reference scene
Proof::REFERENCE
2 dipoles · 1 loop · 1 ferrous · 1 sensor
Seed
0x0000002A
canonical Proof::SEED
Sample count
256
Proof::N_SAMPLES
Status
${e==="ok"?"✓ matches":e==="fail"?"✗ drift":e==="pending"?"… running":"— idle"}
${i?"byte-equivalent":"not yet verified"}
`:""}
Expected (Proof::EXPECTED_WITNESS_HEX)
SHA-256
${R.value||"(loading…)"}
Actual (last verify)
SHA-256
${z.value||"(not verified yet)"}
${a}
${this.expanded?l`
What this verifies
ADR-089 §5
Pressing Verify runs the canonical reference pipeline
(Proof::generate) end-to-end inside this browser's WASM Worker:
scene → Biot-Savart synthesis → material attenuation → NV ensemble → ADC + lock-in →
concatenated MagFrame bytes → SHA-256.
If the resulting hash matches the constant pinned at build time
(cc8de9b01b0ff5bd…), every constant — γ_e, D_GS, μ₀, T₂*, contrast, the PRNG
stream, the frame layout, the pipeline ordering — is byte-identical to the published
reference. If it doesn't match, something drifted; the dashboard names which.
This is the same regression test that runs in
cargo test -p nvsim — running in your browser, against your own WASM build.
`:""}
`}render(){return l`
this.tab="signal"}>Signal
this.tab="frame"}>Frame
this.tab="witness"}>Witness
${this.renderHeader()}
${this.tab==="signal"?this.renderSignalTab():this.tab==="frame"?this.renderFrameTab():this.renderWitnessTab()}
`}};ee.styles=f`
:host {
display: flex; flex-direction: column;
background: var(--bg-1);
border-left: 1px solid var(--line);
overflow: hidden;
height: 100%;
}
:host([expanded]) {
border-left: 0;
background: radial-gradient(ellipse at 50% 30%, var(--bg-2) 0%, var(--bg-0) 70%);
}
:host([expanded]) .tabs {
padding: 0 24px;
background: var(--bg-1);
}
:host([expanded]) .tab {
padding: 16px 22px;
font-size: 13.5px;
flex: 0 0 auto;
}
:host([expanded]) .body {
padding: 24px 28px;
max-width: 1400px;
width: 100%;
margin: 0 auto;
}
:host([expanded]) .card { padding: 18px 20px; }
:host([expanded]) .card-h .ttl { font-size: 14px; }
:host([expanded]) svg { height: 220px; }
:host([expanded]) .frame-strip { height: 48px; }
:host([expanded]) table { font-size: 12.5px; }
:host([expanded]) td { padding: 6px 0; }
:host([expanded]) .hex { font-size: 12px; padding: 14px; line-height: 1.7; }
:host([expanded]) .witness-box { font-size: 13px; padding: 14px 16px; line-height: 1.6; }
:host([expanded]) .verify-btn { padding: 12px; font-size: 13px; }
:host([expanded]) .grid-2 {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
}
:host([expanded]) .grid-2 > .card { margin-bottom: 0; }
@media (max-width: 1024px) {
:host([expanded]) .grid-2 { grid-template-columns: 1fr; }
}
.tabs {
display: flex; border-bottom: 1px solid var(--line);
}
.tab {
flex: 1;
padding: 11px 8px;
background: transparent; border: none;
font-size: 11.5px; font-weight: 500;
color: var(--ink-3);
border-bottom: 2px solid transparent;
cursor: pointer; transition: color 0.15s, border-color 0.15s;
}
.tab.active { color: var(--ink); border-bottom-color: var(--accent); }
.tab:hover { color: var(--ink-2); }
.body { padding: 14px; flex: 1; overflow-y: auto; }
.card {
background: var(--bg-2); border: 1px solid var(--line);
border-radius: var(--radius); padding: 12px;
margin-bottom: 12px;
}
.card-h {
display: flex; justify-content: space-between; align-items: center;
margin-bottom: 8px;
}
.card-h .ttl { font-size: 12px; font-weight: 600; }
.badge {
font-family: var(--mono); font-size: 10px;
padding: 2px 6px;
background: oklch(0.78 0.14 195 / 0.12);
color: var(--accent-2);
border-radius: 4px;
border: 1px solid oklch(0.78 0.14 195 / 0.3);
}
svg { width: 100%; height: 130px; }
.frame-strip {
height: 28px;
display: flex; align-items: flex-end; gap: 1px;
padding: 4px 0;
}
.bar {
flex: 1;
background: linear-gradient(to top, var(--accent-2), var(--accent));
border-radius: 1px;
min-height: 2px;
}
table { width: 100%; border-collapse: collapse; font-family: var(--mono); font-size: 10.5px; }
td { padding: 4px 0; border-bottom: 1px solid var(--line); }
td:first-child { color: var(--ink-3); }
td:last-child { text-align: right; color: var(--ink); }
.hex {
background: var(--bg-3);
border: 1px solid var(--line);
border-radius: var(--radius-sm);
padding: 10px;
font-family: var(--mono);
font-size: 10.5px;
color: var(--ink-2);
line-height: 1.6;
overflow-x: auto;
white-space: nowrap;
}
.hex .magic { color: var(--accent); font-weight: 600; }
.witness-box {
font-family: var(--mono);
font-size: 11px;
color: var(--ink-2);
background: var(--bg-3);
border: 1px solid var(--line);
border-radius: 6px;
padding: 8px 10px;
word-break: break-all;
line-height: 1.5;
}
.verify-btn {
margin-top: 10px;
width: 100%;
padding: 8px;
border: 1px solid var(--line);
background: var(--bg-3);
color: var(--ink);
border-radius: 8px;
cursor: pointer;
font-family: var(--mono);
font-size: 12px;
}
.verify-btn:hover { border-color: var(--accent); }
.verify-btn.ok { border-color: var(--ok); color: var(--ok); }
.verify-btn.fail { border-color: var(--bad); color: var(--bad); }
`;Pe([u()],ee.prototype,"tab",2);Pe([Ce({attribute:!1})],ee.prototype,"pinTab",2);Pe([Ce({type:Boolean,reflect:!0})],ee.prototype,"expanded",2);ee=Pe([x("nv-inspector")],ee);var Pt=Object.defineProperty,Rt=Object.getOwnPropertyDescriptor,Je=(e,t,a,i)=>{for(var s=i>1?void 0:i?Rt(t,a):t,r=e.length-1,o;r>=0;r--)(o=e[r])&&(s=(i?o(t,a,s):o(s))||s);return i&&s&&Pt(t,a,s),s};let _e=class extends y{constructor(){super(...arguments),this.hIdx=-1,this.onKey=e=>{if(e.key==="Enter")this.exec(this.inputEl.value),this.inputEl.value="";else if(e.key==="ArrowUp"){const t=q.value;t.length&&(this.hIdx=Math.max(0,this.hIdx-1),this.inputEl.value=t[this.hIdx]??"",e.preventDefault())}else if(e.key==="ArrowDown"){const t=q.value;t.length&&(this.hIdx=Math.min(t.length,this.hIdx+1),this.inputEl.value=t[this.hIdx]??"",e.preventDefault())}}}connectedCallback(){super.connectedCallback(),w(()=>{P.value,Oe.value,le.value,this.requestUpdate()})}updated(){const e=this.renderRoot.querySelector(".body");e&&(e.scrollTop=e.scrollHeight)}counts(){const e={info:0,warn:0,err:0,dbg:0,ok:0};for(const t of P.value)e[t.level]=(e[t.level]??0)+1;return e.all=P.value.length,e}async exec(e){if(e=e.trim(),!e)return;c("info",`nvsim> ${e}`),ht(e),this.hIdx=q.value.length;const[t,...a]=e.split(/\s+/),i=a.join(" "),s=b();switch(t){case"help":c("info","commands: help · scene.list · sensor.config · run · pause · reset · seed · proof.verify · proof.export · clear · theme · status");break;case"scene.list":c("info","scene rebar-walkby-01:"),c("info"," rebar.steel.coil @ [+2.7, 0.0, +0.3] m χ=5000"),c("info"," dipole.heart_proxy @ [-1.4, +0.2, +0.4] m m=1.0e-6 A·m²"),c("info"," loop.mains_60Hz @ [-1.6, -0.4, 0.0] m I=2 A"),c("info"," eddy.door_steel @ [+0.0, +1.8, +0.4] m σ=1e6 S/m");break;case"sensor.config":c("info","NvSensor::cots_defaults() {"),c("info"," pos=[0,0,0], V=1mm³, N=1e12, C=0.03, T2*=200ns"),c("info"," D=2.870 GHz, γe=28 GHz/T, Γ=1.0 MHz, axes=4×〈111〉"),c("info"," δB ≈ 1.18 pT/√Hz (Barry 2020 §III.A) }");break;case"run":s&&(await s.run(),h.value=!0,c("ok","pipeline RUN"));break;case"pause":s&&(await s.pause(),h.value=!1,c("warn","pipeline PAUSED"));break;case"reset":s&&(await s.reset(),c("info","pipeline reset · t=0"));break;case"seed":{if(!i){c("info",`current seed = 0x${W.value.toString(16).toUpperCase()}`);break}const r=BigInt(i.startsWith("0x")?i:"0x"+i);W.value=r,s&&await s.setSeed(r),c("ok",`seed → 0x${r.toString(16).toUpperCase()}`);break}case"proof.verify":{if(!s)break;c("dbg","computing SHA-256 over 256 frames…");try{const r=R.value,o=new Uint8Array(32);for(let d=0;d<32;d++)o[d]=parseInt(r.slice(d*2,d*2+2),16);(await s.verifyWitness(o)).ok?($.value="ok",z.value=r,c("ok",`witness ${r.slice(0,16)}… matches · determinism gate ✓`)):($.value="fail",c("err","WITNESS MISMATCH"))}catch(r){c("err",`verify failed: ${r.message}`)}break}case"proof.export":{if(!s)break;c("dbg","building proof bundle…");try{const r=await s.exportProofBundle(),o=URL.createObjectURL(r),n=document.createElement("a");n.href=o,n.download=`nvsim-proof-${Date.now()}.json`,n.click(),URL.revokeObjectURL(o),c("ok",`proof bundle exported · ${r.size} bytes`)}catch(r){c("err",`export failed: ${r.message}`)}break}case"clear":P.value=[];break;case"theme":{const r=(i||"").toLowerCase();r==="light"||r==="dark"?(k.value=r,c("ok",`theme → ${r}`)):c("info","theme [light|dark]");break}case"status":c("info",`running=${h.value} seed=0x${W.value.toString(16).toUpperCase()} verified=${$.value}`);break;default:c("err",`unknown command: ${t} · try help`)}}render(){const e=this.counts(),t=Oe.value,a=P.value.filter(i=>t==="all"||i.level===t);return l`
${["all","info","warn","err","dbg"].map(i=>l`
Oe.value=i}>
${i} ${e[i]??0}
`)}
P.value=[]}>×
le.value=!le.value}>
${le.value?"▶":"❚❚"}
${a.map(i=>{const s=new Date(i.ts),r=`${String(s.getSeconds()).padStart(2,"0")}.${String(s.getMilliseconds()).padStart(3,"0")}`;return l`
`})}
nvsim>
`}};_e.styles=f`
:host {
display: flex; flex-direction: column;
background: var(--bg-1);
overflow: hidden;
}
.tabs {
display: flex; align-items: center;
border-bottom: 1px solid var(--line);
padding: 0 10px;
gap: 2px;
}
.tab {
padding: 8px 12px;
background: transparent; border: none;
font-size: 11.5px; color: var(--ink-3);
font-family: var(--mono);
border-bottom: 2px solid transparent;
cursor: pointer;
margin-bottom: -1px;
}
.tab.active { color: var(--ink); border-bottom-color: var(--accent); }
.tab .cnt {
background: var(--bg-3); padding: 1px 5px; border-radius: 999px;
font-size: 9.5px; color: var(--ink-2); margin-left: 4px;
}
.spacer { flex: 1; }
.tools { display: flex; gap: 4px; padding: 4px 0; }
.tools button {
width: 24px; height: 24px;
background: transparent; border: 1px solid var(--line);
border-radius: 6px;
color: var(--ink-3);
font-size: 11px; cursor: pointer;
}
.tools button:hover { color: var(--ink); border-color: var(--line-2); }
.body {
flex: 1; overflow-y: auto;
font-family: var(--mono);
font-size: 11.5px;
padding: 6px 0;
background: var(--bg-0);
}
.line {
display: grid;
grid-template-columns: 70px 60px 1fr;
gap: 12px;
padding: 2px 12px;
color: var(--ink-2);
border-left: 2px solid transparent;
}
.line:hover { background: var(--bg-1); }
.ts { color: var(--ink-4); font-size: 10.5px; padding-top: 1px; }
.lvl {
font-size: 10px; font-weight: 600;
text-transform: uppercase; letter-spacing: 0.04em; padding-top: 1px;
}
.line.info .lvl { color: var(--accent-2); }
.line.warn .lvl { color: var(--warn); }
.line.warn { border-left-color: var(--warn); background: oklch(0.7 0.18 35 / 0.04); }
.line.err .lvl { color: var(--bad); }
.line.err { border-left-color: var(--bad); background: oklch(0.65 0.22 25 / 0.05); }
.line.dbg .lvl { color: var(--ink-3); }
.line.ok .lvl { color: var(--ok); }
.msg { color: var(--ink); white-space: pre-wrap; word-break: break-word; }
.input {
display: flex; align-items: center;
border-top: 1px solid var(--line);
background: var(--bg-0);
padding: 0 10px;
height: 32px; gap: 8px;
}
.prompt { color: var(--accent); font-family: var(--mono); font-size: 12px; }
input[type="text"] {
flex: 1; background: transparent; border: none; outline: none;
color: var(--ink); font-family: var(--mono); font-size: 12px;
height: 100%;
}
input::placeholder { color: var(--ink-4); }
`;Je([Ye("#console-input")],_e.prototype,"inputEl",2);_e=Je([x("nv-console")],_e);const ce=[{id:"nvsim",name:"nvsim — NV-diamond magnetometer",category:"sim",crate:"nvsim",summary:"Deterministic forward simulator: scene → Biot–Savart → NV ensemble → ADC → MagFrame stream + SHA-256 witness.",budget:"L",active:!0,status:"available",tags:["quantum","magnetometer","simulator","witness","wasm"],adr:"ADR-089"},{id:"gesture",name:"Gesture (DTW)",category:"sig",crate:"wifi-densepose-wasm-edge",summary:"Dynamic-Time-Warping gesture classifier from CSI motion templates.",events:[1],budget:"M",status:"available",tags:["hci","csi","classifier","dtw"],adr:"ADR-014"},{id:"coherence",name:"Coherence gate",category:"sig",crate:"wifi-densepose-wasm-edge",summary:"Z-score coherence scoring + Accept/PredictOnly/Reject/Recalibrate gate.",events:[2],budget:"S",status:"available",tags:["gate","csi","coherence","drift"],adr:"ADR-029"},{id:"adversarial",name:"Adversarial-signal detector",category:"ais",crate:"wifi-densepose-wasm-edge",summary:"Physically-impossible-signal detector — multi-link consistency, used to flag spoofed CSI.",events:[3],budget:"M",status:"available",tags:["security","csi","spoofing","mesh"],adr:"ADR-032"},{id:"rvf",name:"RVF — Rust Verified Feature stream",category:"sig",crate:"wifi-densepose-wasm-edge",summary:"Verified-frame builder with SHA-256 hash + version metadata for the feature stream.",budget:"S",status:"available",tags:["witness","csi","hash"],adr:"ADR-040"},{id:"occupancy",name:"Occupancy estimator",category:"bld",crate:"wifi-densepose-wasm-edge",summary:"Through-wall presence + person-count via CSI amplitude perturbation.",events:[300,301,302],budget:"S",status:"available",tags:["csi","building","presence"]},{id:"vital_trend",name:"Vital-trend monitor",category:"med",crate:"wifi-densepose-wasm-edge",summary:"HR + BR trend tracking with bradycardia/tachycardia/apnea events.",events:[100,101,102,103,104,105],budget:"S",status:"available",tags:["medical","vitals","csi"],adr:"ADR-021"},{id:"intrusion",name:"Intrusion detector",category:"sec",crate:"wifi-densepose-wasm-edge",summary:"Zone-based intrusion alert from CSI motion patterns.",events:[200,201],budget:"S",status:"available",tags:["security","zone","csi"]},{id:"med_sleep_apnea",name:"Sleep-apnea detector",category:"med",crate:"wifi-densepose-wasm-edge",summary:"Episodic respiratory pause detection during sleep cycles.",events:[105],budget:"S",status:"available",tags:["medical","sleep","breathing"]},{id:"med_cardiac_arrhythmia",name:"Cardiac arrhythmia",category:"med",crate:"wifi-densepose-wasm-edge",summary:"Beat-to-beat irregularity classifier from cardiac micro-Doppler.",events:[103,104],budget:"M",status:"available",tags:["medical","cardiac","arrhythmia"]},{id:"med_respiratory_distress",name:"Respiratory distress",category:"med",crate:"wifi-densepose-wasm-edge",summary:"Distress signature: rapid shallow breathing + accessory-muscle motion.",events:[101,102],budget:"S",status:"available",tags:["medical","breathing","icu"]},{id:"med_gait_analysis",name:"Gait analysis",category:"med",crate:"wifi-densepose-wasm-edge",summary:"Stride length, cadence, asymmetry from through-wall CSI pose tracking.",budget:"M",status:"available",tags:["medical","gait","pose"]},{id:"med_seizure_detect",name:"Seizure detector",category:"med",crate:"wifi-densepose-wasm-edge",summary:"Tonic-clonic seizure motion signature.",budget:"M",status:"beta",tags:["medical","neuro"]},{id:"sec_perimeter_breach",name:"Perimeter breach",category:"sec",crate:"wifi-densepose-wasm-edge",summary:"Approach/departure detection at user-defined boundary segments.",events:[210,211,212,213],budget:"S",status:"available",tags:["security","perimeter"]},{id:"sec_weapon_detect",name:"Metal anomaly / weapon",category:"sec",crate:"wifi-densepose-wasm-edge",summary:"Metal-perturbation flag in CSI; potential weapon presence (research).",events:[220,221,222],budget:"M",status:"research",tags:["security","metal","csi"]},{id:"sec_tailgating",name:"Tailgating detector",category:"sec",crate:"wifi-densepose-wasm-edge",summary:"Detect 2+ persons crossing a single-passage threshold.",events:[230,231,232],budget:"S",status:"available",tags:["security","access-control"]},{id:"sec_loitering",name:"Loitering detector",category:"sec",crate:"wifi-densepose-wasm-edge",summary:"Stationary occupancy past a configurable dwell threshold.",events:[240,241,242],budget:"S",status:"available",tags:["security","dwell"]},{id:"sec_panic_motion",name:"Panic motion",category:"sec",crate:"wifi-densepose-wasm-edge",summary:"High-energy distress motion: struggle / fleeing pattern.",events:[250,251,252],budget:"S",status:"beta",tags:["security","distress"]},{id:"bld_hvac_presence",name:"HVAC presence",category:"bld",crate:"wifi-densepose-wasm-edge",summary:"Occupied/activity-level/departure-countdown for HVAC zones.",events:[310,311,312],budget:"S",status:"available",tags:["hvac","building","energy"]},{id:"bld_lighting_zones",name:"Lighting zones",category:"bld",crate:"wifi-densepose-wasm-edge",summary:"Per-zone light on/dim/off cues from occupancy.",events:[320,321,322],budget:"S",status:"available",tags:["lighting","building"]},{id:"bld_elevator_count",name:"Elevator count",category:"bld",crate:"wifi-densepose-wasm-edge",summary:"Person count inside elevator car from CSI.",events:[330],budget:"S",status:"available",tags:["elevator","building"]},{id:"bld_meeting_room",name:"Meeting-room utilization",category:"bld",crate:"wifi-densepose-wasm-edge",summary:"Meeting size + duration analytics for booking systems.",budget:"S",status:"available",tags:["meeting","analytics"]},{id:"bld_energy_audit",name:"Energy audit",category:"bld",crate:"wifi-densepose-wasm-edge",summary:"Continuous occupancy-vs-HVAC-state audit for energy savings.",budget:"M",status:"available",tags:["energy","audit"]},{id:"ret_queue_length",name:"Queue length",category:"ret",crate:"wifi-densepose-wasm-edge",summary:"Live queue-length tracking for checkout / kiosks.",budget:"S",status:"available",tags:["retail","queue"]},{id:"ret_dwell_heatmap",name:"Dwell heatmap",category:"ret",crate:"wifi-densepose-wasm-edge",summary:"Per-zone dwell time accumulation; analytics-only export.",budget:"M",status:"available",tags:["retail","heatmap"]},{id:"ret_customer_flow",name:"Customer flow",category:"ret",crate:"wifi-densepose-wasm-edge",summary:"Origin-destination flow graph through a store layout.",budget:"M",status:"available",tags:["retail","flow"]},{id:"ret_table_turnover",name:"Table turnover",category:"ret",crate:"wifi-densepose-wasm-edge",summary:"Restaurant table seat / vacate transitions.",budget:"S",status:"available",tags:["retail","restaurant"]},{id:"ret_shelf_engagement",name:"Shelf engagement",category:"ret",crate:"wifi-densepose-wasm-edge",summary:"Reach-to-shelf gestures and dwell at product zones.",budget:"M",status:"available",tags:["retail","shelf"]},{id:"ind_forklift_proximity",name:"Forklift proximity",category:"ind",crate:"wifi-densepose-wasm-edge",summary:"Worker-near-forklift safety alert.",budget:"S",status:"available",tags:["industrial","safety"]},{id:"ind_confined_space",name:"Confined-space monitor",category:"ind",crate:"wifi-densepose-wasm-edge",summary:"Last-person-out detection + presence audit for OSHA confined-space entries.",budget:"S",status:"available",tags:["industrial","osha"]},{id:"ind_clean_room",name:"Clean-room PPE / motion",category:"ind",crate:"wifi-densepose-wasm-edge",summary:"Motion patterns consistent with proper PPE-clad movement.",budget:"M",status:"beta",tags:["industrial","cleanroom"]},{id:"ind_livestock_monitor",name:"Livestock monitor",category:"ind",crate:"wifi-densepose-wasm-edge",summary:"Vital-sign + activity tracking for stall-bound livestock.",budget:"M",status:"beta",tags:["agriculture","livestock"]},{id:"ind_structural_vibration",name:"Structural vibration",category:"ind",crate:"wifi-densepose-wasm-edge",summary:"Building/equipment micro-vibration via CSI phase derivative.",budget:"M",status:"research",tags:["industrial","vibration"]},{id:"sig_coherence_gate",name:"Coherence gate (extended)",category:"sig",crate:"wifi-densepose-wasm-edge",summary:"Hysteresis + multi-state coherence gate driving downstream apps.",budget:"S",status:"available",tags:["gate","csi"]},{id:"sig_flash_attention",name:"Flash attention (CSI)",category:"sig",crate:"wifi-densepose-wasm-edge",summary:"Edge-friendly attention block for CSI subcarrier weighting.",budget:"M",status:"beta",tags:["attention","csi"]},{id:"sig_temporal_compress",name:"Temporal-tensor compress",category:"sig",crate:"wifi-densepose-wasm-edge",summary:"RuVector temporal-tensor compression on the CSI buffer.",budget:"M",status:"available",tags:["compress","tensor"]},{id:"sig_sparse_recovery",name:"Sparse recovery",category:"sig",crate:"wifi-densepose-wasm-edge",summary:"114→56 subcarrier sparse interpolation via L1 solver.",budget:"M",status:"available",tags:["sparse","csi"]},{id:"sig_mincut_person_match",name:"Mincut person-match",category:"sig",crate:"wifi-densepose-wasm-edge",summary:"Min-cut person assignment across multistatic frames.",budget:"M",status:"available",tags:["mincut","matching"]},{id:"sig_optimal_transport",name:"Optimal transport",category:"sig",crate:"wifi-densepose-wasm-edge",summary:"OT-based feature alignment between mesh nodes.",budget:"M",status:"beta",tags:["ot","alignment"]},{id:"lrn_dtw_gesture_learn",name:"DTW gesture learn",category:"lrn",crate:"wifi-densepose-wasm-edge",summary:"On-device template learning for personalized gesture libraries.",budget:"M",status:"beta",tags:["lifelong","gesture"]},{id:"lrn_anomaly_attractor",name:"Anomaly attractor",category:"lrn",crate:"wifi-densepose-wasm-edge",summary:"Novelty detector with dynamic-attractor recall.",budget:"M",status:"research",tags:["novelty","lifelong"]},{id:"lrn_meta_adapt",name:"Meta-adapt",category:"lrn",crate:"wifi-densepose-wasm-edge",summary:"Meta-learning adapter for fast site-to-site transfer.",budget:"L",status:"research",tags:["meta-learning"]},{id:"lrn_ewc_lifelong",name:"EWC++ lifelong",category:"lrn",crate:"wifi-densepose-wasm-edge",summary:"Elastic-weight-consolidation gate to avoid catastrophic forgetting.",budget:"M",status:"beta",tags:["lifelong","ewc"]},{id:"spt_pagerank_influence",name:"PageRank influence",category:"spt",crate:"wifi-densepose-wasm-edge",summary:"Graph-influence ranking on the multistatic mesh.",budget:"M",status:"beta",tags:["graph","pagerank"]},{id:"spt_micro_hnsw",name:"µHNSW vector index",category:"spt",crate:"wifi-densepose-wasm-edge",summary:"Tiny HNSW index for AETHER re-ID embeddings on-device.",budget:"M",status:"available",tags:["hnsw","reid"]},{id:"spt_spiking_tracker",name:"Spiking tracker",category:"spt",crate:"wifi-densepose-wasm-edge",summary:"Spiking-network multi-target tracker.",budget:"L",status:"research",tags:["snn","tracker"]},{id:"tmp_pattern_sequence",name:"Pattern sequence",category:"tmp",crate:"wifi-densepose-wasm-edge",summary:"Sequence-of-events pattern matcher (e.g. ingress→linger→egress).",budget:"M",status:"available",tags:["temporal","pattern"]},{id:"tmp_temporal_logic_guard",name:"Temporal logic guard",category:"tmp",crate:"wifi-densepose-wasm-edge",summary:"LTL/MTL safety-property guard over event streams.",budget:"M",status:"beta",tags:["ltl","safety"]},{id:"tmp_goap_autonomy",name:"GOAP autonomy",category:"tmp",crate:"wifi-densepose-wasm-edge",summary:"Goal-oriented action planning for adaptive routines.",budget:"L",status:"research",tags:["planning","autonomy"]},{id:"ais_prompt_shield",name:"Prompt shield",category:"ais",crate:"wifi-densepose-wasm-edge",summary:"Edge-side LLM prompt-injection guard for on-device assistants.",budget:"M",status:"beta",tags:["security","llm"]},{id:"ais_behavioral_profiler",name:"Behavioral profiler",category:"ais",crate:"wifi-densepose-wasm-edge",summary:"Anomalous-behaviour profiler (drift in motion habits).",budget:"M",status:"beta",tags:["anomaly","behaviour"]},{id:"qnt_quantum_coherence",name:"Quantum coherence",category:"qnt",crate:"wifi-densepose-wasm-edge",summary:"Coherence diagnostics adapted for quantum-sensor signals.",budget:"M",status:"research",tags:["quantum","coherence"]},{id:"qnt_interference_search",name:"Interference search",category:"qnt",crate:"wifi-densepose-wasm-edge",summary:"Interferometric anomaly search across mesh viewpoints.",budget:"L",status:"research",tags:["quantum","interference"]},{id:"aut_psycho_symbolic",name:"Psycho-symbolic agent",category:"aut",crate:"wifi-densepose-wasm-edge",summary:"Symbolic-rule + neural-feature hybrid for low-power autonomy loops.",budget:"L",status:"research",tags:["autonomy","symbolic"]},{id:"aut_self_healing_mesh",name:"Self-healing mesh",category:"aut",crate:"wifi-densepose-wasm-edge",summary:"Mesh-topology repair with per-node health gossip.",budget:"M",status:"beta",tags:["mesh","health"]},{id:"exo_ghost_hunter",name:"Ghost hunter (anomaly)",category:"exo",crate:"wifi-densepose-wasm-edge",summary:"Empty-room CSI anomaly detector — impulsive/periodic/drift/random + hidden-presence sub-detector.",events:[650,651,652,653],budget:"S",status:"available",tags:["anomaly","paranormal","csi"],adr:"ADR-041"},{id:"exo_breathing_sync",name:"Breathing sync",category:"exo",crate:"wifi-densepose-wasm-edge",summary:"Multi-person breathing synchrony analytics.",budget:"M",status:"beta",tags:["breathing","sync"]},{id:"exo_dream_stage",name:"Dream-stage classifier",category:"exo",crate:"wifi-densepose-wasm-edge",summary:"NREM/REM stage classification from breathing + micro-motion.",budget:"M",status:"research",tags:["sleep","rem"]},{id:"exo_emotion_detect",name:"Emotion detector",category:"exo",crate:"wifi-densepose-wasm-edge",summary:"Coarse arousal/valence from breathing + heart-rate variability.",budget:"M",status:"research",tags:["affect"]},{id:"exo_gesture_language",name:"Gesture language",category:"exo",crate:"wifi-densepose-wasm-edge",summary:"Sign-language pattern recognition.",budget:"L",status:"research",tags:["hci","sign"]},{id:"exo_happiness_score",name:"Happiness score",category:"exo",crate:"wifi-densepose-wasm-edge",summary:"Aggregate well-being score from co-occupancy + activity dynamics.",budget:"M",status:"research",tags:["affect","wellbeing"]},{id:"exo_hyperbolic_space",name:"Hyperbolic space embed",category:"exo",crate:"wifi-densepose-wasm-edge",summary:"Hyperbolic embeddings for hierarchical scene structure.",budget:"L",status:"research",tags:["embedding","hyperbolic"]},{id:"exo_music_conductor",name:"Music conductor",category:"exo",crate:"wifi-densepose-wasm-edge",summary:"Map gesture energy to MIDI tempo/dynamics.",budget:"M",status:"research",tags:["midi","art"]},{id:"exo_plant_growth",name:"Plant-growth tracker",category:"exo",crate:"wifi-densepose-wasm-edge",summary:"Slow CSI drift tracking for greenhouse foliage growth.",budget:"L",status:"research",tags:["agriculture"]},{id:"exo_rain_detect",name:"Rain detector",category:"exo",crate:"wifi-densepose-wasm-edge",summary:"Outdoor CSI signature of rainfall.",budget:"M",status:"research",tags:["weather"]},{id:"exo_time_crystal",name:"Time-crystal periodicity",category:"exo",crate:"wifi-densepose-wasm-edge",summary:"Periodicity diagnostics with anti-aliasing harmonics.",budget:"M",status:"research",tags:["periodicity"]}],oe={sim:{label:"Simulators",color:"oklch(0.78 0.14 70)",range:"—"},med:{label:"Medical & Health",color:"oklch(0.65 0.22 25)",range:"100–199"},sec:{label:"Security & Safety",color:"oklch(0.7 0.18 35)",range:"200–299"},bld:{label:"Smart Building",color:"oklch(0.78 0.12 195)",range:"300–399"},ret:{label:"Retail & Hospitality",color:"oklch(0.78 0.14 145)",range:"400–499"},ind:{label:"Industrial",color:"oklch(0.72 0.18 330)",range:"500–599"},sig:{label:"Signal Processing",color:"oklch(0.78 0.14 70)",range:"600–619"},lrn:{label:"Online Learning",color:"oklch(0.78 0.12 260)",range:"620–639"},spt:{label:"Spatial / Graph",color:"oklch(0.7 0.18 100)",range:"640–659"},tmp:{label:"Temporal / Planning",color:"oklch(0.7 0.16 50)",range:"660–679"},ais:{label:"AI Safety",color:"oklch(0.65 0.22 25)",range:"700–719"},qnt:{label:"Quantum",color:"oklch(0.72 0.18 290)",range:"720–739"},aut:{label:"Autonomy",color:"oklch(0.78 0.14 145)",range:"740–759"},exo:{label:"Exotic / Research",color:"oklch(0.72 0.18 330)",range:"650–699"}};function Et(){return ce.map(e=>({id:e.id,active:e.active===!0,eventCount:0}))}function Dt(e,t){if(!e)return 1;const a=e.toLowerCase();let i=0;return t.id.toLowerCase().includes(a)&&(i+=3),t.name.toLowerCase().includes(a)&&(i+=3),t.summary.toLowerCase().includes(a)&&(i+=1),t.tags?.some(s=>s.toLowerCase().includes(a))&&(i+=2),t.category===a&&(i+=5),i}const Ft="nvsim",Ot=1,te="kv";let we=null;function Ze(){return we||(we=new Promise((e,t)=>{const a=indexedDB.open(Ft,Ot);a.onupgradeneeded=()=>{const i=a.result;i.objectStoreNames.contains(te)||i.createObjectStore(te)},a.onsuccess=()=>e(a.result),a.onerror=()=>t(a.error)}),we)}async function H(e){const t=await Ze();return await new Promise((a,i)=>{const r=t.transaction(te,"readonly").objectStore(te).get(e);r.onsuccess=()=>a(r.result),r.onerror=()=>i(r.error)})}async function I(e,t){const a=await Ze();return await new Promise((i,s)=>{const r=a.transaction(te,"readwrite");r.objectStore(te).put(t,e),r.oncomplete=()=>i(),r.onerror=()=>s(r.error)})}var Ht=Object.defineProperty,It=Object.getOwnPropertyDescriptor,et=(e,t,a,i)=>{for(var s=i>1?void 0:i?It(t,a):t,r=e.length-1,o;r>=0;r--)(o=e[r])&&(s=(i?o(t,a,s):o(s))||s);return i&&s&&Ht(t,a,s),s};const N=p(Et()),ne=p(""),F=p("all"),S=p("all");(async()=>{const e=await H("app-activations");e&&(N.value=e)})();w(()=>{const e=N.value;e.length>0&&I("app-activations",e)});let ze=class extends y{constructor(){super(...arguments),this.renderTick=0}connectedCallback(){super.connectedCallback(),w(()=>{N.value,ne.value,F.value,S.value,this.renderTick++})}isActive(e){return N.value.find(t=>t.id===e)?.active===!0}toggle(e){const t=N.value.map(a=>a.id===e.id?{...a,active:!a.active,lastActivatedAt:Date.now()}:a);N.value=t,c(this.isActive(e.id)?"ok":"info",`app ${e.id} deactivated`)}filtered(){let e=ce;return F.value!=="all"&&(e=e.filter(t=>t.category===F.value)),S.value!=="all"&&(e=e.filter(t=>t.status===S.value)),ne.value.trim()&&(e=e.map(t=>({a:t,s:Dt(ne.value,t)})).filter(t=>t.s>0).sort((t,a)=>a.s-t.s).map(t=>t.a)),e}categoryCounts(){const e={all:ce.length};for(const t of Object.keys(oe))e[t]=0;for(const t of ce)e[t.category]=(e[t.category]??0)+1;return e}render(){const e=this.filtered(),t=this.categoryCounts(),a=N.value.filter(i=>i.active).length;return l`
F.value="all"}>
All${t.all}
${Object.keys(oe).map(i=>l`
F.value=i}>
${oe[i].label}
${t[i]??0}
`)}
S.value="all"}>any
S.value="available"}>available
S.value="beta"}>beta
S.value="research"}>research
${e.length===0?l`No apps match the current filters.
`:l`${e.map(i=>this.card(i))}
`}
`}card(e){const t=this.isActive(e.id),a=oe[e.category];return l`
${e.name}
${e.summary}
${a.label}
${e.status}
${e.budget?l`budget ${e.budget} `:""}
${e.adr?l`${e.adr} `:""}
${e.events?.length?l`events ${e.events.join("·")} `:""}
`}};ze.styles=f`
:host {
display: block;
height: 100%;
overflow-y: auto;
background: radial-gradient(ellipse at 50% 30%, var(--bg-2) 0%, var(--bg-0) 70%);
padding: 24px;
}
.head {
display: flex; align-items: center; gap: 16px;
margin-bottom: 18px;
flex-wrap: wrap;
}
.ttl {
font-size: 22px; font-weight: 700; letter-spacing: -0.02em;
color: var(--ink);
flex: 1; min-width: 200px;
}
.ttl small {
font-size: 12.5px; font-weight: 400;
color: var(--ink-3); margin-left: 8px;
}
.search {
width: 320px; max-width: 100%;
padding: 8px 12px;
background: var(--bg-2);
border: 1px solid var(--line);
border-radius: 8px;
font-family: var(--mono);
font-size: 12.5px;
color: var(--ink); outline: none;
}
.search:focus { border-color: var(--accent); }
.filters {
display: flex; flex-wrap: wrap; gap: 6px;
margin-bottom: 18px;
}
.chip {
padding: 4px 10px;
background: var(--bg-2);
border: 1px solid var(--line);
border-radius: 999px;
font-size: 11.5px; color: var(--ink-3);
cursor: pointer;
font-family: var(--mono);
display: inline-flex; align-items: center; gap: 4px;
}
.chip:hover { color: var(--ink); border-color: var(--line-2); }
.chip.on { background: var(--bg-3); border-color: var(--accent); color: var(--ink); }
.chip .swatch {
width: 7px; height: 7px; border-radius: 50%;
}
.chip .count { color: var(--ink-3); font-size: 10px; }
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 12px;
}
.card {
background: var(--bg-2);
border: 1px solid var(--line);
border-radius: var(--radius);
padding: 12px 14px;
display: flex; flex-direction: column; gap: 6px;
transition: border-color 0.15s, transform 0.15s;
position: relative;
}
.card:hover { border-color: var(--line-2); transform: translateY(-1px); }
.card.active {
border-color: oklch(0.78 0.14 145 / 0.7);
background: linear-gradient(180deg, var(--bg-2) 0%, oklch(0.78 0.14 145 / 0.04) 100%);
}
.card-h {
display: flex; align-items: flex-start; gap: 8px;
margin-bottom: 2px;
}
.card-h .name {
font-size: 13.5px; font-weight: 600; color: var(--ink);
flex: 1; line-height: 1.3;
}
.card-h .swatch {
width: 10px; height: 10px; border-radius: 50%;
flex-shrink: 0; margin-top: 4px;
}
.summary {
font-size: 12px; color: var(--ink-2); line-height: 1.45;
flex: 1;
}
.meta {
display: flex; flex-wrap: wrap; gap: 4px; margin-top: 6px;
font-family: var(--mono); font-size: 10px;
}
.badge {
padding: 1px 6px; border-radius: 4px;
background: var(--bg-3); color: var(--ink-3);
border: 1px solid var(--line);
}
.badge.cat { color: var(--accent); border-color: oklch(0.78 0.14 70 / 0.3); }
.badge.status-available { color: var(--ok); border-color: oklch(0.78 0.14 145 / 0.4); }
.badge.status-beta { color: var(--warn); border-color: oklch(0.7 0.18 35 / 0.4); }
.badge.status-research { color: var(--accent-3); border-color: oklch(0.72 0.18 330 / 0.4); }
.badge.budget { color: var(--accent-2); border-color: oklch(0.78 0.12 195 / 0.3); }
.card-foot {
display: flex; align-items: center; gap: 8px;
padding-top: 8px; margin-top: 4px;
border-top: 1px solid var(--line);
font-size: 11px; color: var(--ink-3);
}
.toggle {
position: relative;
width: 32px; height: 18px;
background: var(--bg-3); border: 1px solid var(--line-2);
border-radius: 999px; cursor: pointer;
transition: background 0.15s;
flex-shrink: 0;
}
.toggle::after {
content: ''; position: absolute;
top: 1px; left: 1px;
width: 12px; height: 12px;
background: var(--ink-3); border-radius: 50%;
transition: transform 0.15s, background 0.15s;
}
.toggle.on { background: var(--accent); border-color: var(--accent); }
.toggle.on::after { background: #1a0f00; transform: translateX(14px); }
.events {
font-family: var(--mono); font-size: 10px; color: var(--ink-3);
flex: 1;
}
.empty {
padding: 40px;
text-align: center; color: var(--ink-3);
font-size: 13px;
}
`;et([u()],ze.prototype,"renderTick",2);ze=et([x("nv-app-store")],ze);var Nt=Object.defineProperty,Wt=Object.getOwnPropertyDescriptor,fe=(e,t,a,i)=>{for(var s=i>1?void 0:i?Wt(t,a):t,r=e.length-1,o;r>=0;r--)(o=e[r])&&(s=(i?o(t,a,s):o(s))||s);return i&&s&&Nt(t,a,s),s};let U=class extends y{constructor(){super(...arguments),this.open=!1,this.filter="",this.idx=0,this.cmds=[{ico:"▶",label:"Run pipeline",kbd:"Space",run:async()=>{await b()?.run(),h.value=!0,C("Pipeline running","▶")}},{ico:"❚",label:"Pause pipeline",run:async()=>{await b()?.pause(),h.value=!1,C("Paused","❚❚")}},{ico:"+",label:"New scene…",kbd:"⌘N",run:()=>de({title:"New scene",body:`Build a fresh magnetic scene. The dashboard generates the JSON
and pushes it to the running pipeline (or you can copy the JSON
for offline use).
Name
Heart-proxy dipole moment (A·m²)
Distance heart → sensor (m)
Add ferrous distractor at +x = 1 m?
No
Yes (steel coil, χ=5000)
Add 60 Hz mains-current loop?
No
Yes (2 A loop, 5 cm radius, +y = 1 m)
`,buttons:[{label:"Cancel",variant:"ghost"},{label:"Create",variant:"primary",onClick:async()=>{const e=document.querySelector("nv-app")?.shadowRoot?.querySelector("nv-modal")?.shadowRoot;if(!e)return;const t=(e.querySelector("#ns-name")?.value??"custom").trim(),a=parseFloat(e.querySelector("#ns-moment")?.value??"1e-6"),i=parseFloat(e.querySelector("#ns-distance")?.value??"0.5"),s=e.querySelector("#ns-ferrous")?.value==="1",r=e.querySelector("#ns-mains")?.value==="1",o={dipoles:[{position:[0,0,i],moment:[0,0,a]}],loops:r?[{centre:[0,1,0],normal:[0,1,0],radius:.05,current:2,n_segments:64}]:[],ferrous:s?[{position:[1,0,0],volume:1e-4,susceptibility:5e3}]:[],eddy:[],sensors:[[0,0,0]],ambient_field:[1e-6,0,0]};await b()?.loadScene(o),c("ok",`scene ${t} loaded · 1 dipole · ${r?"1 loop · ":""}${s?"1 ferrous · ":""}1 sensor`),C(`Scene "${t}" loaded`,"+")}}]})},{ico:"📦",label:"Export proof bundle…",kbd:"⌘E",run:async()=>{const e=b();if(e){c("dbg","building proof bundle…");try{const t=await e.exportProofBundle(),a=URL.createObjectURL(t),i=document.createElement("a");i.href=a,i.download=`nvsim-proof-${Date.now()}.json`,i.click(),URL.revokeObjectURL(a),c("ok",`proof bundle exported · ${t.size} bytes`),C(`Proof bundle saved (${t.size} B)`,"📦")}catch(t){c("err",`export failed: ${t.message}`)}}}},{ico:"⟳",label:"Reset pipeline",kbd:"⌘R",run:()=>de({title:"Reset pipeline?",body:"Clears the frame stream and rewinds t to 0.
",buttons:[{label:"Cancel",variant:"ghost"},{label:"Reset",variant:"danger",onClick:async()=>{await b()?.reset(),c("warn","pipeline reset · t=0"),C("Pipeline reset","⟳")}}]})},{ico:"✓",label:"Verify witness",run:async()=>{const e=b();if(!e)return;$.value="pending";const t=R.value,a=new Uint8Array(32);for(let s=0;s<32;s++)a[s]=parseInt(t.slice(s*2,s*2+2),16);(await e.verifyWitness(a)).ok?($.value="ok",z.value=t,C("Witness verified","✓")):($.value="fail",C("Witness mismatch!","✗"))}},{ico:"☼",label:"Toggle theme",kbd:"⌘/",run:()=>{k.value=k.value==="dark"?"light":"dark"}},{ico:"⚙",label:"Open settings",kbd:"⌘,",run:()=>window.dispatchEvent(new CustomEvent("open-settings"))},{ico:"?",label:"Keyboard shortcuts…",run:()=>de({title:"Keyboard shortcuts",body:`
⌘K / Ctrl K
Command palette
Space
Play / pause
⌘R
Reset
⌘,
Settings
⌘/
Toggle theme
\`
Debug HUD
1 · 2 · 3
Inspector tabs
Esc
Close modal/palette
/
Focus REPL
`,buttons:[{label:"Close",variant:"primary"}]})},{ico:"i",label:"About nvsim…",run:()=>de({title:"About nvsim",body:`nvsim is a deterministic, byte-reproducible forward simulator for nitrogen-vacancy diamond magnetometry.
This dashboard runs nvsim as WASM in a Web Worker. Same (scene, config, seed) → byte-identical SHA-256 witness across runs and machines.
License: MIT OR Apache-2.0 · See ADR-089, ADR-092.
`,buttons:[{label:"Close",variant:"primary"}]})}],this.onKey=e=>{(e.metaKey||e.ctrlKey)&&e.key.toLowerCase()==="k"?(e.preventDefault(),this.openPal()):e.key==="Escape"&&this.open?this.closePal():this.open&&(e.key==="ArrowDown"?(this.idx=Math.min(this.cmds.length-1,this.idx+1),e.preventDefault()):e.key==="ArrowUp"?(this.idx=Math.max(0,this.idx-1),e.preventDefault()):e.key==="Enter"&&(this.runIdx(),e.preventDefault()))},this.onOpen=()=>this.openPal()}connectedCallback(){super.connectedCallback(),window.addEventListener("keydown",this.onKey),window.addEventListener("nv-palette",this.onOpen)}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("keydown",this.onKey),window.removeEventListener("nv-palette",this.onOpen)}openPal(){this.open=!0,this.setAttribute("open",""),this.filter="",this.idx=0,setTimeout(()=>this.inputEl?.focus(),0)}closePal(){this.open=!1,this.removeAttribute("open")}filtered(){if(!this.filter.trim())return this.cmds;const e=this.filter.toLowerCase();return this.cmds.filter(t=>t.label.toLowerCase().includes(e))}runIdx(){const t=this.filtered()[this.idx];t&&(t.run(),this.closePal())}render(){const e=this.filtered();return l`
{this.filter=t.target.value,this.idx=0}} />
${e.map((t,a)=>l`
{this.idx=a,this.runIdx()}}>
${t.ico}
${t.label}
${t.kbd?l`${t.kbd} `:""}
`)}
`}};U.styles=f`
:host {
position: fixed; inset: 0; z-index: 220;
background: rgba(0,0,0,0.5);
opacity: 0; pointer-events: none;
transition: opacity 0.15s;
display: flex; justify-content: center; padding-top: 12vh;
backdrop-filter: blur(4px);
}
:host([open]) { opacity: 1; pointer-events: auto; }
.palette {
width: min(560px, 92vw);
background: var(--bg-1);
border: 1px solid var(--line-2);
border-radius: var(--radius);
box-shadow: 0 30px 80px -20px rgba(0,0,0,0.7);
overflow: hidden;
display: flex; flex-direction: column;
max-height: 60vh;
}
.input {
padding: 14px 16px;
border-bottom: 1px solid var(--line);
}
input {
width: 100%;
background: transparent; border: none; outline: none;
color: var(--ink); font-size: 14px;
font-family: inherit;
}
.list { flex: 1; overflow-y: auto; padding: 4px; }
.item {
display: flex; align-items: center; gap: 10px;
padding: 8px 12px;
border-radius: 6px;
cursor: pointer;
font-size: 12.5px;
}
.item.active { background: var(--bg-3); }
.item .ico { width: 20px; text-align: center; color: var(--accent); }
.item .lbl { flex: 1; }
.item .kbd {
font-family: var(--mono); font-size: 10.5px;
color: var(--ink-3);
padding: 1px 5px; background: var(--bg-3); border-radius: 4px;
}
`;fe([u()],U.prototype,"open",2);fe([u()],U.prototype,"filter",2);fe([u()],U.prototype,"idx",2);fe([Ye("#palette-input")],U.prototype,"inputEl",2);U=fe([x("nv-palette")],U);var Lt=Object.defineProperty,Bt=Object.getOwnPropertyDescriptor,Le=(e,t,a,i)=>{for(var s=i>1?void 0:i?Bt(t,a):t,r=e.length-1,o;r>=0;r--)(o=e[r])&&(s=(i?o(t,a,s):o(s))||s);return i&&s&&Lt(t,a,s),s};let me=class extends y{constructor(){super(...arguments),this.open=!1,this.renderFps=0,this.lastTs=performance.now(),this.frameCount=0,this.rafId=0,this.onKey=e=>{e.key==="`"&&!e.target.matches("input, textarea")&&(this.open=!this.open,this.toggleAttribute("open",this.open))},this.tick=()=>{this.rafId=requestAnimationFrame(this.tick);const e=performance.now();this.frameCount++,e-this.lastTs>=500&&(this.renderFps=this.frameCount*1e3/(e-this.lastTs),this.frameCount=0,this.lastTs=e,this.requestUpdate())}}connectedCallback(){super.connectedCallback(),window.addEventListener("keydown",this.onKey),w(()=>{M.value,Ie.value,V.value,L.value,ut.value,this.requestUpdate()}),this.tick()}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("keydown",this.onKey),cancelAnimationFrame(this.rafId)}render(){return l`
nvsim · debug {this.open=!1,this.removeAttribute("open")}}>✕
render fps ${this.renderFps.toFixed(1)}
sim fps ${M.value>0?Math.round(M.value):"—"}
frames ${Ie.value.toString()}
|B| ${(V.value*1e9).toFixed(3)} nT
SNR ${L.value>0?L.value.toFixed(1):"—"}
DOM ${document.querySelectorAll("*").length}
`}};me.styles=f`
:host {
position: fixed; bottom: 8px; right: 8px;
width: 220px;
background: rgba(13,17,23,0.85);
backdrop-filter: blur(8px);
border: 1px solid var(--line-2);
border-radius: 8px;
padding: 8px 10px;
font-family: var(--mono); font-size: 11px;
color: var(--ink-2);
z-index: 99;
display: none;
box-shadow: var(--shadow);
}
:host([open]) { display: block; }
.h {
display: flex; justify-content: space-between;
font-weight: 600; color: var(--ink);
margin-bottom: 6px; padding-bottom: 4px;
border-bottom: 1px solid var(--line);
}
.x { cursor: pointer; color: var(--ink-3); }
.row {
display: flex; justify-content: space-between;
padding: 1px 0;
}
.k { color: var(--ink-3); }
.v { color: var(--ink); }
`;Le([u()],me.prototype,"open",2);Le([u()],me.prototype,"renderFps",2);me=Le([x("nv-debug-hud")],me);var qt=Object.defineProperty,Vt=Object.getOwnPropertyDescriptor,tt=(e,t,a,i)=>{for(var s=i>1?void 0:i?Vt(t,a):t,r=e.length-1,o;r>=0;r--)(o=e[r])&&(s=(i?o(t,a,s):o(s))||s);return i&&s&&qt(t,a,s),s};let Te=class extends y{constructor(){super(...arguments),this.open=!1}connectedCallback(){super.connectedCallback(),w(()=>{k.value,_.value,T.value,re.value,A.value,Fe.value,this.requestUpdate()}),window.addEventListener("open-settings",()=>{this.open=!0,this.setAttribute("open","")})}close(){this.open=!1,this.removeAttribute("open")}async resetPrefs(){if(confirm("Reset all preferences and IndexedDB state? Reloads the page.")){try{const e=await indexedDB.databases?.();if(e)for(const t of e)t.name&&indexedDB.deleteDatabase(t.name)}catch{}location.reload()}}render(){return l`
this.close()}>
Appearance
Theme
Dark is the default; light has higher contrast for daylight work.
k.value="dark"}>dark
k.value="light"}>light
Density
Affects panel padding and font scale (15 / 14 / 13 px). Choose what your eyes prefer.
_.value="comfy"}>comfy
_.value="default"}>default
_.value="compact"}>compact
Reduce motion
Stops the rotating diamond, animated field lines, and chart easing. Auto-on if your system has the prefers-reduced-motion preference set.
T.value=!T.value}>
Pipeline
Auto-rerun on edit
When you change a Tunables slider or load a new scene, push the change to the worker without a manual restart.
re.value=!re.value}>
Transport
Mode
WASM runs nvsim in your browser (default, no server). WS connects to a host-supplied nvsim-server (REST + binary WebSocket); see ADR-092 §6.2.
A.value="wasm"}>WASM
A.value="ws"}>WS
${A.value==="ws"?l`
WS URL
Where your nvsim-server is listening. The server defaults to 127.0.0.1:7878.
Fe.value=e.target.value} />
`:""}
Help
Open help center
Quickstart, glossary, FAQ, and shortcuts. Press ? any time.
{this.close(),window.dispatchEvent(new CustomEvent("nv-show-help"))}}
style="padding:6px 12px;cursor:pointer;background:var(--bg-3);border:1px solid var(--line);border-radius:6px;color:var(--ink);">
Open
Replay welcome tour
Re-show the 6-step first-run walkthrough.
{this.close(),window.dispatchEvent(new CustomEvent("nv-show-tour"))}}
style="padding:6px 12px;cursor:pointer;background:var(--bg-3);border:1px solid var(--line);border-radius:6px;color:var(--ink);">
Replay
Reset all preferences
Wipe theme, density, motion, scene drag positions, REPL history, and the onboarding-seen flag.
this.resetPrefs()}
style="padding:6px 12px;cursor:pointer;background:var(--bg-3);border:1px solid oklch(0.65 0.22 25 / 0.4);border-radius:6px;color:var(--bad);">
Reset
`}};Te.styles=f`
:host {
position: fixed; top: 0; right: 0; bottom: 0;
width: 420px; max-width: 100vw;
background: var(--bg-1);
border-left: 1px solid var(--line);
z-index: 51;
transform: translateX(100%);
transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);
display: flex; flex-direction: column;
box-shadow: -20px 0 60px -20px rgba(0,0,0,0.5);
}
:host([open]) { transform: translateX(0); }
.scrim {
position: fixed; inset: 0;
background: rgba(0,0,0,0.5);
z-index: 50;
opacity: 0; pointer-events: none;
transition: opacity 0.2s;
}
:host([open]) .scrim { opacity: 1; pointer-events: auto; }
.h {
padding: 14px 16px;
border-bottom: 1px solid var(--line);
display: flex; align-items: center; justify-content: space-between;
}
.h .ttl { font-size: 14px; font-weight: 600; }
.body { flex: 1; overflow-y: auto; padding: 16px; }
.group { margin-bottom: 22px; }
.group h4 {
margin: 0 0 10px;
font-size: 11px; font-weight: 600;
text-transform: uppercase; letter-spacing: 0.08em;
color: var(--ink-3);
}
.row {
display: flex; justify-content: space-between; align-items: center;
padding: 10px 0;
border-bottom: 1px solid var(--line);
}
.row:last-child { border-bottom: 0; }
.row .lbl { font-size: 13px; }
.row .desc { font-size: 11.5px; color: var(--ink-3); margin-top: 2px; }
.row > div:first-child { flex: 1; padding-right: 12px; }
.seg {
display: inline-flex;
background: var(--bg-3);
border: 1px solid var(--line);
border-radius: var(--radius-sm);
padding: 2px;
}
.seg button {
padding: 4px 10px;
background: transparent; border: none;
border-radius: 6px;
font-size: 11.5px; color: var(--ink-3);
font-family: var(--mono);
cursor: pointer;
}
.seg button.on { background: var(--bg-1); color: var(--ink); }
.toggle {
position: relative;
width: 36px; height: 20px;
background: var(--bg-3);
border: 1px solid var(--line-2);
border-radius: 999px;
cursor: pointer;
flex-shrink: 0;
}
.toggle::after {
content: ''; position: absolute;
top: 2px; left: 2px;
width: 14px; height: 14px;
background: var(--ink-3);
border-radius: 50%;
transition: transform 0.15s, background 0.15s;
}
.toggle.on { background: var(--accent); border-color: var(--accent); }
.toggle.on::after { background: #1a0f00; transform: translateX(16px); }
.close {
width: 28px; height: 28px;
background: transparent; border: 1px solid var(--line);
border-radius: 6px;
color: var(--ink-2);
}
input[type="text"] {
background: var(--bg-3);
border: 1px solid var(--line);
border-radius: 6px;
padding: 6px 10px;
color: var(--ink); font-family: var(--mono); font-size: 12px;
outline: none;
}
`;tt([u()],Te.prototype,"open",2);Te=tt([x("nv-settings-drawer")],Te);var jt=Object.defineProperty,Ut=Object.getOwnPropertyDescriptor,Be=(e,t,a,i)=>{for(var s=i>1?void 0:i?Ut(t,a):t,r=e.length-1,o;r>=0;r--)(o=e[r])&&(s=(i?o(t,a,s):o(s))||s);return i&&s&&jt(t,a,s),s};const O=[{icon:"👋",title:"Welcome to nvsim",body:`
nvsim is an open-source, deterministic forward simulator for
nitrogen-vacancy diamond magnetometry — a real Rust crate compiled
to WebAssembly and running in your browser, right now.
This 60-second tour walks you through the four panels, the App Store,
the Ghost Murmur research view, and the determinism contract that
makes nvsim distinctive.
Press Esc any time to skip. You can replay this tour from
Settings → Help .
`,cta:{label:"Start the tour →"}},{icon:"🌐",title:"The Scene canvas",body:`The middle panel shows your magnetic scene — a small simulated
environment with four sources and one NV-diamond sensor at the centre.
The four amber/cyan/magenta blobs are draggable: rebar coil
(steel χ=5000), heart proxy dipole, 60 Hz mains current loop,
and a steel door (eddy current). Field lines connect each source
to the sensor and animate while the pipeline runs.
Top-left toolbar: zoom in/out, fit-to-view, layer toggles. Bottom-right:
sim controls (step / play / step / speed cycle). Drag positions persist
across reloads.
`,hint:"Try dragging the heart_proxy after the tour ends."},{icon:"▶",title:"Run the pipeline",body:`Press ▶ Run in the topbar (or hit Space ) to start
the live frame stream. nvsim runs at ~1.8 kHz on x86_64 WASM —
well above the 1 kHz Cortex-A53 acceptance gate.
The FPS pill in the topbar updates with the throughput. The B-vector
trace and frame-stream sparkline in the right inspector update in real
time.
Space toggles run/pause from anywhere. Reset (⌘R )
rewinds t to 0 without changing the seed.
`},{icon:"🔍",title:"Inspector — three tabs, three depths",body:`The right rail shows the live inspector: Signal (B-vector
trace + frame-stream sparkline), Frame (decoded MagFrame fields +
raw 60-byte hex dump), Witness (SHA-256 determinism gate).
Click the magnifier icon in the left rail to expand the
inspector to the full main area, with bigger charts and an explainer
header. Click the shield icon to do the same focused on Witness.
Number keys 1 2 3 jump between the
three inspector tabs from anywhere.
`},{icon:"✓",title:"The witness — what makes nvsim distinctive",body:`nvsim's defining commitment: same (scene, config, seed) →
byte-identical SHA-256 across runs, machines, and transports.
Click the Witness tab and press Verify witness . The
dashboard re-derives the hash for the canonical reference scene
(seed=42, N=256) and asserts it matches the constant
pinned at compile time
(cc8de9b01b0ff5bd…).
A green check means every constant — γ_e, D_GS, μ₀, T₂*, contrast,
the PRNG stream, the frame layout — is byte-identical to the published
reference. A red ✗ means something drifted; the dashboard names which.
`},{icon:"🎚",title:"Tunables — change the simulation live",body:`The left sidebar's Tunables panel has four sliders:
Sample rate (1–100 kHz) — digitiser frame rate
Lock-in f_mod (0.1–5 kHz) — microwave modulation freq
Integration t (0.1–10 ms) — per-sample integration time
Shot noise (on/off) — toggle quantum noise
Edits debounce 300 ms then rebuild the WASM pipeline without restarting
the frame stream. Watch the noise floor and B-vector spread change
in the Signal trace.
`},{icon:"👻",title:"Ghost Murmur — research view",body:`Click the ghost icon in the left rail. This view audits the
publicly-reported April 2026 CIA Ghost Murmur NV-diamond
heartbeat-detection program against the open physics literature.
Includes a "Try it yourself" sandbox: place a cardiac dipole at
any distance from the sensor, hit Run, and see what the real nvsim
pipeline recovers. Per-tier detectability bars compare the predicted
signal vs each transport's noise floor (NV-ensemble lab, COTS DNV-B1,
SQUID, 60 GHz mmWave, WiFi CSI).
Spoiler: at 1 km the cardiac MCG is ~10⁻¹² of its 10 cm value.
Press claims of 40-mile detection sit far below any published instrument's
floor.
`},{icon:"🛍",title:"App Store — 65 edge apps",body:`Click the grid icon. The App Store catalogues every
hot-loadable WASM edge module RuView ships, organised by category:
medical, security, smart-building, retail, industrial, signal,
learning, autonomy, exotic.
Each card carries id / category / status / event IDs / compute budget /
ADR back-reference. The toggle marks an app active in this session;
the WS transport (when configured) pushes the activation set to a
connected ESP32 mesh.
Try searching for "ghost", "heart", or "occupancy" to fuzzy-filter
the catalogue.
`},{icon:"⌨",title:"Console + REPL",body:`The bottom panel is a structured event log with five filter tabs
(all / info / warn / err / dbg ) plus a REPL prompt.
REPL commands include
help, scene.list, sensor.config,
run, pause, seed [hex],
proof.verify, proof.export,
theme [light|dark], status, clear.
Press / to focus the REPL from anywhere. Arrow ↑/↓ recall
history (persisted across reloads). ⌘K opens the command
palette with every action discoverable.
`},{icon:"🚀",title:"You are ready",body:`That's the whole tour. A few last pointers:
Press ? any time to open the help center
(Quickstart / Glossary / FAQ / Shortcuts / About).
Press ⌘K for the command palette.
Press \` to toggle the debug HUD.
Settings (⌘, ) lets you switch theme, density, motion,
transport, and replay this tour.
Source: github.com/ruvnet/RuView · Apache-2.0 OR MIT ·
ADRs 089/090/091/092/093.
`,cta:{label:"Get started →"}}];let ge=class extends y{constructor(){super(...arguments),this.open=!1,this.step=0,this.show=()=>{this.step=0,this.open=!0,this.setAttribute("open","")}}async connectedCallback(){super.connectedCallback(),window.addEventListener("nv-show-tour",this.show),await H("onboarding-seen")||(this.open=!0,this.setAttribute("open",""))}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("nv-show-tour",this.show)}async dismiss(){this.open=!1,this.removeAttribute("open"),await I("onboarding-seen",!0)}next(){O[this.step].cta?.run?.(),this.step0&&this.step--}render(){const e=O[this.step],t=this.step===O.length-1;return l`
${e.icon}
${e.title}
Step ${this.step+1} of ${O.length}
this.dismiss()} aria-label="Skip tour" title="Skip tour">×
${e.hint?l`
${e.hint}
`:""}
`}};ge.styles=f`
:host {
position: fixed; inset: 0;
background: rgba(0, 0, 0, 0.55);
backdrop-filter: blur(4px);
z-index: 240;
display: grid; place-items: center;
opacity: 0; pointer-events: none;
transition: opacity 0.18s;
}
:host([open]) { opacity: 1; pointer-events: auto; }
.card {
background: var(--bg-1);
border: 1px solid var(--line-2);
border-radius: var(--radius);
box-shadow: 0 30px 80px -20px rgba(0,0,0,0.7);
width: min(640px, 94vw);
max-height: 86vh;
display: flex; flex-direction: column;
transform: translateY(12px) scale(0.98);
transition: transform 0.22s cubic-bezier(0.2,0.7,0.3,1);
overflow: hidden;
}
:host([open]) .card { transform: translateY(0) scale(1); }
.h {
padding: 22px 26px 12px;
display: flex; align-items: flex-start; gap: 14px;
}
.h .icon {
width: 44px; height: 44px;
border-radius: 12px;
background: linear-gradient(135deg, oklch(0.78 0.14 70) 0%, oklch(0.55 0.16 30) 100%);
display: grid; place-items: center;
font-size: 22px;
flex-shrink: 0;
box-shadow: 0 4px 12px -2px oklch(0.55 0.16 30 / 0.35);
}
.h .title-wrap { flex: 1; min-width: 0; }
.h h2 {
margin: 0;
font-size: 18px;
letter-spacing: -0.01em;
color: var(--ink);
}
.h .step-label {
font-family: var(--mono);
font-size: 10.5px;
color: var(--ink-3);
margin-top: 4px;
text-transform: uppercase;
letter-spacing: 0.06em;
}
.h .skip {
width: 28px; height: 28px;
background: transparent;
border: 1px solid var(--line);
border-radius: 6px;
color: var(--ink-2);
cursor: pointer;
flex-shrink: 0;
}
.h .skip:hover { color: var(--ink); border-color: var(--line-2); }
.body {
padding: 0 26px 16px;
font-size: 13px;
color: var(--ink-2);
line-height: 1.6;
overflow-y: auto;
flex: 1;
}
.body p { margin: 0 0 12px; }
.body p:last-child { margin-bottom: 0; }
.body code, .body kbd {
font-family: var(--mono);
font-size: 11.5px;
padding: 1px 5px;
background: var(--bg-3);
border: 1px solid var(--line);
border-radius: 4px;
}
.body code { color: var(--accent); }
.body kbd { color: var(--ink); }
.hint {
margin: 14px 0 0;
padding: 10px 12px;
background: oklch(0.78 0.12 195 / 0.06);
border: 1px solid oklch(0.78 0.12 195 / 0.25);
border-radius: 8px;
font-size: 12px;
color: var(--accent-2);
display: flex; gap: 8px; align-items: flex-start;
}
.hint::before {
content: '💡';
flex-shrink: 0;
}
.footer {
display: flex; align-items: center; gap: 14px;
padding: 14px 22px;
border-top: 1px solid var(--line);
background: var(--bg-1);
}
.progress { flex: 1; }
.dots { display: flex; gap: 5px; margin-bottom: 4px; }
.dot {
width: 6px; height: 6px; border-radius: 50%;
background: var(--bg-3);
border: 1px solid var(--line-2);
transition: background 0.15s, border-color 0.15s, transform 0.15s;
}
.dot.active {
background: var(--accent);
border-color: var(--accent);
transform: scale(1.2);
}
.dot.done {
background: var(--accent-4);
border-color: var(--accent-4);
}
.progress-label {
font-family: var(--mono);
font-size: 10px;
color: var(--ink-3);
}
button.primary, button.ghost {
padding: 9px 16px;
border-radius: 8px;
font-size: 13px;
font-weight: 500;
cursor: pointer;
font-family: inherit;
border: 1px solid var(--line);
background: var(--bg-2);
color: var(--ink);
}
button.ghost:hover { border-color: var(--line-2); }
button.primary {
background: var(--accent);
border-color: var(--accent);
color: #1a0f00;
}
button.primary:hover { filter: brightness(1.08); }
`;Be([u()],ge.prototype,"open",2);Be([u()],ge.prototype,"step",2);ge=Be([x("nv-onboarding")],ge);var Gt=Object.defineProperty,Kt=Object.getOwnPropertyDescriptor,ie=(e,t,a,i)=>{for(var s=i>1?void 0:i?Kt(t,a):t,r=e.length-1,o;r>=0;r--)(o=e[r])&&(s=(i?o(t,a,s):o(s))||s);return i&&s&&Gt(t,a,s),s};const Yt=[{id:"nvBest",label:"NV-ensemble (best lab)",floorT:1e-12,color:"oklch(0.78 0.14 70)"},{id:"nvCots",label:"NV-DNV-B1 (COTS)",floorT:3e-10,color:"oklch(0.72 0.18 50)"},{id:"squid",label:"SQUID (shielded room)",floorT:1e-15,color:"oklch(0.78 0.12 195)"},{id:"mmw",label:"60 GHz mmWave (μ-Doppler)",floorT:0,color:"oklch(0.78 0.14 145)"},{id:"csi",label:"WiFi CSI (presence)",floorT:0,color:"oklch(0.72 0.18 330)"}];let D=class extends y{constructor(){super(...arguments),this.distanceM=.1,this.momentLog10=-8.3,this.result=null,this.running=!1,this.err=null}predictedDipoleFieldT(e,t){return 4*Math.PI*1e-7*t/(4*Math.PI*Math.pow(Math.max(e,1e-6),3))}async runDemo(){const e=b();if(!e){this.err="WASM client not ready";return}this.err=null,this.running=!0,this.requestUpdate();try{const t=this.distanceM,a=Math.pow(10,this.momentLog10),i={dipoles:[{position:[0,0,t],moment:[0,0,a]}],loops:[],ferrous:[],eddy:[],sensors:[[0,0,0]],ambient_field:[0,0,0]},s={digitiser:{f_s_hz:1e4,f_mod_hz:1e3},sensor:{gamma_fwhm_hz:1e6,t1_s:.005,t2_s:1e-6,t2_star_s:2e-7,contrast:.03,n_spins:1e12,shot_noise_disabled:!1},dt_s:null};this.result=await e.runTransient(i,s,42n,64),c("ok",`ghost-demo · r=${t.toFixed(3)} m · |B| recovered = ${(this.result.bMagT*1e12).toExponential(2)} pT`)}catch(t){this.err=t.message,c("err",`ghost-demo failed: ${this.err}`)}finally{this.running=!1,this.requestUpdate()}}formatField(e){if(e===0)return"0 T";const t=Math.abs(e);return t>=.001?`${(e*1e3).toFixed(2)} mT`:t>=1e-6?`${(e*1e6).toFixed(2)} µT`:t>=1e-9?`${(e*1e9).toFixed(3)} nT`:t>=1e-12?`${(e*1e12).toFixed(2)} pT`:t>=1e-15?`${(e*1e15).toFixed(2)} fT`:t>=1e-18?`${(e*1e18).toFixed(2)} aT`:`${e.toExponential(2)} T`}formatDistance(e){return e<1?`${(e*100).toFixed(1)} cm`:e<1e3?`${e.toFixed(2)} m`:e<1e5?`${(e/1e3).toFixed(2)} km`:`${(e/1609).toFixed(0)} mi`}renderDemo(){const e=Math.pow(10,this.momentLog10),t=this.predictedDipoleFieldT(this.distanceM,e),a=this.result?.bMagT??0,i=(this.result?.noiseFloorPtSqrtHz??0)*1e-12,s=Yt.map(n=>{let d="bad",v="below floor";if(n.id==="mmw")this.distanceM<=5?(d="ok",v="µ-Doppler @ chest"):this.distanceM<=15?(d="warn",v="edge of range"):(d="bad",v="out of range");else if(n.id==="csi")this.distanceM<=30?(d=this.distanceM<=10?"ok":"warn",v="presence/breathing"):(d="bad",v="out of range");else if(n.floorT>0){const g=t/n.floorT;g>100?(d="ok",v=`${g.toExponential(1)}× floor`):g>1?(d="warn",v=`${g.toFixed(1)}× floor`):(d="bad",v=`${(1/g).toExponential(1)}× too weak`)}const m=n.floorT>0?Math.max(2,Math.min(100,100+12*Math.log10(t/n.floorT))):n.id==="mmw"?Math.max(2,100-this.distanceM*7):Math.max(2,100-this.distanceM*2);return l`
`}),r=t>1e-12?"ok":t>1e-15?"warn":"bad",o=r==="ok"?`Above NV-ensemble lab floor — close-range MCG plausible at ${this.formatDistance(this.distanceM)}.`:r==="warn"?`Below NV ensemble best, above SQUID — research-grade only at ${this.formatDistance(this.distanceM)}.`:`Below every published instrument's noise floor at ${this.formatDistance(this.distanceM)}. Press-release physics.`;return l`
Try it yourself
Place a cardiac dipole at variable distance from the NV sensor. The
dashboard runs the real nvsim Rust pipeline (compiled to WASM)
end-to-end and reports what each tier would actually detect. Same
determinism contract as the rest of the dashboard.
Distance from sensor
${this.formatDistance(this.distanceM)}
{this.distanceM=Math.pow(10,+n.target.value)}} />
10 cm → 100 km log scale
Heart dipole moment
${e.toExponential(2)} A·m²
{this.momentLog10=+n.target.value}} />
published cardiac MCG ≈ 5×10⁻⁹ A·m²
this.runDemo()}>
${this.running?"Running nvsim…":"▶ Run nvsim at this distance"}
${this.err?l`
Error: ${this.err}
`:""}
Predicted |B| (1/r³)
${this.formatField(t)}
Recovered |B| (nvsim)
${this.result?this.formatField(a):"—"}
Sensor noise floor
${this.result?this.formatField(i)+"/√Hz":"—"}
Frames run
${this.result?.nFrames??"—"}
Witness (this run)
${this.result?.witnessHex.slice(0,16)??"—"}…
Per-tier detectability
${s}
${o}
The predicted value uses the closed-form magnetic-dipole
far field |B| = μ₀·m / (4π·r³). The recovered
value comes from the same Rust pipeline that drives the Witness panel —
scene → Biot-Savart → NV ensemble → ADC → MagFrame. Use the moment
slider to ask "what if the heart were stronger?". Use the distance
slider to walk through 10 cm (clinical MCG), 1 m (close approach),
10 m (room-scale), 1 km (skeptic's range), and 65 km (the press claim).
`}render(){return l`
Ghost Murmur — open-source reality check
The physics-vs-press audit for the publicly-reported April 2026
CIA NV-diamond heartbeat detector, and how RuView's existing
stack maps onto an honest, civilian version of the same idea.
What the press reported
The story
3 Apr 2026: USAF F-15E pilot "Dude 44 Bravo" goes down in southern Iran during the regional exchange and evades for ~2 days.
President Trump publicly suggests detection from 40 miles away on a mountainside at night; CIA Director Ratcliffe says "invisible to the enemy, but not to the CIA."
The named tech
"Ghost Murmur" — Lockheed Skunk Works system using NV defects in synthetic diamond + AI to extract a heartbeat from environmental noise.
Outlets: Newsweek, Scientific American, Military.com, WION, Open The Magazine, Yahoo, Calcalist + HN thread #47679241.
What physicists said
Wikswo (Vanderbilt), Orzel (Union College), Roth (Oakland) — all pushing back hard.
"At 1 km, the heartbeat field drops to ~10⁻¹² of its 10 cm value." MCG-only at multi-mile range is not consistent with published physics .
Live demo — nvsim WASM
${this.renderDemo()}
Physics reality check
Distance Cardiac MCG (peak QRS) vs Earth field (~50 µT)
10 cm 50 pT 10⁹× weaker
1 m 50 fT 10¹²× weaker
10 m 50 aT 10¹⁵× weaker
1 km 5 × 10⁻²³ T 10²⁷× weaker
40 mi (65 km) ~10⁻²⁸ T 10³³× weaker
Best published NV-ensemble lab record: 0.9 pT/√Hz [Wolf 2015].
Best SQUID in a shielded room: ~1 fT/√Hz . To detect a single heartbeat at 10 m
you'd need ~2 billion× more sensitivity than any published ensemble has ever shown,
in a magnetically silent environment. 40 miles is press-release physics.
RuView's three-tier mesh — what is actually buildable
┌──────────────────────────┐
│ Tier 3 — NV-diamond │ Range: 0.1–2 m (lab)
│ magnetometer ring │ Status: nvsim simulator only
│ (close-confirm) │ Hardware: $$$ (≥$8k DNV-B1)
└──────────┬───────────────┘
│
┌──────────┴───────────────┐
│ Tier 2 — 60 GHz FMCW │ Range: 1–10 m HR/BR
│ mmWave radar mesh │ Status: shipping (ADR-021)
│ (vital signs, posture) │ Hardware: $15 (MR60BHA2 + ESP32-C6)
└──────────┬───────────────┘
│
┌──────────┴───────────────┐
│ Tier 1 — WiFi CSI mesh │ Range: 10–30 m through-wall
│ (presence, breathing, │ Status: shipping (ADR-014, ADR-029)
│ pose, intention) │ Hardware: $9 (ESP32-S3 8MB)
└──────────┬───────────────┘
│
▼
┌────────────────────────────────┐
│ RuvSense multistatic fusion │
│ + cross-viewpoint attention │
│ + AETHER re-ID embeddings │
│ + Cramer-Rao gating │
└────────────────────────────────┘
Press claim → RuView equivalent
Press claim RuView equivalent today Crate / ADR Honest range
NV-diamond magnetometry
Deterministic NV pipeline simulator
nvsim · ADR-089
Simulator only
"AI strips environmental noise"
RuvSense multistatic fusion + AETHER
signal/ruvsense/ · ADR-029
Mature
Heartbeat at distance
60 GHz FMCW HR/BR + WiFi CSI breathing
vitals · ADR-021
1–5 m HR · 10–30 m presence
Long-range localisation
Multistatic time-of-flight + CRLB
ruvector/viewpoint/
Limited by node spacing
40-mile single-heartbeat detection
Not feasible at any tier
—
Press-release physics
Build today on $165
Bill of materials
3 × ESP32-S3 8 MB ($9 ea)
3 × PoE injector + cat6 ($6 ea)
1 × ESP32-C6 + Seeed MR60BHA2 ($15)
1 × Raspberry Pi 5 8 GB ($80)
1 × unmanaged GbE switch ($25)
Total: $165
Honest performance
95% TPR (LOS, 0–15 m)
±2 bpm HR (LOS 0–3 m)
±1 br/min BR (any mode)
~10 cm pose error
80–150 ms end-to-end latency
Determinism
Same (scene, config, seed) → byte-identical SHA-256 witness across browsers, OSes, transports.
Reference: cc8de9b01b0ff5bd…
Try the Witness tab on the right — it re-derives the hash live in this browser and compares against the published reference.
Privacy, ethics, legal
This is the open-source version. Same physics, opposite governance.
Civilian opt-in only — search-and-rescue, elder-care, occupancy, ICU vitals. Not surveillance.
No directional pursuit — no beam-steering, target-following, or remote person-of-interest tracking.
Data minimisation — fused output is (presence, HR, BR, pose, p_alive); raw streams discarded at the edge.
PII gates (ADR-040) block identifying biometric streams from leaving the local mesh without consent.
Adversarial-signal detection flags physically-impossible signal patterns from compromised mesh nodes.
No export-controlled hardware — RuView targets < $50 COTS. ITAR/EAR sub-THz coherent radars and shielded NV ensembles are out of scope.
RuView is not affiliated with the United States government, the CIA, Lockheed Martin,
or any classified program. References to "Ghost Murmur" in this view refer
exclusively to the publicly-reported program of that name as covered in the open
press in April 2026.
Cross-references
ADRs: 014 (signal) · 021 (vitals) · 024 (AETHER) · 027 (MERIDIAN) ·
028 (witness audit) · 029 (RuvSense) · 040 (PII gates) · 086 (ESP32 RaBitQ) ·
089 (nvsim, Accepted) · 090 (Lindblad, Proposed-conditional) ·
091 (sub-THz radar research) · 092 (this dashboard) .
Primary physics: Cohen 1970 · Bison 2009 · Wolf 2015 · Barry RMP 2020 · Doherty 2013 · Jackson 3e §5.6/§5.8.
`}};D.styles=f`
:host {
display: block;
height: 100%;
overflow-y: auto;
background: radial-gradient(ellipse at 50% 30%, var(--bg-2) 0%, var(--bg-0) 70%);
padding: 24px 28px 60px;
}
h1 {
margin: 0 0 4px;
font-size: 22px;
letter-spacing: -0.02em;
color: var(--ink);
}
.subtitle {
color: var(--ink-3);
font-size: 13px;
margin-bottom: 22px;
}
.links {
display: flex; flex-wrap: wrap; gap: 6px;
margin-bottom: 22px;
}
.links a {
padding: 5px 10px;
background: var(--bg-2);
border: 1px solid var(--line);
border-radius: 999px;
font-size: 11.5px;
font-family: var(--mono);
color: var(--accent-2);
text-decoration: none;
}
.links a:hover { border-color: var(--accent-2); }
h2 {
font-size: 14px;
font-weight: 600;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--ink-3);
margin: 28px 0 10px;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 12px;
}
.card {
background: var(--bg-2);
border: 1px solid var(--line);
border-radius: var(--radius);
padding: 14px;
}
.card h3 {
margin: 0 0 8px;
font-size: 13.5px; font-weight: 600;
color: var(--ink);
}
.card p {
font-size: 12.5px; color: var(--ink-2);
margin: 0 0 8px;
line-height: 1.5;
}
.card p:last-child { margin-bottom: 0; }
.stat {
display: inline-flex; align-items: baseline; gap: 6px;
margin-right: 10px;
}
.stat .v {
font-family: var(--mono); font-size: 16px; font-weight: 600;
color: var(--accent);
}
.stat .l {
font-size: 10px; color: var(--ink-3);
text-transform: uppercase; letter-spacing: 0.04em;
}
table {
width: 100%; border-collapse: collapse;
font-size: 12.5px;
}
th, td {
padding: 8px 10px;
text-align: left;
border-bottom: 1px solid var(--line);
}
th {
color: var(--ink-3);
font-weight: 600;
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.06em;
}
td.amber { color: var(--accent); font-family: var(--mono); }
td.cyan { color: var(--accent-2); font-family: var(--mono); }
td.bad { color: var(--bad); font-family: var(--mono); }
.pill {
display: inline-block;
padding: 1px 6px;
border-radius: 4px;
font-family: var(--mono);
font-size: 10px;
border: 1px solid var(--line);
}
.pill.ok { color: var(--ok); border-color: oklch(0.78 0.14 145 / 0.4); }
.pill.skeptical { color: var(--bad); border-color: oklch(0.65 0.22 25 / 0.4); }
.pill.partial { color: var(--warn); border-color: oklch(0.7 0.18 35 / 0.4); }
.architecture {
font-family: var(--mono);
font-size: 11px;
color: var(--ink-2);
background: var(--bg-3);
padding: 16px;
border-radius: var(--radius-sm);
border: 1px solid var(--line);
white-space: pre;
overflow-x: auto;
line-height: 1.4;
}
.ethics {
background: linear-gradient(180deg, var(--bg-2) 0%, oklch(0.65 0.22 25 / 0.04) 100%);
border: 1px solid oklch(0.65 0.22 25 / 0.25);
border-radius: var(--radius);
padding: 16px;
}
.ethics h3 { color: var(--bad); margin-top: 0; }
.ethics ul { padding-left: 18px; margin: 8px 0; }
.ethics li { font-size: 12.5px; color: var(--ink-2); margin-bottom: 4px; }
/* Demo */
.demo {
background: linear-gradient(180deg, var(--bg-2) 0%, oklch(0.78 0.14 70 / 0.04) 100%);
border: 1px solid oklch(0.78 0.14 70 / 0.3);
border-radius: var(--radius);
padding: 18px;
}
.demo-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 18px;
margin-top: 12px;
}
@media (max-width: 720px) { .demo-grid { grid-template-columns: 1fr; } }
.control { margin-bottom: 14px; }
.control .top {
display: flex; justify-content: space-between;
font-size: 12px; margin-bottom: 6px;
}
.control .top .lbl { color: var(--ink-3); }
.control .top .val {
font-family: var(--mono); color: var(--ink);
}
.control input[type="range"] {
-webkit-appearance: none; appearance: none;
width: 100%; height: 4px;
background: var(--bg-3); border-radius: 2px; outline: none;
}
.control input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none; appearance: none;
width: 14px; height: 14px; border-radius: 50%;
background: var(--accent); cursor: pointer;
border: 2px solid var(--bg-2);
}
.demo-btn {
width: 100%;
padding: 10px;
border: 1px solid var(--accent);
background: var(--accent);
color: #1a0f00;
border-radius: 8px;
font-size: 13px; font-weight: 600;
cursor: pointer;
}
.demo-btn:hover { filter: brightness(1.08); }
.demo-btn:disabled { opacity: 0.6; cursor: progress; }
.readout {
background: var(--bg-3);
border: 1px solid var(--line);
border-radius: 8px;
padding: 12px;
}
.readout-row {
display: flex; justify-content: space-between;
padding: 4px 0;
font-family: var(--mono); font-size: 12px;
}
.readout-row .l { color: var(--ink-3); }
.readout-row .v { color: var(--ink); }
.readout-row .v.amber { color: var(--accent); }
.tier-bar {
position: relative;
margin: 6px 0;
height: 22px;
background: var(--bg-3);
border: 1px solid var(--line);
border-radius: 4px;
overflow: hidden;
}
.tier-bar .fill {
position: absolute; top: 0; bottom: 0; left: 0;
transition: width 0.2s ease-out;
border-right: 2px solid;
}
.tier-bar .lbl {
position: relative; z-index: 1;
font-family: var(--mono); font-size: 11px;
padding: 3px 8px;
color: var(--ink);
display: flex; justify-content: space-between;
pointer-events: none;
}
.verdict {
margin-top: 10px;
padding: 10px 12px;
border-radius: 8px;
font-size: 12.5px; font-weight: 500;
border: 1px solid;
}
.verdict.ok { background: oklch(0.78 0.14 145 / 0.08); border-color: oklch(0.78 0.14 145 / 0.4); color: var(--ok); }
.verdict.warn { background: oklch(0.7 0.18 35 / 0.08); border-color: oklch(0.7 0.18 35 / 0.4); color: var(--warn); }
.verdict.bad { background: oklch(0.65 0.22 25 / 0.08); border-color: oklch(0.65 0.22 25 / 0.4); color: var(--bad); }
.demo-notes {
font-size: 11.5px; color: var(--ink-3);
margin-top: 10px; line-height: 1.5;
}
`;ie([u()],D.prototype,"distanceM",2);ie([u()],D.prototype,"momentLog10",2);ie([u()],D.prototype,"result",2);ie([u()],D.prototype,"running",2);ie([u()],D.prototype,"err",2);D=ie([x("nv-ghost-murmur")],D);var Xt=Object.defineProperty,Qt=Object.getOwnPropertyDescriptor,Re=(e,t,a,i)=>{for(var s=i>1?void 0:i?Qt(t,a):t,r=e.length-1,o;r>=0;r--)(o=e[r])&&(s=(i?o(t,a,s):o(s))||s);return i&&s&&Xt(t,a,s),s};const je=[{term:"NV-diamond",category:"physics",body:"Nitrogen-vacancy defect in synthetic diamond. The simulator models a 1 mm³ ensemble (~10¹² centers) addressed by 532 nm pump light + a 2.87 GHz microwave drive. Used as a room-temperature magnetometer with shot-noise floor ~1 pT/√Hz at the published lab record."},{term:"CW-ODMR",category:"physics",body:"Continuously-driven optically-detected magnetic resonance. Sweep the microwave frequency around the NV zero-field splitting (D = 2.87 GHz) and watch the photoluminescence dip when the microwave matches the spin transition. The dip splits with applied magnetic field along each of the four ⟨111⟩ NV axes."},{term:"MagFrame",category:"rust",body:"Fixed-layout 60-byte binary record nvsim emits per (sensor × sample). Magic 0xC51A_6E70, version 1, little-endian. Carries timestamp, recovered B vector (pT), per-axis sigma, noise floor, and flag bits for saturation / shot-noise-disabled / heavy-attenuation."},{term:"Witness",category:"rust",body:"SHA-256 hash over the concatenated MagFrame bytes for a canonical reference run (Proof::REFERENCE_SCENE_JSON @ seed=42, N=256). Same inputs → same hash, byte-for-byte, across runs and machines. The dashboard re-derives it in WASM and compares against Proof::EXPECTED_WITNESS_HEX pinned at build time."},{term:"Determinism gate",category:"rust",body:"A pass/fail check: did this build of nvsim produce the expected witness? If yes → every constant (γ_e, D_GS, μ₀, contrast, T₂*, the PRNG stream, the frame layout, the pipeline ordering) is byte-identical to the published reference. If no → something drifted; the dashboard names which."},{term:"Lock-in demod",category:"physics",body:"Multiply the photoluminescence signal by cos(2π·f_mod·t) and low-pass to recover the slowly-varying B-field component. The simulator emulates a lock-in with output gain 2 and a single-pole IIR LP filter; settable via the Tunables panel (f_mod default 1 kHz)."},{term:"Shot-noise floor",category:"physics",body:'δB = 1 / (γ_e · C · √(N · t · T₂*)) — the irreducible quantum noise floor for an NV ensemble. With nvsim defaults (N=10¹², C=0.03, T₂*=200 ns): ≈1.18 pT/√Hz. Toggleable via the Tunables panel for "analytic" runs without noise.'},{term:"Biot-Savart",category:"physics",body:"Closed-form magnetic field at a point from a current loop or a magnetic dipole. The Scene panel's sources (heart proxy, mains loop, ferrous body, eddy current) all reduce to Biot-Savart-style superpositions over the sensor position."},{term:"Multistatic fusion",category:"physics",body:"Combining evidence from multiple sensors at known geometric configurations. RuView's Cramer-Rao-weighted attention over WiFi CSI nodes + 60 GHz radar nodes + (hypothetically) NV nodes; documented in ADR-029 and the Ghost Murmur view."},{term:"Scene",category:"ui",body:'The simulated magnetic environment: a list of sources (dipole, current loop, ferrous body, eddy current) plus one or more sensor positions and an ambient field. The dashboard ships a "rebar-walkby-01" reference scene; click "New scene…" in the command palette (⌘K) to build your own.'},{term:"Tunables",category:"ui",body:"Sliders that change the running pipeline's digitiser config. Each edit debounces 300 ms, then rebuilds the WASM pipeline with the new f_s / f_mod / dt / shot-noise setting. The frame stream picks up the change without a restart."},{term:"Transport",category:"ui",body:"How the dashboard talks to nvsim. Default is WASM — the simulator runs in a Web Worker right here in your browser, no server. The optional WS transport is REST + binary WebSocket against a host-supplied nvsim-server (see ADR-092 §6.2). Toggle in Settings."},{term:"App Store",category:"ui",body:"Catalog of all 65+ hot-loadable WASM edge modules from wifi-densepose-wasm-edge plus the simulators. Each card carries id / category / status / event IDs; the toggle marks an app active in this session and (in WS mode) pushes the activation to a connected ESP32 mesh."},{term:"Ghost Murmur",category:"ui",body:'Research view that audits the publicly-reported April 2026 CIA NV-diamond heartbeat detector against the open physics literature. Includes a live "Try it yourself" sandbox where you can place a heart dipole at any distance from the sensor and ask: which transport tier would actually detect it?'}],Jt=[{q:"Is this a real simulator or a mockup?",a:"Real. The Rust crate at v2/crates/nvsim is the same code that runs in the browser via WASM. Press Verify witness on the Witness panel — the SHA-256 you see is byte-equivalent to what `cargo test -p nvsim` produces."},{q:'Why does my "Recovered |B|" sit much higher than "Predicted |B|" in the Ghost Murmur demo?',a:"The recovered value reads the simulator's ADC quantization floor, not the actual magnetic signal. With COTS-default sensor noise (~300 pT/√Hz) and 16-bit ADC at ±10 µT FS, anything below ~1 pT vanishes into ~2 nT of digitization residual. That's the lesson — the press claim sits far below this floor at any meaningful range."},{q:"Can I run my own scene?",a:'Yes. Press ⌘K to open the command palette and pick "New scene…". You get five fields (name, dipole moment, distance, ferrous toggle, mains toggle); the dashboard builds the JSON and pushes it via client.loadScene().'},{q:"Does any of my data leave the browser?",a:"No. WASM mode is local-only — the worker, the WASM binary, and the IndexedDB persistence all live in your browser. The optional WS transport (off by default) talks to a host of your choosing."},{q:"What does the witness mismatch (red ✗) mean?",a:"The current build of nvsim produced a SHA-256 that doesn't match the constant pinned at compile time. Possible causes: a different Rust toolchain, a dependency version drift, a manual edit to a physics constant, or an honest bug. Audit the diff against ADR-089 §5."},{q:"Why are the Inspector / Witness rail buttons there if there's already a right-side inspector?",a:'The right-side inspector is the compact live view; the rail buttons open a full-width version with bigger charts, an explainer header, reference-scene metadata cards, and (on Witness) a "what this verifies" panel. Both stay in sync — the right rail is for glancing, the main area is for diving in.'},{q:'Why is there an "App Store" if this is a magnetometer simulator?',a:"Because nvsim is one tile in a larger sensing platform. The catalog lists every hot-loadable WASM edge module RuView ships — medical, security, building, retail, industrial, signal, learning, autonomy. The simulators (nvsim today, more in future) are first-class entries in the same catalog."}],Zt=[{step:1,title:"Hit ▶ Run",body:"The big amber button in the topbar starts the live frame stream. The pipeline runs ~1.8 kHz on x86_64 WASM, well above the 1 kHz Cortex-A53 acceptance gate."},{step:2,title:"Watch the B-vector trace",body:"The Inspector → Signal tab shows the recovered field per axis updating in real time. The frame strip below it is one bar per ~32-frame batch."},{step:3,title:"Verify the witness",body:"Click the rail Witness button (or REPL: proof.verify). The dashboard re-runs the canonical reference scene and asserts the SHA-256 byte-for-byte."},{step:4,title:"Drag a source",body:"Grab the rebar / heart proxy / mains loop / ferrous door in the scene canvas; positions persist via IndexedDB."},{step:5,title:"Tweak the tunables",body:"Sliders in the left sidebar update the running pipeline (f_s, f_mod, integration time, shot-noise). Changes debounce 300 ms then push to the worker."},{step:6,title:"Open the Ghost Murmur view",body:'The ghost icon in the rail. Move the distance + moment sliders, hit "Run nvsim at this distance" — the live demo runs the real Rust pipeline through WASM and shows which transport tier would actually detect.'},{step:7,title:"Browse the App Store",body:"The grid icon. 65+ edge apps: medical, security, building, retail, industrial, signal, learning. Toggle to mark active in this session."}],ea=[{keys:"⌘K / Ctrl K",label:"Command palette"},{keys:"Space",label:"Play / pause pipeline"},{keys:"⌘R / Ctrl R",label:"Reset pipeline (with confirm)"},{keys:"⌘, / Ctrl ,",label:"Settings drawer"},{keys:"⌘N / Ctrl N",label:"New scene"},{keys:"⌘E / Ctrl E",label:"Export proof bundle"},{keys:"⌘/ / Ctrl /",label:"Toggle theme (dark / light)"},{keys:"`",label:"Toggle debug HUD"},{keys:"?",label:"Open this help center"},{keys:"1 · 2 · 3",label:"Switch inspector tab (Signal / Frame / Witness)"},{keys:"Esc",label:"Close any modal / palette / drawer"},{keys:"/",label:"Focus the REPL prompt"}];let ae=class extends y{constructor(){super(...arguments),this.open=!1,this.section="quickstart",this.query="",this.show=e=>{const t=e.detail;t?.section&&(this.section=t.section),this.open=!0,this.setAttribute("open","")},this.onKey=e=>{const t=e.target,a=t?.tagName==="INPUT"||t?.tagName==="TEXTAREA";e.key==="?"&&!a&&!e.ctrlKey&&!e.metaKey?(e.preventDefault(),this.show(new CustomEvent("nv-show-help"))):e.key==="Escape"&&this.open&&this.close()}}connectedCallback(){super.connectedCallback(),window.addEventListener("nv-show-help",this.show),window.addEventListener("keydown",this.onKey)}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("nv-show-help",this.show),window.removeEventListener("keydown",this.onKey)}close(){this.open=!1,this.removeAttribute("open")}filteredGlossary(){if(!this.query.trim())return je;const e=this.query.toLowerCase();return je.filter(t=>t.term.toLowerCase().includes(e)||t.body.toLowerCase().includes(e))}renderQuickstart(){return l`
Quickstart
Seven taps to get from "I just opened the dashboard" to "I'm running my own scene with verified determinism."
${Zt.map(e=>l`
`)}
`}renderGlossary(){const e=this.filteredGlossary();return l`
Glossary
Every piece of jargon in the dashboard, defined in one paragraph each.
this.query=t.target.value} />
${e.length===0?l`No terms match.
`:e.map(t=>l`
${t.term}
${t.category}
${t.body}
`)}
`}renderFaq(){return l`
FAQ
The questions I was asked twice in the first week of demos.
${Jt.map(e=>l`
`)}
`}renderShortcuts(){return l`
Keyboard shortcuts
Everything is reachable without a mouse.
${ea.map(e=>l`
${e.keys} ${e.label}
`)}
`}renderAbout(){return l`
About this dashboard
What you're looking at, in one screen.
nvsim is a deterministic forward simulator for nitrogen-vacancy diamond magnetometry.
The Rust crate at v2/crates/nvsim is the source of truth; this dashboard is a
Vite + Lit single-page app that ships the crate compiled to WebAssembly inside a Web Worker.
The defining commitment is determinism : same (scene, config, seed) →
byte-identical SHA-256 witness across browsers, OSes, and transports. Press the
Verify witness button on the Witness tab to assert this live.
The codebase is open source (Apache-2.0 OR MIT). Find it on GitHub:
github.com/ruvnet/RuView. Decisions are documented in ADRs 089 (nvsim),
090 (Lindblad extension, conditional), 091 (sub-THz radar research),
092 (this dashboard), 093 (UX gap analysis).
This dashboard is one of several RuView demos. Sibling demos at
github.io/RuView/ include the Observatory and Pose Fusion views.
`}render(){return l`
${["quickstart","glossary","faq","shortcuts","about"].map(e=>l`
this.section=e}>
${e==="quickstart"?"🚀 Quickstart":e==="glossary"?"📖 Glossary":e==="faq"?"? FAQ":e==="shortcuts"?"⌨ Shortcuts":"ℹ About"}
`)}
${this.section==="quickstart"?this.renderQuickstart():this.section==="glossary"?this.renderGlossary():this.section==="faq"?this.renderFaq():this.section==="shortcuts"?this.renderShortcuts():this.renderAbout()}
Press ? any time to reopen
nvsim · Apache-2.0 OR MIT
`}};ae.styles=f`
:host {
position: fixed; inset: 0;
background: rgba(0, 0, 0, 0.55);
backdrop-filter: blur(4px);
z-index: 230;
display: grid; place-items: center;
opacity: 0; pointer-events: none;
transition: opacity 0.18s;
}
:host([open]) { opacity: 1; pointer-events: auto; }
.modal {
background: var(--bg-1);
border: 1px solid var(--line-2);
border-radius: var(--radius);
box-shadow: 0 30px 80px -20px rgba(0,0,0,0.7);
width: min(880px, 94vw);
max-height: 86vh;
display: grid;
grid-template-columns: 200px 1fr;
grid-template-rows: auto 1fr auto;
overflow: hidden;
transform: translateY(12px) scale(0.98);
transition: transform 0.22s cubic-bezier(0.2,0.7,0.3,1);
}
:host([open]) .modal { transform: translateY(0) scale(1); }
@media (max-width: 700px) {
.modal { grid-template-columns: 1fr; grid-template-rows: auto auto 1fr auto; max-height: 92vh; }
.nav { border-right: 0; border-bottom: 1px solid var(--line); flex-direction: row; overflow-x: auto; }
.nav button { white-space: nowrap; }
}
.h {
grid-column: 1 / -1;
padding: 14px 18px;
border-bottom: 1px solid var(--line);
display: flex; align-items: center; justify-content: space-between;
}
.h .ttl { font-size: 15px; font-weight: 600; }
.nav {
border-right: 1px solid var(--line);
padding: 12px 8px;
display: flex; flex-direction: column; gap: 2px;
background: var(--bg-1);
}
.nav button {
text-align: left;
padding: 8px 12px;
background: transparent;
border: 1px solid transparent;
border-radius: 6px;
color: var(--ink-3);
font-size: 12.5px;
cursor: pointer;
transition: color 0.15s, background 0.15s;
}
.nav button:hover { color: var(--ink); background: var(--bg-2); }
.nav button.on {
color: var(--ink); background: var(--bg-3);
border-color: var(--line-2);
}
.body {
padding: 18px 22px;
overflow-y: auto;
font-size: 13px;
color: var(--ink-2);
line-height: 1.6;
}
.body h2 {
margin: 0 0 8px;
font-size: 18px;
color: var(--ink);
letter-spacing: -0.01em;
}
.body .lead {
color: var(--ink-3);
font-size: 12.5px;
margin: 0 0 14px;
}
.body p { margin: 0 0 12px; }
.body code {
font-family: var(--mono);
background: var(--bg-3);
padding: 1px 5px;
border-radius: 4px;
font-size: 11.5px;
color: var(--accent);
}
.body kbd {
font-family: var(--mono);
padding: 2px 6px;
background: var(--bg-3);
border: 1px solid var(--line);
border-radius: 4px;
font-size: 11.5px;
color: var(--ink);
}
.step {
display: grid;
grid-template-columns: 32px 1fr;
gap: 12px;
padding: 10px 0;
border-bottom: 1px solid var(--line);
}
.step:last-child { border-bottom: 0; }
.step .num {
width: 26px; height: 26px;
border-radius: 50%;
background: var(--accent);
color: #1a0f00;
font-family: var(--mono);
font-size: 12.5px;
font-weight: 700;
display: grid; place-items: center;
}
.step .ttl { color: var(--ink); font-weight: 600; font-size: 13.5px; margin-bottom: 2px; }
.step .body-text { font-size: 12.5px; color: var(--ink-2); line-height: 1.55; }
.glossary-search {
width: 100%;
padding: 8px 12px;
background: var(--bg-3);
border: 1px solid var(--line);
border-radius: 6px;
font-family: var(--mono);
font-size: 12.5px;
color: var(--ink);
outline: none;
margin-bottom: 14px;
}
.glossary-search:focus { border-color: var(--accent); }
.term {
padding: 10px 0;
border-bottom: 1px solid var(--line);
}
.term:last-child { border-bottom: 0; }
.term .head {
display: flex; align-items: center; gap: 8px; margin-bottom: 4px;
}
.term .name {
font-family: var(--mono);
font-size: 13.5px;
color: var(--accent);
font-weight: 600;
}
.term .badge {
font-family: var(--mono);
font-size: 9.5px;
padding: 1px 6px;
border-radius: 4px;
border: 1px solid var(--line);
text-transform: uppercase;
letter-spacing: 0.04em;
}
.term .badge.physics { color: var(--accent-2); border-color: oklch(0.78 0.12 195 / 0.4); }
.term .badge.rust { color: var(--accent); border-color: oklch(0.78 0.14 70 / 0.4); }
.term .badge.ui { color: var(--accent-4); border-color: oklch(0.78 0.14 145 / 0.4); }
.term .body-text {
font-size: 12.5px;
color: var(--ink-2);
line-height: 1.55;
}
.faq-item {
padding: 10px 0;
border-bottom: 1px solid var(--line);
}
.faq-item:last-child { border-bottom: 0; }
.faq-item .q {
color: var(--ink);
font-weight: 600;
font-size: 13.5px;
margin-bottom: 4px;
}
.faq-item .a { font-size: 12.5px; color: var(--ink-2); line-height: 1.55; }
.shortcuts {
display: grid;
grid-template-columns: auto 1fr;
gap: 8px 16px;
align-items: baseline;
}
.f {
grid-column: 1 / -1;
padding: 10px 18px;
border-top: 1px solid var(--line);
display: flex; align-items: center; justify-content: space-between;
font-size: 11.5px; color: var(--ink-3);
}
.close {
width: 28px; height: 28px;
background: transparent; border: 1px solid var(--line);
border-radius: 6px;
color: var(--ink-2);
cursor: pointer;
}
.close:hover { color: var(--ink); border-color: var(--line-2); }
`;Re([u()],ae.prototype,"open",2);Re([u()],ae.prototype,"section",2);Re([u()],ae.prototype,"query",2);ae=Re([x("nv-help")],ae);var ta=Object.defineProperty,aa=Object.getOwnPropertyDescriptor,at=(e,t,a,i)=>{for(var s=i>1?void 0:i?aa(t,a):t,r=e.length-1,o;r>=0;r--)(o=e[r])&&(s=(i?o(t,a,s):o(s))||s);return i&&s&&ta(t,a,s),s};let Me=class extends y{constructor(){super(...arguments),this.view="scene"}render(){return l`
{e.preventDefault(),this.shadowRoot?.querySelector(".main")?.focus()}}>
Skip to main content
this.view=e.detail}>
${this.view==="apps"?l` `:this.view==="ghost-murmur"?l` `:this.view==="inspector"?l` `:this.view==="witness"?l` `:l` `}
`}};Me.styles=f`
:host {
display: block;
height: 100vh;
width: 100vw;
background: var(--bg-0);
}
.skip-link {
position: absolute;
top: -40px;
left: 8px;
padding: 6px 12px;
background: var(--accent);
color: #1a0f00;
border-radius: 6px;
font-size: 12.5px;
font-weight: 600;
text-decoration: none;
z-index: 1000;
transition: top 0.15s;
}
.skip-link:focus { top: 8px; }
.app {
display: grid;
grid-template-columns: 56px 280px 1fr 340px;
grid-template-rows: 48px 1fr 220px;
grid-template-areas:
'rail topbar topbar topbar'
'rail sidebar main inspector'
'rail sidebar console inspector';
height: 100vh;
width: 100vw;
}
nv-rail { grid-area: rail; }
nv-topbar { grid-area: topbar; }
nv-sidebar { grid-area: sidebar; }
.main { grid-area: main; min-width: 0; min-height: 0; position: relative; overflow: hidden; }
nv-inspector { grid-area: inspector; }
nv-console { grid-area: console; min-height: 0; }
@media (max-width: 1180px) {
.app {
grid-template-columns: 56px 1fr 320px;
grid-template-areas:
'rail topbar topbar'
'rail main inspector'
'rail console console';
}
nv-sidebar { display: none; }
}
@media (max-width: 860px) {
.app {
grid-template-columns: 1fr;
grid-template-rows: 52px 1fr 200px;
grid-template-areas:
'topbar'
'main'
'console';
}
nv-rail, nv-sidebar, nv-inspector { display: none; }
}
`;at([u()],Me.prototype,"view",2);Me=at([x("nv-app")],Me);function sa(e,t,a){const i=e.getUint32(t+0,!0),s=e.getUint16(t+4,!0),r=e.getUint16(t+6,!0),o=e.getUint16(t+8,!0),n=e.getBigUint64(t+12,!0),d=e.getFloat32(t+20,!0),v=e.getFloat32(t+24,!0),m=e.getFloat32(t+28,!0),g=e.getFloat32(t+32,!0),G=e.getFloat32(t+36,!0),Ee=e.getFloat32(t+40,!0),De=e.getFloat32(t+44,!0),st=e.getFloat32(t+48,!0);return{magic:i,version:s,flags:r,sensorId:o,tUs:n,bPt:[d,v,m],sigmaPt:[g,G,Ee],noiseFloorPtSqrtHz:De,temperatureK:st,raw:a.subarray(t,t+60)}}function ia(e){const a=new DataView(e.buffer,e.byteOffset,e.byteLength),i=[];for(let s=0;s+60<=e.byteLength;s+=60)i.push(sa(a,s,e));return i}class ra{constructor(){this.nextId=1,this.pending=new Map,this.frameSubs=new Set,this.eventSubs=new Set,this.bootInfo=null,this.worker=new Worker(new URL("/RuView/nvsim/assets/worker-C19MRcXs.js",import.meta.url),{type:"module"}),this.worker.addEventListener("message",t=>this.onMessage(t)),this.worker.addEventListener("error",t=>this.eventSubs.forEach(a=>a({type:"log",level:"err",msg:String(t.message)})))}onMessage(t){const a=t.data;if(a.type==="frames"){const i=a.batch,s=new Uint8Array(i),o={frames:ia(s),bytes:s};this.frameSubs.forEach(d=>d(o));const n=a.fps;n>0&&this.eventSubs.forEach(d=>d({type:"fps",value:n}));return}if(a.type==="state"){this.eventSubs.forEach(i=>i({type:"state",running:!!a.running,t:0,framesEmitted:Number(a.framesEmitted??0)}));return}if(a.type!=="ready"){if(a.type==="err"&&a.id==null){this.eventSubs.forEach(i=>i({type:"log",level:"err",msg:String(a.msg)}));return}if(typeof a.id=="number"&&this.pending.has(a.id)){const i=this.pending.get(a.id);this.pending.delete(a.id),a.type==="err"?i.reject(new Error(String(a.msg))):i.resolve(a)}}}rpc(t,a=[]){const i=this.nextId++;return new Promise((s,r)=>{this.pending.set(i,{resolve:s,reject:r}),this.worker.postMessage({...t,id:i},a)})}async boot(){if(this.bootInfo)return this.bootInfo;const a=await this.rpc({type:"boot",base:"/RuView/nvsim/"});return this.bootInfo={buildVersion:a.buildVersion,frameMagic:a.frameMagic,frameBytes:a.frameBytes,expectedWitnessHex:a.expectedWitnessHex},this.bootInfo}async loadScene(t){await this.rpc({type:"setScene",json:JSON.stringify(t)})}async setConfig(t){await this.rpc({type:"setConfig",json:JSON.stringify(t)})}async setSeed(t){await this.rpc({type:"setSeed",seed:Number(t&0xFFFFFFFFn)})}async reset(){await this.rpc({type:"reset"})}async run(t){await this.rpc({type:"run"})}async pause(){await this.rpc({type:"pause"})}async step(t,a){await this.rpc({type:"step"})}onFrames(t){this.frameSubs.add(t)}onEvent(t){this.eventSubs.add(t)}async generateWitness(t){const a=await this.rpc({type:"witnessGenerate",samples:t});return new Uint8Array(a.witness)}async verifyWitness(t){const a=t.slice().buffer,i=await this.rpc({type:"witnessVerify",samples:256,expected:a},[a]);return i.ok?{ok:!0}:{ok:!1,actual:new Uint8Array(i.actual)}}async runTransient(t,a,i,s){const r=await this.rpc({type:"runTransient",scene:JSON.stringify(t),config:JSON.stringify(a),seed:Number(i&0xFFFFFFFFn),samples:s});return{bRecoveredT:[r.bRecoveredT[0],r.bRecoveredT[1],r.bRecoveredT[2]],bMagT:r.bMagT,noiseFloorPtSqrtHz:r.noiseFloorPtSqrtHz,sigmaPt:[r.sigmaPt[0],r.sigmaPt[1],r.sigmaPt[2]],nFrames:r.nFrames,witnessHex:r.witnessHex}}async exportProofBundle(){const t=await this.generateWitness(256),a=Array.from(t).map(r=>r.toString(16).padStart(2,"0")).join(""),i=this.bootInfo??await this.boot(),s=JSON.stringify({kind:"nvsim-proof-bundle",version:i.buildVersion,seed:"0x0000002A",nSamples:256,witness:a,expected:i.expectedWitnessHex,ok:a===i.expectedWitnessHex,ts:new Date().toISOString()},null,2);return new Blob([s],{type:"application/json"})}async buildId(){return(await this.rpc({type:"buildId"})).buildId}async close(){this.worker.terminate()}}function Ue(e){document.documentElement.setAttribute("data-theme",e)}function Ge(e){document.body.classList.remove("density-comfy","density-default","density-compact"),document.body.classList.add(`density-${e}`)}function Ke(e){document.body.classList.toggle("reduce-motion",e)}(async()=>{const e=await H("theme")??"dark",t=await H("density")??"default",a=window.matchMedia?.("(prefers-reduced-motion: reduce)").matches??!1,i=await H("motionReduced")??a;k.value=e,Ue(e),_.value=t,Ge(t),T.value=i,Ke(i),w(()=>{Ue(k.value),I("theme",k.value)}),w(()=>{Ge(_.value),I("density",_.value)}),w(()=>{Ke(T.value),I("motionReduced",T.value)});const s=await H("repl-history");s&&Array.isArray(s)&&(q.value=s),w(()=>{I("repl-history",q.value)});const r=await H("scene-positions");r&&Array.isArray(r)&&(ve.value=r),w(()=>{I("scene-positions",ve.value)});const o=new ra;mt(o),c("info","nvsim — booting WASM runtime"),o.onEvent(n=>{n.type==="log"&&c(n.level,n.msg),n.type==="fps"&&(M.value=n.value),n.type==="state"&&(Ie.value=BigInt(n.framesEmitted))}),o.onFrames(n=>{if(n.frames.length===0)return;const d=n.frames[n.frames.length-1];B.value=d;const v=d.bPt[0]*1e-12,m=d.bPt[1]*1e-12,g=d.bPt[2]*1e-12;he.value=[v,m,g],V.value=Math.sqrt(v*v+m*m+g*g),bt([v*1e9,m*1e9,g*1e9]);const G=Math.min(1,Math.abs(g*1e9)/5+.3);ft(G)});try{const n=await o.boot();R.value=n.expectedWitnessHex,c("ok",`WASM module ready · nvsim@${n.buildVersion} · magic=0x${n.frameMagic.toString(16).toUpperCase()}`),c("info",`expected witness · ${n.expectedWitnessHex.slice(0,16)}…`),vt.value="(reference scene)",A.value="wasm"}catch(n){c("err",`boot failed: ${n.message}`)}try{const n=R.value;if(n){const d=new Uint8Array(32);for(let m=0;m<32;m++)d[m]=parseInt(n.slice(m*2,m*2+2),16);const v=await o.verifyWitness(d);if(v.ok)z.value=n,c("ok","witness verified · determinism gate ✓");else{const m=Array.from(v.actual).map(g=>g.toString(16).padStart(2,"0")).join("");z.value=m,c("err",`WITNESS MISMATCH · expected ${n.slice(0,16)}… got ${m.slice(0,16)}…`)}}}catch(n){c("warn",`witness verify skipped: ${n.message}`)}})();
//# sourceMappingURL=index-DgoW5uYG.js.map