diff --git a/changelog.json b/changelog.json index 3cf280c..ebf8bd4 100644 --- a/changelog.json +++ b/changelog.json @@ -8,7 +8,8 @@ "Lookup cache: a TTL of 0 switches it off, so a callbook record you are correcting is re-read every time.", "TCI radios: when the rig forbids transmitting, PTT says so instead of doing nothing silently.", "WAJA carried Japan’s civil prefecture numbers instead of the JARL’s: 35 of the 47 references are renumbered.", - "Award references can be renumbered in the editor — the number was the one field it would not let you correct." + "Award references can be renumbered in the editor — the number was the one field it would not let you correct.", + "The compass fills the moment Station Control opens, instead of waiting out the rest of a polling interval." ], "fr": [ "Une entité qui est un seul groupe d’îles remplit désormais la référence IOTA toute seule, sans abonnement callbook.", @@ -16,7 +17,8 @@ "Cache des recherches : un TTL à 0 le désactive, pour relire à chaque fois une fiche callbook en cours de correction.", "Radios TCI : quand la radio interdit l’émission, le PTT le dit au lieu de ne rien faire en silence.", "WAJA portait les numéros civils des préfectures japonaises et non ceux de la JARL : 35 des 47 références sont renumérotées.", - "Les références d’un diplôme se renumérotent dans l’éditeur : le numéro était le seul champ qu’il refusait de corriger." + "Les références d’un diplôme se renumérotent dans l’éditeur : le numéro était le seul champ qu’il refusait de corriger.", + "La boussole se remplit dès l’ouverture de Station Control, au lieu d’attendre la fin d’un intervalle d’interrogation." ] }, { diff --git a/frontend/src/lib/rotorHeading.ts b/frontend/src/lib/rotorHeading.ts index 2f2aac1..19aad25 100644 --- a/frontend/src/lib/rotorHeading.ts +++ b/frontend/src/lib/rotorHeading.ts @@ -36,6 +36,14 @@ let timer: number | undefined; let inFlight = false; let lastAz: number | null = null; let lastMoveAt = 0; +// The last heading anyone received, replayed to whoever subscribes next. +// +// Without it, opening Station Control left the compass blank for one or two +// seconds while every other panel filled at once. Nothing was slow: the status +// bar already had the loop running with a tick pending, so a component mounting +// halfway through an idle interval simply waited out the rest of it. The +// heading was known the whole time — it just had nowhere to be read from. +let last: RotorHeading | null = null; function schedule(delay: number) { if (timer !== undefined) window.clearTimeout(timer); @@ -54,6 +62,7 @@ async function tick() { if (lastAz !== null && h.azimuth !== lastAz) lastMoveAt = Date.now(); lastAz = h.azimuth; } + last = h; subs.forEach((fn) => { try { fn(h); } catch { /* a subscriber must not stop the loop */ } }); } catch { // Leave the last heading alone: a single failed poll on a shared serial port @@ -69,6 +78,12 @@ async function tick() { // unsubscribe. The loop stops when the last subscriber leaves. export function subscribeRotorHeading(fn: (h: RotorHeading) => void): () => void { subs.add(fn); + // Hand over what is already known, at once. An Alpha SPID poll is an open, + // a read at 600 baud and a close, so even an immediate one takes a moment — + // the cached heading is what makes the compass appear with the panel rather + // than after it. In a microtask, so a subscriber is never called back before + // subscribeRotorHeading has returned to it. + if (last) { const h = last; queueMicrotask(() => { if (subs.has(fn)) fn(h); }); } if (timer === undefined && !inFlight) void tick(); return () => { subs.delete(fn);