mirror of
https://github.com/OpenSquawk/OpenSquawk
synced 2026-08-06 09:16:26 +08:00
Simplify bridge onboarding pages
This commit is contained in:
@@ -3,251 +3,283 @@
|
||||
<div class="pointer-events-none absolute inset-0 bg-[radial-gradient(circle_at_top,_rgba(22,187,215,0.12),transparent_55%)]"/>
|
||||
<div class="pointer-events-none absolute inset-0 bg-[radial-gradient(circle_at_bottom,_rgba(79,70,229,0.12),transparent_60%)]"/>
|
||||
|
||||
<div class="relative mx-auto flex min-h-screen w-full max-w-6xl flex-col justify-center px-6 py-16 lg:px-12">
|
||||
<div class="space-y-10">
|
||||
<header class="text-center">
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.45em] text-[#16BBD7]">OpenSquawk Bridge</p>
|
||||
<h1 class="mt-4 text-3xl font-semibold sm:text-4xl">Bridge mit deinem Account verknüpfen</h1>
|
||||
<p class="mx-auto mt-4 max-w-3xl text-sm text-white/70">
|
||||
Dieser Bildschirm verbindet den lokal erzeugten Bridge-Token mit deinem OpenSquawk Account. Nach erfolgreicher
|
||||
Verknüpfung kannst du in die Bridge App zurückkehren – die Anwendung erkennt dich dann automatisch.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<div class="flex justify-center">
|
||||
<NuxtLink
|
||||
to="/bridge"
|
||||
class="inline-flex items-center gap-2 rounded-full bg-[#292D3B] px-5 py-2.5 text-sm font-medium text-white/80 transition hover:bg-[#353a4c] hover:text-white"
|
||||
>
|
||||
<span aria-hidden="true">←</span>
|
||||
Zur Bridge Übersicht
|
||||
</NuxtLink>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="!hasToken"
|
||||
class="rounded-3xl border border-red-500/30 bg-red-500/10 px-8 py-12 text-center shadow-[0_25px_80px_rgba(64,18,18,0.35)]"
|
||||
<div class="relative mx-auto flex min-h-screen w-full max-w-6xl flex-col gap-10 px-6 py-12 lg:px-12">
|
||||
<div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<NuxtLink
|
||||
to="/"
|
||||
class="inline-flex w-full items-center justify-center gap-2 rounded-2xl border border-white/10 bg-white/5 px-4 py-2 text-sm font-medium text-white transition hover:border-white/20 hover:bg-white/10 sm:w-auto"
|
||||
>
|
||||
<h2 class="text-2xl font-semibold">Token fehlt</h2>
|
||||
<p class="mt-4 text-sm text-red-100/80">
|
||||
Bitte öffne diesen Bildschirm direkt über die Bridge App. Nur so kann der Token automatisch übergeben werden.
|
||||
</p>
|
||||
<p class="mt-6 text-xs uppercase tracking-[0.4em] text-red-200/60">Bridge-Link neu starten</p>
|
||||
</div>
|
||||
<span aria-hidden="true">←</span>
|
||||
Back to opensquawk.de
|
||||
</NuxtLink>
|
||||
<NuxtLink
|
||||
to="/bridge"
|
||||
class="inline-flex w-full items-center justify-center gap-2 rounded-2xl bg-[#292D3B] px-4 py-2 text-sm font-semibold text-white transition hover:bg-[#353a4c] sm:w-auto"
|
||||
>
|
||||
Bridge landing
|
||||
</NuxtLink>
|
||||
</div>
|
||||
|
||||
<div v-else class="grid gap-6 lg:grid-cols-[1.35fr_1fr]">
|
||||
<div class="space-y-6">
|
||||
<section
|
||||
class="rounded-3xl border border-white/10 bg-[#111832]/85 p-8 shadow-[0_30px_80px_rgba(5,10,30,0.55)] backdrop-blur"
|
||||
>
|
||||
<div class="flex flex-wrap items-center justify-between gap-3">
|
||||
<h2 class="text-xl font-semibold">Schritt 1 · Anmelden</h2>
|
||||
<span class="rounded-full bg-[#292D3B] px-3 py-1 text-xs uppercase tracking-[0.32em] text-white/70">Login</span>
|
||||
<header class="space-y-4 text-center">
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.45em] text-[#16BBD7]">OpenSquawk Bridge</p>
|
||||
<h1 class="text-3xl font-semibold sm:text-4xl">Connect your Bridge token</h1>
|
||||
<p class="mx-auto max-w-2xl text-sm text-white/70">
|
||||
Sign in, confirm the token from the desktop Bridge app, and then jump straight back into the simulator.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<div
|
||||
v-if="!hasToken"
|
||||
class="rounded-3xl border border-red-500/30 bg-red-500/10 px-8 py-12 text-center shadow-[0_25px_80px_rgba(64,18,18,0.35)]"
|
||||
>
|
||||
<h2 class="text-2xl font-semibold">Token missing</h2>
|
||||
<p class="mt-4 text-sm text-red-100/80">Open this page from the Bridge app so the token can be passed through automatically.</p>
|
||||
<p class="mt-6 text-xs uppercase tracking-[0.4em] text-red-200/60">Waiting for desktop link</p>
|
||||
</div>
|
||||
|
||||
<div v-else class="grid gap-6 lg:grid-cols-[1.35fr_1fr]">
|
||||
<div class="space-y-6">
|
||||
<section class="rounded-3xl border border-white/10 bg-[#111832]/85 p-8 shadow-[0_30px_80px_rgba(5,10,30,0.55)] backdrop-blur">
|
||||
<div class="flex flex-wrap items-center justify-between gap-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="flex h-11 w-11 items-center justify-center rounded-2xl bg-[#16BBD7]/10 text-[#16BBD7]">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
class="h-5 w-5"
|
||||
>
|
||||
<path d="M7 9V7a5 5 0 1110 0v2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<rect x="5" y="9" width="14" height="11" rx="3" ry="3" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12 14v3" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</span>
|
||||
<h2 class="text-xl font-semibold">Step 1 · Sign in</h2>
|
||||
</div>
|
||||
<p class="mt-3 text-sm text-white/70">
|
||||
Melde dich mit deinen OpenSquawk Zugangsdaten an. Ohne Anmeldung kann der Token nicht verknüpft werden.
|
||||
<span class="rounded-full bg-[#292D3B] px-3 py-1 text-[11px] uppercase tracking-[0.32em] text-white/70">Login</span>
|
||||
</div>
|
||||
<p class="mt-3 text-sm text-white/70">Use your OpenSquawk credentials to unlock the token linking step.</p>
|
||||
|
||||
<div
|
||||
v-if="isAuthenticated"
|
||||
class="mt-6 rounded-2xl border border-[#16BBD7]/40 bg-[#16BBD7]/10 px-5 py-4 text-sm"
|
||||
>
|
||||
<p class="font-medium text-[#16BBD7]">Signed in</p>
|
||||
<p class="mt-1 text-white/70">
|
||||
You are currently logged in as
|
||||
<span class="font-semibold text-white">{{ authDisplayName }}</span>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form v-else class="mt-6 space-y-5" @submit.prevent="submitLogin">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-white/70">Email</label>
|
||||
<input
|
||||
v-model.trim="loginForm.email"
|
||||
type="email"
|
||||
required
|
||||
autocomplete="email"
|
||||
class="mt-2 w-full rounded-2xl border border-white/10 bg-[#0B132A]/90 px-4 py-3 text-sm text-white placeholder-white/40 outline-none transition focus:border-[#16BBD7]/60 focus:ring-2 focus:ring-[#16BBD7]/60"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex items-center justify-between">
|
||||
<label class="block text-sm font-medium text-white/70">Password</label>
|
||||
<NuxtLink
|
||||
to="/forgot-password"
|
||||
class="text-xs font-semibold uppercase tracking-[0.28em] text-[#16BBD7] transition hover:text-[#72d9ea]"
|
||||
>
|
||||
Forgot?
|
||||
</NuxtLink>
|
||||
</div>
|
||||
<input
|
||||
v-model="loginForm.password"
|
||||
type="password"
|
||||
required
|
||||
autocomplete="current-password"
|
||||
class="mt-2 w-full rounded-2xl border border-white/10 bg-[#0B132A]/90 px-4 py-3 text-sm text-white placeholder-white/40 outline-none transition focus:border-[#16BBD7]/60 focus:ring-2 focus:ring-[#16BBD7]/60"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
class="inline-flex w-full items-center justify-center gap-2 rounded-2xl bg-[#16BBD7] px-5 py-3 text-sm font-semibold text-[#0B1020] transition hover:bg-[#13a7c4] focus:outline-none focus-visible:ring-2 focus-visible:ring-[#72d9ea] disabled:cursor-not-allowed disabled:bg-[#16BBD7]/60"
|
||||
:disabled="loginLoading"
|
||||
>
|
||||
<span v-if="loginLoading" class="flex items-center gap-2">
|
||||
<span class="h-4 w-4 animate-spin rounded-full border-2 border-[#0B1020]/30 border-t-[#0B1020]"/>
|
||||
Signing in …
|
||||
</span>
|
||||
<span v-else>Sign in</span>
|
||||
</button>
|
||||
|
||||
<p v-if="loginError" class="text-sm text-red-300">{{ loginError }}</p>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section class="rounded-3xl border border-white/10 bg-[#111832]/85 p-8 shadow-[0_30px_80px_rgba(5,10,30,0.55)] backdrop-blur">
|
||||
<div class="flex flex-wrap items-center justify-between gap-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="flex h-11 w-11 items-center justify-center rounded-2xl bg-[#16BBD7]/10 text-[#16BBD7]">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
class="h-5 w-5"
|
||||
>
|
||||
<path d="M8 12l-3 3 3 3" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M16 12l3 3-3 3" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M9 15h6" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M7.5 9A4.5 4.5 0 0112 4.5h0A4.5 4.5 0 0116.5 9" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</span>
|
||||
<h2 class="text-xl font-semibold">Step 2 · Link the token</h2>
|
||||
</div>
|
||||
<span class="rounded-full bg-[#16BBD7]/10 px-3 py-1 text-[11px] uppercase tracking-[0.32em] text-[#16BBD7]">Active</span>
|
||||
</div>
|
||||
<p class="mt-3 text-sm text-white/70">Confirm the token that the desktop Bridge generated for you.</p>
|
||||
|
||||
<div class="mt-6 flex flex-wrap items-center gap-3">
|
||||
<code class="rounded-2xl bg-[#0B132A]/80 px-4 py-2 font-mono text-sm text-[#72d9ea] shadow-inner">{{ token }}</code>
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex items-center gap-2 rounded-2xl border border-white/15 bg-white/5 px-4 py-2 text-xs font-medium text-white/70 transition hover:border-white/30 hover:text-white"
|
||||
@click="copyToken"
|
||||
>
|
||||
<span aria-hidden="true">⧉</span>
|
||||
<span>{{ copiedToken ? 'Copied' : 'Copy token' }}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="mt-8 space-y-4">
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex w-full items-center justify-center gap-2 rounded-2xl bg-[#16BBD7] px-5 py-3 text-sm font-semibold text-[#0B1020] transition hover:bg-[#13a7c4] focus:outline-none focus-visible:ring-2 focus-visible:ring-[#72d9ea] disabled:cursor-not-allowed disabled:bg-[#16BBD7]/60"
|
||||
:disabled="!isAuthenticated || connectLoading"
|
||||
@click="connectBridge"
|
||||
>
|
||||
<span v-if="connectLoading" class="flex items-center gap-2">
|
||||
<span class="h-4 w-4 animate-spin rounded-full border-2 border-[#0B1020]/30 border-t-[#0B1020]"/>
|
||||
Linking …
|
||||
</span>
|
||||
<span v-else>Link token</span>
|
||||
</button>
|
||||
|
||||
<p v-if="!isAuthenticated" class="text-sm text-white/60">Sign in first to enable the link button.</p>
|
||||
<p v-if="connectError" class="text-sm text-red-300">{{ connectError }}</p>
|
||||
|
||||
<div
|
||||
v-if="isAuthenticated"
|
||||
class="mt-6 rounded-2xl border border-[#16BBD7]/40 bg-[#16BBD7]/10 px-5 py-4 text-sm"
|
||||
v-if="successBannerVisible"
|
||||
class="rounded-2xl border border-emerald-400/40 bg-emerald-400/10 px-5 py-4 text-sm text-emerald-100 shadow-[0_0_35px_rgba(16,185,129,0.25)]"
|
||||
>
|
||||
<p class="font-medium text-[#16BBD7]">Bereits angemeldet</p>
|
||||
<p class="mt-1 text-white/70">
|
||||
Du bist aktuell als
|
||||
<span class="font-semibold text-white">{{ authDisplayName }}</span>
|
||||
angemeldet.
|
||||
</p>
|
||||
<p class="font-semibold">Linked successfully!</p>
|
||||
<p class="mt-1 text-emerald-100/80">You can close this tab and return to the Bridge app.</p>
|
||||
</div>
|
||||
|
||||
<form v-else class="mt-6 space-y-5" @submit.prevent="submitLogin">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-white/70">E-Mail</label>
|
||||
<input
|
||||
v-model.trim="loginForm.email"
|
||||
type="email"
|
||||
required
|
||||
autocomplete="email"
|
||||
class="mt-2 w-full rounded-2xl border border-white/10 bg-[#0B132A]/90 px-4 py-3 text-sm text-white placeholder-white/40 outline-none transition focus:border-[#16BBD7]/60 focus:ring-2 focus:ring-[#16BBD7]/60"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex items-center justify-between">
|
||||
<label class="block text-sm font-medium text-white/70">Passwort</label>
|
||||
<NuxtLink
|
||||
to="/forgot-password"
|
||||
class="text-xs font-semibold uppercase tracking-[0.28em] text-[#16BBD7] transition hover:text-[#72d9ea]"
|
||||
>
|
||||
Passwort vergessen?
|
||||
</NuxtLink>
|
||||
</div>
|
||||
<input
|
||||
v-model="loginForm.password"
|
||||
type="password"
|
||||
required
|
||||
autocomplete="current-password"
|
||||
class="mt-2 w-full rounded-2xl border border-white/10 bg-[#0B132A]/90 px-4 py-3 text-sm text-white placeholder-white/40 outline-none transition focus:border-[#16BBD7]/60 focus:ring-2 focus:ring-[#16BBD7]/60"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
class="inline-flex w-full items-center justify-center gap-2 rounded-2xl bg-[#16BBD7] px-5 py-3 text-sm font-semibold text-[#0B1020] transition hover:bg-[#13a7c4] focus:outline-none focus-visible:ring-2 focus-visible:ring-[#72d9ea] disabled:cursor-not-allowed disabled:bg-[#16BBD7]/60"
|
||||
:disabled="loginLoading"
|
||||
>
|
||||
<span v-if="loginLoading" class="flex items-center gap-2">
|
||||
<span class="h-4 w-4 animate-spin rounded-full border-2 border-[#0B1020]/30 border-t-[#0B1020]"/>
|
||||
Anmeldung läuft …
|
||||
</span>
|
||||
<span v-else>Anmelden</span>
|
||||
</button>
|
||||
|
||||
<p v-if="loginError" class="text-sm text-red-300">{{ loginError }}</p>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section
|
||||
class="rounded-3xl border border-white/10 bg-[#111832]/85 p-8 shadow-[0_30px_80px_rgba(5,10,30,0.55)] backdrop-blur"
|
||||
>
|
||||
<div class="flex flex-wrap items-center justify-between gap-3">
|
||||
<h2 class="text-xl font-semibold">Schritt 2 · Bridge verknüpfen</h2>
|
||||
<span class="rounded-full bg-[#16BBD7]/10 px-3 py-1 text-xs uppercase tracking-[0.32em] text-[#16BBD7]">
|
||||
Token aktiv
|
||||
</span>
|
||||
</div>
|
||||
<p class="mt-3 text-sm text-white/70">
|
||||
Der unten stehende Token stammt direkt aus deiner Bridge App. Verknüpfe ihn mit einem Klick mit deinem Account.
|
||||
</p>
|
||||
|
||||
<div class="mt-6 flex flex-wrap items-center gap-3">
|
||||
<code class="rounded-2xl bg-[#0B132A]/80 px-4 py-2 font-mono text-sm text-[#72d9ea] shadow-inner">
|
||||
{{ token }}
|
||||
</code>
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex items-center gap-2 rounded-2xl border border-white/15 bg-white/5 px-4 py-2 text-xs font-medium text-white/70 transition hover:border-white/30 hover:text-white"
|
||||
@click="copyToken"
|
||||
>
|
||||
<span aria-hidden="true">⧉</span>
|
||||
<span>{{ copiedToken ? 'Token kopiert' : 'Token kopieren' }}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="mt-8 space-y-4">
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex w-full items-center justify-center gap-2 rounded-2xl bg-[#16BBD7] px-5 py-3 text-sm font-semibold text-[#0B1020] transition hover:bg-[#13a7c4] focus:outline-none focus-visible:ring-2 focus-visible:ring-[#72d9ea] disabled:cursor-not-allowed disabled:bg-[#16BBD7]/60"
|
||||
:disabled="!isAuthenticated || connectLoading"
|
||||
@click="connectBridge"
|
||||
>
|
||||
<span v-if="connectLoading" class="flex items-center gap-2">
|
||||
<span class="h-4 w-4 animate-spin rounded-full border-2 border-[#0B1020]/30 border-t-[#0B1020]"/>
|
||||
Token wird verbunden …
|
||||
</span>
|
||||
<span v-else>Bridge jetzt verbinden</span>
|
||||
</button>
|
||||
|
||||
<p v-if="!isAuthenticated" class="text-sm text-white/60">
|
||||
Bitte melde dich zuerst an, um die Verknüpfung durchführen zu können.
|
||||
</p>
|
||||
<p v-if="connectError" class="text-sm text-red-300">{{ connectError }}</p>
|
||||
|
||||
<div
|
||||
v-if="successBannerVisible"
|
||||
class="rounded-2xl border border-emerald-400/40 bg-emerald-400/10 px-5 py-4 text-sm text-emerald-100 shadow-[0_0_35px_rgba(16,185,129,0.25)]"
|
||||
>
|
||||
<p class="font-semibold">Verbindung hergestellt!</p>
|
||||
<p class="mt-1 text-emerald-100/80">
|
||||
Du kannst dieses Fenster jetzt schließen und zur Bridge App zurückkehren. Statusupdates werden automatisch
|
||||
synchronisiert.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<aside>
|
||||
<section
|
||||
class="h-full rounded-3xl border border-white/10 bg-[#111832]/80 p-8 shadow-[0_30px_80px_rgba(4,8,24,0.55)] backdrop-blur"
|
||||
:class="{ 'opacity-80': statusLoading }"
|
||||
>
|
||||
<div class="flex flex-wrap items-center justify-between gap-3">
|
||||
<h3 class="text-lg font-semibold">Live Status</h3>
|
||||
<span class="rounded-full bg-white/5 px-3 py-1 text-[11px] uppercase tracking-[0.3em] text-white/50">
|
||||
Update alle 5s
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 space-y-4">
|
||||
<div class="rounded-2xl border border-white/10 bg-[#0B132A]/80 px-5 py-4 shadow-inner">
|
||||
<p class="text-sm font-semibold text-white/70">Verbindung</p>
|
||||
<p
|
||||
class="mt-1 text-base font-medium"
|
||||
:class="connectionStatus?.connected ? 'text-emerald-300' : 'text-white/50'"
|
||||
>
|
||||
{{ connectionLabel }}
|
||||
</p>
|
||||
<p v-if="connectionSubLabel" class="text-xs text-white/40">{{ connectionSubLabel }}</p>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-3 sm:grid-cols-2">
|
||||
<div
|
||||
class="flex items-center gap-3 rounded-2xl border px-4 py-3 transition"
|
||||
:class="connectionStatus?.simConnected
|
||||
? 'border-emerald-400/60 bg-emerald-400/10 shadow-[0_0_30px_rgba(16,185,129,0.35)]'
|
||||
: 'border-white/10 bg-[#0B132A]/70'"
|
||||
>
|
||||
<span
|
||||
class="h-3 w-3 rounded-full"
|
||||
:class="connectionStatus?.simConnected
|
||||
? 'bg-emerald-400 shadow-[0_0_18px_rgba(16,185,129,0.85)]'
|
||||
: 'bg-white/30'"
|
||||
/>
|
||||
<div>
|
||||
<p class="text-sm font-semibold">Simulator</p>
|
||||
<p class="text-xs text-white/60">
|
||||
{{ connectionStatus?.simConnected ? 'Simulator erkannt' : 'Kein Simulator aktiv' }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="flex items-center gap-3 rounded-2xl border px-4 py-3 transition"
|
||||
:class="connectionStatus?.flightActive
|
||||
? 'border-emerald-400/60 bg-emerald-400/10 shadow-[0_0_30px_rgba(16,185,129,0.35)]'
|
||||
: 'border-white/10 bg-[#0B132A]/70'"
|
||||
>
|
||||
<span
|
||||
class="h-3 w-3 rounded-full"
|
||||
:class="connectionStatus?.flightActive
|
||||
? 'bg-emerald-400 shadow-[0_0_18px_rgba(16,185,129,0.85)]'
|
||||
: 'bg-white/30'"
|
||||
/>
|
||||
<div>
|
||||
<p class="text-sm font-semibold">Flugstatus</p>
|
||||
<p class="text-xs text-white/60">
|
||||
{{ connectionStatus?.flightActive ? 'Flug aktiv' : 'Kein Flug erkannt' }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-2xl border border-white/10 bg-[#0B132A]/80 px-5 py-4 text-sm text-white/65">
|
||||
<div class="flex items-center justify-between">
|
||||
<span>Verbunden seit</span>
|
||||
<span class="font-medium text-white">{{ formattedConnectedAt }}</span>
|
||||
</div>
|
||||
<div class="mt-2 flex items-center justify-between text-white/60">
|
||||
<span>Letztes Status-Update</span>
|
||||
<span class="font-medium text-white">{{ formattedLastStatusAt }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p v-if="statusLoading" class="mt-6 text-xs uppercase tracking-[0.4em] text-white/45">Status wird aktualisiert …</p>
|
||||
<p v-if="statusError" class="mt-2 text-sm text-red-300">{{ statusError }}</p>
|
||||
</section>
|
||||
</aside>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<aside>
|
||||
<section
|
||||
class="h-full rounded-3xl border border-white/10 bg-[#111832]/80 p-8 shadow-[0_30px_80px_rgba(4,8,24,0.55)] backdrop-blur"
|
||||
:class="{ 'opacity-80': statusLoading }"
|
||||
>
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="flex h-11 w-11 items-center justify-center rounded-2xl bg-white/5 text-white/70">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
class="h-5 w-5"
|
||||
>
|
||||
<path d="M4 17l4-4 3 3 5-5 4 4" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M4 7h16" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M4 12h10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</span>
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold">Live status</h3>
|
||||
<p class="text-xs uppercase tracking-[0.32em] text-white/50">Refreshes every 5s</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 space-y-4">
|
||||
<div class="rounded-2xl border border-white/10 bg-[#0B132A]/80 px-5 py-4 shadow-inner">
|
||||
<p class="text-sm font-semibold text-white/70">Connection</p>
|
||||
<p
|
||||
class="mt-1 text-base font-medium"
|
||||
:class="connectionStatus?.connected ? 'text-emerald-300' : 'text-white/50'"
|
||||
>
|
||||
{{ connectionLabel }}
|
||||
</p>
|
||||
<p v-if="connectionSubLabel" class="text-xs text-white/40">{{ connectionSubLabel }}</p>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-3 sm:grid-cols-2">
|
||||
<div
|
||||
class="flex items-center gap-3 rounded-2xl border px-4 py-3 transition"
|
||||
:class="connectionStatus?.simConnected
|
||||
? 'border-emerald-400/60 bg-emerald-400/10 shadow-[0_0_30px_rgba(16,185,129,0.35)]'
|
||||
: 'border-white/10 bg-[#0B132A]/70'"
|
||||
>
|
||||
<span
|
||||
class="flex h-3 w-3 items-center justify-center rounded-full"
|
||||
:class="connectionStatus?.simConnected
|
||||
? 'bg-emerald-400 shadow-[0_0_18px_rgba(16,185,129,0.85)]'
|
||||
: 'bg-white/30'"
|
||||
/>
|
||||
<div>
|
||||
<p class="text-sm font-semibold">Simulator</p>
|
||||
<p class="text-xs text-white/60">
|
||||
{{ connectionStatus?.simConnected ? 'Simulator detected' : 'Waiting for simulator' }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="flex items-center gap-3 rounded-2xl border px-4 py-3 transition"
|
||||
:class="connectionStatus?.flightActive
|
||||
? 'border-emerald-400/60 bg-emerald-400/10 shadow-[0_0_30px_rgba(16,185,129,0.35)]'
|
||||
: 'border-white/10 bg-[#0B132A]/70'"
|
||||
>
|
||||
<span
|
||||
class="flex h-3 w-3 items-center justify-center rounded-full"
|
||||
:class="connectionStatus?.flightActive
|
||||
? 'bg-emerald-400 shadow-[0_0_18px_rgba(16,185,129,0.85)]'
|
||||
: 'bg-white/30'"
|
||||
/>
|
||||
<div>
|
||||
<p class="text-sm font-semibold">Flight</p>
|
||||
<p class="text-xs text-white/60">
|
||||
{{ connectionStatus?.flightActive ? 'Flight in progress' : 'No flight detected' }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-2xl border border-white/10 bg-[#0B132A]/80 px-5 py-4 text-sm text-white/65">
|
||||
<div class="flex items-center justify-between">
|
||||
<span>Connected since</span>
|
||||
<span class="font-medium text-white">{{ formattedConnectedAt }}</span>
|
||||
</div>
|
||||
<div class="mt-2 flex items-center justify-between text-white/60">
|
||||
<span>Last update</span>
|
||||
<span class="font-medium text-white">{{ formattedLastStatusAt }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p v-if="statusLoading" class="mt-6 text-xs uppercase tracking-[0.4em] text-white/45">Refreshing …</p>
|
||||
<p v-if="statusError" class="mt-2 text-sm text-red-300">{{ statusError }}</p>
|
||||
</section>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -273,7 +305,7 @@ interface BridgeStatusPayload {
|
||||
lastStatusAt: string | null
|
||||
}
|
||||
|
||||
useHead({ title: 'Bridge verbinden · OpenSquawk' })
|
||||
useHead({ title: 'Link Bridge · OpenSquawk' })
|
||||
|
||||
const route = useRoute()
|
||||
const auth = useAuthStore()
|
||||
@@ -312,10 +344,10 @@ const authDisplayName = computed(() => {
|
||||
|
||||
const connectionLabel = computed(() => {
|
||||
if (!connectionStatus.value?.connected) {
|
||||
return 'Wartet auf Verbindung'
|
||||
return 'Waiting for the desktop app'
|
||||
}
|
||||
const name = connectionStatus.value.user?.name || connectionStatus.value.user?.email
|
||||
return name ? `Verbunden als ${name}` : 'Verbunden'
|
||||
return name ? `Linked as ${name}` : 'Linked'
|
||||
})
|
||||
|
||||
const connectionSubLabel = computed(() => {
|
||||
@@ -338,7 +370,7 @@ function formatTimestamp(value: string | null) {
|
||||
if (Number.isNaN(parsed.getTime())) {
|
||||
return '—'
|
||||
}
|
||||
return new Intl.DateTimeFormat('de-DE', {
|
||||
return new Intl.DateTimeFormat('en-GB', {
|
||||
dateStyle: 'short',
|
||||
timeStyle: 'medium',
|
||||
}).format(parsed)
|
||||
@@ -348,7 +380,7 @@ async function submitLogin() {
|
||||
loginError.value = ''
|
||||
connectError.value = ''
|
||||
if (!loginForm.email || !loginForm.password) {
|
||||
loginError.value = 'Bitte E-Mail und Passwort eingeben.'
|
||||
loginError.value = 'Please enter your email and password.'
|
||||
return
|
||||
}
|
||||
|
||||
@@ -362,7 +394,7 @@ async function submitLogin() {
|
||||
err?.data?.statusMessage ||
|
||||
err?.response?._data?.statusMessage ||
|
||||
err?.message ||
|
||||
'Login fehlgeschlagen.'
|
||||
'Login failed.'
|
||||
} finally {
|
||||
loginLoading.value = false
|
||||
}
|
||||
@@ -390,7 +422,7 @@ async function connectBridge() {
|
||||
err?.data?.statusMessage ||
|
||||
err?.response?._data?.statusMessage ||
|
||||
err?.message ||
|
||||
'Verknüpfung fehlgeschlagen.'
|
||||
'Could not link the token.'
|
||||
} finally {
|
||||
connectLoading.value = false
|
||||
}
|
||||
@@ -424,7 +456,7 @@ async function fetchStatus(force = false) {
|
||||
err?.data?.statusMessage ||
|
||||
err?.response?._data?.statusMessage ||
|
||||
err?.message ||
|
||||
'Status konnte nicht geladen werden.'
|
||||
'We could not refresh the status.'
|
||||
} finally {
|
||||
statusRequestActive.value = false
|
||||
statusLoading.value = false
|
||||
@@ -441,7 +473,7 @@ async function copyToken() {
|
||||
copiedToken.value = false
|
||||
}, 2000)
|
||||
} catch {
|
||||
// Ignorieren – Kopie ist optional
|
||||
// Ignore copy errors – clipboard access is optional
|
||||
}
|
||||
}
|
||||
|
||||
@@ -500,4 +532,3 @@ code {
|
||||
word-break: break-all;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -3,23 +3,36 @@
|
||||
<div class="pointer-events-none absolute inset-0 bg-[radial-gradient(circle_at_top,_rgba(22,187,215,0.12),transparent_55%)]"/>
|
||||
<div class="pointer-events-none absolute inset-0 bg-[radial-gradient(circle_at_bottom,_rgba(14,165,233,0.18),transparent_65%)]"/>
|
||||
|
||||
<div class="relative mx-auto w-full max-w-6xl px-6 py-16 lg:px-12">
|
||||
<header class="text-center">
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.45em] text-[#16BBD7]">OpenSquawk Bridge</p>
|
||||
<h1 class="mt-4 text-3xl font-semibold sm:text-4xl">Deine Verbindung zwischen Simulator und OpenSquawk</h1>
|
||||
<p class="mx-auto mt-4 max-w-3xl text-sm text-white/70">
|
||||
Die OpenSquawk Bridge streamt Ereignisse aus deinem Simulator direkt zu unserem Netzwerk. Installiere die passende
|
||||
Version für deine Plattform, starte die App und verknüpfe sie in wenigen Klicks mit deinem Account.
|
||||
<div class="relative mx-auto w-full max-w-6xl px-6 py-12 lg:px-12">
|
||||
<div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<NuxtLink
|
||||
to="/"
|
||||
class="inline-flex w-full items-center justify-center gap-2 rounded-2xl border border-white/10 bg-white/5 px-4 py-2 text-sm font-medium text-white transition hover:border-white/20 hover:bg-white/10 sm:w-auto"
|
||||
>
|
||||
<span aria-hidden="true">←</span>
|
||||
Back to opensquawk.de
|
||||
</NuxtLink>
|
||||
<NuxtLink
|
||||
to="/bridge/connect"
|
||||
class="inline-flex w-full items-center justify-center gap-2 rounded-2xl bg-[#292D3B] px-4 py-2 text-sm font-semibold text-white transition hover:bg-[#353a4c] sm:w-auto"
|
||||
>
|
||||
Already have a token?
|
||||
</NuxtLink>
|
||||
</div>
|
||||
|
||||
<header class="mt-10 space-y-4 text-center sm:text-left">
|
||||
<span class="inline-flex items-center gap-2 rounded-full bg-[#16BBD7]/10 px-4 py-2 text-xs font-semibold uppercase tracking-[0.45em] text-[#16BBD7]">Bridge</span>
|
||||
<h1 class="text-3xl font-semibold sm:text-4xl">Download the OpenSquawk Bridge</h1>
|
||||
<p class="max-w-3xl text-sm text-white/70">
|
||||
Pick the build for your simulator and let the desktop Bridge handle the rest. Tokens, login, and live status are all baked in.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<div class="mt-12 grid gap-6 lg:grid-cols-[1.15fr_0.85fr]">
|
||||
<div class="mt-12 grid gap-6 lg:grid-cols-[1.2fr_0.8fr]">
|
||||
<section class="space-y-6">
|
||||
<div class="rounded-3xl border border-white/10 bg-[#111832]/80 p-8 shadow-[0_30px_80px_rgba(5,10,30,0.55)] backdrop-blur">
|
||||
<h2 class="text-xl font-semibold">Download-Zentrale</h2>
|
||||
<p class="mt-2 text-sm text-white/70">
|
||||
Wähle die Version deiner Bridge. Neue Simulatoren werden automatisch nachgereicht, sobald Builds verfügbar sind.
|
||||
</p>
|
||||
<h2 class="text-xl font-semibold">Downloads</h2>
|
||||
<p class="mt-2 text-sm text-white/70">Short and sweet. Click, install, fly.</p>
|
||||
|
||||
<div class="mt-6 grid gap-5 md:grid-cols-2">
|
||||
<article
|
||||
@@ -27,15 +40,58 @@
|
||||
:key="item.id"
|
||||
class="flex h-full flex-col justify-between rounded-3xl border border-white/10 bg-[#0B132A]/80 p-6 transition hover:border-white/20 hover:shadow-[0_25px_60px_rgba(12,18,40,0.5)]"
|
||||
>
|
||||
<div class="space-y-3">
|
||||
<span
|
||||
class="inline-flex items-center gap-2 rounded-full px-3 py-1 text-[11px] uppercase tracking-[0.35em]"
|
||||
:class="item.available ? 'bg-[#16BBD7]/20 text-[#16BBD7]' : 'bg-white/10 text-white/60'"
|
||||
>
|
||||
{{ item.badge }}
|
||||
<div class="space-y-4">
|
||||
<span class="flex h-12 w-12 items-center justify-center rounded-2xl bg-white/5 text-[#16BBD7]">
|
||||
<svg
|
||||
v-if="item.icon === 'plane'"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
class="h-6 w-6"
|
||||
>
|
||||
<path d="M2 13l10-3 10 3" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12 10V3" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12 14v7" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M7 21l5-7 5 7" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
<svg
|
||||
v-else-if="item.icon === 'future'"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
class="h-6 w-6"
|
||||
>
|
||||
<path d="M12 3l3 6 6 1-4.5 4.3L18 21l-6-3-6 3 1.5-6.7L3 10l6-1z" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
<svg
|
||||
v-else
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
class="h-6 w-6"
|
||||
>
|
||||
<path d="M4 17l8-11 8 11" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M4 7h16" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M4 12h16" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</span>
|
||||
<h3 class="text-lg font-semibold">{{ item.title }}</h3>
|
||||
<p class="text-sm text-white/65">{{ item.description }}</p>
|
||||
|
||||
<div class="space-y-2">
|
||||
<span
|
||||
class="inline-flex items-center gap-2 rounded-full px-3 py-1 text-[11px] uppercase tracking-[0.35em]"
|
||||
:class="item.available ? 'bg-[#16BBD7]/20 text-[#16BBD7]' : 'bg-white/10 text-white/60'"
|
||||
>
|
||||
{{ item.badge }}
|
||||
</span>
|
||||
<h3 class="text-lg font-semibold">{{ item.title }}</h3>
|
||||
<p class="text-sm text-white/65">{{ item.description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-6">
|
||||
@@ -47,7 +103,7 @@
|
||||
class="inline-flex w-full items-center justify-center gap-2 rounded-2xl bg-[#16BBD7] px-4 py-3 text-sm font-semibold text-[#0B1020] transition hover:bg-[#13a7c4] focus:outline-none focus-visible:ring-2 focus-visible:ring-[#72d9ea]"
|
||||
>
|
||||
<span aria-hidden="true">⇩</span>
|
||||
Download starten
|
||||
Download
|
||||
</a>
|
||||
<button
|
||||
v-else
|
||||
@@ -64,57 +120,58 @@
|
||||
</div>
|
||||
|
||||
<div class="rounded-3xl border border-white/10 bg-[#111832]/70 p-8 shadow-[0_20px_60px_rgba(4,8,24,0.45)] backdrop-blur">
|
||||
<h2 class="text-xl font-semibold">So funktioniert die Verknüpfung</h2>
|
||||
<h2 class="text-xl font-semibold">How it works</h2>
|
||||
<ol class="mt-6 space-y-4 text-sm text-white/70">
|
||||
<li class="flex items-start gap-4">
|
||||
<span class="mt-1 flex h-8 w-8 items-center justify-center rounded-full bg-[#16BBD7]/20 text-[#16BBD7]">1</span>
|
||||
<span>Bridge herunterladen, installieren und starten. Die App erzeugt automatisch deinen Token.</span>
|
||||
</li>
|
||||
<li class="flex items-start gap-4">
|
||||
<span class="mt-1 flex h-8 w-8 items-center justify-center rounded-full bg-[#16BBD7]/20 text-[#16BBD7]">2</span>
|
||||
<span>Folge dem angezeigten Link <code class="rounded bg-[#0B132A]/80 px-2 py-1 text-xs">opensquawk.de/bridge/connect</code>
|
||||
– der Token wird beim Öffnen automatisch übergeben.</span>
|
||||
</li>
|
||||
<li class="flex items-start gap-4">
|
||||
<span class="mt-1 flex h-8 w-8 items-center justify-center rounded-full bg-[#16BBD7]/20 text-[#16BBD7]">3</span>
|
||||
<span>Melde dich mit deinem OpenSquawk Account an, bestätige die Verknüpfung und kehre zur App zurück.</span>
|
||||
<li v-for="step in steps" :key="step.id" class="flex items-start gap-4">
|
||||
<span class="mt-1 flex h-8 w-8 items-center justify-center rounded-full bg-[#16BBD7]/20 text-[#16BBD7]">{{ step.id }}</span>
|
||||
<div>
|
||||
<p class="font-medium text-white">{{ step.title }}</p>
|
||||
<p class="text-white/70">{{ step.description }}</p>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<div class="mt-6 rounded-2xl border border-white/10 bg-[#0B132A]/70 px-5 py-4 text-sm text-white/70">
|
||||
<p class="font-semibold text-white">Hinweis</p>
|
||||
<p class="mt-1 text-white/65">
|
||||
Die Bridge zeigt dir jederzeit an, ob der Simulator erkannt wurde und ob ein Flug aktiv ist. Alle Statusmeldungen
|
||||
werden alle zehn Sekunden an OpenSquawk übertragen.
|
||||
</p>
|
||||
<p class="font-semibold text-white">Status at a glance</p>
|
||||
<p class="mt-1 text-white/65">The desktop Bridge mirrors your login, shows simulator + flight status, and updates OpenSquawk every few seconds.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<aside class="space-y-6">
|
||||
<div class="rounded-3xl border border-white/10 bg-[#111832]/80 p-8 shadow-[0_30px_80px_rgba(4,8,24,0.55)] backdrop-blur">
|
||||
<h2 class="text-xl font-semibold">Systemvoraussetzungen</h2>
|
||||
<h2 class="text-xl font-semibold">Requirements</h2>
|
||||
<ul class="mt-4 space-y-3 text-sm text-white/70">
|
||||
<li>Windows 10/11 (64-bit)</li>
|
||||
<li>.NET 8 Desktop Runtime</li>
|
||||
<li>Aktive OpenSquawk Mitgliedschaft</li>
|
||||
<li>Internetverbindung (mind. 5 Mbit/s)</li>
|
||||
<li class="flex items-center gap-3">
|
||||
<span class="h-2 w-2 rounded-full bg-[#16BBD7] shadow-[0_0_12px_rgba(22,187,215,0.8)]"/>
|
||||
Windows 10/11 (64-bit)
|
||||
</li>
|
||||
<li class="flex items-center gap-3">
|
||||
<span class="h-2 w-2 rounded-full bg-[#16BBD7] shadow-[0_0_12px_rgba(22,187,215,0.8)]"/>
|
||||
.NET 8 Desktop Runtime
|
||||
</li>
|
||||
<li class="flex items-center gap-3">
|
||||
<span class="h-2 w-2 rounded-full bg-[#16BBD7] shadow-[0_0_12px_rgba(22,187,215,0.8)]"/>
|
||||
Active OpenSquawk membership
|
||||
</li>
|
||||
<li class="flex items-center gap-3">
|
||||
<span class="h-2 w-2 rounded-full bg-[#16BBD7] shadow-[0_0_12px_rgba(22,187,215,0.8)]"/>
|
||||
Reliable internet connection
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="rounded-3xl border border-white/10 bg-[#111832]/70 p-8 shadow-[0_20px_60px_rgba(4,8,24,0.45)] backdrop-blur">
|
||||
<h2 class="text-xl font-semibold">Support</h2>
|
||||
<h2 class="text-xl font-semibold">Need help?</h2>
|
||||
<p class="mt-3 text-sm text-white/70">
|
||||
Fragen oder Feedback? Unser Team hilft dir gerne weiter. Schreibe uns jederzeit an
|
||||
<a href="mailto:support@opensquawk.de" class="text-[#16BBD7] underline decoration-dotted underline-offset-4">support@opensquawk.de</a>.
|
||||
Ping us at
|
||||
<a href="mailto:support@opensquawk.de" class="text-[#16BBD7] underline decoration-dotted underline-offset-4">support@opensquawk.de</a>
|
||||
and we'll get you flying again.
|
||||
</p>
|
||||
|
||||
<div class="mt-6 rounded-2xl border border-[#16BBD7]/30 bg-[#16BBD7]/10 px-5 py-4 text-sm text-white/75">
|
||||
<p class="font-semibold text-[#16BBD7]">Pro Tipp</p>
|
||||
<p class="mt-1">
|
||||
Lass die Bridge App im Hintergrund laufen – sie synchronisiert automatisch, sobald dein Simulator startet und ein
|
||||
Flug aktiv ist.
|
||||
</p>
|
||||
<p class="font-semibold text-[#16BBD7]">Tip</p>
|
||||
<p class="mt-1">Leave the Bridge running in the background. It wakes up when the simulator launches and keeps the data fresh.</p>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
@@ -126,32 +183,53 @@
|
||||
<script setup lang="ts">
|
||||
import { useHead } from '#imports'
|
||||
|
||||
useHead({ title: 'Bridge · OpenSquawk' })
|
||||
useHead({ title: 'Bridge Downloads · OpenSquawk' })
|
||||
|
||||
const downloads = [
|
||||
{
|
||||
id: 'msfs2020',
|
||||
title: 'Microsoft Flight Simulator 2020',
|
||||
description: 'Stabile Bridge-Version für den MSFS 2020 mit automatischer Token-Verknüpfung und Status-Updates.',
|
||||
badge: 'Verfügbar',
|
||||
description: 'Stable build with automatic token linking and live updates.',
|
||||
badge: 'Available now',
|
||||
available: true,
|
||||
href: 'https://github.com/itsrubberduck/OpenSquawk-MSFS-Bridge/',
|
||||
icon: 'plane',
|
||||
},
|
||||
{
|
||||
id: 'msfs2024',
|
||||
title: 'Microsoft Flight Simulator 2024',
|
||||
description: 'Die neue Generation des MSFS wird aktuell angebunden. Release der Bridge folgt in Kürze.',
|
||||
badge: 'In Arbeit',
|
||||
description: 'Integration in progress. The release build is almost ready.',
|
||||
badge: 'In progress',
|
||||
available: false,
|
||||
href: '#',
|
||||
icon: 'future',
|
||||
},
|
||||
{
|
||||
id: 'xplane',
|
||||
title: 'X-Plane 12',
|
||||
description: 'Gemeinsame Bridge-Architektur – X-Plane Support ist geplant, sobald die MSFS-Versionen stabil laufen.',
|
||||
badge: 'In Planung',
|
||||
description: 'Shared Bridge architecture. Support arrives later this year.',
|
||||
badge: 'Planned',
|
||||
available: false,
|
||||
href: '#',
|
||||
icon: 'xplane',
|
||||
},
|
||||
]
|
||||
|
||||
const steps = [
|
||||
{
|
||||
id: 1,
|
||||
title: 'Install the Bridge',
|
||||
description: 'Download the build, run the installer, and launch the desktop app.',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: 'Open the connect page',
|
||||
description: 'Follow the in-app link to opensquawk.de/bridge/connect. The token comes along automatically.',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: 'Link and fly',
|
||||
description: 'Sign in, link the token, and head back to the simulator. The Bridge keeps sending updates in the background.',
|
||||
},
|
||||
]
|
||||
</script>
|
||||
@@ -161,4 +239,3 @@ code {
|
||||
font-family: 'JetBrains Mono', 'Fira Code', monospace;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user