diff --git a/app/components/HoldSelect.vue b/app/components/HoldSelect.vue
index 342251c..5694835 100644
--- a/app/components/HoldSelect.vue
+++ b/app/components/HoldSelect.vue
@@ -239,7 +239,8 @@ onBeforeUnmount(() => {
{{ opt.sublabel }}
-
Keine Optionen
+ Keine Optionen
+
diff --git a/app/pages/pm.vue b/app/pages/pm.vue
index d9def89..a445cb8 100644
--- a/app/pages/pm.vue
+++ b/app/pages/pm.vue
@@ -353,6 +353,20 @@
+
+
+
+
+
+
@@ -3857,6 +3885,42 @@ const onPresetSelectStandby = (opt: { value: string | number }) => {
if (entry) setStandbyFrequencyFromList(entry)
}
+// --- Manual frequency entry (free-tune any VHF airband channel) --------------
+const manualFreqActive = ref('')
+const manualFreqStandby = ref('')
+
+// Accepts inputs like "121.5", "121,500" or "118" and normalises to a valid
+// VHF airband frequency string (118.000–136.975). Returns null when invalid so
+// callers/UI can disable the action.
+function normalizeManualFreq(input: string): string | null {
+ const raw = input.trim().replace(',', '.')
+ if (!raw) return null
+ const num = Number(raw)
+ if (!Number.isFinite(num) || num < 118 || num >= 137) return null
+ return num.toFixed(3)
+}
+
+function applyManualFrequency(target: 'active' | 'standby', close?: () => void) {
+ const model = target === 'active' ? manualFreqActive : manualFreqStandby
+ const freq = normalizeManualFreq(model.value)
+ if (!freq) return
+
+ if (target === 'active') {
+ if (frequencies.value.active !== freq) {
+ // Tuning away from the current frequency — cut any in-progress ATC speech
+ // so the pilot no longer "hears" the controller on the old channel.
+ stopCurrentSpeech()
+ frequencies.value.standby = frequencies.value.active
+ frequencies.value.active = freq
+ }
+ } else {
+ frequencies.value.standby = freq
+ }
+
+ model.value = ''
+ close?.()
+}
+
// Readability / signal strength quick-select (top bar)
const readabilityOptions = [
{ value: 5, label: 'Excellent', sublabel: 'Readability 5', color: '#22c55e' },
@@ -4720,6 +4784,56 @@ onUnmounted(() => {
flex-shrink: 0;
}
+/* Manual free-tune frequency entry inside the freq hold-select menu */
+.freq-manual {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ margin-top: 4px;
+ padding-top: 6px;
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
+}
+.freq-manual-input {
+ flex: 1 1 auto;
+ min-width: 0;
+ padding: 7px 10px;
+ border-radius: 10px;
+ border: 1px solid rgba(255, 255, 255, 0.16);
+ background: rgba(255, 255, 255, 0.04);
+ color: #fff;
+ font-size: 13px;
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
+ outline: none;
+}
+.freq-manual-input::placeholder {
+ color: rgba(255, 255, 255, 0.35);
+ font-family: inherit;
+}
+.freq-manual-input:focus {
+ border-color: rgba(34, 211, 238, 0.6);
+ background: rgba(34, 211, 238, 0.06);
+}
+.freq-manual-btn {
+ flex-shrink: 0;
+ padding: 7px 12px;
+ border-radius: 10px;
+ border: 1px solid rgba(34, 211, 238, 0.4);
+ background: rgba(34, 211, 238, 0.12);
+ color: #67e8f9;
+ font-size: 11px;
+ font-weight: 700;
+ letter-spacing: 0.08em;
+ cursor: pointer;
+ transition: background 90ms ease, opacity 90ms ease;
+}
+.freq-manual-btn:hover {
+ background: rgba(34, 211, 238, 0.2);
+}
+.freq-manual-btn:disabled {
+ opacity: 0.4;
+ cursor: not-allowed;
+}
+
/* Preset hold-select trigger buttons (freq tab) */
.preset-action-chip {
display: inline-flex;