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:
2026-06-01 15:07:03 +02:00
commit ebc33a78b7
110 changed files with 14671 additions and 0 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

+16
View File
@@ -0,0 +1,16 @@
{
"name": "G1000 Glass Cockpit",
"short_name": "G1000",
"description": "X-Plane 12 G1000 glass cockpit — PFD / MFD / FMS over your LAN",
"start_url": "/",
"scope": "/",
"display": "standalone",
"orientation": "landscape",
"background_color": "#000000",
"theme_color": "#000000",
"icons": [
{ "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png" },
{ "src": "/icons/icon-512-maskable.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
]
}
+31
View File
@@ -0,0 +1,31 @@
// Minimal service worker: caches the app shell so the cockpit launches fast and
// survives brief network blips. Live data (the bridge WebSocket, /api, and map
// tiles) is never cached — only same-origin GET app assets.
const CACHE = 'g1000-shell-v1';
self.addEventListener('install', () => self.skipWaiting());
self.addEventListener('activate', (e) => {
e.waitUntil(
caches.keys().then((keys) => Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k))))
.then(() => self.clients.claim())
);
});
self.addEventListener('fetch', (e) => {
const url = new URL(e.request.url);
// Only same-origin GET app shell; skip the API and let the WS pass through.
if (e.request.method !== 'GET' || url.origin !== location.origin) return;
if (url.pathname.startsWith('/api') || url.pathname === '/ws') return;
// Stale-while-revalidate: serve cache fast, refresh in the background.
e.respondWith(
caches.open(CACHE).then(async (cache) => {
const cached = await cache.match(e.request);
const network = fetch(e.request)
.then((res) => { if (res && res.ok) cache.put(e.request, res.clone()); return res; })
.catch(() => cached);
return cached || network;
})
);
});