@zendir/ui 0.2.20 → 0.3.0

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.
Files changed (260) hide show
  1. package/CHANGELOG.md +192 -1
  2. package/README.md +70 -28
  3. package/dist/index.d.ts +1 -1
  4. package/dist/index.js +57 -41
  5. package/dist/index.js.map +1 -1
  6. package/dist/react/3d/CesiumCaptureSource.d.ts +119 -0
  7. package/dist/react/3d/CesiumCaptureSource.js +307 -0
  8. package/dist/react/3d/CesiumCaptureSource.js.map +1 -0
  9. package/dist/react/3d/ZenSpace3D.js +1253 -0
  10. package/dist/react/3d/ZenSpace3D.js.map +1 -0
  11. package/dist/react/3d/ZenSpace3DCesium.js +579 -0
  12. package/dist/react/3d/ZenSpace3DCesium.js.map +1 -0
  13. package/dist/react/3d/ZenSpace3DTypes.d.ts +28 -1
  14. package/dist/react/3d/ZenSpace3DUtils.d.ts +17 -173
  15. package/dist/react/3d/ZenSpace3DUtils.js +28 -0
  16. package/dist/react/3d/ZenSpace3DUtils.js.map +1 -0
  17. package/dist/react/3d/capturePngAnalysis.d.ts +16 -0
  18. package/dist/react/3d/index.d.ts +10 -12
  19. package/dist/react/3d/threeLoader.js +18 -0
  20. package/dist/react/3d/threeLoader.js.map +1 -0
  21. package/dist/react/astro/MonitoringIcon.js +1 -1
  22. package/dist/react/astro/MonitoringIcon.js.map +1 -1
  23. package/dist/react/astro/SimulationControls.js +2 -2
  24. package/dist/react/astro/SimulationControls.js.map +1 -1
  25. package/dist/react/astro/UnifiedTimeline.js +4 -4
  26. package/dist/react/astro/UnifiedTimeline.js.map +1 -1
  27. package/dist/react/charts/GroundTrackMap.d.ts +2 -15
  28. package/dist/react/charts/GroundTrackMap.js +1 -1
  29. package/dist/react/charts/GroundTrackMap.js.map +1 -1
  30. package/dist/react/charts/unified/AstroChart.js +34 -13
  31. package/dist/react/charts/unified/AstroChart.js.map +1 -1
  32. package/dist/react/chatgpt/AppCard.d.ts +0 -4
  33. package/dist/react/chatgpt/index.d.ts +0 -19
  34. package/dist/react/context/SpatialSelectionContext.d.ts +40 -0
  35. package/dist/react/context/SpatialSelectionContext.js +10 -0
  36. package/dist/react/context/SpatialSelectionContext.js.map +1 -0
  37. package/dist/react/context/index.d.ts +2 -0
  38. package/dist/react/core/{DataTable.d.ts → data/DataTable.d.ts} +1 -1
  39. package/dist/react/core/{DataTable.js → data/DataTable.js} +4 -4
  40. package/dist/react/core/data/DataTable.js.map +1 -0
  41. package/dist/react/core/{DataValue.d.ts → data/DataValue.d.ts} +2 -2
  42. package/dist/react/core/{DataValue.js → data/DataValue.js} +2 -2
  43. package/dist/react/core/data/DataValue.js.map +1 -0
  44. package/dist/react/core/{propertyConfig.d.ts → data/propertyConfig.d.ts} +2 -2
  45. package/dist/react/core/data/propertyConfig.js.map +1 -0
  46. package/dist/react/core/{AstroIcon.js → display/AstroIcon.js} +1 -1
  47. package/dist/react/core/display/AstroIcon.js.map +1 -0
  48. package/dist/react/core/{Badge.d.ts → display/Badge.d.ts} +1 -1
  49. package/dist/react/core/{Badge.js → display/Badge.js} +2 -2
  50. package/dist/react/core/display/Badge.js.map +1 -0
  51. package/dist/react/core/{CardHeader.d.ts → display/CardHeader.d.ts} +1 -1
  52. package/dist/react/core/{CardHeader.js → display/CardHeader.js} +2 -2
  53. package/dist/react/core/display/CardHeader.js.map +1 -0
  54. package/dist/react/core/{Container.d.ts → display/Container.d.ts} +1 -1
  55. package/dist/react/core/{Container.js → display/Container.js} +3 -3
  56. package/dist/react/core/display/Container.js.map +1 -0
  57. package/dist/react/core/{CopyButton.js → display/CopyButton.js} +1 -1
  58. package/dist/react/core/display/CopyButton.js.map +1 -0
  59. package/dist/react/core/{GlassCard.d.ts → display/GlassCard.d.ts} +1 -1
  60. package/dist/react/core/{GlassCard.js → display/GlassCard.js} +2 -2
  61. package/dist/react/core/display/GlassCard.js.map +1 -0
  62. package/dist/react/core/{HeaderIconWithStatus.d.ts → display/HeaderIconWithStatus.d.ts} +1 -1
  63. package/dist/react/core/{HeaderIconWithStatus.js → display/HeaderIconWithStatus.js} +1 -1
  64. package/dist/react/core/display/HeaderIconWithStatus.js.map +1 -0
  65. package/dist/react/core/{Icon.d.ts → display/Icon.d.ts} +1 -1
  66. package/dist/react/core/{Icon.js → display/Icon.js} +1 -1
  67. package/dist/react/core/display/Icon.js.map +1 -0
  68. package/dist/react/core/{Typography.d.ts → display/Typography.d.ts} +13 -4
  69. package/dist/react/core/{Typography.js → display/Typography.js} +1 -1
  70. package/dist/react/core/display/Typography.js.map +1 -0
  71. package/dist/react/core/{ConfirmDialog.js → feedback/ConfirmDialog.js} +1 -1
  72. package/dist/react/core/feedback/ConfirmDialog.js.map +1 -0
  73. package/dist/react/core/{Dialog.js → feedback/Dialog.js} +2 -2
  74. package/dist/react/core/feedback/Dialog.js.map +1 -0
  75. package/dist/react/core/{Toast.js → feedback/Toast.js} +3 -3
  76. package/dist/react/core/feedback/Toast.js.map +1 -0
  77. package/dist/react/core/index.d.ts +85 -83
  78. package/dist/react/core/{Button.js → inputs/Button.js} +2 -2
  79. package/dist/react/core/inputs/Button.js.map +1 -0
  80. package/dist/react/core/{Checkbox.js → inputs/Checkbox.js} +2 -2
  81. package/dist/react/core/inputs/Checkbox.js.map +1 -0
  82. package/dist/react/core/{Input.d.ts → inputs/Input.d.ts} +1 -1
  83. package/dist/react/core/{Input.js → inputs/Input.js} +3 -3
  84. package/dist/react/core/inputs/Input.js.map +1 -0
  85. package/dist/react/core/{LimitsBar.js → inputs/LimitsBar.js} +1 -1
  86. package/dist/react/core/inputs/LimitsBar.js.map +1 -0
  87. package/dist/react/core/{NumberInput.d.ts → inputs/NumberInput.d.ts} +2 -2
  88. package/dist/react/core/{NumberInput.js → inputs/NumberInput.js} +3 -3
  89. package/dist/react/core/inputs/NumberInput.js.map +1 -0
  90. package/dist/react/core/{PinInput.js → inputs/PinInput.js} +2 -2
  91. package/dist/react/core/inputs/PinInput.js.map +1 -0
  92. package/dist/react/core/{Select.js → inputs/Select.js} +3 -3
  93. package/dist/react/core/inputs/Select.js.map +1 -0
  94. package/dist/react/core/{Toggle.js → inputs/Toggle.js} +2 -2
  95. package/dist/react/core/inputs/Toggle.js.map +1 -0
  96. package/dist/react/core/{AppBar.d.ts → navigation/AppBar.d.ts} +1 -1
  97. package/dist/react/core/{AppBar.js → navigation/AppBar.js} +7 -7
  98. package/dist/react/core/navigation/AppBar.js.map +1 -0
  99. package/dist/react/core/{Pagination.js → navigation/Pagination.js} +2 -2
  100. package/dist/react/core/navigation/Pagination.js.map +1 -0
  101. package/dist/react/core/{SideNav.d.ts → navigation/SideNav.d.ts} +1 -1
  102. package/dist/react/core/{SideNav.js → navigation/SideNav.js} +8 -9
  103. package/dist/react/core/navigation/SideNav.js.map +1 -0
  104. package/dist/react/core/{Tabs.js → navigation/Tabs.js} +2 -2
  105. package/dist/react/core/navigation/Tabs.js.map +1 -0
  106. package/dist/react/core/{Popover.js → overlays/Popover.js} +1 -1
  107. package/dist/react/core/overlays/Popover.js.map +1 -0
  108. package/dist/react/core/{SidePanel.js → overlays/SidePanel.js} +7 -7
  109. package/dist/react/core/overlays/SidePanel.js.map +1 -0
  110. package/dist/react/core/{Tooltip.js → overlays/Tooltip.js} +2 -2
  111. package/dist/react/core/overlays/Tooltip.js.map +1 -0
  112. package/dist/react/core/{ActivityPlanner.js → widgets/ActivityPlanner.js} +1 -1
  113. package/dist/react/core/widgets/ActivityPlanner.js.map +1 -0
  114. package/dist/react/core/widgets/Capture.d.ts +140 -0
  115. package/dist/react/core/widgets/Capture.js +804 -0
  116. package/dist/react/core/widgets/Capture.js.map +1 -0
  117. package/dist/react/core/{ChatPanel.d.ts → widgets/ChatPanel.d.ts} +1 -1
  118. package/dist/react/core/{ChatPanel.js → widgets/ChatPanel.js} +5 -4
  119. package/dist/react/core/widgets/ChatPanel.js.map +1 -0
  120. package/dist/react/core/{ColorPickerPanel.d.ts → widgets/ColorPickerPanel.d.ts} +1 -1
  121. package/dist/react/core/{ColorPickerPanel.js → widgets/ColorPickerPanel.js} +3 -3
  122. package/dist/react/core/widgets/ColorPickerPanel.js.map +1 -0
  123. package/dist/react/core/{CommandBuilder.js → widgets/CommandBuilder.js} +1 -1
  124. package/dist/react/core/widgets/CommandBuilder.js.map +1 -0
  125. package/dist/react/core/{ConnectionForm.d.ts → widgets/ConnectionForm.d.ts} +1 -1
  126. package/dist/react/core/{ConnectionForm.js → widgets/ConnectionForm.js} +2 -2
  127. package/dist/react/core/widgets/ConnectionForm.js.map +1 -0
  128. package/dist/react/core/{FileExplorer.js → widgets/FileExplorer.js} +2 -2
  129. package/dist/react/core/widgets/FileExplorer.js.map +1 -0
  130. package/dist/react/core/{HexViewer.js → widgets/HexViewer.js} +1 -1
  131. package/dist/react/core/widgets/HexViewer.js.map +1 -0
  132. package/dist/react/core/{ImageGallery.d.ts → widgets/ImageGallery.d.ts} +1 -1
  133. package/dist/react/core/{ImageGallery.js → widgets/ImageGallery.js} +3 -3
  134. package/dist/react/core/widgets/ImageGallery.js.map +1 -0
  135. package/dist/react/core/{LogViewer.d.ts → widgets/LogViewer.d.ts} +13 -3
  136. package/dist/react/core/{LogViewer.js → widgets/LogViewer.js} +28 -8
  137. package/dist/react/core/widgets/LogViewer.js.map +1 -0
  138. package/dist/react/core/{MessageStream.d.ts → widgets/MessageStream.d.ts} +2 -2
  139. package/dist/react/core/{MessageStream.js → widgets/MessageStream.js} +4 -4
  140. package/dist/react/core/widgets/MessageStream.js.map +1 -0
  141. package/dist/react/core/{MissionCalendar.js → widgets/MissionCalendar.js} +2 -2
  142. package/dist/react/core/widgets/MissionCalendar.js.map +1 -0
  143. package/dist/react/core/{PacketViewer.js → widgets/PacketViewer.js} +1 -1
  144. package/dist/react/core/widgets/PacketViewer.js.map +1 -0
  145. package/dist/react/core/widgets/capture-placeholder.png.js +5 -0
  146. package/dist/react/core/widgets/capture-placeholder.png.js.map +1 -0
  147. package/dist/react/hooks/index.d.ts +9 -11
  148. package/dist/react/hooks/useAccessWindows.d.ts +15 -19
  149. package/dist/react/hooks/useGroundTrackHistory.d.ts +34 -0
  150. package/dist/react/hooks/useSimulationScene.d.ts +141 -0
  151. package/dist/react/hooks/useSimulationScene.js +401 -0
  152. package/dist/react/hooks/useSimulationScene.js.map +1 -0
  153. package/dist/react/hooks/useZendirSession.d.ts +44 -69
  154. package/dist/react/index.d.ts +10 -5
  155. package/dist/react/panels/LayerControlPanel.d.ts +54 -0
  156. package/dist/react/panels/LayerControlPanel.js +184 -0
  157. package/dist/react/panels/LayerControlPanel.js.map +1 -0
  158. package/dist/react/panels/ObjectInventoryPanel.d.ts +57 -0
  159. package/dist/react/panels/ObjectInventoryPanel.js +261 -0
  160. package/dist/react/panels/ObjectInventoryPanel.js.map +1 -0
  161. package/dist/react/panels/index.d.ts +15 -0
  162. package/dist/react/theme/ThemeProvider.d.ts +2 -0
  163. package/dist/react/theme/ThemeProvider.js +50 -72
  164. package/dist/react/theme/ThemeProvider.js.map +1 -1
  165. package/dist/react/types.d.ts +32 -3
  166. package/dist/react/types.js.map +1 -1
  167. package/dist/react.js +57 -41
  168. package/dist/react.js.map +1 -1
  169. package/dist/shaders/atmosphere.frag.js +5 -0
  170. package/dist/shaders/atmosphere.frag.js.map +1 -0
  171. package/dist/shaders/atmosphere.vert.js +5 -0
  172. package/dist/shaders/atmosphere.vert.js.map +1 -0
  173. package/dist/shaders/stars.frag.js +5 -0
  174. package/dist/shaders/stars.frag.js.map +1 -0
  175. package/dist/shaders/stars.vert.js +5 -0
  176. package/dist/shaders/stars.vert.js.map +1 -0
  177. package/dist/style.css +6 -4
  178. package/dist/tokens/css-vars.d.ts +91 -0
  179. package/dist/tokens/css-vars.js +228 -0
  180. package/dist/tokens/css-vars.js.map +1 -0
  181. package/dist/tokens/index.d.ts +71 -18
  182. package/dist/tokens/index.js +206 -97
  183. package/dist/tokens/index.js.map +1 -1
  184. package/dist/tokens/tokens.css +50 -50
  185. package/package.json +27 -22
  186. package/sdk-stub.js +10 -5
  187. package/dist/react/3d/EarthViewer.d.ts +0 -46
  188. package/dist/react/3d/SolarSystemViewer.d.ts +0 -43
  189. package/dist/react/chatgpt/ChatGPTCard.d.ts +0 -6
  190. package/dist/react/core/ActivityPlanner.js.map +0 -1
  191. package/dist/react/core/AppBar.js.map +0 -1
  192. package/dist/react/core/AstroIcon.js.map +0 -1
  193. package/dist/react/core/Badge.js.map +0 -1
  194. package/dist/react/core/Button.js.map +0 -1
  195. package/dist/react/core/CardHeader.js.map +0 -1
  196. package/dist/react/core/ChatPanel.js.map +0 -1
  197. package/dist/react/core/Checkbox.js.map +0 -1
  198. package/dist/react/core/ColorPickerPanel.js.map +0 -1
  199. package/dist/react/core/CommandBuilder.js.map +0 -1
  200. package/dist/react/core/ConfirmDialog.js.map +0 -1
  201. package/dist/react/core/ConnectionForm.js.map +0 -1
  202. package/dist/react/core/Container.js.map +0 -1
  203. package/dist/react/core/CopyButton.js.map +0 -1
  204. package/dist/react/core/DataTable.js.map +0 -1
  205. package/dist/react/core/DataValue.js.map +0 -1
  206. package/dist/react/core/Dialog.js.map +0 -1
  207. package/dist/react/core/FileExplorer.js.map +0 -1
  208. package/dist/react/core/GlassCard.js.map +0 -1
  209. package/dist/react/core/HeaderIconWithStatus.js.map +0 -1
  210. package/dist/react/core/HexViewer.js.map +0 -1
  211. package/dist/react/core/Icon.js.map +0 -1
  212. package/dist/react/core/ImageGallery.js.map +0 -1
  213. package/dist/react/core/Input.js.map +0 -1
  214. package/dist/react/core/LimitsBar.js.map +0 -1
  215. package/dist/react/core/LogViewer.js.map +0 -1
  216. package/dist/react/core/MessageStream.js.map +0 -1
  217. package/dist/react/core/MissionCalendar.js.map +0 -1
  218. package/dist/react/core/NumberInput.js.map +0 -1
  219. package/dist/react/core/PacketViewer.js.map +0 -1
  220. package/dist/react/core/Pagination.js.map +0 -1
  221. package/dist/react/core/PinInput.js.map +0 -1
  222. package/dist/react/core/Popover.js.map +0 -1
  223. package/dist/react/core/Select.js.map +0 -1
  224. package/dist/react/core/SideNav.js.map +0 -1
  225. package/dist/react/core/SidePanel.js.map +0 -1
  226. package/dist/react/core/Tabs.js.map +0 -1
  227. package/dist/react/core/Toast.js.map +0 -1
  228. package/dist/react/core/Toggle.js.map +0 -1
  229. package/dist/react/core/Tooltip.js.map +0 -1
  230. package/dist/react/core/Typography.js.map +0 -1
  231. package/dist/react/core/propertyConfig.js.map +0 -1
  232. package/dist/react/hooks/useSimulationTime.d.ts +0 -61
  233. package/dist/react/hooks/useSpacecraftPosition.d.ts +0 -50
  234. package/dist/react/hooks/useTelemetry.d.ts +0 -55
  235. package/dist/types.d.ts +0 -1
  236. package/dist/types.js +0 -2
  237. package/dist/types.js.map +0 -1
  238. /package/dist/react/core/{propertyConfig.js → data/propertyConfig.js} +0 -0
  239. /package/dist/react/core/{AstroIcon.d.ts → display/AstroIcon.d.ts} +0 -0
  240. /package/dist/react/core/{CopyButton.d.ts → display/CopyButton.d.ts} +0 -0
  241. /package/dist/react/core/{ConfirmDialog.d.ts → feedback/ConfirmDialog.d.ts} +0 -0
  242. /package/dist/react/core/{Dialog.d.ts → feedback/Dialog.d.ts} +0 -0
  243. /package/dist/react/core/{Toast.d.ts → feedback/Toast.d.ts} +0 -0
  244. /package/dist/react/core/{Button.d.ts → inputs/Button.d.ts} +0 -0
  245. /package/dist/react/core/{Checkbox.d.ts → inputs/Checkbox.d.ts} +0 -0
  246. /package/dist/react/core/{LimitsBar.d.ts → inputs/LimitsBar.d.ts} +0 -0
  247. /package/dist/react/core/{PinInput.d.ts → inputs/PinInput.d.ts} +0 -0
  248. /package/dist/react/core/{Select.d.ts → inputs/Select.d.ts} +0 -0
  249. /package/dist/react/core/{Toggle.d.ts → inputs/Toggle.d.ts} +0 -0
  250. /package/dist/react/core/{Pagination.d.ts → navigation/Pagination.d.ts} +0 -0
  251. /package/dist/react/core/{Tabs.d.ts → navigation/Tabs.d.ts} +0 -0
  252. /package/dist/react/core/{Popover.d.ts → overlays/Popover.d.ts} +0 -0
  253. /package/dist/react/core/{SidePanel.d.ts → overlays/SidePanel.d.ts} +0 -0
  254. /package/dist/react/core/{Tooltip.d.ts → overlays/Tooltip.d.ts} +0 -0
  255. /package/dist/react/core/{ActivityPlanner.d.ts → widgets/ActivityPlanner.d.ts} +0 -0
  256. /package/dist/react/core/{CommandBuilder.d.ts → widgets/CommandBuilder.d.ts} +0 -0
  257. /package/dist/react/core/{FileExplorer.d.ts → widgets/FileExplorer.d.ts} +0 -0
  258. /package/dist/react/core/{HexViewer.d.ts → widgets/HexViewer.d.ts} +0 -0
  259. /package/dist/react/core/{MissionCalendar.d.ts → widgets/MissionCalendar.d.ts} +0 -0
  260. /package/dist/react/core/{PacketViewer.d.ts → widgets/PacketViewer.d.ts} +0 -0
@@ -1,21 +1,19 @@
1
1
  /**
2
2
  * @zendir/ui - React Hooks
3
3
  *
4
- * Hooks for integrating with Zendir SDK and space operations.
4
+ * Integration hooks for Zendir simulations and UI helpers.
5
5
  */
6
- export { useZendirSession } from './useZendirSession';
7
- export type { UseZendirSessionOptions, UseZendirSessionResult } from './useZendirSession';
8
- export { useTelemetry } from './useTelemetry';
9
- export type { UseTelemetryOptions, UseTelemetryResult } from './useTelemetry';
10
- export { useSpacecraftPosition } from './useSpacecraftPosition';
11
- export type { UseSpacecraftPositionOptions, UseSpacecraftPositionResult } from './useSpacecraftPosition';
6
+ export { useZendirSession, isZendirSdkAvailable, preloadZendirSdk } from './useZendirSession';
7
+ export type { UseZendirSessionOptions, UseZendirSessionResult, SessionInfo } from './useZendirSession';
8
+ export { useSimulationScene } from './useSimulationScene';
9
+ export type { CelestialBody, PositionTickDiag, SimulationSceneClient, SimulationSceneObject, StructureDiag, UseSimulationSceneOptions, UseSimulationSceneResult, } from './useSimulationScene';
10
+ export { useGroundTrackHistory } from './useGroundTrackHistory';
11
+ export type { UseGroundTrackHistoryOptions } from './useGroundTrackHistory';
12
+ export { useAccessWindows, createDemoAccessPasses } from './useAccessWindows';
13
+ export type { AccessPass, AccessWindowsState, UseAccessWindowsOptions } from './useAccessWindows';
12
14
  export { useCompactMode } from './useCompactMode';
13
15
  export type { UseCompactModeOptions, UseCompactModeResult } from './useCompactMode';
14
- export { useSimulationTime } from './useSimulationTime';
15
- export type { SimulationTimeState, UseSimulationTimeOptions } from './useSimulationTime';
16
16
  export { useSimulationPlayback } from './useSimulationPlayback';
17
17
  export type { SimulationPlaybackState, UseSimulationPlaybackOptions, UseSimulationPlaybackResult, } from './useSimulationPlayback';
18
- export { useAccessWindows, createDemoAccessWindows } from './useAccessWindows';
19
- export type { AccessWindow, AccessWindowsState, UseAccessWindowsOptions } from './useAccessWindows';
20
18
  export { useLiveSelection } from './useLiveSelection';
21
19
  export type { UseLiveSelectionOptions, UseLiveSelectionResult } from './useLiveSelection';
@@ -1,6 +1,6 @@
1
1
  import { TimerConfig } from '../astro/MissionClock';
2
2
 
3
- export interface AccessWindow {
3
+ export interface AccessPass {
4
4
  /** Start time (AOS) */
5
5
  startTime: Date;
6
6
  /** End time (LOS) */
@@ -18,11 +18,11 @@ export interface AccessWindow {
18
18
  }
19
19
  export interface AccessWindowsState {
20
20
  /** Currently active pass (if any) */
21
- currentPass: AccessWindow | null;
21
+ currentPass: AccessPass | null;
22
22
  /** Next upcoming pass */
23
- nextPass: AccessWindow | null;
23
+ nextPass: AccessPass | null;
24
24
  /** All upcoming passes */
25
- upcomingPasses: AccessWindow[];
25
+ upcomingPasses: AccessPass[];
26
26
  /** Whether currently in a pass */
27
27
  isInPass: boolean;
28
28
  /** Current elevation (degrees, if in pass) */
@@ -39,28 +39,24 @@ export interface AccessWindowsState {
39
39
  losTimer: TimerConfig | null;
40
40
  /** Timer config for TCA (for MissionClock) */
41
41
  tcaTimer: TimerConfig | null;
42
- /** Whether connected to SDK */
43
- isConnected: boolean;
44
- /** Whether SDK is available */
45
- isSdkAvailable: boolean;
46
- /** Error message if any */
47
- error: string | null;
48
42
  }
49
43
  export interface UseAccessWindowsOptions {
50
- /** Spacecraft ID to track */
51
- spacecraftId?: string;
52
- /** Ground station ID (or 'all' for all stations) */
53
- groundStationId?: string;
54
44
  /** Update interval in milliseconds (default: 1000) */
55
45
  updateInterval?: number;
56
- /** Number of passes to fetch (default: 5) */
57
- passCount?: number;
58
- /** Manual access windows (for demo/testing) */
59
- manualWindows?: AccessWindow[];
46
+ /**
47
+ * Access windows to track. Provide pre-computed passes (from your own
48
+ * orbital propagator, SGP4, demo data, etc.). Pass `[]` or omit to
49
+ * yield an empty/idle state.
50
+ */
51
+ windows?: AccessPass[];
52
+ /**
53
+ * @deprecated Use `windows`. Old alias kept for one minor for migration.
54
+ */
55
+ manualWindows?: AccessPass[];
60
56
  }
61
57
  export declare function useAccessWindows(options?: UseAccessWindowsOptions): AccessWindowsState;
62
58
  /**
63
59
  * Generate demo access windows for testing
64
60
  */
65
- export declare function createDemoAccessWindows(count?: number): AccessWindow[];
61
+ export declare function createDemoAccessPasses(count?: number): AccessPass[];
66
62
  export default useAccessWindows;
@@ -0,0 +1,34 @@
1
+ import { Satellite } from '../3d/ZenSpace3DTypes';
2
+ import { SatelliteTrack } from '../charts/GroundTrackMap';
3
+
4
+ export interface UseGroundTrackHistoryOptions {
5
+ /**
6
+ * How long to retain ground-track history. Default 90 minutes (≈ one
7
+ * full LEO orbit). Older points fall off the front of the buffer.
8
+ */
9
+ historyMinutes?: number;
10
+ /**
11
+ * Hard cap on points per satellite. Default 600. Protects against
12
+ * runaway history when the upstream data source ticks faster than
13
+ * expected. The shorter of `historyMinutes` and `maxPoints` wins.
14
+ */
15
+ maxPoints?: number;
16
+ /**
17
+ * Optional default colour to assign to each track. The hook does not
18
+ * synthesise palette assignments — pass per-id via `colorBySatelliteId`
19
+ * if you want per-satellite colours.
20
+ */
21
+ color?: string;
22
+ /** Map of satellite-id → colour string. */
23
+ colorBySatelliteId?: Record<string, string>;
24
+ }
25
+ /**
26
+ * Build per-satellite ground-track history from a live ECI position feed.
27
+ *
28
+ * @param satellites Live satellite array (ECI km positions). Pass
29
+ * `useSimulationScene().satellites` directly.
30
+ * @param options History length and palette options.
31
+ * @returns `SatelliteTrack[]` ready for `<GroundTrackMap />`.
32
+ */
33
+ export declare function useGroundTrackHistory(satellites: Satellite[], options?: UseGroundTrackHistoryOptions): SatelliteTrack[];
34
+ export default useGroundTrackHistory;
@@ -0,0 +1,141 @@
1
+ import { GroundStation } from '../types';
2
+ import { Satellite } from '../3d/ZenSpace3DTypes';
3
+
4
+ /**
5
+ * Minimal client interface this hook depends on. Compatible with the
6
+ * `zendir-ts` `ZendirClient` surface but typed structurally so the hook
7
+ * can be used with any compatible client (e.g. a test fake).
8
+ */
9
+ export interface SimulationSceneClient {
10
+ getSimulationStructure(simulationId: string, containerId?: string, opts?: {
11
+ signal?: AbortSignal;
12
+ retry?: unknown;
13
+ }): Promise<Record<string, unknown>>;
14
+ getProperties(objectId: string, properties: string | string[], containerId?: string, opts?: {
15
+ signal?: AbortSignal;
16
+ retry?: unknown;
17
+ }): Promise<unknown>;
18
+ getAllProperties(objectId: string, containerId?: string, opts?: {
19
+ signal?: AbortSignal;
20
+ retry?: unknown;
21
+ }): Promise<Record<string, unknown>>;
22
+ }
23
+ export interface UseSimulationSceneOptions {
24
+ /** ZendirClient (or any compatible client). Pass `null` to defer. */
25
+ client: SimulationSceneClient | null;
26
+ /** Container ID hosting the simulation. */
27
+ containerId: string;
28
+ /** Simulation ID to introspect. */
29
+ simulationId: string;
30
+ /** Polling interval in ms for spacecraft positions. Default: 2000. */
31
+ pollIntervalMs?: number;
32
+ /** Pause polling without unmounting. Default: true. */
33
+ enabled?: boolean;
34
+ }
35
+ export interface SimulationSceneObject {
36
+ id: string;
37
+ name: string;
38
+ kind: 'spacecraft' | 'groundStation' | 'celestialBody';
39
+ }
40
+ /**
41
+ * Lightweight diagnostic snapshot of the raw `getSimulationStructure`
42
+ * response — useful for debugging parsing mismatches in the UI without
43
+ * shipping the full raw blob.
44
+ */
45
+ export interface StructureDiag {
46
+ /** Every top-level key the engine returned (e.g. "Objects","objects","Time"). */
47
+ topLevelKeys: string[];
48
+ /** Length of the `Objects` / `objects` array in the raw response (-1 if absent). */
49
+ rawObjectCount: number;
50
+ /** Length of the `Systems` / `systems` array in the raw response (-1 if absent). */
51
+ rawSystemCount: number;
52
+ }
53
+ /**
54
+ * Per-tick diagnostic — answers "did the position fetch succeed and what came back?".
55
+ * Surfaced in the debug UI so a failed-but-silent tick (parser miss, engine
56
+ * returning a different property name, all-zero positions, etc.) is visible.
57
+ */
58
+ export interface PositionTickDiag {
59
+ /** Wall-clock instant of the most recent tick attempt. */
60
+ attemptedAt: Date;
61
+ /** Number of spacecraft IDs the tick requested positions for. */
62
+ requested: number;
63
+ /** Number of position responses that parsed successfully. */
64
+ parsedOk: number;
65
+ /** Number of position responses that arrived but couldn't be parsed. */
66
+ parseFailed: number;
67
+ /** Number of position responses rejected by the SDK (network / 4xx / 5xx). */
68
+ requestFailed: number;
69
+ /**
70
+ * The exact property names this tick asked the engine for. `null` means
71
+ * the schema probe failed and the tick fell back to `getAllProperties`.
72
+ * Surfaced so the debug UI can show which schema variant is in use.
73
+ */
74
+ requestedProps: string[] | null;
75
+ /** All keys present on the first satellite's response (helps spot wrong property names). */
76
+ firstResponseKeys: string[];
77
+ /** Compact JSON of the first satellite's raw response (capped to 200 chars). */
78
+ firstResponseSample: string;
79
+ }
80
+ export interface UseSimulationSceneResult {
81
+ /** Spacecraft with live ECI position/velocity (km, km/s). */
82
+ satellites: Satellite[];
83
+ /** Static ground stations (lat/lon). */
84
+ groundStations: GroundStation[];
85
+ /**
86
+ * Celestial bodies (planets, moons, sun) with live ECI positions in km.
87
+ * The simulation's central body is filtered out — Cesium renders it
88
+ * natively as the focal globe. Pass directly to
89
+ * `<ZenSpace3D customObjects={scene.celestialBodies} />`.
90
+ */
91
+ celestialBodies: CelestialBody[];
92
+ /** Objects discovered from `getSimulationStructure` (for diagnostics/UI lists). */
93
+ objects: SimulationSceneObject[];
94
+ /** True until the first structure fetch resolves. */
95
+ isLoading: boolean;
96
+ /**
97
+ * Set when the structure-fetch fails (polling stops). Per-tick
98
+ * polling errors do NOT surface here — they retry silently to keep
99
+ * the UI from flapping during transient network blips.
100
+ */
101
+ error: string | null;
102
+ /** Re-run structure fetch and a position tick. */
103
+ refresh: () => Promise<void>;
104
+ /**
105
+ * UTC `Date` representing wall-clock at the most recent successful
106
+ * tick. Pass straight into `<ZenSpace3D referenceTime={...} />` so
107
+ * ECI/J2000 positions rotate to ECEF at "now".
108
+ */
109
+ referenceDate: Date;
110
+ /**
111
+ * Diagnostic snapshot of the raw structure response — null until the
112
+ * first fetch resolves. Use in debug panels to verify key casing and
113
+ * array lengths returned by the engine.
114
+ */
115
+ structureDiag: StructureDiag | null;
116
+ /**
117
+ * Diagnostic for the most recent position-polling tick — null until
118
+ * the first tick has run. Use in debug panels to verify that
119
+ * `getProperties` is actually returning parsable position vectors.
120
+ */
121
+ positionTickDiag: PositionTickDiag | null;
122
+ }
123
+ export declare function useSimulationScene(options: UseSimulationSceneOptions): UseSimulationSceneResult;
124
+ export default useSimulationScene;
125
+ /**
126
+ * Celestial body with the position polled from the engine's `Position`
127
+ * property (in km, ECI), suitable to feed straight into
128
+ * `<ZenSpace3D customObjects={...} />`. The simulation's central body
129
+ * (e.g. Earth in Earth-centric sims) is filtered out — Cesium renders
130
+ * the focal globe natively, so emitting it as a marker would collide.
131
+ */
132
+ export interface CelestialBody {
133
+ id: string;
134
+ name: string;
135
+ /** ECI position in km. */
136
+ position: {
137
+ x: number;
138
+ y: number;
139
+ z: number;
140
+ };
141
+ }
@@ -0,0 +1,401 @@
1
+ import { useState, useRef, useCallback, useEffect } from "react";
2
+ const PLACEHOLDER_SCENECRAFT_POSITION = { x: 6771, y: 0, z: 0 };
3
+ const TICK_RETRY = {
4
+ maxRetries: 2,
5
+ initialDelayMs: 500,
6
+ maxDelayMs: 1500
7
+ };
8
+ const DEFAULT_POLL_MS = 2e3;
9
+ const M_TO_KM = 1 / 1e3;
10
+ function useSimulationScene(options) {
11
+ const {
12
+ client,
13
+ containerId,
14
+ simulationId,
15
+ pollIntervalMs = DEFAULT_POLL_MS,
16
+ enabled = true
17
+ } = options;
18
+ const [satellites, setSatellites] = useState([]);
19
+ const [groundStations, setGroundStations] = useState([]);
20
+ const [celestialBodies, setCelestialBodies] = useState([]);
21
+ const [objects, setObjects] = useState([]);
22
+ const [isLoading, setIsLoading] = useState(true);
23
+ const [error, setError] = useState(null);
24
+ const [referenceDate, setReferenceDate] = useState(() => /* @__PURE__ */ new Date());
25
+ const [structureDiag, setStructureDiag] = useState(null);
26
+ const [positionTickDiag, setPositionTickDiag] = useState(null);
27
+ const spacecraftIdsRef = useRef([]);
28
+ const celestialBodyIdsRef = useRef([]);
29
+ const celestialBodyNamesRef = useRef([]);
30
+ const intervalRef = useRef(null);
31
+ const abortRef = useRef(null);
32
+ const tickInFlightRef = useRef(false);
33
+ const positionPropsRef = useRef(null);
34
+ const tickPositions = useCallback(
35
+ async (signal) => {
36
+ if (!client) return;
37
+ const ids = spacecraftIdsRef.current;
38
+ if (ids.length === 0) return;
39
+ if (tickInFlightRef.current) return;
40
+ tickInFlightRef.current = true;
41
+ try {
42
+ const propsToFetch = positionPropsRef.current;
43
+ const useFallbackAll = !propsToFetch || propsToFetch.length === 0;
44
+ const updates = await Promise.allSettled(
45
+ ids.map(
46
+ (id) => useFallbackAll ? (
47
+ // `retry: TICK_RETRY` overrides the SDK's engineWarmup default
48
+ // for this one call. See the TICK_RETRY definition above for
49
+ // the rationale (avoid a stuck tick stalling the viewer).
50
+ client.getAllProperties(id, containerId, { signal, retry: TICK_RETRY })
51
+ ) : client.getProperties(id, propsToFetch, containerId, {
52
+ signal,
53
+ retry: TICK_RETRY
54
+ })
55
+ )
56
+ );
57
+ if (signal.aborted) return;
58
+ let parsedOk = 0;
59
+ let parseFailed = 0;
60
+ let requestFailed = 0;
61
+ let firstResponseKeys = [];
62
+ let firstResponseSample = "";
63
+ for (let i = 0; i < ids.length; i++) {
64
+ const result = updates[i];
65
+ if (result.status !== "fulfilled") {
66
+ requestFailed += 1;
67
+ continue;
68
+ }
69
+ if (i === 0) {
70
+ const v = result.value;
71
+ if (v && typeof v === "object") firstResponseKeys = Object.keys(v);
72
+ try {
73
+ const json = JSON.stringify(v);
74
+ firstResponseSample = json.length > 200 ? `${json.slice(0, 200)}…` : json;
75
+ } catch {
76
+ firstResponseSample = String(v);
77
+ }
78
+ }
79
+ if (parsePositionVelocity(result.value)) parsedOk += 1;
80
+ else parseFailed += 1;
81
+ }
82
+ setPositionTickDiag({
83
+ attemptedAt: /* @__PURE__ */ new Date(),
84
+ requested: ids.length,
85
+ parsedOk,
86
+ parseFailed,
87
+ requestFailed,
88
+ requestedProps: useFallbackAll ? null : propsToFetch,
89
+ firstResponseKeys,
90
+ firstResponseSample
91
+ });
92
+ setSatellites((prev) => {
93
+ if (prev.length === 0) return prev;
94
+ const next = prev.slice();
95
+ for (let i = 0; i < ids.length; i++) {
96
+ const result = updates[i];
97
+ if (result.status !== "fulfilled") continue;
98
+ const idx = next.findIndex((sc) => sc.id === ids[i]);
99
+ if (idx < 0) continue;
100
+ const parsed = parsePositionVelocity(result.value);
101
+ if (!parsed) continue;
102
+ next[idx] = { ...next[idx], position: parsed.position, velocity: parsed.velocity };
103
+ }
104
+ return next;
105
+ });
106
+ const celIds = celestialBodyIdsRef.current;
107
+ if (celIds.length > 0) {
108
+ const celResults = await Promise.allSettled(
109
+ celIds.map(
110
+ (id) => client.getProperties(id, ["Position", "IsCentralBody"], containerId, {
111
+ signal,
112
+ retry: TICK_RETRY
113
+ })
114
+ )
115
+ );
116
+ if (signal.aborted) return;
117
+ const next = [];
118
+ for (let i = 0; i < celIds.length; i++) {
119
+ const result = celResults[i];
120
+ if (result.status !== "fulfilled") continue;
121
+ const raw = result.value;
122
+ const obj = raw && typeof raw === "object" ? raw : null;
123
+ if (!obj) continue;
124
+ if (obj.IsCentralBody === true) continue;
125
+ const posVec = parseVec3(obj.Position);
126
+ if (!posVec) continue;
127
+ const positionKm = scaleVec(posVec, M_TO_KM);
128
+ const id = celIds[i];
129
+ const name = celestialBodyNamesRef.current[i] ?? `CelestialBody ${shortId(id)}`;
130
+ next.push({ id, name, position: positionKm });
131
+ }
132
+ setCelestialBodies(next);
133
+ }
134
+ } catch {
135
+ } finally {
136
+ tickInFlightRef.current = false;
137
+ if (!signal.aborted) {
138
+ setReferenceDate(/* @__PURE__ */ new Date());
139
+ }
140
+ }
141
+ },
142
+ [client, containerId]
143
+ );
144
+ const loadStructure = useCallback(
145
+ async (signal) => {
146
+ if (!client) return;
147
+ setIsLoading(true);
148
+ setError(null);
149
+ try {
150
+ const structure = await client.getSimulationStructure(simulationId, containerId, { signal });
151
+ if (signal.aborted) return;
152
+ setStructureDiag({
153
+ topLevelKeys: Object.keys(structure),
154
+ rawObjectCount: Array.isArray(structure.Objects) ? structure.Objects.length : Array.isArray(structure.objects) ? structure.objects.length : -1,
155
+ rawSystemCount: Array.isArray(structure.Systems) ? structure.Systems.length : Array.isArray(structure.systems) ? structure.systems.length : -1
156
+ });
157
+ const buckets = bucketStructure(structure);
158
+ spacecraftIdsRef.current = buckets.spacecraft.map((s) => s.id);
159
+ celestialBodyIdsRef.current = buckets.celestialBodyIds;
160
+ celestialBodyNamesRef.current = buckets.celestialBodyNames;
161
+ setSatellites(buckets.spacecraft);
162
+ setObjects(buckets.objects);
163
+ if (buckets.spacecraft.length > 0) {
164
+ try {
165
+ const probeId = buckets.spacecraft[0].id;
166
+ const all = await client.getAllProperties(probeId, containerId, { signal });
167
+ if (signal.aborted) return;
168
+ positionPropsRef.current = detectPositionPropertyNames(all);
169
+ } catch {
170
+ positionPropsRef.current = null;
171
+ }
172
+ } else {
173
+ positionPropsRef.current = null;
174
+ }
175
+ const stationResults = await Promise.allSettled(
176
+ buckets.groundStationIds.map(
177
+ (id) => client.getAllProperties(id, containerId, { signal })
178
+ )
179
+ );
180
+ if (signal.aborted) return;
181
+ const stations = [];
182
+ for (let i = 0; i < buckets.groundStationIds.length; i++) {
183
+ const result = stationResults[i];
184
+ const id = buckets.groundStationIds[i];
185
+ const fallbackName = buckets.groundStationNames[i] ?? shortId(id);
186
+ if (result.status !== "fulfilled") {
187
+ stations.push({ id, name: fallbackName, latitude: 0, longitude: 0 });
188
+ continue;
189
+ }
190
+ stations.push(toGroundStation(id, fallbackName, result.value));
191
+ }
192
+ setGroundStations(stations);
193
+ setIsLoading(false);
194
+ if (spacecraftIdsRef.current.length > 0) {
195
+ await tickPositions(signal);
196
+ }
197
+ } catch (err) {
198
+ if (signal.aborted) return;
199
+ setError(err instanceof Error ? err.message : "Failed to load simulation structure");
200
+ setIsLoading(false);
201
+ }
202
+ },
203
+ [client, containerId, simulationId, tickPositions]
204
+ );
205
+ useEffect(() => {
206
+ if (!client || !containerId || !simulationId) {
207
+ setIsLoading(false);
208
+ return;
209
+ }
210
+ const controller = new AbortController();
211
+ abortRef.current = controller;
212
+ void loadStructure(controller.signal);
213
+ return () => {
214
+ controller.abort();
215
+ if (abortRef.current === controller) abortRef.current = null;
216
+ };
217
+ }, [client, containerId, simulationId, loadStructure]);
218
+ useEffect(() => {
219
+ if (!enabled || !client || error) {
220
+ if (intervalRef.current) {
221
+ clearInterval(intervalRef.current);
222
+ intervalRef.current = null;
223
+ }
224
+ return;
225
+ }
226
+ const interval = setInterval(() => {
227
+ const controller = new AbortController();
228
+ void tickPositions(controller.signal).catch(() => void 0);
229
+ }, pollIntervalMs);
230
+ intervalRef.current = interval;
231
+ return () => {
232
+ clearInterval(interval);
233
+ if (intervalRef.current === interval) intervalRef.current = null;
234
+ };
235
+ }, [client, enabled, error, pollIntervalMs, tickPositions]);
236
+ const refresh = useCallback(async () => {
237
+ const controller = new AbortController();
238
+ await loadStructure(controller.signal);
239
+ }, [loadStructure]);
240
+ return {
241
+ satellites,
242
+ groundStations,
243
+ celestialBodies,
244
+ objects,
245
+ isLoading,
246
+ error,
247
+ refresh,
248
+ referenceDate,
249
+ structureDiag,
250
+ positionTickDiag
251
+ };
252
+ }
253
+ function firstStringField(obj, keys) {
254
+ for (const key of keys) {
255
+ const v = obj[key];
256
+ if (typeof v === "string" && v.length > 0) return v;
257
+ }
258
+ return void 0;
259
+ }
260
+ function bucketStructure(structure) {
261
+ const out = {
262
+ spacecraft: [],
263
+ groundStationIds: [],
264
+ groundStationNames: [],
265
+ celestialBodyIds: [],
266
+ celestialBodyNames: [],
267
+ objects: []
268
+ };
269
+ const topObjects = pickArray(structure, ["Objects", "objects"]);
270
+ for (const obj of topObjects) walkObject(obj, out);
271
+ const topSystems = pickArray(structure, ["Systems", "systems"]);
272
+ for (const sys of topSystems) walkObject(sys, out);
273
+ return out;
274
+ }
275
+ function pickArray(obj, keys) {
276
+ for (const key of keys) {
277
+ const v = obj[key];
278
+ if (Array.isArray(v) && v.length > 0) return v;
279
+ }
280
+ for (const key of keys) {
281
+ const v = obj[key];
282
+ if (Array.isArray(v)) return v;
283
+ }
284
+ return [];
285
+ }
286
+ function walkObject(node, out) {
287
+ if (!node || typeof node !== "object") return;
288
+ const obj = node;
289
+ const id = firstStringField(obj, ["ID", "id", "Id"]);
290
+ const type = firstStringField(obj, ["Type", "type"]);
291
+ const name = firstStringField(obj, ["Name", "name"]);
292
+ if (id && type) {
293
+ if (type === "Spacecraft" || type.endsWith(".Spacecraft")) {
294
+ upsertObjectSummary(out, {
295
+ id,
296
+ name: name ?? `Spacecraft ${shortId(id)}`,
297
+ kind: "spacecraft"
298
+ });
299
+ out.spacecraft.push({
300
+ id,
301
+ name: name ?? `Spacecraft ${shortId(id)}`,
302
+ category: "satellite",
303
+ position: { ...PLACEHOLDER_SCENECRAFT_POSITION },
304
+ visualRadius: 50
305
+ });
306
+ } else if (type === "GroundStation" || type.endsWith(".GroundStation")) {
307
+ upsertObjectSummary(out, {
308
+ id,
309
+ name: name ?? `GroundStation ${shortId(id)}`,
310
+ kind: "groundStation"
311
+ });
312
+ out.groundStationIds.push(id);
313
+ out.groundStationNames.push(name);
314
+ } else if (type === "CelestialBody" || type.endsWith(".CelestialBody")) {
315
+ upsertObjectSummary(out, {
316
+ id,
317
+ name: name ?? `CelestialBody ${shortId(id)}`,
318
+ kind: "celestialBody"
319
+ });
320
+ out.celestialBodyIds.push(id);
321
+ out.celestialBodyNames.push(name);
322
+ }
323
+ }
324
+ const children = pickArray(obj, ["Children", "children"]);
325
+ for (const child of children) walkObject(child, out);
326
+ }
327
+ function upsertObjectSummary(out, next) {
328
+ const idx = out.objects.findIndex((o) => o.id === next.id);
329
+ if (idx >= 0) {
330
+ out.objects[idx] = next;
331
+ return;
332
+ }
333
+ out.objects.push(next);
334
+ }
335
+ function detectPositionPropertyNames(props) {
336
+ const positionCandidates = ["Position_BN_N", "Position", "r_BN_N", "r_N"];
337
+ for (const name of positionCandidates) {
338
+ if (name in props && parseVec3(props[name]) !== null) {
339
+ return [name];
340
+ }
341
+ }
342
+ return [];
343
+ }
344
+ function parsePositionVelocity(raw) {
345
+ if (!raw || typeof raw !== "object") return null;
346
+ const obj = raw;
347
+ const position = parseVec3(obj.Position_BN_N) ?? parseVec3(obj.Position) ?? parseVec3(obj.position);
348
+ if (!position) return null;
349
+ if (position.x === 0 && position.y === 0 && position.z === 0) return null;
350
+ const velocity = parseVec3(obj.Velocity_BN_N) ?? parseVec3(obj.Velocity) ?? parseVec3(obj.velocity);
351
+ return {
352
+ position: scaleVec(position, M_TO_KM),
353
+ velocity: velocity ? scaleVec(velocity, M_TO_KM) : void 0
354
+ };
355
+ }
356
+ function parseVec3(raw) {
357
+ if (Array.isArray(raw) && raw.length >= 3) {
358
+ const [x, y, z] = raw;
359
+ if (typeof x === "number" && typeof y === "number" && typeof z === "number") {
360
+ return { x, y, z };
361
+ }
362
+ }
363
+ if (raw && typeof raw === "object") {
364
+ const obj = raw;
365
+ const x = obj.X ?? obj.x;
366
+ const y = obj.Y ?? obj.y;
367
+ const z = obj.Z ?? obj.z;
368
+ if (typeof x === "number" && typeof y === "number" && typeof z === "number") {
369
+ return { x, y, z };
370
+ }
371
+ }
372
+ return null;
373
+ }
374
+ function scaleVec(v, k) {
375
+ return { x: v.x * k, y: v.y * k, z: v.z * k };
376
+ }
377
+ function toGroundStation(id, fallbackName, props) {
378
+ const name = firstStringField(props, ["Name"]) ?? fallbackName;
379
+ const latitude = pickNumber(props, ["Latitude", "latitude"]) ?? 0;
380
+ const longitude = pickNumber(props, ["Longitude", "longitude"]) ?? 0;
381
+ const elevation = pickNumber(props, ["Altitude", "altitude", "Elevation"]);
382
+ const minElevation = pickNumber(props, ["MinElevation", "MinimumElevation"]);
383
+ const station = { id, name, latitude, longitude };
384
+ if (elevation !== void 0) station.elevation = elevation;
385
+ if (minElevation !== void 0) station.minElevation = minElevation;
386
+ return station;
387
+ }
388
+ function pickNumber(obj, keys) {
389
+ for (const key of keys) {
390
+ const v = obj[key];
391
+ if (typeof v === "number" && Number.isFinite(v)) return v;
392
+ }
393
+ return void 0;
394
+ }
395
+ function shortId(id) {
396
+ return id.length > 8 ? id.slice(0, 8) : id;
397
+ }
398
+ export {
399
+ useSimulationScene
400
+ };
401
+ //# sourceMappingURL=useSimulationScene.js.map