@skyvexsoftware/stratos-sdk 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +201 -0
- package/dist/helpers/createPlugin.d.ts +22 -0
- package/dist/helpers/createPlugin.js +32 -0
- package/dist/helpers/index.d.ts +4 -0
- package/dist/helpers/index.js +3 -0
- package/dist/helpers/units.d.ts +36 -0
- package/dist/helpers/units.js +101 -0
- package/dist/hooks/context.d.ts +12 -0
- package/dist/hooks/context.js +18 -0
- package/dist/hooks/index.d.ts +16 -0
- package/dist/hooks/index.js +16 -0
- package/dist/hooks/useFlightEvents.d.ts +37 -0
- package/dist/hooks/useFlightEvents.js +152 -0
- package/dist/hooks/useFlightManager.d.ts +20 -0
- package/dist/hooks/useFlightManager.js +90 -0
- package/dist/hooks/useFlightPhase.d.ts +31 -0
- package/dist/hooks/useFlightPhase.js +67 -0
- package/dist/hooks/useLandingAnalysis.d.ts +37 -0
- package/dist/hooks/useLandingAnalysis.js +87 -0
- package/dist/hooks/usePluginLogger.d.ts +13 -0
- package/dist/hooks/usePluginLogger.js +16 -0
- package/dist/hooks/useShellAuth.d.ts +14 -0
- package/dist/hooks/useShellAuth.js +13 -0
- package/dist/hooks/useShellConfig.d.ts +14 -0
- package/dist/hooks/useShellConfig.js +14 -0
- package/dist/hooks/useShellNavigation.d.ts +12 -0
- package/dist/hooks/useShellNavigation.js +15 -0
- package/dist/hooks/useShellToast.d.ts +12 -0
- package/dist/hooks/useShellToast.js +15 -0
- package/dist/hooks/useSimData.d.ts +70 -0
- package/dist/hooks/useSimData.js +135 -0
- package/dist/hooks/useSimulatorData.d.ts +57 -0
- package/dist/hooks/useSimulatorData.js +258 -0
- package/dist/hooks/useTrackingSession.d.ts +40 -0
- package/dist/hooks/useTrackingSession.js +152 -0
- package/dist/icons.d.ts +13 -0
- package/dist/icons.js +13 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +28 -0
- package/dist/shared-types/flight-manager.d.ts +112 -0
- package/dist/shared-types/flight-manager.js +2 -0
- package/dist/shared-types/index.d.ts +7 -0
- package/dist/shared-types/index.js +4 -0
- package/dist/shared-types/simulator.d.ts +386 -0
- package/dist/shared-types/simulator.js +48 -0
- package/dist/shared-types/socket-events.d.ts +171 -0
- package/dist/shared-types/socket-events.js +79 -0
- package/dist/shared-types/theme.d.ts +3 -0
- package/dist/shared-types/theme.js +2 -0
- package/dist/types/context.d.ts +206 -0
- package/dist/types/context.js +8 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.js +2 -0
- package/dist/types/manifest.d.ts +42 -0
- package/dist/types/manifest.js +12 -0
- package/dist/types/module.d.ts +31 -0
- package/dist/types/module.js +7 -0
- package/dist/ui/alert-dialog.d.ts +21 -0
- package/dist/ui/alert-dialog.js +27 -0
- package/dist/ui/badge.d.ts +9 -0
- package/dist/ui/badge.js +21 -0
- package/dist/ui/button.d.ts +17 -0
- package/dist/ui/button.js +39 -0
- package/dist/ui/card.d.ts +10 -0
- package/dist/ui/card.js +25 -0
- package/dist/ui/dialog.d.ts +14 -0
- package/dist/ui/dialog.js +25 -0
- package/dist/ui/index.d.ts +18 -0
- package/dist/ui/index.js +16 -0
- package/dist/ui/input.d.ts +4 -0
- package/dist/ui/input.js +7 -0
- package/dist/ui/label.d.ts +5 -0
- package/dist/ui/label.js +8 -0
- package/dist/ui/radio-group.d.ts +6 -0
- package/dist/ui/radio-group.js +11 -0
- package/dist/ui/select.d.ts +14 -0
- package/dist/ui/select.js +27 -0
- package/dist/ui/separator.d.ts +5 -0
- package/dist/ui/separator.js +8 -0
- package/dist/ui/slider.d.ts +5 -0
- package/dist/ui/slider.js +8 -0
- package/dist/ui/switch.d.ts +13 -0
- package/dist/ui/switch.js +9 -0
- package/dist/ui/tabs.d.ts +8 -0
- package/dist/ui/tabs.js +13 -0
- package/dist/ui/textarea.d.ts +4 -0
- package/dist/ui/textarea.js +7 -0
- package/dist/ui/tooltip.d.ts +8 -0
- package/dist/ui/tooltip.js +11 -0
- package/dist/utils/cn.d.ts +3 -0
- package/dist/utils/cn.js +6 -0
- package/dist/vite/externals.d.ts +9 -0
- package/dist/vite/externals.js +19 -0
- package/dist/vite/plugin-config.d.ts +49 -0
- package/dist/vite/plugin-config.js +236 -0
- package/dist/vite/serve-externals.d.ts +9 -0
- package/dist/vite/serve-externals.js +78 -0
- package/dist/vite/stratos-dev-server.d.ts +21 -0
- package/dist/vite/stratos-dev-server.js +188 -0
- package/package.json +96 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useFlightEvents — TanStack Query + Socket.io hook for flight event log.
|
|
3
|
+
*
|
|
4
|
+
* Hydrates from GET /api/flight-events on mount, then subscribes to
|
|
5
|
+
* flight:event Socket.io events to append new events to the query cache.
|
|
6
|
+
* Provides comment mutations with optimistic updates.
|
|
7
|
+
*/
|
|
8
|
+
import { useEffect, useMemo } from "react";
|
|
9
|
+
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
|
10
|
+
import { useSocket } from "./useSimulatorData";
|
|
11
|
+
import { SOCKET_EVENTS } from "../shared-types/socket-events";
|
|
12
|
+
const SERVER_PORT = 2066;
|
|
13
|
+
const API_BASE = `http://127.0.0.1:${SERVER_PORT}`;
|
|
14
|
+
export const flightEventsKeys = {
|
|
15
|
+
all: ["flight-events"],
|
|
16
|
+
log: (flightId) => [...flightEventsKeys.all, "log", flightId ?? "current"],
|
|
17
|
+
};
|
|
18
|
+
async function fetchFlightEvents() {
|
|
19
|
+
const response = await fetch(`${API_BASE}/api/flight-events`, {
|
|
20
|
+
headers: { Accept: "application/json" },
|
|
21
|
+
});
|
|
22
|
+
const json = (await response.json());
|
|
23
|
+
return json.data;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Hook for the live flight event log using TanStack Query + Socket.io.
|
|
27
|
+
*
|
|
28
|
+
* On mount, fetches the current event log via REST.
|
|
29
|
+
* Then subscribes to flight:event Socket.io events and appends new events
|
|
30
|
+
* to the query cache as they arrive.
|
|
31
|
+
*
|
|
32
|
+
* Supports optional category filtering via the `categories` parameter.
|
|
33
|
+
* Comment mutations use optimistic updates and call the REST API.
|
|
34
|
+
*/
|
|
35
|
+
export function useFlightEvents(options) {
|
|
36
|
+
const queryClient = useQueryClient();
|
|
37
|
+
const { socket } = useSocket();
|
|
38
|
+
const queryKey = flightEventsKeys.log(options?.flightId);
|
|
39
|
+
const { data, isLoading } = useQuery({
|
|
40
|
+
queryKey,
|
|
41
|
+
queryFn: fetchFlightEvents,
|
|
42
|
+
staleTime: Infinity,
|
|
43
|
+
refetchOnWindowFocus: false,
|
|
44
|
+
refetchOnReconnect: false,
|
|
45
|
+
});
|
|
46
|
+
// Subscribe to Socket.io events and append new events to the cache
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
if (!socket)
|
|
49
|
+
return;
|
|
50
|
+
const handleEvent = (payload) => {
|
|
51
|
+
queryClient.setQueryData(queryKey, (prev) => {
|
|
52
|
+
if (!prev)
|
|
53
|
+
return prev;
|
|
54
|
+
return {
|
|
55
|
+
...prev,
|
|
56
|
+
events: [...prev.events, payload.event],
|
|
57
|
+
};
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
socket.on(SOCKET_EVENTS.FLIGHT_EVENT, handleEvent);
|
|
61
|
+
return () => {
|
|
62
|
+
socket.off(SOCKET_EVENTS.FLIGHT_EVENT, handleEvent);
|
|
63
|
+
};
|
|
64
|
+
}, [socket, queryClient, options?.flightId]);
|
|
65
|
+
// ── Comment Mutations ──────────────────────────────────────────────
|
|
66
|
+
const addCommentMutation = useMutation({
|
|
67
|
+
mutationFn: async (message) => {
|
|
68
|
+
const response = await fetch(`${API_BASE}/api/flight-events/comments`, {
|
|
69
|
+
method: "POST",
|
|
70
|
+
headers: { "Content-Type": "application/json" },
|
|
71
|
+
body: JSON.stringify({ message }),
|
|
72
|
+
});
|
|
73
|
+
const json = (await response.json());
|
|
74
|
+
return json.data.event;
|
|
75
|
+
},
|
|
76
|
+
// No optimistic update for add — the Socket.io flight:event broadcast
|
|
77
|
+
// will append the new comment event to the cache automatically
|
|
78
|
+
});
|
|
79
|
+
const editCommentMutation = useMutation({
|
|
80
|
+
mutationFn: async ({ id, message }) => {
|
|
81
|
+
await fetch(`${API_BASE}/api/flight-events/comments/${id}`, {
|
|
82
|
+
method: "PUT",
|
|
83
|
+
headers: { "Content-Type": "application/json" },
|
|
84
|
+
body: JSON.stringify({ message }),
|
|
85
|
+
});
|
|
86
|
+
},
|
|
87
|
+
onMutate: async ({ id, message }) => {
|
|
88
|
+
// Optimistically update the comment in the cache
|
|
89
|
+
await queryClient.cancelQueries({ queryKey: queryKey });
|
|
90
|
+
const previous = queryClient.getQueryData(queryKey);
|
|
91
|
+
queryClient.setQueryData(queryKey, (prev) => {
|
|
92
|
+
if (!prev)
|
|
93
|
+
return prev;
|
|
94
|
+
return {
|
|
95
|
+
...prev,
|
|
96
|
+
events: prev.events.map((e) => e.eventId === id ? { ...e, message } : e),
|
|
97
|
+
};
|
|
98
|
+
});
|
|
99
|
+
return { previous };
|
|
100
|
+
},
|
|
101
|
+
onError: (_err, _vars, context) => {
|
|
102
|
+
// Rollback on error
|
|
103
|
+
if (context?.previous) {
|
|
104
|
+
queryClient.setQueryData(queryKey, context.previous);
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
const deleteCommentMutation = useMutation({
|
|
109
|
+
mutationFn: async (id) => {
|
|
110
|
+
await fetch(`${API_BASE}/api/flight-events/comments/${id}`, {
|
|
111
|
+
method: "DELETE",
|
|
112
|
+
});
|
|
113
|
+
},
|
|
114
|
+
onMutate: async (id) => {
|
|
115
|
+
// Optimistically remove the comment from the cache
|
|
116
|
+
await queryClient.cancelQueries({ queryKey: queryKey });
|
|
117
|
+
const previous = queryClient.getQueryData(queryKey);
|
|
118
|
+
queryClient.setQueryData(queryKey, (prev) => {
|
|
119
|
+
if (!prev)
|
|
120
|
+
return prev;
|
|
121
|
+
return {
|
|
122
|
+
...prev,
|
|
123
|
+
events: prev.events.filter((e) => e.eventId !== id),
|
|
124
|
+
};
|
|
125
|
+
});
|
|
126
|
+
return { previous };
|
|
127
|
+
},
|
|
128
|
+
onError: (_err, _vars, context) => {
|
|
129
|
+
// Rollback on error
|
|
130
|
+
if (context?.previous) {
|
|
131
|
+
queryClient.setQueryData(queryKey, context.previous);
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
// Apply category filter if provided
|
|
136
|
+
const events = useMemo(() => {
|
|
137
|
+
const allEvents = data?.events ?? [];
|
|
138
|
+
if (!options?.categories || options.categories.length === 0) {
|
|
139
|
+
return allEvents;
|
|
140
|
+
}
|
|
141
|
+
return allEvents.filter((e) => options.categories.includes(e.category));
|
|
142
|
+
}, [data?.events, options?.categories]);
|
|
143
|
+
return {
|
|
144
|
+
events,
|
|
145
|
+
isTracking: data?.isTracking ?? false,
|
|
146
|
+
isLoading,
|
|
147
|
+
addComment: (message) => addCommentMutation.mutate(message),
|
|
148
|
+
editComment: (id, message) => editCommentMutation.mutate({ id, message }),
|
|
149
|
+
deleteComment: (id) => deleteCommentMutation.mutate(id),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=useFlightEvents.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useFlightManager — React hook for the shell's FlightManager service.
|
|
3
|
+
*
|
|
4
|
+
* Subscribes to `flight:manager` Socket.io events for reactive state.
|
|
5
|
+
* Hydrates initial state from GET /api/flight-manager/state on mount.
|
|
6
|
+
* Action methods call REST endpoints to control the flight lifecycle.
|
|
7
|
+
*/
|
|
8
|
+
import type { CurrentFlight, FlightPlan, StartFlightResult, PreflightCheckResult } from "../shared-types/flight-manager";
|
|
9
|
+
type UseFlightManagerReturn = {
|
|
10
|
+
currentFlight: CurrentFlight | null;
|
|
11
|
+
startFlight: (plan: FlightPlan) => Promise<StartFlightResult>;
|
|
12
|
+
pauseFlight: () => Promise<boolean>;
|
|
13
|
+
resumeFlight: () => Promise<boolean>;
|
|
14
|
+
endFlight: (status?: "completed" | "diverted" | "crashed" | "cancelled") => Promise<boolean>;
|
|
15
|
+
addComment: (message: string) => Promise<boolean>;
|
|
16
|
+
runPreflightChecks: (plan: FlightPlan) => Promise<PreflightCheckResult | null>;
|
|
17
|
+
};
|
|
18
|
+
export declare function useFlightManager(): UseFlightManagerReturn;
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=useFlightManager.d.ts.map
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useFlightManager — React hook for the shell's FlightManager service.
|
|
3
|
+
*
|
|
4
|
+
* Subscribes to `flight:manager` Socket.io events for reactive state.
|
|
5
|
+
* Hydrates initial state from GET /api/flight-manager/state on mount.
|
|
6
|
+
* Action methods call REST endpoints to control the flight lifecycle.
|
|
7
|
+
*/
|
|
8
|
+
import { useEffect, useState, useCallback } from "react";
|
|
9
|
+
import { useSocket } from "./useSimulatorData";
|
|
10
|
+
import { SOCKET_EVENTS } from "../shared-types/socket-events";
|
|
11
|
+
const SERVER_PORT = 2066;
|
|
12
|
+
const API_BASE = `http://127.0.0.1:${SERVER_PORT}/api/flight-manager`;
|
|
13
|
+
async function api(path, method = "GET", body) {
|
|
14
|
+
const response = await fetch(`${API_BASE}${path}`, {
|
|
15
|
+
method,
|
|
16
|
+
headers: { "Content-Type": "application/json", Accept: "application/json" },
|
|
17
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
18
|
+
});
|
|
19
|
+
return (await response.json());
|
|
20
|
+
}
|
|
21
|
+
export function useFlightManager() {
|
|
22
|
+
const { socket } = useSocket();
|
|
23
|
+
const [currentFlight, setCurrentFlight] = useState(null);
|
|
24
|
+
// Hydrate on mount
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
api("/state")
|
|
27
|
+
.then((res) => {
|
|
28
|
+
if (res.success)
|
|
29
|
+
setCurrentFlight(res.data);
|
|
30
|
+
})
|
|
31
|
+
.catch(() => {
|
|
32
|
+
// Server may not be ready yet
|
|
33
|
+
});
|
|
34
|
+
}, []);
|
|
35
|
+
// Subscribe to Socket.io events
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
if (!socket)
|
|
38
|
+
return;
|
|
39
|
+
const handleEvent = (payload) => {
|
|
40
|
+
setCurrentFlight(payload.flight);
|
|
41
|
+
};
|
|
42
|
+
socket.on(SOCKET_EVENTS.FLIGHT_MANAGER, handleEvent);
|
|
43
|
+
return () => {
|
|
44
|
+
socket.off(SOCKET_EVENTS.FLIGHT_MANAGER, handleEvent);
|
|
45
|
+
};
|
|
46
|
+
}, [socket]);
|
|
47
|
+
const startFlight = useCallback(async (plan) => {
|
|
48
|
+
const result = await api("/start", "POST", { flightPlan: plan });
|
|
49
|
+
return result.data;
|
|
50
|
+
}, []);
|
|
51
|
+
const pauseFlight = useCallback(async () => {
|
|
52
|
+
const result = await api("/pause", "POST");
|
|
53
|
+
return result.success;
|
|
54
|
+
}, []);
|
|
55
|
+
const resumeFlight = useCallback(async () => {
|
|
56
|
+
const result = await api("/resume", "POST");
|
|
57
|
+
return result.success;
|
|
58
|
+
}, []);
|
|
59
|
+
const endFlight = useCallback(async (status) => {
|
|
60
|
+
const result = await api("/end", "POST", {
|
|
61
|
+
status,
|
|
62
|
+
});
|
|
63
|
+
return result.success;
|
|
64
|
+
}, []);
|
|
65
|
+
const addComment = useCallback(async (message) => {
|
|
66
|
+
const result = await api("/comment", "POST", {
|
|
67
|
+
message,
|
|
68
|
+
});
|
|
69
|
+
return result.success;
|
|
70
|
+
}, []);
|
|
71
|
+
const runPreflightChecks = useCallback(async (plan) => {
|
|
72
|
+
try {
|
|
73
|
+
const result = await api("/preflight", "POST", { flightPlan: plan });
|
|
74
|
+
return result.success ? result.data : null;
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}, []);
|
|
80
|
+
return {
|
|
81
|
+
currentFlight,
|
|
82
|
+
startFlight,
|
|
83
|
+
pauseFlight,
|
|
84
|
+
resumeFlight,
|
|
85
|
+
endFlight,
|
|
86
|
+
addComment,
|
|
87
|
+
runPreflightChecks,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=useFlightManager.js.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useFlightPhase — TanStack Query + Socket.io hook for flight phase tracking.
|
|
3
|
+
*
|
|
4
|
+
* Hydrates from GET /api/simulator/flight-phase/snapshot on mount, then
|
|
5
|
+
* subscribes to flight:phase Socket.io events for real-time phase updates.
|
|
6
|
+
* Only re-renders when the phase actually changes, not on every data tick.
|
|
7
|
+
*/
|
|
8
|
+
import type { FlightPhaseSnapshot } from "../shared-types/simulator";
|
|
9
|
+
export declare const flightPhaseKeys: {
|
|
10
|
+
snapshot: readonly ["flight-phase", "snapshot"];
|
|
11
|
+
};
|
|
12
|
+
type UseFlightPhaseOptions<TSelected = FlightPhaseSnapshot> = {
|
|
13
|
+
select?: (snapshot: FlightPhaseSnapshot) => TSelected;
|
|
14
|
+
};
|
|
15
|
+
type UseFlightPhaseReturn<TSelected = FlightPhaseSnapshot> = {
|
|
16
|
+
data: TSelected;
|
|
17
|
+
isLoading: boolean;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Hook for real-time flight phase tracking using TanStack Query + Socket.io.
|
|
21
|
+
*
|
|
22
|
+
* On mount, fetches the current phase state via REST.
|
|
23
|
+
* Then subscribes to flight:phase Socket.io events and updates the query cache
|
|
24
|
+
* only when the phase actually changes — not on every data tick.
|
|
25
|
+
*
|
|
26
|
+
* Supports an optional `select` parameter to subscribe to specific fields
|
|
27
|
+
* and only re-render when those fields change.
|
|
28
|
+
*/
|
|
29
|
+
export declare function useFlightPhase<TSelected = FlightPhaseSnapshot>(options?: UseFlightPhaseOptions<TSelected>): UseFlightPhaseReturn<TSelected>;
|
|
30
|
+
export {};
|
|
31
|
+
//# sourceMappingURL=useFlightPhase.d.ts.map
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useFlightPhase — TanStack Query + Socket.io hook for flight phase tracking.
|
|
3
|
+
*
|
|
4
|
+
* Hydrates from GET /api/simulator/flight-phase/snapshot on mount, then
|
|
5
|
+
* subscribes to flight:phase Socket.io events for real-time phase updates.
|
|
6
|
+
* Only re-renders when the phase actually changes, not on every data tick.
|
|
7
|
+
*/
|
|
8
|
+
import { useEffect } from "react";
|
|
9
|
+
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
10
|
+
import { useSocket } from "./useSimulatorData";
|
|
11
|
+
import { SOCKET_EVENTS } from "../shared-types/socket-events";
|
|
12
|
+
const SERVER_PORT = 2066;
|
|
13
|
+
const API_BASE = `http://127.0.0.1:${SERVER_PORT}`;
|
|
14
|
+
export const flightPhaseKeys = {
|
|
15
|
+
snapshot: ["flight-phase", "snapshot"],
|
|
16
|
+
};
|
|
17
|
+
async function fetchPhaseSnapshot() {
|
|
18
|
+
const response = await fetch(`${API_BASE}/api/simulator/flight-phase/snapshot`, {
|
|
19
|
+
headers: { Accept: "application/json" },
|
|
20
|
+
});
|
|
21
|
+
const json = (await response.json());
|
|
22
|
+
return json.data;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Hook for real-time flight phase tracking using TanStack Query + Socket.io.
|
|
26
|
+
*
|
|
27
|
+
* On mount, fetches the current phase state via REST.
|
|
28
|
+
* Then subscribes to flight:phase Socket.io events and updates the query cache
|
|
29
|
+
* only when the phase actually changes — not on every data tick.
|
|
30
|
+
*
|
|
31
|
+
* Supports an optional `select` parameter to subscribe to specific fields
|
|
32
|
+
* and only re-render when those fields change.
|
|
33
|
+
*/
|
|
34
|
+
export function useFlightPhase(options) {
|
|
35
|
+
const queryClient = useQueryClient();
|
|
36
|
+
const { socket } = useSocket();
|
|
37
|
+
const { data, isLoading } = useQuery({
|
|
38
|
+
queryKey: flightPhaseKeys.snapshot,
|
|
39
|
+
queryFn: fetchPhaseSnapshot,
|
|
40
|
+
select: options?.select,
|
|
41
|
+
staleTime: Infinity,
|
|
42
|
+
refetchOnWindowFocus: false,
|
|
43
|
+
refetchOnReconnect: false,
|
|
44
|
+
});
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
if (!socket)
|
|
47
|
+
return;
|
|
48
|
+
const handlePhaseChange = (payload) => {
|
|
49
|
+
queryClient.setQueryData(flightPhaseKeys.snapshot, (prev) => ({
|
|
50
|
+
currentPhase: payload.currentPhase,
|
|
51
|
+
previousPhase: payload.previousPhase,
|
|
52
|
+
pendingTransition: payload.pendingTransition ?? null,
|
|
53
|
+
trends: prev?.trends ?? null,
|
|
54
|
+
isTracking: prev?.isTracking ?? false,
|
|
55
|
+
}));
|
|
56
|
+
};
|
|
57
|
+
socket.on(SOCKET_EVENTS.FLIGHT_PHASE, handlePhaseChange);
|
|
58
|
+
return () => {
|
|
59
|
+
socket.off(SOCKET_EVENTS.FLIGHT_PHASE, handlePhaseChange);
|
|
60
|
+
};
|
|
61
|
+
}, [socket, queryClient]);
|
|
62
|
+
return {
|
|
63
|
+
data: data,
|
|
64
|
+
isLoading,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=useFlightPhase.js.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useLandingAnalysis — TanStack Query + Socket.io hook for landing analysis.
|
|
3
|
+
*
|
|
4
|
+
* Hydrates from GET /api/simulator/landing-analysis/snapshot on mount, then
|
|
5
|
+
* subscribes to flight:landing Socket.io events for real-time touchdown/bounce
|
|
6
|
+
* notifications. Only re-renders when landing-relevant events occur.
|
|
7
|
+
*/
|
|
8
|
+
import type { LandingAnalysisSnapshot, CapturePoint, BounceData, LandingAnalysis } from "../shared-types/simulator";
|
|
9
|
+
export declare const landingAnalysisKeys: {
|
|
10
|
+
snapshot: readonly ["landing-analysis", "snapshot"];
|
|
11
|
+
};
|
|
12
|
+
type UseLandingAnalysisOptions<TSelected = LandingAnalysisSnapshot> = {
|
|
13
|
+
select?: (snapshot: LandingAnalysisSnapshot) => TSelected;
|
|
14
|
+
};
|
|
15
|
+
type UseLandingAnalysisReturn = {
|
|
16
|
+
landingRate: number | null;
|
|
17
|
+
bounceCount: number;
|
|
18
|
+
touchdowns: BounceData[];
|
|
19
|
+
isOnGround: boolean;
|
|
20
|
+
approachData: CapturePoint | null;
|
|
21
|
+
landingAnalysis: LandingAnalysis | null;
|
|
22
|
+
isLoading: boolean;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Hook for real-time landing analysis using TanStack Query + Socket.io.
|
|
26
|
+
*
|
|
27
|
+
* On mount, fetches the current landing state via REST.
|
|
28
|
+
* Then subscribes to flight:landing Socket.io events and updates the query
|
|
29
|
+
* cache when touchdown, bounce, or settled events arrive.
|
|
30
|
+
*
|
|
31
|
+
* Only updates when landing-relevant events occur (not every data tick).
|
|
32
|
+
* Data resets when a new flight starts (LandingAnalyzer.reset() is called
|
|
33
|
+
* by FlightEventPipeline.startTracking()).
|
|
34
|
+
*/
|
|
35
|
+
export declare function useLandingAnalysis<TSelected = LandingAnalysisSnapshot>(options?: UseLandingAnalysisOptions<TSelected>): UseLandingAnalysisReturn;
|
|
36
|
+
export {};
|
|
37
|
+
//# sourceMappingURL=useLandingAnalysis.d.ts.map
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useLandingAnalysis — TanStack Query + Socket.io hook for landing analysis.
|
|
3
|
+
*
|
|
4
|
+
* Hydrates from GET /api/simulator/landing-analysis/snapshot on mount, then
|
|
5
|
+
* subscribes to flight:landing Socket.io events for real-time touchdown/bounce
|
|
6
|
+
* notifications. Only re-renders when landing-relevant events occur.
|
|
7
|
+
*/
|
|
8
|
+
import { useEffect } from "react";
|
|
9
|
+
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
10
|
+
import { useSocket } from "./useSimulatorData";
|
|
11
|
+
import { SOCKET_EVENTS } from "../shared-types/socket-events";
|
|
12
|
+
const SERVER_PORT = 2066;
|
|
13
|
+
const API_BASE = `http://127.0.0.1:${SERVER_PORT}`;
|
|
14
|
+
export const landingAnalysisKeys = {
|
|
15
|
+
snapshot: ["landing-analysis", "snapshot"],
|
|
16
|
+
};
|
|
17
|
+
async function fetchLandingSnapshot() {
|
|
18
|
+
const response = await fetch(`${API_BASE}/api/simulator/landing-analysis/snapshot`, {
|
|
19
|
+
headers: { Accept: "application/json" },
|
|
20
|
+
});
|
|
21
|
+
const json = (await response.json());
|
|
22
|
+
return json.data;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Hook for real-time landing analysis using TanStack Query + Socket.io.
|
|
26
|
+
*
|
|
27
|
+
* On mount, fetches the current landing state via REST.
|
|
28
|
+
* Then subscribes to flight:landing Socket.io events and updates the query
|
|
29
|
+
* cache when touchdown, bounce, or settled events arrive.
|
|
30
|
+
*
|
|
31
|
+
* Only updates when landing-relevant events occur (not every data tick).
|
|
32
|
+
* Data resets when a new flight starts (LandingAnalyzer.reset() is called
|
|
33
|
+
* by FlightEventPipeline.startTracking()).
|
|
34
|
+
*/
|
|
35
|
+
export function useLandingAnalysis(options) {
|
|
36
|
+
const queryClient = useQueryClient();
|
|
37
|
+
const { socket } = useSocket();
|
|
38
|
+
const { data, isLoading } = useQuery({
|
|
39
|
+
queryKey: landingAnalysisKeys.snapshot,
|
|
40
|
+
queryFn: fetchLandingSnapshot,
|
|
41
|
+
select: options?.select,
|
|
42
|
+
staleTime: Infinity,
|
|
43
|
+
refetchOnWindowFocus: false,
|
|
44
|
+
refetchOnReconnect: false,
|
|
45
|
+
});
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
if (!socket)
|
|
48
|
+
return;
|
|
49
|
+
const handleLanding = (payload) => {
|
|
50
|
+
queryClient.setQueryData(landingAnalysisKeys.snapshot, (prev) => {
|
|
51
|
+
if (!prev)
|
|
52
|
+
return prev;
|
|
53
|
+
if (payload.type === "settled" && payload.landingAnalysis) {
|
|
54
|
+
return {
|
|
55
|
+
...prev,
|
|
56
|
+
landingRate: payload.landingAnalysis.landingRateFpm,
|
|
57
|
+
bounceCount: payload.landingAnalysis.bounceCount,
|
|
58
|
+
touchdowns: payload.landingAnalysis.bounces,
|
|
59
|
+
landingAnalysis: payload.landingAnalysis,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
if (payload.type === "bounce" && payload.bounceData) {
|
|
63
|
+
return {
|
|
64
|
+
...prev,
|
|
65
|
+
bounceCount: prev.bounceCount + 1,
|
|
66
|
+
touchdowns: [...prev.touchdowns, payload.bounceData],
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return prev;
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
socket.on(SOCKET_EVENTS.FLIGHT_LANDING, handleLanding);
|
|
73
|
+
return () => {
|
|
74
|
+
socket.off(SOCKET_EVENTS.FLIGHT_LANDING, handleLanding);
|
|
75
|
+
};
|
|
76
|
+
}, [socket, queryClient]);
|
|
77
|
+
return {
|
|
78
|
+
landingRate: data?.landingRate ?? null,
|
|
79
|
+
bounceCount: data?.bounceCount ?? 0,
|
|
80
|
+
touchdowns: data?.touchdowns ?? [],
|
|
81
|
+
isOnGround: data?.isOnGround ?? true,
|
|
82
|
+
approachData: data?.approachData ?? null,
|
|
83
|
+
landingAnalysis: data?.landingAnalysis ?? null,
|
|
84
|
+
isLoading,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=useLandingAnalysis.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Access a scoped logger from a plugin component.
|
|
3
|
+
* Logs are sent to the shell's logging infrastructure.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```tsx
|
|
7
|
+
* const logger = usePluginLogger();
|
|
8
|
+
* logger.info("TrackingPage", "User opened flight map");
|
|
9
|
+
* logger.error("TrackingPage", "Failed to load route", error);
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
export declare function usePluginLogger(): import("..").PluginLogger;
|
|
13
|
+
//# sourceMappingURL=usePluginLogger.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { usePluginContext } from "./context";
|
|
2
|
+
/**
|
|
3
|
+
* Access a scoped logger from a plugin component.
|
|
4
|
+
* Logs are sent to the shell's logging infrastructure.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```tsx
|
|
8
|
+
* const logger = usePluginLogger();
|
|
9
|
+
* logger.info("TrackingPage", "User opened flight map");
|
|
10
|
+
* logger.error("TrackingPage", "Failed to load route", error);
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
export function usePluginLogger() {
|
|
14
|
+
return usePluginContext().logger;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=usePluginLogger.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Access the shell's auth state from a plugin component.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```tsx
|
|
6
|
+
* const { isAuthenticated, token, user } = useShellAuth();
|
|
7
|
+
* ```
|
|
8
|
+
*/
|
|
9
|
+
export declare function useShellAuth(): {
|
|
10
|
+
isAuthenticated: boolean;
|
|
11
|
+
token: string | null;
|
|
12
|
+
user: unknown;
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=useShellAuth.d.ts.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { usePluginContext } from "./context";
|
|
2
|
+
/**
|
|
3
|
+
* Access the shell's auth state from a plugin component.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```tsx
|
|
7
|
+
* const { isAuthenticated, token, user } = useShellAuth();
|
|
8
|
+
* ```
|
|
9
|
+
*/
|
|
10
|
+
export function useShellAuth() {
|
|
11
|
+
return usePluginContext().auth;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=useShellAuth.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Access the shell's config store from a plugin component.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```tsx
|
|
6
|
+
* const config = useShellConfig();
|
|
7
|
+
* const value = config.get<string>("myKey", "default");
|
|
8
|
+
* ```
|
|
9
|
+
*/
|
|
10
|
+
export declare function useShellConfig(): {
|
|
11
|
+
get<T>(key: string): T | undefined;
|
|
12
|
+
get<T>(key: string, defaultValue: T): T;
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=useShellConfig.d.ts.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { usePluginContext } from "./context";
|
|
2
|
+
/**
|
|
3
|
+
* Access the shell's config store from a plugin component.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```tsx
|
|
7
|
+
* const config = useShellConfig();
|
|
8
|
+
* const value = config.get<string>("myKey", "default");
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
11
|
+
export function useShellConfig() {
|
|
12
|
+
return usePluginContext().config;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=useShellConfig.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Access the shell's navigation helpers from a plugin component.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```tsx
|
|
6
|
+
* const nav = useShellNavigation();
|
|
7
|
+
* nav.navigateTo("settings");
|
|
8
|
+
* nav.navigateToPlugin("flight-tracking", "history");
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
11
|
+
export declare function useShellNavigation(): import("..").PluginNavigationHelper;
|
|
12
|
+
//# sourceMappingURL=useShellNavigation.d.ts.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { usePluginContext } from "./context";
|
|
2
|
+
/**
|
|
3
|
+
* Access the shell's navigation helpers from a plugin component.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```tsx
|
|
7
|
+
* const nav = useShellNavigation();
|
|
8
|
+
* nav.navigateTo("settings");
|
|
9
|
+
* nav.navigateToPlugin("flight-tracking", "history");
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
export function useShellNavigation() {
|
|
13
|
+
return usePluginContext().navigation;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=useShellNavigation.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Access the shell's toast/notification API from a plugin component.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```tsx
|
|
6
|
+
* const toast = useShellToast();
|
|
7
|
+
* toast.success("Flight saved!");
|
|
8
|
+
* toast.error("Connection lost");
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
11
|
+
export declare function useShellToast(): import("..").PluginToastAPI;
|
|
12
|
+
//# sourceMappingURL=useShellToast.d.ts.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { usePluginContext } from "./context";
|
|
2
|
+
/**
|
|
3
|
+
* Access the shell's toast/notification API from a plugin component.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```tsx
|
|
7
|
+
* const toast = useShellToast();
|
|
8
|
+
* toast.success("Flight saved!");
|
|
9
|
+
* toast.error("Connection lost");
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
export function useShellToast() {
|
|
13
|
+
return usePluginContext().toast;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=useShellToast.js.map
|