diff --git a/app/pages/pm.vue b/app/pages/pm.vue index b35a16e..49b2f19 100644 --- a/app/pages/pm.vue +++ b/app/pages/pm.vue @@ -508,6 +508,15 @@ + + mdi-help-circle-outline + {{ helpLang === 'de' ? 'Hilfe' : 'Help' }} + + + + + + + {{ helpContent.title }} + + {{ helpLang === 'de' ? 'EN' : 'DE' }} + + + + {{ helpContent.intro }} + + + {{ step.icon }} + + {{ step.title }} + {{ step.text }} + + + + + + + + {{ helpLang === 'de' ? 'Verstanden' : 'Got it' }} + + + + @@ -1483,8 +1522,58 @@ const STORAGE_KEYS = { vatsimId: 'pm_vatsim_id', prerecEnabled: 'pm_prerec_enabled', prerecSeconds: 'pm_prerec_seconds', + helpSeen: 'pm_help_seen', + helpLang: 'pm_help_lang', } as const +// ── Help / how-it-works overlay ──────────────────────────────────────────── +const HELP_CONTENT = { + de: { + title: 'Wie /pm funktioniert', + intro: 'Du bist der Pilot. Du sprichst per Funk mit der Flugsicherung (ATC) und arbeitest ein realistisches Szenario ab.', + steps: [ + { icon: 'mdi-radio-handheld', title: 'Frequenz einstellen', text: 'Stelle die aktive COM1-Frequenz auf den zuständigen Lotsen. Auf der falschen Frequenz kommt dein Funkspruch nicht durch.' }, + { icon: 'mdi-microphone', title: 'Sprechtaste (PTT)', text: 'Halte die Sprechtaste gedrückt, sprich, lass los. Beim Senden wird die Taste grün. Ein neuer Funkspruch unterbricht die laufende ATC-Ausgabe.' }, + { icon: 'mdi-repeat', title: 'Readback', text: 'Lies Anweisungen zurück (Frequenz, Piste, Squawk, Höhe …). Stimmt es nicht, sagt ATC „say again“. Nach 3 Versuchen wird übersprungen.' }, + { icon: 'mdi-alert-octagon', title: 'Notfall', text: 'Mit „Mayday“ oder „Pan-Pan“ rufst du einen Notfall aus, mit „cancel mayday“ beendest du ihn wieder.' }, + { icon: 'mdi-bug-outline', title: 'Fehler melden', text: 'Mit dem Bug-Knopf meldest du einen Fehler — die ganze Funksession wird mitgeschickt.' }, + ], + }, + en: { + title: 'How /pm works', + intro: 'You are the pilot. You talk to ATC over the radio and work through a realistic scenario.', + steps: [ + { icon: 'mdi-radio-handheld', title: 'Tune the frequency', text: 'Set the active COM1 frequency to the controller you need. On the wrong frequency your call will not get through.' }, + { icon: 'mdi-microphone', title: 'Push-to-talk (PTT)', text: 'Hold the talk button, speak, release. The pad turns green while you transmit. A new transmission cuts any ATC speech still playing.' }, + { icon: 'mdi-repeat', title: 'Read back', text: 'Read instructions back (frequency, runway, squawk, altitude …). If it is wrong ATC says “say again”. After 3 tries it is skipped.' }, + { icon: 'mdi-alert-octagon', title: 'Emergency', text: 'Say “Mayday” or “Pan-Pan” to declare an emergency, and “cancel mayday” to end it.' }, + { icon: 'mdi-bug-outline', title: 'Report a bug', text: 'Use the Bug button to report an issue — the whole radio session is attached.' }, + ], + }, +} as const + +const showHelp = ref(false) +const helpLang = ref<'de' | 'en'>('de') +const helpContent = computed(() => HELP_CONTENT[helpLang.value]) +const openHelp = () => { showHelp.value = true } +const dismissHelp = () => { + showHelp.value = false + try { window.localStorage.setItem(STORAGE_KEYS.helpSeen, '1') } catch { /* ignore */ } +} +const toggleHelpLang = () => { + helpLang.value = helpLang.value === 'de' ? 'en' : 'de' + try { window.localStorage.setItem(STORAGE_KEYS.helpLang, helpLang.value) } catch { /* ignore */ } +} +/** Open the help overlay automatically the first time a session is entered. */ +const maybeShowFirstRunHelp = () => { + if (typeof window === 'undefined') return + try { + const lang = window.localStorage.getItem(STORAGE_KEYS.helpLang) + if (lang === 'de' || lang === 'en') helpLang.value = lang + if (!window.localStorage.getItem(STORAGE_KEYS.helpSeen)) showHelp.value = true + } catch { /* ignore */ } +} + let restoringFromStorage = true const persistSelectedPlan = (plan: any | null) => { @@ -3595,6 +3684,7 @@ const startMonitoring = async (flightPlan: any, scenario: Scenario) => { selectedPlan.value = flightPlan currentScreen.value = 'monitor' persistSelectedPlan(flightPlan) + maybeShowFirstRunHelp() // Start from a known baseline; the first controller's frequency is tuned in // automatically below, once the initial state-walk tells us which pilot state
{{ helpContent.intro }}