Files
OpenSquawk/shared/utils/intervalWorker.ts
itsrubberduck cadc0aefb6 fix(websim): keep bridge connected in background tabs, fix PFD clipping and layout
- Move physics and telemetry/status ticking from requestAnimationFrame/setInterval
  to a Web Worker (new shared/utils/intervalWorker.ts) — the intended usage is
  WebSim in one tab and /live-atc in another, so the WebSim tab is almost always
  backgrounded, and rAF stops entirely / setInterval throttles to ~1/min in that
  state, dropping the bridge connection after a few minutes.
- Surface bridge failures in the header badge (connecting/error/connected) instead
  of showing "verbindet…" forever when telemetry POSTs are failing.
- Replace the PFD's hardcoded 0.85 scale with a ResizeObserver-driven fit so it
  no longer clips the VS tape/altitude readout at typical panel widths.
- Give the sidestick pad the same panel shell as the FCU/radio panels and stretch
  all three to equal height.
- ExteriorView: camera height now reacts to altitude, the runway renders at its
  real distance/lateral offset (was a fixed spot only shown under 3 NM) with
  centerline/threshold markings, widened for visibility from the 12 NM range
  it's now shown from, plus fog and a gradient sky.

Verified live: bridge badge stays green, PFD fits inside its panel at 1280x720,
and the runway becomes visible on approach for all four spawn presets.
2026-07-16 23:48:18 +02:00

37 lines
1.3 KiB
TypeScript

// A plain Worker's timers are not subject to the background-tab throttling
// that setInterval/requestAnimationFrame get on the main thread (Chrome clamps
// background-tab setInterval down to ~1/min after a few minutes; rAF stops
// entirely) — used by anything in WebSim that must keep ticking while its tab
// is backgrounded (the intended usage: WebSim in one tab, /live-atc in
// another). docs/plans/2026-07-16-codex-fixes-live-atc-loop-websim.md WP2 Fix 1.
export function createIntervalWorker(intervalMs: number, onTick: () => void): { stop: () => void } | null {
if (typeof Worker === 'undefined') return null
const source = `
let timer = null
self.onmessage = (e) => {
if (e.data === 'start') {
timer = setInterval(() => self.postMessage('tick'), ${intervalMs})
} else if (e.data === 'stop') {
if (timer !== null) clearInterval(timer)
self.close()
}
}
`
const blob = new Blob([source], { type: 'application/javascript' })
const url = URL.createObjectURL(blob)
const worker = new Worker(url)
worker.onmessage = (e: MessageEvent) => {
if (e.data === 'tick') onTick()
}
worker.postMessage('start')
return {
stop: () => {
worker.postMessage('stop')
worker.terminate()
URL.revokeObjectURL(url)
},
}
}