import React, { useEffect, useState } from 'react'; import { createRoot } from 'react-dom/client'; import type { Session, RequestRecord } from '../types/index.js'; import { LatencyChart } from '../components/LatencyChart.js'; import { LiveCapture } from '../components/LiveCapture.js'; import { conclusions } from '../lib/analysis.js'; import './style.css'; const initial:Session={id:'',url:'',phase:'idle',started:null,requests:0,responses:0,changesTotal:0,records:[],endpoints:[],changes:[],logs:[],error:null,lastDom:null,lastVisible:null,comparisons:[],source:'isolated',watches:[],liveValues:[],valueUpdates:[]}; const ms=(v:number|null|undefined)=>v==null?'—':`${v.toLocaleString(undefined,{maximumFractionDigits:1})} ms`; const time=(v:number|null)=>v?new Date(v).toLocaleTimeString():'—'; const average=(a:(number|null|undefined)[])=>{const n=a.filter((v):v is number=>typeof v==='number');return n.length?n.reduce((a,b)=>a+b,0)/n.length:null;}; function App(){ const [s,setS]=useState(initial),[url,setUrl]=useState(''),[tab,setTab]=useState('Endpoints'),[error,setError]=useState(''),[connected,setConnected]=useState(false),[busy,setBusy]=useState(false),[search,setSearch]=useState(''),[selected,setSelected]=useState(null),[label,setLabel]=useState(''),[view,setView]=useState('overview'); useEffect(()=>{const events=new EventSource('/api/events');events.onmessage=e=>{setS(JSON.parse(e.data));setConnected(true);};events.onerror=()=>setConnected(false);return()=>events.close();},[]); async function action(name:string,body:unknown={}){setBusy(true);setError('');try{const r=await fetch(`/api/${name}`,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)});const data=await r.json();if(!r.ok)throw new Error(data.error);if(name==='clear'){setSelected(null);setS(initial);}return data as {token?:string};}catch(e){setError(e instanceof Error?e.message:'Unable to reach the local server.');}finally{setBusy(false);}} const active=['starting','monitoring'].includes(s.phase),api=s.records.filter(r=>r.api),complete=api.filter(r=>r.duration!==null),avg=average(complete.map(r=>r.duration)),last=s.changes[0],total=average(complete.filter(r=>r.visibleDelay!==undefined).map(r=>(r.duration??0)+r.visibleDelay!)); const timingRows=complete.length?complete:s.records.filter(r=>r.duration!==null); const filtered=s.records.filter(r=>`${r.endpoint} ${r.method} ${r.status} ${r.kind}`.toLowerCase().includes(search.toLowerCase())); return
WORKSPACE / {view==='comparison'?'Compare sessions':'Analysis overview'}
⌘   LOCALHOST ONLY
BROWSER OBSERVABILITY

Web Latency & Real-Time Data Analyzer

Follow the request. Find the delay.

LOCAL-FIRST v1.0

◎ Website analyzer

◇   Isolated browser · memory-only
{e.preventDefault();void action('start',{url});}}>
{s.phase==='idle'?'Ready to analyze':s.phase==='starting'?'Launching browser…':s.phase==='monitoring'?'Monitoring':s.phase==='error'?'Error':'Stopped'}{s.url&&{s.url}}Client-observed measurements · no saved credentials
{(error||s.error||!connected)&&
{error||s.error||'Connecting to the local observer engine…'}
}
{view==='comparison'?

Compare two observed sessions

MEMORY ONLY

Save a summary, stop the analysis, then run a second session. The latest two snapshots stay in memory until cleared. Values reflect the retained request window.

setLabel(e.target.value)} placeholder="Session label, e.g. baseline" maxLength={60}/>
{s.comparisons.map((c,i)=>)}{(['dns','connect','ttfb','duration','rendering','total'] as const).map(k=>{s.comparisons.map(c=>)})}
MEASUREMENT{i===0?'A':'B'} · {c.label}
{{dns:'DNS',connect:'Connection (includes TLS)',ttfb:'Time to first byte',duration:'Completed response',rendering:'Correlated frame callback',total:'Correlated request → frame'}[k]}{ms(c[k])}
{!s.comparisons.length&&}
:<>

Response latency

Completed requests over time

Duration · ms
Last 60 completed requestsPeak {ms(s.records.reduce((max,r)=>r.duration===null?max:Math.max(max??0,r.duration),null))}

Latency breakdown

OBSERVED
{[['DNS lookup',average(timingRows.map(r=>r.timing.dns))],['Connection · includes TLS',average(timingRows.map(r=>r.timing.connect))],['Time to first byte',average(timingRows.map(r=>r.timing.ttfb))],['Response download',average(timingRows.map(r=>r.timing.download))],['Browser processing',null],['Response → frame¹',average(complete.map(r=>r.visibleDelay))]].map(([name,val])=>
{name}{ms(val as number|null)}
)}
Request → frame¹{ms(total)}

¹ Temporal correlation, not causal latency. DNS/connection samples include reused connections. Processing cannot be measured reliably.

{['Endpoints','Network','Changes','Logs'].map(t=>)}
{tab==='Network'&&setSearch(e.target.value)}/>}{active?'LIVE':'SESSION'}
{tab==='Endpoints'&&
{s.endpoints.map(e=>)}
ENDPOINTMETHOD / TYPESTATUSRESPONSEPOLLINGLAST CHANGE
{e.url}{e.contentType||'Awaiting response'}{e.method}{e.kind} · {e.count} observed{ms(e.duration)}{e.polling?`≈ ${(e.polling/1000).toFixed(1)} s`:'—'}{time(e.lastChange)}
{!s.endpoints.length&&}
} {tab==='Network'&&
{filtered.map(r=>)}
TIMEREQUESTTYPESTATUSDURATIONSIZE
{time(r.started)}{r.method} {r.endpoint}{r.failure&&{r.failure}}{r.kind}{ms(r.duration)}{r.size===null?'—':`${(r.size/1024).toFixed(1)} KB`}
{!filtered.length&&}
} {tab==='Changes'&&
{s.changes.map(c=>
⌁Response fingerprint changed

{c.endpoint}

PREVIOUS {c.previous.slice(0,20)}…→NEW {c.current.slice(0,20)}…
{c.transport} · {c.size} bytes · response payloads are not retained
)}{!s.changes.length&&}
} {tab==='Logs'&&
{s.logs.map((l,i)=>
{l.level}{l.message}
)}{!s.logs.length&&}
}
Query values, credentials, response bodies and sensitive headers are withheld.600-request window

⌕ WHERE IS THE DELAY?

EVIDENCE-BASED FINDINGS
{conclusions(s).map((f,i)=>
{String(i+1).padStart(2,'0')}

{f.title}

{f.detail}

)}

Observed browser journey

{[['01','Request started','Browser timestamp'],['02','Response received','Headers / completion'],['03','Data processed','Unavailable without instrumentation'],['04','DOM changed','Page-wide mutation'],['05','Frame callback','Double requestAnimationFrame']].map(([n,title,desc])=>
{n}{title}{desc}
)}

DOM and frame events are matched to the latest API response within two seconds. Relevant-element changes and actual screen paint are not established. Last DOM event: {time(s.lastDom)} · Last frame callback: {time(s.lastVisible)}

}
PROJAWAL • Local web-performance researchObservation only. Website access restrictions remain respected.
{selected&&
setSelected(null)}>
e.stopPropagation()}>

Request details

{selected.method} {selected.endpoint}

{Object.entries({Started:time(selected.started),'Response headers':time(selected.responded),Completed:time(selected.finished),Duration:ms(selected.duration),DNS:ms(selected.timing.dns),Connection:ms(selected.timing.connect),TLS:ms(selected.timing.tls),TTFB:ms(selected.timing.ttfb),Download:ms(selected.timing.download),'DOM correlation':ms(selected.domDelay),'Frame correlation':ms(selected.visibleDelay)}).map(([k,v])=>
{k}{v}
)}

Public cache / infrastructure headers

{JSON.stringify(selected.headers,null,2)}

TLS overlaps connection time. TTFB includes server and network time. Processing and exact publication time are unavailable.

}
; } function Stat({label,value,foot,icon}:{label:string;value:string;foot:string;icon:string}){return
{label}{icon}
{value}{foot}
;} function Status({value}:{value:number|null}){return value===null?—:{value};} function Empty({title,text}:{title:string;text:string}){return
⌁{title}

{text}

;} createRoot(document.getElementById('root')!).render();