Commit Graph

183 Commits

Author SHA1 Message Date
itsrubberduck
bcc749bdb5 refactor(live-atc): extract frequency/preset management to useFrequencyPresets
Moves radio frequency state, airport-frequency fetching, manual tuning,
swap, and hold-select presets out of live-atc.vue into a composable.
atisVariantLabel and FREQ_ROLE_LABEL also moved here since both the
frequency-preset labeling and the (still-inline) ATIS/radio-speech code
need them — avoids a circular dependency once those move out in later
tasks. Verified via typecheck plus a browser mount/redirect smoke test
(no component test harness exists in this repo).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-10 09:25:30 +02:00
itsrubberduck
cc521ce8e0 feat(live-atc): add shared RadarBackdrop component for navy/radar visual language
Extracted from start.vue's inline .start-bg__* treatment so /live-atc's
setup screens (Milestone 3) can reuse the same navy/radar/glow look
instead of duplicating the gradients/keyframes a third time.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-10 09:13:10 +02:00
itsrubberduck
cf9c81c23f refactor(live-atc): extract encodeWav to shared/utils, add tests
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-10 09:10:29 +02:00
itsrubberduck
c8a07b980f refactor(live-atc): extract bridge-telemetry normalization to shared/utils, add tests
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-10 09:09:17 +02:00
itsrubberduck
964e33414a refactor(live-atc): extract normalizeManualFreq to shared/utils, add tests
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-10 09:07:33 +02:00
itsrubberduck
9f0a51e678 atc header 2026-07-10 08:33:36 +02:00
itsrubberduck
9014955ce2 feat(start): add tile illustrations, radar background, equal-height cards 2026-07-10 08:05:14 +02:00
itsrubberduck
8a172f085a light mdoe fix 2026-07-10 07:46:52 +02:00
itsrubberduck
46437d975e feat(pm): trim nav bar — remove Feedback, move Theme into Settings
- Remove the Feedback link from /pm's nav entirely
- Theme (dark/light/system) now lives as a segmented control at the
  top of the Settings dialog instead of its own nav menu
- Help and Logout become icon-only and move to the far right,
  separated by a divider from Bug/Settings
2026-07-10 07:03:40 +02:00
itsrubberduck
5b6ffe4d73 feat(onboarding): add /start mode chooser, fix pilot-profile-setup UX
- Fix missing icon for the free-tier/self-host pricing option
- Make selected cards visually obvious (checkmark badge, stronger border)
- Add Back navigation between onboarding steps
- Network-experience screen: button reads "No, not yet" instead of an
  ambiguous "Continue" when nothing is selected
- classroom-introduction reverts to Classroom-only content; the
  Classroom/Live ATC chooser moves to a new /start page instead
- Remove the "Skip to Classroom" button in favor of a subtle text link
- New /start page: choose Classroom or Live ATC after login (when
  there's no specific redirect target) or right after finishing
  onboarding. Shows a "start with Classroom" hint only for first-time
  arrivals from pilot-profile-setup. Live ATC is locked for 24h after
  registration.
2026-07-09 23:53:12 +02:00
itsrubberduck
fbfc494f6d fix(pm): re-theme custom CSS classes missed by the light-mode pass
The first light-mode pass only covered Tailwind utility classes in the
template. ~40 hand-written classes (.pm-flow-node, .pm-readback-*,
.freq-*, .pm-seg-*, .pm-bottomnav, signal bars) still hardcoded
rgba(255,255,255,N)/#fff, so scenario chips, the readback panel, and
the frequency display were unreadable in light mode. Switched them to
var(--text/--t2/--t3/--border) and color-mix() so they follow the
theme. Debug panel (dev-only) left as-is.
2026-07-09 22:59:32 +02:00
itsrubberduck
6884c5c011 feat(classroom-intro): add Classroom/Live ATC alpha chooser
LiveATC is now in alpha, not closed testing, so the intro offers both
paths up front: Classroom (default, dry radio practice) or Live ATC
alpha test connected to the simulator, with an honest disclaimer that
it's not fully reliable yet and we want bug reports.
2026-07-09 17:26:29 +02:00
itsrubberduck
8fd643a748 feat(pm): add light/dark/system theme toggle
Defaults to dark (matching the rest of the app) with a menu to switch
to Light or System. Custom CSS-variable overrides handle the
hand-rolled Tailwind classes; a second Vuetify theme is swapped in
sync for Vuetify components. Scoped entirely to /pm.
2026-07-09 17:26:21 +02:00
itsrubberduck
9b245a3f07 fix(onboarding): also ask OS for X-Plane, not just 'other'
X-Plane runs on Windows/Mac/Linux same as any non-MSFS sim, so it
should trigger the OS follow-up too.
2026-07-09 17:26:11 +02:00
itsrubberduck
8bfc4d161c feat(onboarding): add pilot-profile-setup page with all 7 screens
Single-question-per-screen wizard (cockpit, radio confidence, stress
point, network experience, hangar tools + paid-tool duration, feature
wish capped at 2, funding preference) plus the reward callsign card
and skip handling. Verified end-to-end against a mocked API layer in
the browser (MongoDB Atlas isn't reachable from this sandbox).
2026-07-09 16:48:12 +02:00
leubeem
e32cee5f87 feat(pm): silence auto-advance timer for backend timeout states
Arm a timer whenever the session lands on a pilot state with
auto_advance_on_silence (from the runtime tree); on expiry call the backend
/timeout endpoint and apply the response through applyBackendDecision. Cleared
on pilot transmission, stale-guarded against session/state changes, and
deferred while PTT is recording so ATC never talks over the pilot.

This makes the tower-v1 airborne handoff work without a sim bridge: telemetry
fires it when connected, the silence timeout covers everyone else.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 17:18:43 +02:00
leubeem
04022ef654 Merge branch 'feat/telemetry-atc-backbone' 2026-07-06 17:07:32 +02:00
leubeem
6b8d2b9eb8 feat(pm): runway from live ATIS + bridge position for real taxi routes
- The VATSIM flight plan carries no runway, so the runway in use is now parsed
  from live ATIS text (DEP/ARR/EXPECT...APPROACH phrasing preferred, generic
  RWY mention as fallback, "7L" normalised to "07L"). Falls back to the
  engine-generated runway when no ATIS text mentions one. Local engine vars
  are patched so the HUD matches what ATC says.
- The bridge poller now tracks PLANE_LATITUDE/LONGITUDE; when the bridge is
  connected, the aircraft position is sent at session creation so the backend
  starts the departure taxi route at the real parking position instead of a
  random stand.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 17:00:21 +02:00
leubeem
834df29fbb fix(pm): stop sending placeholder taxi_route so backend computes it
The backend computes the real OSM taxi route from airport_icao + stand/runway
and falls back to the flow's YAML default on its own. Sending a placeholder
taxi_route counted as a caller override and suppressed that computation, so no
session ever got a computed route. Drop it from the create payload.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 09:08:54 +02:00
leubeem
25929b9c06 feat(pm): forward normalized sim telemetry to the backend decision engine
Send live bridge telemetry to the backend so it can drive proactive,
aircraft-state-aware ATC. Purely additive — bridge-less sessions are unchanged.

- NormalizedTelemetry contract + sendTelemetry() + telemetry_fired
  (useRadioBackend.ts), sim-agnostic and matched to the backend contract
- extract applyBackendDecision() so telemetry- and pilot-driven ATC responses
  land through one shared path
- normalizeBridgeTelemetry() + change-detection + forwardTelemetryToBackend()
  wired into the existing bridge poll

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 09:23:53 +02:00
leubeem
dfc836bf2d feat(pm): add Uncontrolled Field (Info) arrival to the Drills chooser (#38)
Surfaces the info-arrival-v1 flow as a standalone drill and gives it a
German D-registration callsign (same as VFR scenarios).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 16:28:06 +02:00
leubeem
3b79adf407 feat(pm): send ICAO for taxi-route compute + show start spinner
- useRadioBackend.createSession: forward airport_icao / destination_icao so the
  backend can compute the real OSM taxi route for taxi flows.
- pm.vue: pass the resolved airport ICAO from startMonitoring, and show a
  full-screen overlay ("Calculating taxi route…") while the session is being
  created — the backend computes taxi routes synchronously for taxi-only
  training, so the create call can take a few seconds.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 16:18:13 +02:00
leubeem
54e5029bc2 feat(pm): say-again, sign-off, readback-correct, drop stale transmissions
- "say again" / "repeat" → ATC re-speaks its last transmission locally (#8/#40)
- bare sign-off ("thank you", "tschüss", "good day" with no readback/digits)
  gets a polite "Good day" instead of being evaluated (#32)
- positive "Readback correct" confirmation prefixed when every required
  readback field matched, not only on the IFR clearance (#10)
- a newer pilot transmission supersedes an older one still awaiting its
  backend reply; the stale reply is dropped (#16)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 16:02:07 +02:00
leubeem
e21414e45a feat(pm): ignore sub-threshold voice transmissions (STT noise gate)
Whisper hallucinates short real words ("Test", "Thank you", "Okay") on
near-silent or noisy audio. Unfiltered these reached the backend as a wrong
readback attempt — counting toward the 3x-skip — and triggered paid
LLM-router calls. Drop voice (PTT) transcripts below NUXT_PUBLIC_PTT_MIN_WORDS
(default 2); text input is exempt so deliberate short commands still work.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 11:35:47 +02:00
leubeem
cecd27ea97 feat(pm): bilingual how-it-works help overlay
First-run overlay explaining how /pm works (tune frequency, push-to-talk,
read back, emergency, bug button), reopenable via a new "?" button in the
HUD. Shows one language with a DE/EN toggle; the choice and "seen" state
persist in localStorage so it auto-opens only on first use.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 10:21:33 +02:00
leubeem
4b07609c52 feat(pm): VFR registration callsigns and pronunciation (#28)
- VFR scenarios get a German D-registration (e.g. D-EKLM) and its
  abbreviated form (callsign_short, D-EKLM -> D-LM) instead of the airline
  callsign; the pilot's first call uses the full registration, ATC uses the
  short form thereafter.
- radioSpeech: spell aircraft registrations phonetically for TTS
  ("D-EKLM" -> "Delta Echo Kilo Lima Mike", "D-LM" -> "Delta Lima Mike").

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 10:17:52 +02:00
leubeem
e36f5b3277 feat(pm): add Rejected Take-Off drill to the scenario chooser (#14)
New standalone "Drills" section in the scenario picker, with the
Rejected Take-Off drill (rto-v1). Drills are surfaced separately from
the journey chains via DRILL_IDS / drillScenarios.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 06:34:59 +02:00
leubeem
5b46aed008 feat(pm): accept any published frequency for a position (#43)
The wrong-frequency gate now accepts any of the frequencies a logical
position publishes (e.g. EDDM has two Tower frequencies, 118.700 and
120.500); expectedFrequencyForState() still returns the primary for the
"contact X on <freq>" prompt. Falls back to the single expected value
when no frequency list resolves.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 06:21:48 +02:00
leubeem
9345bf3614 fix(pm): tester-round readback, frequency tuning, and PTT fixes
From /pm tester bug reports:
- pre-tune COM1 to the opening pilot state's frequency on scenario start,
  so the first call isn't met with a "wrong frequency" rejection (#5/#6/#21)
- taxi-route phonetics no longer stop at the first comma: "via A, V" now
  speaks "via Alfa, Victor" (#31)
- barge-in: keying the mic stops any ATC speech still playing
- ignore empty / punctuation-only transmissions (silence, stray PTT taps)
- PTT pad turns green while transmitting (was red)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 06:02:03 +02:00
leubeem
03a49c3b57 Merge branch 'main' of github.com:OpenSquawk/OpenSquawk 2026-06-21 21:00:20 +02:00
itsrubberduck
4ba3a41aea Add bridge connect PTT controls 2026-06-21 13:11:30 +02:00
itsrubberduck
8735f190f1 hotkey anzeige online 2026-06-20 03:25:06 +02:00
itsrubberduck
d9e9be2b54 hotkey support 2026-06-20 03:17:53 +02:00
itsrubberduck
6603677214 fix(pm): standby strictly mirrors sim while bridge connected
Standby used change-detection like active, so a locally tuned standby
lingered as "the last" channel. While the bridge is connected, standby now
always reflects COM_STANDBY_FREQUENCY from telemetry on every poll. Active
keeps its change-detection anchor so flow/manual tuning isn't overridden.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 13:40:20 +02:00
itsrubberduck
1c8febf4b7 fix(pm): preserve redirect when sending logged-out users to login
Unauthenticated /pm visits pushed to /login without a redirect query, so
after sign-in they landed on the classroom fallback instead of returning
to /pm. Pass route.fullPath as ?redirect= so the bridge link (incl.
?token=…) survives the login round-trip.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 12:55:12 +02:00
itsrubberduck
f4342c7015 feat(pm): mirror full COM1 panel (active + standby) from SimBridge
Sync both COM_ACTIVE_FREQUENCY and COM_STANDBY_FREQUENCY from bridge
telemetry into the radio, each tracked independently so they only retune
on an actual sim change. Active retune still cuts in-progress ATC speech;
standby has no audio side effects.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 12:40:26 +02:00
itsrubberduck
bcd48159b5 feat(pm): auto-tune radio from SimBridge telemetry via ?token
When /pm is opened with ?token=<bridge-token>, poll /api/bridge/live for
fresh telemetry. While the bridge keeps posting, mirror the sim's COM1
active frequency into the radio (only on actual sim change, so manual/flow
tuning isn't clobbered) and show a "Bridge connected" badge in the HUD.

Telemetry now carries COM_ACTIVE_FREQUENCY/COM_STANDBY_FREQUENCY from the
bridge's com_active_frequency/com_standby_frequency fields.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 12:30:23 +02:00
itsrubberduck
d275c027ff refactor(bridge): simplify connect page to a focused linking flow
Strip the overloaded two-step grid, status cards and elaborate success
overlay down to a single centered card with clear states: ask for code,
prompt login, link (auto), and a state-aware success view. Keep the
background photo (more visible) and the server log as a collapsible panel.
Attempt to auto-close the tab once linked.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 12:18:26 +02:00
itsrubberduck
71f5df0fbe fix(pm): clearer arrow-annotation instructions in bug report dialog
Explain plainly that testers drag an arrow to the broken spot (mouse or finger),
add an on-image hint until the first arrow is drawn, and switch the annotation
canvas to pointer events so touch dragging works as the instructions promise.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 10:58:26 +02:00
itsrubberduck
c16a30d7c1 fix(pm): make bug-report screenshots and state restore actually work
Screenshots: html2canvas 1.4.1 only parses rgb/hsl and throws on color-mix(),
which the app uses app-wide; the throw was swallowed so no screenshot was ever
captured. Swap to modern-screenshot (native SVG foreignObject — supports
color-mix/oklch), capture before opening the dialog, surface failures instead of
hiding them, and show a capture spinner on the Bug button.

State restore: the old handler only patched local vars/flags and loaded the tree
— it never entered the monitor screen or created a backend session, so nothing
happened. New restoreBugReportState() reuses startMonitoring() to spin up a real
session for the same flight/scenario from the snapshot, overlays saved
variables/flags, and replays the captured comm log. The Python backend has no
mid-session resume, so the session restarts at the flow start and the captured
state id is surfaced in a banner.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 10:43:55 +02:00
itsrubberduck
8a1d9e1ab6 feat(pm): add bug-report button with screenshot annotation and admin panel
- New BugReport MongoDB model (comment, contact, userId, screenshot, pmState, status)
- POST /api/bug-reports — authenticated submit; emails emanuel@faktorxmensch.com on receipt
- GET/PATCH /api/admin/bug-reports + /[id] — admin list, detail with screenshot, status toggle
- /pm: "Bug" button in HUD captures viewport screenshot (html2canvas), shows annotation
  canvas where testers can draw arrows; submits comment + contact + state snapshot
- /admin: new "Bug Reports" tab with open-count badge, screenshot expand, "Erledigt" toggle,
  and "In /pm öffnen" link that restores captured engine state via ?restoreBugReport=<id>

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 10:05:25 +02:00
leubeem
2749100938 fix(pm): forward arrival/shared variables to the backend session
backendVariables was a departure-shaped subset, so arrival flows (IFR and VFR)
ignored the selected flight and ran on YAML defaults for runway, QNH, surface
wind, aircraft type, cruise level and assigned squawk — even though the engine
generates them. Forward those (with name mapping qnh_hpa→qnh, acf_type→
aircraft_type, cruise_flight_level→cruise_level). Also fixes taxi/tower on
departure, which use runway/qnh/surface_wind.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 21:56:07 +02:00
leubeem
1c3c9ffaa6 fix(pm): require manual tuning at session start; name the correct frequency
Reverses the auto-tune: tuning to the first controller is part of the exercise.
Each scenario now starts from a fixed baseline frequency (121.900), and the
first call is rejected until the pilot dials the correct one — we do NOT silently
put them on the expected frequency.

The wrong-frequency reply now names the position and frequency to switch to,
e.g. "DLH6RK, you are on the wrong frequency. Contact Approach on 119.900."

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 15:28:10 +02:00
leubeem
1aec82fce5 fix(pm): tune radio to the starting position's frequency on session start
The initial active frequency was only set via a hardcoded EDDF special-case
(121.900); every other airport — and any arrival that begins on Center/Approach
rather than Delivery — kept a stale/default active frequency. That triggered an
immediate "check frequency" on the pilot's very first call (e.g. IFR enroute
start: on 121.900 but Center expects 121.800).

Now the radio is tuned to expectedFrequencyForState() for the start position
when the session begins, so the first call is always on the right frequency and
subsequent handoffs change to the correct next-position frequency.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 15:24:26 +02:00
leubeem
b627288f42 feat(pm): group scenario chooser by journey with a phase flow
Reworked the scenario picker so it's clear which single-phase practice belongs
to which chain. Scenarios are grouped by Departure / Arrival; each complete
chain renders as a left-to-right flow of its phases with arrows
(e.g. Clearance → Startup & Taxi → Tower → Departure). Tap any step to practise
just that phase, or "Fly full" to run the whole chain.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 15:09:07 +02:00
leubeem
82fe5a125d feat(pm): add IFR arrival scenarios to the chooser
Adds the IFR arrival to the PM scenario picker:
- Complete chain "IFR Arrival" (Enroute → Approach → Landing → Taxi-in),
  starting at ifr-enroute-arrival-v1.
- Individual phases: Enroute Descent, IFR Approach, IFR Landing
  (taxi-in already present). All use the arrival airport for frequencies.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 15:04:10 +02:00
leubeem
6fae54c89f feat(stt): seed Whisper prompt with expected readback + per-field debug UI
Whisper prompt seeding (per request):
- ptt.post.ts builds the prompt as generic ICAO bias + this state's expected
  readback appended LAST (survives the 224-token truncation), in both raw token
  form and spoken ICAO form via new radioSpeech.speakToken().
- pm.vue passes the expected phrase + active variable values; classroom.vue
  passes the lesson's expected field values.

Per-field readback debug:
- sttMatch.matchTranscriptionToFields returns fields[] (matched/missing + which
  view matched) plus normalized/denormalized transcription views.
- useRadioBackend types readback_report on the transmit response.
- pm.vue renders a "Readback check" panel in the right log rail; classroom.vue
  renders per-field rows under the STT panel.

Radio-pronunciation fixes (radioSpeech.ts):
- callsign expander handles multi-letter suffixes (DLH6RK -> Lufthansa six Romeo
  Kilo).
- toRadioSpeech now expands airports (EDDC -> Echo Delta Delta Charlie).
- bare altitudes >=1000 in a clearance context are spoken ("climb initially
  5000" -> "climb initially five thousand feet"); speeds/headings untouched.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 14:12:54 +02:00
leubeem
d40f82a10c Merge branch 'main' of github.com:OpenSquawk/OpenSquawk 2026-06-15 13:00:57 +02:00
itsrubberduck
e5cb27d5f1 manual frequencies 2026-06-12 10:14:38 +02:00
leubeem
868358b32b feat(pm): recover gracefully when the backend session expires
The radio backend now expires sessions idle for 5+ hours and may restart
without in-memory state. A 404 on transmit previously left the monitor
screen stuck; now it clears the session, returns to the scenario picker,
and shows a dismissible "session expired" alert there.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 23:17:14 +02:00