G1000: two-way sim sync, more PFD/MFD fidelity, authentic dialogs

Sync (FlyWithLua companions in plugins/ + server/fmssync.js):
- FMS flight-plan two-way sync (App <-> in-sim FMS) via fms-sync.lua
- G1000 UI-state publish (page/range/inset) via ui-sync.lua + CDI source,
  baro, map-range follow
- Terrain awareness: elevation grid probe (terrain-probe.lua) -> red/yellow
  MFD overlay vs aircraft altitude

PFD:
- AFCS mode annunciation bar from autopilot _status datarefs
- CDI source GPS/VLOC colouring, BRG1/BRG2 pointers + DME windows, marker beacons
- magenta speed/altitude trend vectors, selected-altitude alerting
- time-based (frame-rate-independent) smoothing for attitude/heading/tapes

MFD:
- nav data bar (DTK/ETE/active leg), airways overlay from earth_awy.dat,
  compass rose anchored to the ownship

Dialogs (NEAREST/FLIGHTPLAN/DIRECT-TO/PROCEDURES):
- flat, square, embedded G1000 look (no shadow/rounded/transparency)
- compact lower-right placement, no close X (softkey toggles), single window
- NEAREST 2-line entries (ILS/VFR, COM freq, runway length), PROC action menu

Service worker: network-first HTML so reloads pick up new builds (cache v2).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-02 02:17:06 +02:00
parent 354ea5d44b
commit 38b048ad41
23 changed files with 1707 additions and 213 deletions
+48
View File
@@ -0,0 +1,48 @@
import React from 'react';
import { num } from '../api/useXplane.js';
// Touch-friendly radio tuner, styled like our KAP 140 (green LCD) + the desktop
// launcher (macOS-dark chrome). Tunes the STANDBY frequency and swaps it active
// via X-Plane's own per-radio commands (no unit-sensitive frequency writes).
const fmt = (v, isCom) => (num(v) / 100).toFixed(isCom ? 3 : 2);
export default function RadioTuner({ values, command, radio, onClose }) {
const { id, label, isCom } = radio;
const sb = values[`${id}Sb`];
const act = values[id];
const cmd = (s) => command(`${id}${s}`);
return (
<div className="dlg-backdrop" onClick={onClose}>
<div className="rtuner" onClick={(e) => e.stopPropagation()}>
<div className="rt-head">
<span className="rt-title">{label}</span>
<span className="rt-kind">{isCom ? 'COM' : 'NAV'}</span>
</div>
<div className="rt-lcd">
<div className="rt-f"><span>ACTIVE</span><b className="act">{fmt(act, isCom)}</b></div>
<button className="rt-swap" onClick={() => cmd('Swap')} title="Aktiv ↔ Standby"></button>
<div className="rt-f right"><span>STANDBY</span><b className="sby">{fmt(sb, isCom)}</b></div>
</div>
<div className="rt-tune">
<div className="rt-row">
<span className="rt-unit">MHz</span>
<button className="rt-step" onClick={() => cmd('CoarseDown')}></button>
<button className="rt-step" onClick={() => cmd('CoarseUp')}>+</button>
</div>
<div className="rt-row">
<span className="rt-unit">kHz</span>
<button className="rt-step" onClick={() => cmd('FineDown')}></button>
<button className="rt-step" onClick={() => cmd('FineUp')}>+</button>
</div>
</div>
<div className="rt-actions">
<button className="rt-btn primary" onClick={() => cmd('Swap')}> Auf Aktiv</button>
<button className="rt-btn" onClick={onClose}>Schließen</button>
</div>
</div>
</div>
);
}