Initial commit: X-Plane G1000 web cockpit + bridge + Tauri desktop app
- server/: Node bridge (datarefs/commands, navdata, CIFP procedures, flight plan) - web/: React cockpit (PFD/MFD/Map, VFR six-pack, AFCS, FMS CDU), PWA, collapsible sidebar - desktop/: Tauri 2 launcher (Bun sidecar, system tray, updater) + Linux build via Docker - scripts/: prep-desktop, build-linux, Gitea release + latest.json Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+115
@@ -0,0 +1,115 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useXplane } from './api/useXplane.js';
|
||||
import PFD from './components/PFD.jsx';
|
||||
import AutopilotPanel from './components/AutopilotPanel.jsx';
|
||||
import MFD from './components/MFD.jsx';
|
||||
import MapView from './components/MapView.jsx';
|
||||
import CDU from './components/CDU.jsx';
|
||||
import VFR from './components/VFR.jsx';
|
||||
import Bezel from './components/Bezel.jsx';
|
||||
import DirectTo from './components/DirectTo.jsx';
|
||||
import Proc from './components/Proc.jsx';
|
||||
|
||||
// Compact line icons for the nav rail (stroke = currentColor).
|
||||
const ICONS = {
|
||||
pfd: 'M11 3a8 8 0 100 16 8 8 0 000-16zM3.5 11h15M7 8l1.5 1M15 8l-1.5 1',
|
||||
mfd: 'M3 6l5-2 6 2 5-2v12l-5 2-6-2-5 2zM8 4v12M14 6v12',
|
||||
map: 'M11 2c-3.3 0-6 2.6-6 5.9 0 4.4 6 11.1 6 11.1s6-6.7 6-11.1C17 4.6 14.3 2 11 2z',
|
||||
fms: 'M4 6h14M4 11h14M4 16h9',
|
||||
ap: 'M11 4a7 7 0 100 14 7 7 0 000-14zM11 4v3M11 15v3M4 11h3M15 11h3',
|
||||
vfr: 'M11 4a7 7 0 100 14 7 7 0 000-14zM11 11l4.5-3',
|
||||
};
|
||||
function Icon({ name }) {
|
||||
return (
|
||||
<svg className="snav-ic" viewBox="0 0 22 22" width="22" height="22" fill="none"
|
||||
stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d={ICONS[name]} />
|
||||
{name === 'map' && <circle cx="11" cy="8" r="2" />}
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
const TABS = [
|
||||
{ id: 'pfd', label: 'PFD' },
|
||||
{ id: 'mfd', label: 'MFD' },
|
||||
{ id: 'map', label: 'Map' },
|
||||
{ id: 'fms', label: 'FMS' },
|
||||
{ id: 'vfr', label: 'VFR' },
|
||||
{ id: 'ap', label: 'Autopilot' },
|
||||
];
|
||||
|
||||
export default function App() {
|
||||
const xp = useXplane();
|
||||
const [tab, setTab] = useState(() => location.hash.replace('#', '') || 'pfd');
|
||||
// Collapsible nav rail: narrow (icons) ↔ wide (icons + labels), remembered.
|
||||
const [navWide, setNavWide] = useState(() => localStorage.getItem('navWide') === '1');
|
||||
const go = (id) => { setTab(id); history.replaceState(null, '', `#${id}`); };
|
||||
const toggleNav = () => setNavWide((w) => { localStorage.setItem('navWide', w ? '0' : '1'); return !w; });
|
||||
// Synthetic-terrain (3D) vs. classic blue/brown attitude — toggled by the
|
||||
// PFD → SYN TERR softkey, exactly like the real XPLANE 1000.
|
||||
const [svt3d, setSvt3d] = useState(true);
|
||||
// The PFD INSET map (bottom-left) is off by default and toggled by its softkey.
|
||||
const [inset, setInset] = useState(false);
|
||||
// INSET map options (base layer + declutter), set from the INSET submenu.
|
||||
const [insetMode, setInsetMode] = useState({ base: 'topo', dcltr: 0 });
|
||||
// The NRST (nearest airports/navaids) window, toggled by the PFD NRST softkey.
|
||||
const [nrst, setNrst] = useState(false);
|
||||
// The TMR/REF (timer / references) window, toggled by the PFD TMR/REF softkey.
|
||||
const [tmr, setTmr] = useState(false);
|
||||
// MFD map mode (base layer), switched via the Map-Opt softkeys.
|
||||
const [mapMode, setMapMode] = useState({ base: 'topo' });
|
||||
// Direct-To (D→) dialog — opened from the bezel on either GDU.
|
||||
const [dto, setDto] = useState(false);
|
||||
// PROC (procedures: SID/STAR/approach) dialog — opened from the bezel.
|
||||
const [proc, setProc] = useState(false);
|
||||
|
||||
const connKind = xp.xpConnected ? 'ok' : xp.connected ? 'warn' : 'bad';
|
||||
const connText = xp.xpConnected ? 'X-PLANE' : xp.connected ? 'NO SIM' : 'OFFLINE';
|
||||
|
||||
return (
|
||||
<div className={`app ${navWide ? 'nav-wide' : 'nav-narrow'}`}>
|
||||
<aside className="sidebar">
|
||||
<button className="sb-top" onClick={toggleNav} title="Menü ein-/ausklappen">
|
||||
<span className="brand">G<span>1000</span></span>
|
||||
<span className="sb-chev">{navWide ? '◂' : '▸'}</span>
|
||||
</button>
|
||||
<nav className="snav">
|
||||
{TABS.map((t) => (
|
||||
<button key={t.id} className={tab === t.id ? 'snav-i active' : 'snav-i'}
|
||||
onClick={() => go(t.id)} title={t.label}>
|
||||
<Icon name={t.id} />
|
||||
<span className="snav-lbl">{t.label}</span>
|
||||
</button>
|
||||
))}
|
||||
</nav>
|
||||
<div className={`sb-conn ${connKind}`} title={connText}>
|
||||
<span className="dot" />
|
||||
<span className="snav-lbl">{connText}</span>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<main className="screen">
|
||||
{tab === 'pfd' && (
|
||||
<Bezel variant="pfd" xp={xp} svt3d={svt3d} onToggleSvt={() => setSvt3d((v) => !v)}
|
||||
inset={inset} onSetInset={setInset} insetMode={insetMode} onInsetMode={setInsetMode}
|
||||
nrst={nrst} onToggleNrst={() => setNrst((v) => !v)} onDirect={() => setDto(true)}
|
||||
tmr={tmr} onToggleTmr={() => setTmr((v) => !v)} onProc={() => setProc(true)}>
|
||||
<PFD values={xp.values} svt={svt3d} inset={inset} insetMode={insetMode} nrst={nrst} onCloseNrst={() => setNrst(false)}
|
||||
tmr={tmr} onCloseTmr={() => setTmr(false)} flightPlan={xp.flightPlan} fp={xp.fp} />
|
||||
</Bezel>
|
||||
)}
|
||||
{tab === 'mfd' && (
|
||||
<Bezel variant="mfd" xp={xp} mapMode={mapMode} onMapMode={setMapMode} onDirect={() => setDto(true)} onProc={() => setProc(true)}>
|
||||
<MFD values={xp.values} flightPlan={xp.flightPlan} fp={xp.fp} mapMode={mapMode} />
|
||||
</Bezel>
|
||||
)}
|
||||
{tab === 'map' && <MapView values={xp.values} flightPlan={xp.flightPlan} fp={xp.fp} />}
|
||||
{tab === 'fms' && <CDU xp={xp} />}
|
||||
{tab === 'vfr' && <VFR values={xp.values} />}
|
||||
{tab === 'ap' && <AutopilotPanel xp={xp} />}
|
||||
</main>
|
||||
{dto && <DirectTo xp={xp} onClose={() => setDto(false)} />}
|
||||
{proc && <Proc xp={xp} onClose={() => setProc(false)} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user