mirror of
https://github.com/OpenSquawk/OpenSquawk
synced 2026-08-06 01:06:28 +08:00
feat(flightlab): sidebar, progress bars, skip speech, SimBridge telemetry & auth
- Add collapsible sidebar with phase stepper (jump between phases) - Add SimBridge conditions panel in sidebar (live values, progress bars, targets) - Add global progress bar (top edge, glowing) + phase-local TTS progress bar - Add skip button to skip TTS speech while ATC is speaking - Add skipSpeech() to audio composable (stops current Pizzicato sound) - Wire up bridge data.post.ts with user auth (JWT) + example payload - Add server-side telemetry store with pub/sub for Bridge→WS relay - Extend WS handler with subscribe-telemetry message + userId tracking - Extend sync composable with subscribeTelemetry() + onTelemetry() callback - Add require-auth middleware to all flightlab pages - Fix instructor station ECONNREFUSED via import.meta.client guard - Add animations: phase transitions, button lists, fade-scale, check-pop, pulse Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
// server/api/flightlab/ws.ts
|
||||
import { defineWebSocketHandler } from 'h3'
|
||||
import { flightlabTelemetryStore } from '../../utils/flightlabTelemetry'
|
||||
|
||||
interface FlightLabSession {
|
||||
code: string
|
||||
@@ -10,11 +11,25 @@ interface FlightLabSession {
|
||||
participantConnected: boolean
|
||||
instructorConnected: boolean
|
||||
history: Array<{ phaseId: string; buttonId: string; timestamp: number }>
|
||||
peers: Map<string, { role: 'instructor' | 'participant'; peer: any }>
|
||||
peers: Map<string, { role: 'instructor' | 'participant'; peer: any; userId?: string }>
|
||||
/** User IDs subscribed to telemetry for this session */
|
||||
telemetryUserIds: Set<string>
|
||||
}
|
||||
|
||||
const sessions = new Map<string, FlightLabSession>()
|
||||
|
||||
// Map userId → session code for telemetry routing
|
||||
const userSessionMap = new Map<string, string>()
|
||||
|
||||
// Subscribe to telemetry store updates and relay to WebSocket clients
|
||||
flightlabTelemetryStore.subscribe((userId, data) => {
|
||||
const sessionCode = userSessionMap.get(userId)
|
||||
if (!sessionCode) return
|
||||
const session = sessions.get(sessionCode)
|
||||
if (!session) return
|
||||
broadcastToSession(session, { type: 'telemetry', data })
|
||||
})
|
||||
|
||||
function generateCode(): string {
|
||||
const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789' // No I,O,0,1 for readability
|
||||
let code = ''
|
||||
@@ -77,6 +92,7 @@ export default defineWebSocketHandler({
|
||||
instructorConnected: true,
|
||||
history: [],
|
||||
peers: new Map([[peerId, { role: 'instructor', peer }]]),
|
||||
telemetryUserIds: new Set(),
|
||||
}
|
||||
sessions.set(code, session)
|
||||
peer.send(JSON.stringify({ type: 'session-created', code, state: getSessionState(session) }))
|
||||
@@ -147,6 +163,17 @@ export default defineWebSocketHandler({
|
||||
}, peerId)
|
||||
break
|
||||
}
|
||||
|
||||
case 'subscribe-telemetry': {
|
||||
// Client sends their userId so bridge data gets routed to their session
|
||||
const session = findSessionByPeer(peerId)
|
||||
if (!session || !data.userId) return
|
||||
const peerInfo = session.peers.get(peerId)
|
||||
if (peerInfo) peerInfo.userId = data.userId
|
||||
session.telemetryUserIds.add(data.userId)
|
||||
userSessionMap.set(data.userId, session.code)
|
||||
break
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -155,6 +182,11 @@ export default defineWebSocketHandler({
|
||||
for (const [code, session] of sessions) {
|
||||
const peerInfo = session.peers.get(peerId)
|
||||
if (peerInfo) {
|
||||
// Clean up telemetry subscription
|
||||
if (peerInfo.userId) {
|
||||
session.telemetryUserIds.delete(peerInfo.userId)
|
||||
userSessionMap.delete(peerInfo.userId)
|
||||
}
|
||||
session.peers.delete(peerId)
|
||||
if (peerInfo.role === 'participant') session.participantConnected = false
|
||||
if (peerInfo.role === 'instructor') session.instructorConnected = false
|
||||
|
||||
Reference in New Issue
Block a user