diff --git a/app/pages/flightlab/takeoff.vue b/app/pages/flightlab/takeoff.vue index 10d2432..8099611 100644 --- a/app/pages/flightlab/takeoff.vue +++ b/app/pages/flightlab/takeoff.vue @@ -383,12 +383,33 @@ const pageRoot = ref(null) const isFullscreen = ref(false) const canFullscreen = ref(false) const fullscreenEvents = ['fullscreenchange', 'webkitfullscreenchange', 'mozfullscreenchange', 'MSFullscreenChange'] +const ESC_DOUBLE_PRESS_WINDOW_MS = 450 + +let lastEscapeAt = 0 function onFullscreenChange() { if (typeof document === 'undefined') return isFullscreen.value = Boolean(document.fullscreenElement) } +function onGlobalKeydown(event: KeyboardEvent) { + if (event.key !== 'Escape') return + const now = Date.now() + const isQuickDoubleEscape = now - lastEscapeAt <= ESC_DOUBLE_PRESS_WINDOW_MS + lastEscapeAt = now + + if (initialSpeechTimeout) { + clearTimeout(initialSpeechTimeout) + initialSpeechTimeout = null + } + audio.skipSpeech() + + if (isQuickDoubleEscape && engine.hasSimConditions.value) { + engine.toggleAutoAdvance() + lastEscapeAt = 0 + } +} + async function toggleFullscreen() { if (typeof document === 'undefined') return try { @@ -443,7 +464,7 @@ watch(() => engine.currentPhase.value, (phase) => { const currentPhase = computed(() => engine.currentPhase.value) // Main phase IDs for sidebar stepper -const mainPhaseIds = ['welcome', 'briefing', 'runway', 'engines_pre', 'engines_spool', 'takeoff_roll', 'rotation', 'gear_retract', 'climb', 'climb_high', 'leveloff', 'debrief', 'end'] +const mainPhaseIds = ['welcome', 'seatbelt_on', 'briefing', 'runway', 'engines_pre', 'engines_spool', 'takeoff_roll', 'rotation', 'gear_retract', 'climb', 'climb_high', 'leveloff', 'seatbelt_off', 'debrief', 'end'] // Collect all unique sound IDs for preloading const allSoundIds = computed(() => { @@ -461,6 +482,7 @@ const allSoundIds = computed(() => { function getPhaseLabel(id: string): string { const labels: Record = { welcome: 'Willkommen', + seatbelt_on: 'Seatbelts EIN', briefing: 'Briefing', runway: 'Startbahn', engines_pre: 'Triebwerke (Vorb.)', @@ -471,6 +493,7 @@ function getPhaseLabel(id: string): string { climb: 'Steigflug', climb_high: 'Steigflug (hoch)', leveloff: 'Level-off', + seatbelt_off: 'Seatbelts AUS', debrief: 'Nachbesprechung', end: 'Ende', } @@ -676,6 +699,9 @@ onMounted(async () => { }) onFullscreenChange() } + if (typeof window !== 'undefined') { + window.addEventListener('keydown', onGlobalKeydown) + } // Speak initial welcome const phase = engine.currentPhase.value @@ -688,6 +714,9 @@ onMounted(async () => { }) onBeforeUnmount(() => { + if (typeof window !== 'undefined') { + window.removeEventListener('keydown', onGlobalKeydown) + } if (typeof document !== 'undefined') { fullscreenEvents.forEach((eventName) => { document.removeEventListener(eventName, onFullscreenChange) diff --git a/shared/composables/flightlab/useFlightLabEngine.ts b/shared/composables/flightlab/useFlightLabEngine.ts index 32d54a6..bc8c7c7 100644 --- a/shared/composables/flightlab/useFlightLabEngine.ts +++ b/shared/composables/flightlab/useFlightLabEngine.ts @@ -33,7 +33,7 @@ export function useFlightLabEngine(scenario: FlightLabScenario) { const currentPhase = computed(() => phasesMap.value.get(currentPhaseId.value) ?? null) // Count only main phases (not comfort/info branches) for progress - const mainPhaseIds = ['welcome', 'briefing', 'runway', 'engines_pre', 'engines_spool', 'takeoff_roll', 'rotation', 'gear_retract', 'climb', 'climb_high', 'leveloff', 'debrief', 'end'] + const mainPhaseIds = ['welcome', 'seatbelt_on', 'briefing', 'runway', 'engines_pre', 'engines_spool', 'takeoff_roll', 'rotation', 'gear_retract', 'climb', 'climb_high', 'leveloff', 'seatbelt_off', 'debrief', 'end'] const progress = computed(() => { const idx = mainPhaseIds.indexOf(currentPhaseId.value) diff --git a/shared/data/flightlab/takeoff-eddf.ts b/shared/data/flightlab/takeoff-eddf.ts index f39002d..5f0fed6 100644 --- a/shared/data/flightlab/takeoff-eddf.ts +++ b/shared/data/flightlab/takeoff-eddf.ts @@ -126,9 +126,9 @@ export const takeoffEddf: FlightLabScenario = { buttons: [ { id: 'seatbelt_on_done', - label: 'Seat Belt Signs sind EIN', + label: 'Seat Belt Signs sind EIN (warte auf Bestätigung)', icon: 'mdi-seatbelt', - next: 'briefing', + next: 'seatbelt_on', type: 'primary' }, ], @@ -748,9 +748,9 @@ export const takeoffEddf: FlightLabScenario = { buttons: [ { id: 'seatbelt_off_done', - label: 'Seat Belt Signs sind AUS', + label: 'Seat Belt Signs sind AUS (warte auf Bestätigung)', icon: 'mdi-seatbelt', - next: 'debrief', + next: 'seatbelt_off', type: 'primary' }, ],