From 39a3650b4e0a29c82ada1cf3a276a497f04d516d Mon Sep 17 00:00:00 2001 From: itsrubberduck Date: Fri, 10 Jul 2026 16:35:34 +0200 Subject: [PATCH] refactor(live-atc): extract FlightInfoSheet, SettingsSheet, BugReportDialog, DebugPanel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DebugPanel now owns the FAB, the drawer, and useDebugSimulation, and the page mounts it behind `v-if="debugMode"` — so on the normal cockpit path the whole simulation machinery is never constructed. That required moving `debugMode` out of useDebugSimulation and into the page: a flag cannot both live inside the thing it gates and be the gate. The settings sheet is the one place it is toggled. runFullSimulation's `debugMode = true` line went with it; nothing called that function, so the write was already unreachable. BugReportDialog takes the useBugReport handle as a single prop rather than a dozen individual ones. The composable stays owned by the page because the HUD's "Report issue" button starts the screenshot capture before the dialog exists. SettingsSheet carries its own copy of the .pm-seg segmented-control styles and routes the theme choice back through the page, so usePmTheme.setPreference still persists it to the cookie. Co-Authored-By: Claude Opus 4.8 --- .../live-atc/cockpit/BugReportDialog.vue | 154 ++++ .../live-atc/cockpit/DebugPanel.vue | 374 +++++++++ .../live-atc/cockpit/FlightInfoSheet.vue | 98 +++ .../live-atc/cockpit/SettingsSheet.vue | 190 +++++ app/composables/useDebugSimulation.ts | 4 - app/pages/live-atc.vue | 742 +----------------- 6 files changed, 858 insertions(+), 704 deletions(-) create mode 100644 app/components/live-atc/cockpit/BugReportDialog.vue create mode 100644 app/components/live-atc/cockpit/DebugPanel.vue create mode 100644 app/components/live-atc/cockpit/FlightInfoSheet.vue create mode 100644 app/components/live-atc/cockpit/SettingsSheet.vue diff --git a/app/components/live-atc/cockpit/BugReportDialog.vue b/app/components/live-atc/cockpit/BugReportDialog.vue new file mode 100644 index 0000000..e6dcabe --- /dev/null +++ b/app/components/live-atc/cockpit/BugReportDialog.vue @@ -0,0 +1,154 @@ + + + diff --git a/app/components/live-atc/cockpit/DebugPanel.vue b/app/components/live-atc/cockpit/DebugPanel.vue new file mode 100644 index 0000000..e486b26 --- /dev/null +++ b/app/components/live-atc/cockpit/DebugPanel.vue @@ -0,0 +1,374 @@ + + + + + diff --git a/app/components/live-atc/cockpit/FlightInfoSheet.vue b/app/components/live-atc/cockpit/FlightInfoSheet.vue new file mode 100644 index 0000000..1aae952 --- /dev/null +++ b/app/components/live-atc/cockpit/FlightInfoSheet.vue @@ -0,0 +1,98 @@ + + + diff --git a/app/components/live-atc/cockpit/SettingsSheet.vue b/app/components/live-atc/cockpit/SettingsSheet.vue new file mode 100644 index 0000000..8260d42 --- /dev/null +++ b/app/components/live-atc/cockpit/SettingsSheet.vue @@ -0,0 +1,190 @@ + + + + + diff --git a/app/composables/useDebugSimulation.ts b/app/composables/useDebugSimulation.ts index 62635b3..9ec17da 100644 --- a/app/composables/useDebugSimulation.ts +++ b/app/composables/useDebugSimulation.ts @@ -25,8 +25,6 @@ export function useDebugSimulation( } = engine const { setLastTransmission, clearLog, startDemoFlight } = deps - const debugMode = ref(true) - const simulationRunning = ref(false) const simulationTrace = ref([]) const simulationError = ref('') @@ -297,7 +295,6 @@ export function useDebugSimulation( simulationTrace.value = [] simulationError.value = '' recordedAtcStates.clear() - debugMode.value = true try { simulationTrace.value.push({ @@ -418,7 +415,6 @@ export function useDebugSimulation( } return { - debugMode, simulationRunning, simulationTrace, simulationError, diff --git a/app/pages/live-atc.vue b/app/pages/live-atc.vue index 328c00c..80b7475 100644 --- a/app/pages/live-atc.vue +++ b/app/pages/live-atc.vue @@ -488,564 +488,41 @@ - - + :engine="engine" + :session-label="sessionLabel" + :set-last-transmission="setLastTransmission" + :clear-log="clearLog" + :start-demo-flight="startDemoFlight" + /> - - - - - - - - - -
- - Flight info -
- -
- -
-
-

Active flight

-

{{ flightContext.callsign || 'N/A' }}

-

{{ flightContext.dep }} → {{ flightContext.dest }}

-
-
- - {{ flags.in_air ? 'IN-AIR' : 'GROUND' }} - -
- - EMERG - - - {{ flags.current_unit }} - -
-
-
- -
-
-

Stand

-

{{ vars.stand }}

-
-
-

Runway

-

{{ vars.runway }}

-
-
-

Squawk

-

{{ vars.squawk }}

-
-
-

SID

-

{{ vars.sid }}

-
-
- -
-
-

{{ flags.radio_checks_done || 0 }}

-

Radio Checks

-
-
-

{{ flags.off_schema_count || 0 }}

-

Off-Schema

-
-
-

{{ log.length }}

-

Transmissions

-
-
- - - Select new flight - -
-
-
- - - - - -
- - Settings -
- -
- -
- -
- - - -
-
- -
-
- - {{ speechSpeedLabel }} -
- -
- -
- - - - -
- -
-
-

Voice input

-

- Pre-recording keeps the mic listening in the background so the start of your transmission isn't clipped. -

-
- -
-
- - {{ prerecSeconds.toFixed(1) }}s -
- -
-
-
-
-
- - - - - - - Fehler melden - - -
- -

Danke! Bug Report wurde gesendet.

-
- -
- - - Abbrechen - - - Bug melden - - -
-
+ @@ -1134,8 +611,11 @@ import PttPad from '~/components/live-atc/cockpit/PttPad.vue' import { useAtisPlayback } from '~/composables/useAtisPlayback' import { usePttRecording } from '~/composables/usePttRecording' import { useBugReport } from '~/composables/useBugReport' -import { useDebugSimulation } from '~/composables/useDebugSimulation' import { useSimBridgeSync } from '~/composables/useSimBridgeSync' +import FlightInfoSheet from '~/components/live-atc/cockpit/FlightInfoSheet.vue' +import SettingsSheet from '~/components/live-atc/cockpit/SettingsSheet.vue' +import BugReportDialog from '~/components/live-atc/cockpit/BugReportDialog.vue' +import DebugPanel from '~/components/live-atc/cockpit/DebugPanel.vue' import { CHAIN_GROUPS, DRILL_SCENARIOS } from '../../shared/constants/scenarios' import FlightSourceStep from '~/components/live-atc/FlightSourceStep.vue' import FlightSelectStep from '~/components/live-atc/FlightSelectStep.vue' @@ -1287,27 +767,10 @@ const radioEffectsEnabled = ref(true) const readbackEnabled = ref(false) // ── Bug Report ─────────────────────────────────────────────────────────────── -const { - showBugReportDialog, - bugReportComment, - bugReportContact, - bugReportScreenshot, - bugReportArrows, - bugReportLoading, - bugReportCapturing, - bugReportError, - bugReportSuccess, - bugReportCanvasRef, - bugReportImgRef, - setupAnnotationCanvas, - onCanvasMouseDown, - onCanvasMouseMove, - onCanvasMouseUp, - onCanvasMouseLeave, - undoLastArrow, - openBugReport, - submitBugReport, -} = useBugReport(engine, { activeScenario }) +// Owned here rather than by the dialog: the HUD button starts the screenshot +// capture before the dialog ever renders. The dialog gets the whole handle. +const bugReport = useBugReport(engine, { activeScenario }) +const { bugReportCapturing, openBugReport } = bugReport // Layout / view state const activeTab = ref<'funk' | 'log'>('funk') @@ -1321,9 +784,11 @@ const showExpectedComm = computed( // Overlays / sheets opened from HUD controls const showFlightSheet = ref(false) const showSettingsSheet = ref(false) -const showDebugDrawer = ref(false) const hudStatusMenu = ref(false) +/** The one gate for developer tooling: DebugPanel is not mounted while this is off. */ +const debugMode = ref(true) + type PmTab = { id: 'funk' | 'log' label: string @@ -1595,25 +1060,6 @@ const { restoreBugReportState, } = session -// Debug drawer: scripted simulation + decision-trace inspectors. -const { - debugMode, - simulationRunning, - simulationTrace, - simulationError, - simulationStepCount, - completedPilotSteps, - timelineSteps, - timelineUsedFallback, - traceAutoSelection, - traceFallback, - debugState, - debugNextStates, - describeElimination, - formatTracePayload, - runFullSimulation, -} = useDebugSimulation(engine, { setLastTransmission, clearLog, startDemoFlight }) - onUnmounted(() => { stopAtisLoop() stopPrerecCapture() @@ -2263,110 +1709,6 @@ onUnmounted(() => { .pm-log-tab { display: none !important; } - /* No bottom nav on desktop: drop the FAB back down to the corner. */ - .debug-fab { - bottom: calc(env(safe-area-inset-bottom) + 16px); - } - .debug-drawer { - bottom: calc(env(safe-area-inset-bottom) + 16px); - } } -/* ========================================================================= - * Debug FAB + drawer (left, non-blocking, can stay open in parallel) - * ======================================================================= */ -.debug-fab { - position: fixed; - left: 16px; - /* Clear the mobile bottom nav (~64px) by default; desktop overrides below. */ - bottom: calc(env(safe-area-inset-bottom) + 78px); - z-index: 60; - display: inline-flex; - align-items: center; - justify-content: center; - width: 48px; - height: 48px; - border-radius: 16px; - border: 1px solid rgba(249, 115, 22, 0.45); - background: rgba(249, 115, 22, 0.16); - color: #fdba74; - box-shadow: 0 10px 30px rgba(0, 0, 0, 0.45); - cursor: pointer; - transition: background 160ms ease, color 160ms ease, transform 160ms ease, border-color 160ms ease; -} -.debug-fab:hover { - background: rgba(249, 115, 22, 0.26); - color: #fed7aa; - transform: translateY(-1px); -} -.debug-fab.is-open { - background: rgba(249, 115, 22, 0.35); - border-color: rgba(249, 115, 22, 0.7); - color: #fff; -} - -.debug-drawer { - position: fixed; - top: 72px; - left: 12px; - bottom: calc(env(safe-area-inset-bottom) + 76px); - width: min(420px, calc(100vw - 24px)); - z-index: 55; - display: flex; - flex-direction: column; - border-radius: 18px; - border: 1px solid rgba(255, 255, 255, 0.12); - background: rgba(8, 13, 24, 0.96); - box-shadow: 0 24px 60px rgba(0, 0, 0, 0.6); - backdrop-filter: blur(14px); - pointer-events: auto; -} -.debug-drawer-head { - display: flex; - align-items: center; - justify-content: space-between; - gap: 8px; - padding: 12px 14px; - border-bottom: 1px solid rgba(255, 255, 255, 0.08); -} -.debug-drawer-close { - display: inline-flex; - align-items: center; - justify-content: center; - width: 28px; - height: 28px; - border-radius: 8px; - color: rgba(255, 255, 255, 0.6); - background: transparent; - transition: background 120ms ease, color 120ms ease; -} -.debug-drawer-close:hover { - background: rgba(255, 255, 255, 0.08); - color: #fff; -} -.debug-drawer-body { - padding: 12px 14px; - overflow-y: auto; - -webkit-overflow-scrolling: touch; -} - -.debug-drawer-enter-active, -.debug-drawer-leave-active { - transition: transform 220ms ease, opacity 220ms ease; -} -.debug-drawer-enter-from, -.debug-drawer-leave-to { - transform: translateX(-12px); - opacity: 0; -} - -@media (max-width: 640px) { - .debug-drawer { - top: 64px; - left: 8px; - right: 8px; - width: auto; - bottom: calc(env(safe-area-inset-bottom) + 84px); - } -}