@zendir/ui 0.1.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 (359) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/LICENSE +21 -0
  3. package/README.md +589 -0
  4. package/dist/index.d.ts +8 -0
  5. package/dist/index.js +421 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/react/3d/EarthViewer.d.ts +46 -0
  8. package/dist/react/3d/EarthViewer.js +836 -0
  9. package/dist/react/3d/EarthViewer.js.map +1 -0
  10. package/dist/react/3d/SolarSystemViewer.d.ts +43 -0
  11. package/dist/react/3d/SolarSystemViewer.js +372 -0
  12. package/dist/react/3d/SolarSystemViewer.js.map +1 -0
  13. package/dist/react/3d/ZenSpace3D.d.ts +16 -0
  14. package/dist/react/3d/ZenSpace3D.js +1253 -0
  15. package/dist/react/3d/ZenSpace3D.js.map +1 -0
  16. package/dist/react/3d/ZenSpace3DCesium.d.ts +9 -0
  17. package/dist/react/3d/ZenSpace3DCesium.js +186 -0
  18. package/dist/react/3d/ZenSpace3DCesium.js.map +1 -0
  19. package/dist/react/3d/ZenSpace3DShaders.d.ts +78 -0
  20. package/dist/react/3d/ZenSpace3DShaders.js +94 -0
  21. package/dist/react/3d/ZenSpace3DShaders.js.map +1 -0
  22. package/dist/react/3d/ZenSpace3DTypes.d.ts +614 -0
  23. package/dist/react/3d/ZenSpace3DUtils.d.ts +183 -0
  24. package/dist/react/3d/ZenSpace3DUtils.js +213 -0
  25. package/dist/react/3d/ZenSpace3DUtils.js.map +1 -0
  26. package/dist/react/3d/index.d.ts +23 -0
  27. package/dist/react/3d/threeLoader.d.ts +22 -0
  28. package/dist/react/3d/threeLoader.js +18 -0
  29. package/dist/react/3d/threeLoader.js.map +1 -0
  30. package/dist/react/astro/ClassificationBanner.d.ts +25 -0
  31. package/dist/react/astro/ClassificationBanner.js +83 -0
  32. package/dist/react/astro/ClassificationBanner.js.map +1 -0
  33. package/dist/react/astro/GlobalStatusBar.d.ts +42 -0
  34. package/dist/react/astro/GlobalStatusBar.js +165 -0
  35. package/dist/react/astro/GlobalStatusBar.js.map +1 -0
  36. package/dist/react/astro/MissionClock.d.ts +169 -0
  37. package/dist/react/astro/MissionClock.js +411 -0
  38. package/dist/react/astro/MissionClock.js.map +1 -0
  39. package/dist/react/astro/MonitoringIcon.d.ts +60 -0
  40. package/dist/react/astro/MonitoringIcon.js +369 -0
  41. package/dist/react/astro/MonitoringIcon.js.map +1 -0
  42. package/dist/react/astro/Notification.d.ts +42 -0
  43. package/dist/react/astro/Notification.js +156 -0
  44. package/dist/react/astro/Notification.js.map +1 -0
  45. package/dist/react/astro/Progress.d.ts +39 -0
  46. package/dist/react/astro/Progress.js +149 -0
  47. package/dist/react/astro/Progress.js.map +1 -0
  48. package/dist/react/astro/SimulationControls.d.ts +136 -0
  49. package/dist/react/astro/SimulationControls.js +668 -0
  50. package/dist/react/astro/SimulationControls.js.map +1 -0
  51. package/dist/react/astro/StatusIndicator.d.ts +34 -0
  52. package/dist/react/astro/StatusIndicator.js +189 -0
  53. package/dist/react/astro/StatusIndicator.js.map +1 -0
  54. package/dist/react/astro/UnifiedTimeline.d.ts +106 -0
  55. package/dist/react/astro/UnifiedTimeline.js +1768 -0
  56. package/dist/react/astro/UnifiedTimeline.js.map +1 -0
  57. package/dist/react/astro/index.d.ts +63 -0
  58. package/dist/react/cards/AccessCard.d.ts +37 -0
  59. package/dist/react/cards/AccessCard.js +410 -0
  60. package/dist/react/cards/AccessCard.js.map +1 -0
  61. package/dist/react/cards/OrbitCard.d.ts +31 -0
  62. package/dist/react/cards/OrbitCard.js +372 -0
  63. package/dist/react/cards/OrbitCard.js.map +1 -0
  64. package/dist/react/cards/SpacecraftCard.d.ts +54 -0
  65. package/dist/react/cards/SpacecraftCard.js +941 -0
  66. package/dist/react/cards/SpacecraftCard.js.map +1 -0
  67. package/dist/react/cards/TelemetryCard.d.ts +40 -0
  68. package/dist/react/cards/TelemetryCard.js +742 -0
  69. package/dist/react/cards/TelemetryCard.js.map +1 -0
  70. package/dist/react/cards/TelemetryStreamCard.d.ts +59 -0
  71. package/dist/react/cards/TelemetryStreamCard.js +309 -0
  72. package/dist/react/cards/TelemetryStreamCard.js.map +1 -0
  73. package/dist/react/cards/index.d.ts +13 -0
  74. package/dist/react/charts/GroundTrackMap.d.ts +112 -0
  75. package/dist/react/charts/GroundTrackMap.js +1123 -0
  76. package/dist/react/charts/GroundTrackMap.js.map +1 -0
  77. package/dist/react/charts/GroundTrackMapLeaflet.d.ts +26 -0
  78. package/dist/react/charts/GroundTrackMapLeaflet.js +571 -0
  79. package/dist/react/charts/GroundTrackMapLeaflet.js.map +1 -0
  80. package/dist/react/charts/groundTrackMapLeafletTiles.d.ts +22 -0
  81. package/dist/react/charts/groundTrackMapLeafletTiles.js +11 -0
  82. package/dist/react/charts/groundTrackMapLeafletTiles.js.map +1 -0
  83. package/dist/react/charts/groundTrackMapLeafletUtils.d.ts +24 -0
  84. package/dist/react/charts/groundTrackMapLeafletUtils.js +109 -0
  85. package/dist/react/charts/groundTrackMapLeafletUtils.js.map +1 -0
  86. package/dist/react/charts/index.d.ts +10 -0
  87. package/dist/react/charts/unified/AstroChart.d.ts +24 -0
  88. package/dist/react/charts/unified/AstroChart.js +1405 -0
  89. package/dist/react/charts/unified/AstroChart.js.map +1 -0
  90. package/dist/react/charts/unified/PowerOverviewChart.d.ts +73 -0
  91. package/dist/react/charts/unified/PowerOverviewChart.js +488 -0
  92. package/dist/react/charts/unified/PowerOverviewChart.js.map +1 -0
  93. package/dist/react/charts/unified/domain.d.ts +845 -0
  94. package/dist/react/charts/unified/domain.js +3168 -0
  95. package/dist/react/charts/unified/domain.js.map +1 -0
  96. package/dist/react/charts/unified/generators.d.ts +276 -0
  97. package/dist/react/charts/unified/generators.js +518 -0
  98. package/dist/react/charts/unified/generators.js.map +1 -0
  99. package/dist/react/charts/unified/index.d.ts +55 -0
  100. package/dist/react/charts/unified/presets.d.ts +290 -0
  101. package/dist/react/charts/unified/presets.js +999 -0
  102. package/dist/react/charts/unified/presets.js.map +1 -0
  103. package/dist/react/charts/unified/sync.d.ts +69 -0
  104. package/dist/react/charts/unified/sync.js +219 -0
  105. package/dist/react/charts/unified/sync.js.map +1 -0
  106. package/dist/react/charts/unified/theme.d.ts +447 -0
  107. package/dist/react/charts/unified/theme.js +562 -0
  108. package/dist/react/charts/unified/theme.js.map +1 -0
  109. package/dist/react/charts/unified/types.d.ts +826 -0
  110. package/dist/react/charts/unified/useChartStream.d.ts +58 -0
  111. package/dist/react/charts/unified/useChartStream.js +226 -0
  112. package/dist/react/charts/unified/useChartStream.js.map +1 -0
  113. package/dist/react/chatgpt/AppCard.d.ts +59 -0
  114. package/dist/react/chatgpt/AppCard.js +306 -0
  115. package/dist/react/chatgpt/AppCard.js.map +1 -0
  116. package/dist/react/chatgpt/ChatGPTCard.d.ts +6 -0
  117. package/dist/react/chatgpt/index.d.ts +167 -0
  118. package/dist/react/chatgpt/index.js +166 -0
  119. package/dist/react/chatgpt/index.js.map +1 -0
  120. package/dist/react/context/DisplaySettingsContext.d.ts +107 -0
  121. package/dist/react/context/DisplaySettingsContext.js +169 -0
  122. package/dist/react/context/DisplaySettingsContext.js.map +1 -0
  123. package/dist/react/context/index.d.ts +5 -0
  124. package/dist/react/core/ActivityPlanner.d.ts +45 -0
  125. package/dist/react/core/ActivityPlanner.js +532 -0
  126. package/dist/react/core/ActivityPlanner.js.map +1 -0
  127. package/dist/react/core/AppBar.d.ts +71 -0
  128. package/dist/react/core/AppBar.js +817 -0
  129. package/dist/react/core/AppBar.js.map +1 -0
  130. package/dist/react/core/AstroIcon.d.ts +84 -0
  131. package/dist/react/core/AstroIcon.js +1243 -0
  132. package/dist/react/core/AstroIcon.js.map +1 -0
  133. package/dist/react/core/Badge.d.ts +27 -0
  134. package/dist/react/core/Badge.js +134 -0
  135. package/dist/react/core/Badge.js.map +1 -0
  136. package/dist/react/core/Button.d.ts +26 -0
  137. package/dist/react/core/Button.js +306 -0
  138. package/dist/react/core/Button.js.map +1 -0
  139. package/dist/react/core/CardHeader.d.ts +34 -0
  140. package/dist/react/core/CardHeader.js +316 -0
  141. package/dist/react/core/CardHeader.js.map +1 -0
  142. package/dist/react/core/ChatPanel.d.ts +627 -0
  143. package/dist/react/core/ChatPanel.js +1144 -0
  144. package/dist/react/core/ChatPanel.js.map +1 -0
  145. package/dist/react/core/Checkbox.d.ts +26 -0
  146. package/dist/react/core/Checkbox.js +130 -0
  147. package/dist/react/core/Checkbox.js.map +1 -0
  148. package/dist/react/core/ColorPickerPanel.d.ts +25 -0
  149. package/dist/react/core/ColorPickerPanel.js +293 -0
  150. package/dist/react/core/ColorPickerPanel.js.map +1 -0
  151. package/dist/react/core/CommandBuilder.d.ts +74 -0
  152. package/dist/react/core/CommandBuilder.js +518 -0
  153. package/dist/react/core/CommandBuilder.js.map +1 -0
  154. package/dist/react/core/ConfirmDialog.d.ts +45 -0
  155. package/dist/react/core/ConfirmDialog.js +315 -0
  156. package/dist/react/core/ConfirmDialog.js.map +1 -0
  157. package/dist/react/core/ConnectionForm.d.ts +57 -0
  158. package/dist/react/core/ConnectionForm.js +496 -0
  159. package/dist/react/core/ConnectionForm.js.map +1 -0
  160. package/dist/react/core/Container.d.ts +51 -0
  161. package/dist/react/core/Container.js +670 -0
  162. package/dist/react/core/Container.js.map +1 -0
  163. package/dist/react/core/CopyButton.d.ts +39 -0
  164. package/dist/react/core/CopyButton.js +105 -0
  165. package/dist/react/core/CopyButton.js.map +1 -0
  166. package/dist/react/core/DataTable.d.ts +113 -0
  167. package/dist/react/core/DataTable.js +446 -0
  168. package/dist/react/core/DataTable.js.map +1 -0
  169. package/dist/react/core/DataValue.d.ts +64 -0
  170. package/dist/react/core/DataValue.js +545 -0
  171. package/dist/react/core/DataValue.js.map +1 -0
  172. package/dist/react/core/Dialog.d.ts +32 -0
  173. package/dist/react/core/Dialog.js +201 -0
  174. package/dist/react/core/Dialog.js.map +1 -0
  175. package/dist/react/core/FileExplorer.d.ts +65 -0
  176. package/dist/react/core/FileExplorer.js +520 -0
  177. package/dist/react/core/FileExplorer.js.map +1 -0
  178. package/dist/react/core/GlassCard.d.ts +129 -0
  179. package/dist/react/core/GlassCard.js +375 -0
  180. package/dist/react/core/GlassCard.js.map +1 -0
  181. package/dist/react/core/HeaderIconWithStatus.d.ts +39 -0
  182. package/dist/react/core/HeaderIconWithStatus.js +157 -0
  183. package/dist/react/core/HeaderIconWithStatus.js.map +1 -0
  184. package/dist/react/core/HexViewer.d.ts +143 -0
  185. package/dist/react/core/HexViewer.js +1106 -0
  186. package/dist/react/core/HexViewer.js.map +1 -0
  187. package/dist/react/core/Icon.d.ts +32 -0
  188. package/dist/react/core/Icon.js +142 -0
  189. package/dist/react/core/Icon.js.map +1 -0
  190. package/dist/react/core/ImageGallery.d.ts +41 -0
  191. package/dist/react/core/ImageGallery.js +320 -0
  192. package/dist/react/core/ImageGallery.js.map +1 -0
  193. package/dist/react/core/Input.d.ts +38 -0
  194. package/dist/react/core/Input.js +288 -0
  195. package/dist/react/core/Input.js.map +1 -0
  196. package/dist/react/core/LimitsBar.d.ts +51 -0
  197. package/dist/react/core/LimitsBar.js +200 -0
  198. package/dist/react/core/LimitsBar.js.map +1 -0
  199. package/dist/react/core/LogViewer.d.ts +61 -0
  200. package/dist/react/core/LogViewer.js +599 -0
  201. package/dist/react/core/LogViewer.js.map +1 -0
  202. package/dist/react/core/MessageStream.d.ts +58 -0
  203. package/dist/react/core/MessageStream.js +455 -0
  204. package/dist/react/core/MessageStream.js.map +1 -0
  205. package/dist/react/core/MissionCalendar.d.ts +81 -0
  206. package/dist/react/core/MissionCalendar.js +1049 -0
  207. package/dist/react/core/MissionCalendar.js.map +1 -0
  208. package/dist/react/core/NumberInput.d.ts +85 -0
  209. package/dist/react/core/NumberInput.js +507 -0
  210. package/dist/react/core/NumberInput.js.map +1 -0
  211. package/dist/react/core/PacketViewer.d.ts +73 -0
  212. package/dist/react/core/PacketViewer.js +431 -0
  213. package/dist/react/core/PacketViewer.js.map +1 -0
  214. package/dist/react/core/Pagination.d.ts +30 -0
  215. package/dist/react/core/Pagination.js +190 -0
  216. package/dist/react/core/Pagination.js.map +1 -0
  217. package/dist/react/core/PinInput.d.ts +41 -0
  218. package/dist/react/core/PinInput.js +210 -0
  219. package/dist/react/core/PinInput.js.map +1 -0
  220. package/dist/react/core/Popover.d.ts +55 -0
  221. package/dist/react/core/Popover.js +288 -0
  222. package/dist/react/core/Popover.js.map +1 -0
  223. package/dist/react/core/Select.d.ts +42 -0
  224. package/dist/react/core/Select.js +303 -0
  225. package/dist/react/core/Select.js.map +1 -0
  226. package/dist/react/core/SideNav.d.ts +103 -0
  227. package/dist/react/core/SideNav.js +551 -0
  228. package/dist/react/core/SideNav.js.map +1 -0
  229. package/dist/react/core/SidePanel.d.ts +33 -0
  230. package/dist/react/core/SidePanel.js +199 -0
  231. package/dist/react/core/SidePanel.js.map +1 -0
  232. package/dist/react/core/Tabs.d.ts +47 -0
  233. package/dist/react/core/Tabs.js +129 -0
  234. package/dist/react/core/Tabs.js.map +1 -0
  235. package/dist/react/core/Toast.d.ts +56 -0
  236. package/dist/react/core/Toast.js +229 -0
  237. package/dist/react/core/Toast.js.map +1 -0
  238. package/dist/react/core/Toggle.d.ts +22 -0
  239. package/dist/react/core/Toggle.js +151 -0
  240. package/dist/react/core/Toggle.js.map +1 -0
  241. package/dist/react/core/Tooltip.d.ts +19 -0
  242. package/dist/react/core/Tooltip.js +179 -0
  243. package/dist/react/core/Tooltip.js.map +1 -0
  244. package/dist/react/core/Typography.d.ts +127 -0
  245. package/dist/react/core/Typography.js +187 -0
  246. package/dist/react/core/Typography.js.map +1 -0
  247. package/dist/react/core/index.d.ts +108 -0
  248. package/dist/react/core/layout/Box.d.ts +77 -0
  249. package/dist/react/core/layout/Box.js +126 -0
  250. package/dist/react/core/layout/Box.js.map +1 -0
  251. package/dist/react/core/layout/Center.d.ts +20 -0
  252. package/dist/react/core/layout/Center.js +34 -0
  253. package/dist/react/core/layout/Center.js.map +1 -0
  254. package/dist/react/core/layout/Divider.d.ts +16 -0
  255. package/dist/react/core/layout/Divider.js +108 -0
  256. package/dist/react/core/layout/Divider.js.map +1 -0
  257. package/dist/react/core/layout/Flex.d.ts +30 -0
  258. package/dist/react/core/layout/Flex.js +128 -0
  259. package/dist/react/core/layout/Flex.js.map +1 -0
  260. package/dist/react/core/layout/Grid.d.ts +36 -0
  261. package/dist/react/core/layout/Grid.js +142 -0
  262. package/dist/react/core/layout/Grid.js.map +1 -0
  263. package/dist/react/core/layout/Spacer.d.ts +8 -0
  264. package/dist/react/core/layout/Spacer.js +31 -0
  265. package/dist/react/core/layout/Spacer.js.map +1 -0
  266. package/dist/react/core/layout/Stack.d.ts +54 -0
  267. package/dist/react/core/layout/Stack.js +74 -0
  268. package/dist/react/core/layout/Stack.js.map +1 -0
  269. package/dist/react/core/layout/index.d.ts +38 -0
  270. package/dist/react/core/layout/responsive.d.ts +23 -0
  271. package/dist/react/core/layout/responsive.js +26 -0
  272. package/dist/react/core/layout/responsive.js.map +1 -0
  273. package/dist/react/core/layout/useBreakpoint.d.ts +77 -0
  274. package/dist/react/core/layout/useBreakpoint.js +73 -0
  275. package/dist/react/core/layout/useBreakpoint.js.map +1 -0
  276. package/dist/react/core/propertyConfig.d.ts +443 -0
  277. package/dist/react/core/propertyConfig.js +399 -0
  278. package/dist/react/core/propertyConfig.js.map +1 -0
  279. package/dist/react/hooks/index.d.ts +21 -0
  280. package/dist/react/hooks/useAccessWindows.d.ts +66 -0
  281. package/dist/react/hooks/useCompactMode.d.ts +82 -0
  282. package/dist/react/hooks/useCompactMode.js +62 -0
  283. package/dist/react/hooks/useCompactMode.js.map +1 -0
  284. package/dist/react/hooks/useLiveSelection.d.ts +57 -0
  285. package/dist/react/hooks/useSimulationPlayback.d.ts +97 -0
  286. package/dist/react/hooks/useSimulationTime.d.ts +61 -0
  287. package/dist/react/hooks/useSpacecraftPosition.d.ts +50 -0
  288. package/dist/react/hooks/useSpacecraftPosition.js +89 -0
  289. package/dist/react/hooks/useSpacecraftPosition.js.map +1 -0
  290. package/dist/react/hooks/useTelemetry.d.ts +55 -0
  291. package/dist/react/hooks/useTelemetry.js +73 -0
  292. package/dist/react/hooks/useTelemetry.js.map +1 -0
  293. package/dist/react/hooks/useZendirSession.d.ts +109 -0
  294. package/dist/react/hooks/useZendirSession.js +148 -0
  295. package/dist/react/hooks/useZendirSession.js.map +1 -0
  296. package/dist/react/index.d.ts +74 -0
  297. package/dist/react/shared/ErrorBoundary.d.ts +63 -0
  298. package/dist/react/shared/ErrorBoundary.js +142 -0
  299. package/dist/react/shared/ErrorBoundary.js.map +1 -0
  300. package/dist/react/shared/Skeleton.d.ts +110 -0
  301. package/dist/react/shared/Skeleton.js +324 -0
  302. package/dist/react/shared/Skeleton.js.map +1 -0
  303. package/dist/react/shared/index.d.ts +12 -0
  304. package/dist/react/theme/ThemeProvider.d.ts +385 -0
  305. package/dist/react/theme/ThemeProvider.js +1096 -0
  306. package/dist/react/theme/ThemeProvider.js.map +1 -0
  307. package/dist/react/theme/astro-tokens.d.ts +153 -0
  308. package/dist/react/theme/cardAccent.d.ts +75 -0
  309. package/dist/react/theme/cardAccent.js +137 -0
  310. package/dist/react/theme/cardAccent.js.map +1 -0
  311. package/dist/react/theme/config.d.ts +39 -0
  312. package/dist/react/theme/index.d.ts +9 -0
  313. package/dist/react/types.d.ts +360 -0
  314. package/dist/react/types.js +58 -0
  315. package/dist/react/types.js.map +1 -0
  316. package/dist/react/utils/index.d.ts +247 -0
  317. package/dist/react/utils/index.js +423 -0
  318. package/dist/react/utils/index.js.map +1 -0
  319. package/dist/react/visualizations/EclipseTimerCard.d.ts +17 -0
  320. package/dist/react/visualizations/EclipseTimerCard.js +250 -0
  321. package/dist/react/visualizations/EclipseTimerCard.js.map +1 -0
  322. package/dist/react/visualizations/LinkBudgetCard.d.ts +50 -0
  323. package/dist/react/visualizations/LinkBudgetCard.js +444 -0
  324. package/dist/react/visualizations/LinkBudgetCard.js.map +1 -0
  325. package/dist/react/visualizations/NavBallCard.d.ts +17 -0
  326. package/dist/react/visualizations/NavBallCard.js +243 -0
  327. package/dist/react/visualizations/NavBallCard.js.map +1 -0
  328. package/dist/react/visualizations/PropulsionCard.d.ts +37 -0
  329. package/dist/react/visualizations/PropulsionCard.js +298 -0
  330. package/dist/react/visualizations/PropulsionCard.js.map +1 -0
  331. package/dist/react/visualizations/SensorFootprintCard.d.ts +33 -0
  332. package/dist/react/visualizations/SensorFootprintCard.js +326 -0
  333. package/dist/react/visualizations/SensorFootprintCard.js.map +1 -0
  334. package/dist/react/visualizations/ThermalHeatmapCard.d.ts +38 -0
  335. package/dist/react/visualizations/ThermalHeatmapCard.js +372 -0
  336. package/dist/react/visualizations/ThermalHeatmapCard.js.map +1 -0
  337. package/dist/react/visualizations/index.d.ts +17 -0
  338. package/dist/react.d.ts +1 -0
  339. package/dist/react.js +421 -0
  340. package/dist/react.js.map +1 -0
  341. package/dist/shaders/atmosphere.frag.js +5 -0
  342. package/dist/shaders/atmosphere.frag.js.map +1 -0
  343. package/dist/shaders/atmosphere.vert.js +5 -0
  344. package/dist/shaders/atmosphere.vert.js.map +1 -0
  345. package/dist/shaders/stars.frag.js +5 -0
  346. package/dist/shaders/stars.frag.js.map +1 -0
  347. package/dist/shaders/stars.vert.js +5 -0
  348. package/dist/shaders/stars.vert.js.map +1 -0
  349. package/dist/style.css +143 -0
  350. package/dist/tokens/index.d.ts +296 -0
  351. package/dist/tokens/index.js +263 -0
  352. package/dist/tokens/index.js.map +1 -0
  353. package/dist/tokens/tokens.css +155 -0
  354. package/dist/types/index.d.ts +23 -0
  355. package/dist/types.d.ts +1 -0
  356. package/dist/types.js +2 -0
  357. package/dist/types.js.map +1 -0
  358. package/package.json +220 -0
  359. package/sdk-stub.js +22 -0
@@ -0,0 +1,1253 @@
1
+ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
+ import React, { forwardRef, useState, useEffect, useRef, useMemo, useCallback, useImperativeHandle } from "react";
3
+ import { AstroIcon } from "../core/AstroIcon.js";
4
+ import { SCENE_EARTH_RADIUS, latLonAltToCartesian, KM_TO_SCENE, easeOutCubic } from "./ZenSpace3DUtils.js";
5
+ import atmosphereVertexShader from "../../shaders/atmosphere.vert.js";
6
+ import atmosphereFragmentShader from "../../shaders/atmosphere.frag.js";
7
+ import starsVertexShader from "../../shaders/stars.vert.js";
8
+ import starsFragmentShader from "../../shaders/stars.frag.js";
9
+ import { loadThree } from "./threeLoader.js";
10
+ import { ZenSpace3DCesium } from "./ZenSpace3DCesium.js";
11
+ import { useTheme } from "../theme/ThemeProvider.js";
12
+ const DEFAULT_LAYERS = {
13
+ planets: true,
14
+ moons: true,
15
+ satellites: true,
16
+ debris: false,
17
+ stations: true,
18
+ asteroids: false,
19
+ groundStations: true,
20
+ orbits: true,
21
+ labels: true,
22
+ constellations: false,
23
+ coverage: true,
24
+ atmosphere: true,
25
+ stars: true,
26
+ grid: false,
27
+ terminator: true
28
+ };
29
+ const CAMERA_TRANSITION_DURATION = 2e3;
30
+ const STARS_COUNT = 5e3;
31
+ const EARTH_SEGMENTS = 64;
32
+ const EARTH_SEGMENTS_V = 32;
33
+ const SPHERICAL_SUBDIVISIONS = 16;
34
+ function checkWebGLCapabilities() {
35
+ try {
36
+ const canvas = document.createElement("canvas");
37
+ const gl2 = canvas.getContext("webgl2");
38
+ if (gl2) return { available: true, version: 2 };
39
+ const gl = canvas.getContext("webgl");
40
+ if (gl) return { available: true, version: 1 };
41
+ } catch {
42
+ }
43
+ return { available: false, version: 0 };
44
+ }
45
+ function slerp(v1, v2, t) {
46
+ const len1 = Math.sqrt(v1.x * v1.x + v1.y * v1.y + v1.z * v1.z);
47
+ const len2 = Math.sqrt(v2.x * v2.x + v2.y * v2.y + v2.z * v2.z);
48
+ if (len1 < 1e-4 || len2 < 1e-4) {
49
+ return { x: v1.x, y: v1.y, z: v1.z };
50
+ }
51
+ const n1 = { x: v1.x / len1, y: v1.y / len1, z: v1.z / len1 };
52
+ const n2 = { x: v2.x / len2, y: v2.y / len2, z: v2.z / len2 };
53
+ let dot = n1.x * n2.x + n1.y * n2.y + n1.z * n2.z;
54
+ dot = Math.max(-1, Math.min(1, dot));
55
+ const theta = Math.acos(dot);
56
+ if (theta < 1e-3) {
57
+ return {
58
+ x: n1.x + t * (n2.x - n1.x),
59
+ y: n1.y + t * (n2.y - n1.y),
60
+ z: n1.z + t * (n2.z - n1.z)
61
+ };
62
+ }
63
+ const sinTheta = Math.sin(theta);
64
+ if (Math.abs(sinTheta) < 1e-4) {
65
+ return {
66
+ x: n1.x + t * (n2.x - n1.x),
67
+ y: n1.y + t * (n2.y - n1.y),
68
+ z: n1.z + t * (n2.z - n1.z)
69
+ };
70
+ }
71
+ const a = Math.sin((1 - t) * theta) / sinTheta;
72
+ const b = Math.sin(t * theta) / sinTheta;
73
+ return {
74
+ x: a * n1.x + b * n2.x,
75
+ y: a * n1.y + b * n2.y,
76
+ z: a * n1.z + b * n2.z
77
+ };
78
+ }
79
+ function subdivideSphericalPolygon(corners, subdivisions, radius) {
80
+ if (corners.length < 3 || subdivisions < 1 || radius <= 0) {
81
+ return corners;
82
+ }
83
+ const result = [];
84
+ for (let i = 0; i < corners.length; i++) {
85
+ const p1 = corners[i];
86
+ const p2 = corners[(i + 1) % corners.length];
87
+ if (!isFinite(p1.x) || !isFinite(p1.y) || !isFinite(p1.z) || !isFinite(p2.x) || !isFinite(p2.y) || !isFinite(p2.z)) {
88
+ continue;
89
+ }
90
+ for (let j = 0; j < subdivisions; j++) {
91
+ const t = j / subdivisions;
92
+ const interpolated = slerp(p1, p2, t);
93
+ if (!isFinite(interpolated.x) || !isFinite(interpolated.y) || !isFinite(interpolated.z)) {
94
+ continue;
95
+ }
96
+ const len = Math.sqrt(interpolated.x ** 2 + interpolated.y ** 2 + interpolated.z ** 2);
97
+ if (len < 1e-4) continue;
98
+ result.push({
99
+ x: interpolated.x / len * radius,
100
+ y: interpolated.y / len * radius,
101
+ z: interpolated.z / len * radius
102
+ });
103
+ }
104
+ }
105
+ return result.length > 0 ? result : corners;
106
+ }
107
+ function createSatelliteShape(THREE, category, size) {
108
+ const group = new THREE.Group();
109
+ if (category === "station") {
110
+ const geo = new THREE.BoxGeometry(size * 1.2, size * 0.6, size * 0.6);
111
+ const mat = new THREE.MeshBasicMaterial({ color: 13421772 });
112
+ group.add(new THREE.Mesh(geo, mat));
113
+ const panelGeo = new THREE.BoxGeometry(size * 2.5, size * 0.03, size * 0.6);
114
+ const panelMat = new THREE.MeshBasicMaterial({ color: 1723002 });
115
+ const leftPanel = new THREE.Mesh(panelGeo, panelMat);
116
+ leftPanel.position.x = -size * 1.5;
117
+ group.add(leftPanel);
118
+ const rightPanel = new THREE.Mesh(panelGeo, panelMat);
119
+ rightPanel.position.x = size * 1.5;
120
+ group.add(rightPanel);
121
+ } else if (category === "satellite") {
122
+ const geo = new THREE.BoxGeometry(size * 0.5, size * 0.5, size * 0.6);
123
+ const mat = new THREE.MeshBasicMaterial({ color: 11184810 });
124
+ group.add(new THREE.Mesh(geo, mat));
125
+ const panelGeo = new THREE.BoxGeometry(size * 1.2, size * 0.02, size * 0.4);
126
+ const panelMat = new THREE.MeshBasicMaterial({ color: 1718890 });
127
+ const leftPanel = new THREE.Mesh(panelGeo, panelMat);
128
+ leftPanel.position.x = -size * 0.7;
129
+ group.add(leftPanel);
130
+ const rightPanel = new THREE.Mesh(panelGeo, panelMat);
131
+ rightPanel.position.x = size * 0.7;
132
+ group.add(rightPanel);
133
+ } else if (category === "debris") {
134
+ const geo = new THREE.IcosahedronGeometry(size * 0.3, 0);
135
+ const mat = new THREE.MeshBasicMaterial({ color: 6710886 });
136
+ group.add(new THREE.Mesh(geo, mat));
137
+ } else {
138
+ const geo = new THREE.SphereGeometry(size * 0.4, 8, 6);
139
+ const mat = new THREE.MeshBasicMaterial({ color: 8947848 });
140
+ group.add(new THREE.Mesh(geo, mat));
141
+ }
142
+ return group;
143
+ }
144
+ const ZenSpace3D = forwardRef(
145
+ function ZenSpace3D2(props, ref) {
146
+ const [cesiumModule, setCesiumModule] = useState(null);
147
+ useEffect(() => {
148
+ import("cesium").then((m) => setCesiumModule(m)).catch(() => setCesiumModule(null));
149
+ }, []);
150
+ if (cesiumModule) {
151
+ return /* @__PURE__ */ jsx(ZenSpace3DCesium, { ref, cesium: cesiumModule, ...props });
152
+ }
153
+ return /* @__PURE__ */ jsx(ZenSpace3DThree, { ref, ...props });
154
+ }
155
+ );
156
+ const ZenSpace3DThree = forwardRef(
157
+ function ZenSpace3DThree2(props, ref) {
158
+ const {
159
+ satellites = [],
160
+ debris = [],
161
+ stations = [],
162
+ groundStations = [],
163
+ customObjects = [],
164
+ view = "earth-orbit",
165
+ focusedObjectId,
166
+ initialCamera,
167
+ sunDirection,
168
+ planetPositions,
169
+ visibilityCones = [],
170
+ satelliteCoverages = [],
171
+ overpasses: _overpasses = [],
172
+ orbitPaths = [],
173
+ orbitDesignArcs: _orbitDesignArcs = [],
174
+ communicationLinks = [],
175
+ showAxisHelper = false,
176
+ layers: layerOverrides,
177
+ showTools = true,
178
+ showInfoPanel = true,
179
+ width = "100%",
180
+ height = 600,
181
+ callbacks,
182
+ maxObjects = 1e4,
183
+ className = "",
184
+ style
185
+ } = props;
186
+ const { tokens: _tokens } = useTheme();
187
+ const [webglCapabilities] = useState(() => checkWebGLCapabilities());
188
+ const containerRef = useRef(null);
189
+ const rendererRef = useRef(null);
190
+ const sceneRef = useRef(null);
191
+ const cameraRef = useRef(null);
192
+ const controlsRef = useRef(null);
193
+ const animationRef = useRef(null);
194
+ const raycasterRef = useRef(null);
195
+ const mouseRef = useRef(null);
196
+ const earthRef = useRef(null);
197
+ const starsMaterialRef = useRef(null);
198
+ const atmosphereMaterialRef = useRef(null);
199
+ const objectsGroupRef = useRef(null);
200
+ const linksGroupRef = useRef(null);
201
+ const conesGroupRef = useRef(null);
202
+ const orbitsGroupRef = useRef(null);
203
+ const planetsGroupRef = useRef(null);
204
+ const timeRef = useRef(0);
205
+ const focusTransitionRef = useRef(null);
206
+ const isUserInteractingRef = useRef(false);
207
+ const objectMeshMapRef = useRef(/* @__PURE__ */ new Map());
208
+ const isAnimatingRef = useRef(false);
209
+ useRef(false);
210
+ const [THREE, setTHREE] = useState(null);
211
+ const threeRef = useRef(null);
212
+ threeRef.current = THREE;
213
+ const [OrbitControlsClass, setOrbitControlsClass] = useState(null);
214
+ const [isLoading, setIsLoading] = useState(true);
215
+ const [error, setError] = useState(null);
216
+ const [layers, setLayers] = useState({ ...DEFAULT_LAYERS, ...layerOverrides });
217
+ const [selection, setSelection] = useState({ selectedId: null, selectedObject: null, hoveredId: null });
218
+ const [hoverTooltip, setHoverTooltip] = useState({ visible: false, x: 0, y: 0, object: null });
219
+ const [currentView, setCurrentView] = useState(view);
220
+ const currentViewRef = useRef(view);
221
+ const effectiveLayers = useMemo(() => ({ ...layers, ...layerOverrides }), [layers, layerOverrides]);
222
+ useEffect(() => {
223
+ setCurrentView(view);
224
+ currentViewRef.current = view;
225
+ }, [view]);
226
+ useEffect(() => {
227
+ if (!webglCapabilities.available) {
228
+ setIsLoading(false);
229
+ return;
230
+ }
231
+ loadThree().then(({ THREE: threeModule, OrbitControls: controls }) => {
232
+ setTHREE(threeModule);
233
+ setOrbitControlsClass(() => controls);
234
+ }).catch((err) => {
235
+ setError("Failed to load 3D library");
236
+ setIsLoading(false);
237
+ });
238
+ }, [webglCapabilities.available]);
239
+ const initScene = useCallback(() => {
240
+ var _a, _b;
241
+ if (!THREE || !containerRef.current || !OrbitControlsClass) return;
242
+ if (rendererRef.current) {
243
+ isAnimatingRef.current = false;
244
+ if (animationRef.current) {
245
+ cancelAnimationFrame(animationRef.current);
246
+ animationRef.current = null;
247
+ }
248
+ (_a = controlsRef.current) == null ? void 0 : _a.dispose();
249
+ rendererRef.current.dispose();
250
+ if ((_b = containerRef.current) == null ? void 0 : _b.contains(rendererRef.current.domElement)) {
251
+ containerRef.current.removeChild(rendererRef.current.domElement);
252
+ }
253
+ }
254
+ const container = containerRef.current;
255
+ const rect = container.getBoundingClientRect();
256
+ const w = rect.width;
257
+ const h = typeof height === "number" ? height : rect.height;
258
+ const scene = new THREE.Scene();
259
+ sceneRef.current = scene;
260
+ const viewMode = currentViewRef.current;
261
+ const farClip = viewMode === "solar-system" ? 1e6 : 1e5;
262
+ const camera = new THREE.PerspectiveCamera(45, w / h, 1, farClip);
263
+ const initialDistance = viewMode === "solar-system" ? 5e3 : 1e4;
264
+ camera.position.set(0, initialDistance * 0.3, initialDistance);
265
+ cameraRef.current = camera;
266
+ if (initialCamera == null ? void 0 : initialCamera.position) {
267
+ camera.position.set(initialCamera.position.x, initialCamera.position.y, initialCamera.position.z);
268
+ }
269
+ const renderer = new THREE.WebGLRenderer({
270
+ antialias: true,
271
+ alpha: true,
272
+ powerPreference: "high-performance",
273
+ preserveDrawingBuffer: true
274
+ // Prevents flickering from buffer swaps
275
+ });
276
+ renderer.setSize(w, h);
277
+ renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
278
+ container.appendChild(renderer.domElement);
279
+ rendererRef.current = renderer;
280
+ const controls = new OrbitControlsClass(camera, renderer.domElement);
281
+ controls.enableDamping = true;
282
+ controls.dampingFactor = 0.05;
283
+ controls.rotateSpeed = 0.5;
284
+ controls.minDistance = viewMode === "solar-system" ? 500 : 3050;
285
+ controls.maxDistance = viewMode === "solar-system" ? 1e5 : 25e3;
286
+ controls.enablePan = viewMode === "solar-system";
287
+ let interactionTimer;
288
+ const handleControlStart = () => {
289
+ isUserInteractingRef.current = true;
290
+ };
291
+ const handleControlEnd = () => {
292
+ clearTimeout(interactionTimer);
293
+ interactionTimer = setTimeout(() => {
294
+ isUserInteractingRef.current = false;
295
+ }, 3e3);
296
+ };
297
+ controls.addEventListener("start", handleControlStart);
298
+ controls.addEventListener("end", handleControlEnd);
299
+ controlsRef.current = controls;
300
+ raycasterRef.current = new THREE.Raycaster();
301
+ mouseRef.current = new THREE.Vector2();
302
+ objectsGroupRef.current = new THREE.Group();
303
+ objectsGroupRef.current.name = "objects";
304
+ scene.add(objectsGroupRef.current);
305
+ linksGroupRef.current = new THREE.Group();
306
+ linksGroupRef.current.name = "links";
307
+ scene.add(linksGroupRef.current);
308
+ conesGroupRef.current = new THREE.Group();
309
+ conesGroupRef.current.name = "cones";
310
+ scene.add(conesGroupRef.current);
311
+ orbitsGroupRef.current = new THREE.Group();
312
+ orbitsGroupRef.current.name = "orbits";
313
+ scene.add(orbitsGroupRef.current);
314
+ planetsGroupRef.current = new THREE.Group();
315
+ planetsGroupRef.current.name = "planets";
316
+ scene.add(planetsGroupRef.current);
317
+ if (viewMode === "solar-system") {
318
+ createSolarSystem(THREE, scene);
319
+ } else {
320
+ createEarth(THREE, scene);
321
+ createAtmosphere(THREE, scene);
322
+ }
323
+ createStars(THREE, scene);
324
+ createLighting(THREE, scene);
325
+ if (showAxisHelper && viewMode !== "solar-system") {
326
+ createAxisHelper(THREE, scene);
327
+ }
328
+ setIsLoading(false);
329
+ return () => {
330
+ clearTimeout(interactionTimer);
331
+ if (animationRef.current) cancelAnimationFrame(animationRef.current);
332
+ controls.removeEventListener("start", handleControlStart);
333
+ controls.removeEventListener("end", handleControlEnd);
334
+ controls.dispose();
335
+ renderer.dispose();
336
+ if (container.contains(renderer.domElement)) container.removeChild(renderer.domElement);
337
+ };
338
+ }, [THREE, OrbitControlsClass, height, initialCamera, showAxisHelper]);
339
+ const createSolarSystem = useCallback((THREE2, _scene) => {
340
+ const planetsGroup = planetsGroupRef.current;
341
+ if (!planetsGroup) return;
342
+ const DISTANCE_SCALE = 5e-6;
343
+ const SIZE_SCALE = 0.01;
344
+ const sunRadius = 696340 * SIZE_SCALE;
345
+ const sunGeo = new THREE2.SphereGeometry(sunRadius, 32, 24);
346
+ const sunMat = new THREE2.MeshBasicMaterial({ color: 16768324 });
347
+ const sunMesh = new THREE2.Mesh(sunGeo, sunMat);
348
+ sunMesh.name = "sun";
349
+ sunMesh.userData = { objectId: "sun", planetId: "sun", isPlanet: true, isSelectable: true };
350
+ planetsGroup.add(sunMesh);
351
+ const glowGeo = new THREE2.SphereGeometry(sunRadius * 1.3, 24, 16);
352
+ const glowMat = new THREE2.MeshBasicMaterial({ color: 16755200, transparent: true, opacity: 0.2 });
353
+ planetsGroup.add(new THREE2.Mesh(glowGeo, glowMat));
354
+ const configs = [
355
+ { id: "mercury", color: 9205843, radius: 2439.7, distance: 579e5 },
356
+ { id: "venus", color: 16762441, radius: 6051.8, distance: 1082e5 },
357
+ { id: "earth", color: 7050198, radius: 6371, distance: 1496e5 },
358
+ { id: "mars", color: 12665870, radius: 3389.5, distance: 2279e5 },
359
+ { id: "jupiter", color: 14207645, radius: 69911, distance: 7785e5 },
360
+ { id: "saturn", color: 16045470, radius: 58232, distance: 1434e6 },
361
+ { id: "uranus", color: 13756391, radius: 25362, distance: 2871e6 },
362
+ { id: "neptune", color: 5987807, radius: 24622, distance: 4495e6 }
363
+ ];
364
+ configs.forEach((c) => {
365
+ const visualScale = c.distance < 3e8 ? 15 : 5;
366
+ const planetRadius = c.radius * SIZE_SCALE * visualScale;
367
+ const geo = new THREE2.SphereGeometry(planetRadius, 24, 16);
368
+ const mat = new THREE2.MeshPhongMaterial({ color: c.color, shininess: 30 });
369
+ const mesh = new THREE2.Mesh(geo, mat);
370
+ const orbitDist = c.distance * DISTANCE_SCALE;
371
+ mesh.position.x = orbitDist;
372
+ mesh.name = c.id;
373
+ mesh.userData = { objectId: c.id, planetId: c.id, isPlanet: true, isSelectable: true, data: { id: c.id, name: c.id.charAt(0).toUpperCase() + c.id.slice(1), category: "planet" } };
374
+ planetsGroup.add(mesh);
375
+ objectMeshMapRef.current.set(c.id, mesh);
376
+ if (c.id === "saturn") {
377
+ const ringGeo = new THREE2.RingGeometry(planetRadius * 1.4, planetRadius * 2.2, 32);
378
+ const ringMat = new THREE2.MeshBasicMaterial({ color: 13219990, transparent: true, opacity: 0.6, side: THREE2.DoubleSide });
379
+ const ring = new THREE2.Mesh(ringGeo, ringMat);
380
+ ring.rotation.x = Math.PI / 2.5;
381
+ ring.position.copy(mesh.position);
382
+ planetsGroup.add(ring);
383
+ }
384
+ const points = [];
385
+ for (let i = 0; i <= 64; i++) {
386
+ const angle = i / 64 * Math.PI * 2;
387
+ points.push(new THREE2.Vector3(orbitDist * Math.cos(angle), 0, orbitDist * Math.sin(angle)));
388
+ }
389
+ const orbitGeo = new THREE2.BufferGeometry().setFromPoints(points);
390
+ const orbitMat = new THREE2.LineBasicMaterial({ color: 4473958, transparent: true, opacity: 0.3 });
391
+ planetsGroup.add(new THREE2.Line(orbitGeo, orbitMat));
392
+ });
393
+ const sunLight = new THREE2.PointLight(16777198, 2, 1e5);
394
+ sunLight.position.set(0, 0, 0);
395
+ planetsGroup.add(sunLight);
396
+ }, []);
397
+ const createEarth = useCallback((THREE2, scene) => {
398
+ const earthGeometry = new THREE2.SphereGeometry(SCENE_EARTH_RADIUS, EARTH_SEGMENTS, EARTH_SEGMENTS_V);
399
+ const earthMaterial = new THREE2.MeshPhongMaterial({ color: 2245802, shininess: 10 });
400
+ const textureLoader = new THREE2.TextureLoader();
401
+ textureLoader.load("/world.topo.jpg", (texture) => {
402
+ texture.wrapS = THREE2.RepeatWrapping;
403
+ texture.wrapT = THREE2.ClampToEdgeWrapping;
404
+ texture.minFilter = THREE2.LinearFilter;
405
+ texture.magFilter = THREE2.LinearFilter;
406
+ earthMaterial.map = texture;
407
+ earthMaterial.needsUpdate = true;
408
+ }, void 0, () => {
409
+ });
410
+ const earth = new THREE2.Mesh(earthGeometry, earthMaterial);
411
+ earth.rotation.y = -Math.PI / 2 + Math.PI;
412
+ earth.name = "earth";
413
+ scene.add(earth);
414
+ earthRef.current = earth;
415
+ }, []);
416
+ const createAtmosphere = useCallback((THREE2, scene) => {
417
+ if (!effectiveLayers.atmosphere) return;
418
+ const defaultSunDir = new THREE2.Vector3(1, 0.3, 0.5).normalize();
419
+ const atmosphereGeometry = new THREE2.SphereGeometry(SCENE_EARTH_RADIUS * 1.15, 32, 16);
420
+ const atmosphereMaterial = new THREE2.ShaderMaterial({
421
+ uniforms: {
422
+ uSunDirection: { value: defaultSunDir }
423
+ },
424
+ vertexShader: atmosphereVertexShader,
425
+ fragmentShader: atmosphereFragmentShader,
426
+ blending: THREE2.AdditiveBlending,
427
+ side: THREE2.BackSide,
428
+ transparent: true,
429
+ depthWrite: false
430
+ });
431
+ const atmosphere = new THREE2.Mesh(atmosphereGeometry, atmosphereMaterial);
432
+ atmosphere.rotation.y = -Math.PI / 2;
433
+ atmosphere.name = "atmosphere";
434
+ scene.add(atmosphere);
435
+ atmosphereMaterialRef.current = atmosphereMaterial;
436
+ }, [effectiveLayers.atmosphere]);
437
+ const createStars = useCallback((THREE2, scene) => {
438
+ const starsGeometry = new THREE2.BufferGeometry();
439
+ const starsDist = currentView === "solar-system" ? 5e10 : 5e4;
440
+ const positions = new Float32Array(STARS_COUNT * 3);
441
+ const opacities = new Float32Array(STARS_COUNT);
442
+ for (let i = 0; i < STARS_COUNT; i++) {
443
+ const radius = starsDist + Math.random() * starsDist;
444
+ const theta = Math.random() * Math.PI * 2;
445
+ const phi = Math.acos(2 * Math.random() - 1);
446
+ positions[i * 3] = radius * Math.sin(phi) * Math.cos(theta);
447
+ positions[i * 3 + 1] = radius * Math.sin(phi) * Math.sin(theta);
448
+ positions[i * 3 + 2] = radius * Math.cos(phi);
449
+ opacities[i] = Math.random();
450
+ }
451
+ starsGeometry.setAttribute("position", new THREE2.BufferAttribute(positions, 3));
452
+ starsGeometry.setAttribute("opacity", new THREE2.BufferAttribute(opacities, 1));
453
+ const starsMaterial = new THREE2.ShaderMaterial({
454
+ uniforms: { time: { value: 0 } },
455
+ vertexShader: starsVertexShader,
456
+ fragmentShader: starsFragmentShader,
457
+ transparent: true,
458
+ depthWrite: false,
459
+ blending: THREE2.AdditiveBlending
460
+ });
461
+ scene.add(new THREE2.Points(starsGeometry, starsMaterial));
462
+ starsMaterialRef.current = starsMaterial;
463
+ }, [currentView]);
464
+ const createLighting = useCallback((THREE2, scene) => {
465
+ scene.add(new THREE2.AmbientLight(4210752, 0.6));
466
+ const sunLight = new THREE2.DirectionalLight(16777215, 1.2);
467
+ sunLight.position.set(1e4, 5e3, 1e4);
468
+ scene.add(sunLight);
469
+ }, []);
470
+ const createAxisHelper = useCallback((THREE2, scene) => {
471
+ const len = SCENE_EARTH_RADIUS * 1.5;
472
+ const group = new THREE2.Group();
473
+ [
474
+ { color: 16724787, dir: [1, 0, 0] },
475
+ { color: 3407667, dir: [0, 1, 0] },
476
+ { color: 3355647, dir: [0, 0, 1] }
477
+ ].forEach(({ color, dir }) => {
478
+ const points = [new THREE2.Vector3(0, 0, 0), new THREE2.Vector3(dir[0] * len, dir[1] * len, dir[2] * len)];
479
+ const geo = new THREE2.BufferGeometry().setFromPoints(points);
480
+ const mat = new THREE2.LineBasicMaterial({ color });
481
+ group.add(new THREE2.Line(geo, mat));
482
+ });
483
+ scene.add(group);
484
+ }, []);
485
+ const updateObjects = useCallback(() => {
486
+ if (!THREE || !objectsGroupRef.current || currentView === "solar-system") return;
487
+ const group = objectsGroupRef.current;
488
+ const meshMap = objectMeshMapRef.current;
489
+ while (group.children.length > 0) {
490
+ const child = group.children[0];
491
+ group.remove(child);
492
+ }
493
+ meshMap.clear();
494
+ const allObjects = [...satellites, ...debris, ...stations, ...customObjects];
495
+ allObjects.slice(0, maxObjects).forEach((obj) => {
496
+ const categoryLayer = obj.category === "satellite" ? "satellites" : obj.category === "debris" ? "debris" : obj.category === "station" ? "stations" : "satellites";
497
+ if (!effectiveLayers[categoryLayer]) return;
498
+ let size = 50;
499
+ if (obj.category === "station") size = 100;
500
+ else if (obj.category === "debris") size = 25;
501
+ const satelliteGroup = createSatelliteShape(THREE, obj.category, size);
502
+ satelliteGroup.position.set(obj.position.x, obj.position.y, obj.position.z);
503
+ satelliteGroup.lookAt(0, 0, 0);
504
+ satelliteGroup.userData = { objectId: obj.id, category: obj.category, data: obj, isSelectable: true };
505
+ satelliteGroup.name = obj.id;
506
+ group.add(satelliteGroup);
507
+ meshMap.set(obj.id, satelliteGroup);
508
+ });
509
+ }, [THREE, satellites, debris, stations, customObjects, maxObjects, effectiveLayers, currentView]);
510
+ const updateGroundStations = useCallback(() => {
511
+ if (!THREE || !objectsGroupRef.current || currentView === "solar-system") return;
512
+ if (!effectiveLayers.groundStations) return;
513
+ groundStations.forEach((gs) => {
514
+ const pos = latLonAltToCartesian(gs.latitude, gs.longitude, 0);
515
+ const geo = new THREE.SphereGeometry(35, 8, 6);
516
+ const mat = new THREE.MeshBasicMaterial({ color: 65535 });
517
+ const marker = new THREE.Mesh(geo, mat);
518
+ marker.position.set(pos.x, pos.y, pos.z);
519
+ marker.userData = { objectId: gs.id, category: "groundStation", data: gs, isSelectable: true };
520
+ objectsGroupRef.current.add(marker);
521
+ objectMeshMapRef.current.set(gs.id, marker);
522
+ const ringGeo = new THREE.RingGeometry(45, 65, 16);
523
+ const ringMat = new THREE.MeshBasicMaterial({ color: 65535, transparent: true, opacity: 0.4, side: THREE.DoubleSide });
524
+ const ring = new THREE.Mesh(ringGeo, ringMat);
525
+ ring.position.set(pos.x, pos.y, pos.z);
526
+ ring.lookAt(0, 0, 0);
527
+ objectsGroupRef.current.add(ring);
528
+ });
529
+ }, [THREE, groundStations, effectiveLayers.groundStations, currentView]);
530
+ const updateCoverage = useCallback(() => {
531
+ const T = threeRef.current;
532
+ if (!T || !conesGroupRef.current || currentView === "solar-system") return;
533
+ while (conesGroupRef.current.children.length > 0) {
534
+ conesGroupRef.current.remove(conesGroupRef.current.children[0]);
535
+ }
536
+ if (!effectiveLayers.coverage) return;
537
+ satelliteCoverages.forEach((coverage) => {
538
+ let satPos;
539
+ if (coverage.satellitePosition) {
540
+ satPos = new T.Vector3(coverage.satellitePosition.x, coverage.satellitePosition.y, coverage.satellitePosition.z);
541
+ } else {
542
+ const satMesh = objectMeshMapRef.current.get(coverage.satelliteId);
543
+ if (!satMesh) return;
544
+ satPos = satMesh.position.clone();
545
+ }
546
+ const satDistance = satPos.length();
547
+ const coneColor = new T.Color(coverage.color || "#22d3ee");
548
+ const highlightColor = new T.Color(coverage.color || "#22d3ee").multiplyScalar(1.3);
549
+ if (coverage.footprintPolygon && coverage.footprintPolygon.length >= 3 && coverage.showFootprint !== false) {
550
+ const cornerPoints = coverage.footprintPolygon.map((pt) => latLonAltToCartesian(pt.latitude, pt.longitude, 0));
551
+ const borderPts = subdivideSphericalPolygon(cornerPoints, SPHERICAL_SUBDIVISIONS, SCENE_EARTH_RADIUS * 1.003).map((pt) => new T.Vector3(pt.x, pt.y, pt.z));
552
+ borderPts.push(borderPts[0].clone());
553
+ const borderGeo = new T.BufferGeometry().setFromPoints(borderPts);
554
+ const borderMat = new T.LineBasicMaterial({ color: highlightColor, transparent: true, opacity: 0.95, linewidth: 2 });
555
+ conesGroupRef.current.add(new T.Line(borderGeo, borderMat));
556
+ const glowPts = subdivideSphericalPolygon(cornerPoints, SPHERICAL_SUBDIVISIONS, SCENE_EARTH_RADIUS * 1.005).map((pt) => new T.Vector3(pt.x, pt.y, pt.z));
557
+ glowPts.push(glowPts[0].clone());
558
+ const glowGeo = new T.BufferGeometry().setFromPoints(glowPts);
559
+ const glowMat = new T.LineBasicMaterial({ color: coneColor, transparent: true, opacity: 0.4 });
560
+ conesGroupRef.current.add(new T.Line(glowGeo, glowMat));
561
+ if (coverage.showCone !== false) {
562
+ for (const corner of cornerPoints) {
563
+ const edgePt = new T.Vector3(corner.x, corner.y, corner.z).normalize().multiplyScalar(SCENE_EARTH_RADIUS);
564
+ const lineGeo = new T.BufferGeometry().setFromPoints([satPos, edgePt]);
565
+ const lineMat = new T.LineBasicMaterial({ color: coneColor, transparent: true, opacity: 0.4 });
566
+ conesGroupRef.current.add(new T.Line(lineGeo, lineMat));
567
+ }
568
+ }
569
+ } else if (coverage.halfAngle && coverage.showCone !== false) {
570
+ const halfAngleRad = coverage.halfAngle * (Math.PI / 180);
571
+ const coneHeight = satDistance - SCENE_EARTH_RADIUS * 0.98;
572
+ const coneRadius = coneHeight * Math.tan(halfAngleRad);
573
+ const coneGeo = new T.ConeGeometry(coneRadius, coneHeight, 48, 1, true);
574
+ const coneMat = new T.MeshBasicMaterial({
575
+ color: coneColor,
576
+ transparent: true,
577
+ opacity: 0.12,
578
+ side: T.DoubleSide,
579
+ depthWrite: false
580
+ });
581
+ const cone = new T.Mesh(coneGeo, coneMat);
582
+ cone.position.copy(satPos);
583
+ cone.lookAt(0, 0, 0);
584
+ cone.rotateX(Math.PI / 2);
585
+ cone.translateY(-coneHeight / 2);
586
+ conesGroupRef.current.add(cone);
587
+ if (coverage.showFootprint !== false) {
588
+ const nadirDir = satPos.clone().normalize();
589
+ const footprintPos = nadirDir.clone().multiplyScalar(SCENE_EARTH_RADIUS * 1.003);
590
+ const footprintRadius = coneRadius * (SCENE_EARTH_RADIUS / satDistance);
591
+ const footGeo = new T.CircleGeometry(footprintRadius, 64);
592
+ const footMat = new T.MeshBasicMaterial({
593
+ color: coneColor,
594
+ transparent: true,
595
+ opacity: 0.3,
596
+ side: T.DoubleSide,
597
+ depthWrite: false
598
+ });
599
+ const footprint = new T.Mesh(footGeo, footMat);
600
+ footprint.position.copy(footprintPos);
601
+ footprint.lookAt(satPos);
602
+ conesGroupRef.current.add(footprint);
603
+ const ringGeo = new T.RingGeometry(footprintRadius * 0.97, footprintRadius, 64);
604
+ const ringMat = new T.MeshBasicMaterial({
605
+ color: highlightColor,
606
+ transparent: true,
607
+ opacity: 0.7,
608
+ side: T.DoubleSide
609
+ });
610
+ const ring = new T.Mesh(ringGeo, ringMat);
611
+ ring.position.copy(footprintPos);
612
+ ring.lookAt(satPos);
613
+ conesGroupRef.current.add(ring);
614
+ }
615
+ }
616
+ if (coverage.showNadirLine !== false) {
617
+ let nadirGroundPoint;
618
+ if (coverage.nadirPoint) {
619
+ const np = latLonAltToCartesian(coverage.nadirPoint.latitude, coverage.nadirPoint.longitude, 0);
620
+ nadirGroundPoint = new T.Vector3(np.x, np.y, np.z);
621
+ } else {
622
+ nadirGroundPoint = satPos.clone().normalize().multiplyScalar(SCENE_EARTH_RADIUS);
623
+ }
624
+ const lineGeo = new T.BufferGeometry().setFromPoints([satPos, nadirGroundPoint]);
625
+ const lineMat = new T.LineBasicMaterial({ color: highlightColor, transparent: true, opacity: 0.7 });
626
+ conesGroupRef.current.add(new T.Line(lineGeo, lineMat));
627
+ }
628
+ });
629
+ visibilityCones.forEach((cone) => {
630
+ const pos = latLonAltToCartesian(cone.center.latitude, cone.center.longitude, 0);
631
+ const coneHeight = 1e3 * KM_TO_SCENE;
632
+ const minElev = cone.minElevation || 10;
633
+ const coneRadius = coneHeight * Math.tan((90 - minElev) * (Math.PI / 180) * 0.4);
634
+ const coneColor = new T.Color(cone.color || "#22d3ee");
635
+ const coneGeo = new T.ConeGeometry(coneRadius, coneHeight, 24, 1, true);
636
+ const coneMat = new T.MeshBasicMaterial({
637
+ color: coneColor,
638
+ transparent: true,
639
+ opacity: cone.opacity || 0.12,
640
+ side: T.DoubleSide,
641
+ depthWrite: false
642
+ });
643
+ const coneMesh = new T.Mesh(coneGeo, coneMat);
644
+ coneMesh.position.set(pos.x, pos.y, pos.z);
645
+ coneMesh.lookAt(0, 0, 0);
646
+ coneMesh.rotateX(-Math.PI / 2);
647
+ coneMesh.translateY(coneHeight / 2);
648
+ conesGroupRef.current.add(coneMesh);
649
+ const footGeo = new T.RingGeometry(coneRadius * 0.7, coneRadius * 0.9, 32);
650
+ const footMat = new T.MeshBasicMaterial({
651
+ color: coneColor,
652
+ transparent: true,
653
+ opacity: 0.3,
654
+ side: T.DoubleSide
655
+ });
656
+ const foot = new T.Mesh(footGeo, footMat);
657
+ foot.position.set(pos.x * 1.002, pos.y * 1.002, pos.z * 1.002);
658
+ foot.lookAt(0, 0, 0);
659
+ conesGroupRef.current.add(foot);
660
+ });
661
+ }, [visibilityCones, satelliteCoverages, effectiveLayers.coverage, currentView]);
662
+ const updateLinks = useCallback(() => {
663
+ if (!THREE || !linksGroupRef.current || currentView === "solar-system") return;
664
+ while (linksGroupRef.current.children.length > 0) {
665
+ linksGroupRef.current.remove(linksGroupRef.current.children[0]);
666
+ }
667
+ communicationLinks.forEach((link) => {
668
+ const fromMesh = objectMeshMapRef.current.get(link.fromId);
669
+ const toMesh = objectMeshMapRef.current.get(link.toId);
670
+ if (!fromMesh || !toMesh) return;
671
+ const color = link.color || (link.status === "active" ? "#00ff88" : "#666666");
672
+ const geo = new THREE.BufferGeometry().setFromPoints([fromMesh.position.clone(), toMesh.position.clone()]);
673
+ const mat = new THREE.LineDashedMaterial({
674
+ color: new THREE.Color(color),
675
+ transparent: true,
676
+ opacity: link.status === "active" ? 0.8 : 0.4,
677
+ dashSize: 30,
678
+ gapSize: 20
679
+ });
680
+ const line = new THREE.Line(geo, mat);
681
+ line.computeLineDistances();
682
+ line.userData = { isAnimatedLink: true };
683
+ linksGroupRef.current.add(line);
684
+ });
685
+ }, [THREE, communicationLinks, currentView]);
686
+ const updateOrbits = useCallback(() => {
687
+ if (!THREE || !orbitsGroupRef.current || currentView === "solar-system") return;
688
+ while (orbitsGroupRef.current.children.length > 0) {
689
+ orbitsGroupRef.current.remove(orbitsGroupRef.current.children[0]);
690
+ }
691
+ if (!effectiveLayers.orbits) return;
692
+ orbitPaths.forEach((path) => {
693
+ if (path.points.length < 2) return;
694
+ const points = path.points.map((p) => new THREE.Vector3(p.x, p.y, p.z));
695
+ const geo = new THREE.BufferGeometry().setFromPoints(points);
696
+ const mat = new THREE.LineBasicMaterial({ color: new THREE.Color(path.color || "#888888"), transparent: true, opacity: 0.5 });
697
+ orbitsGroupRef.current.add(new THREE.Line(geo, mat));
698
+ });
699
+ }, [THREE, orbitPaths, effectiveLayers.orbits, currentView]);
700
+ const handleMouseMove = useCallback((event) => {
701
+ var _a, _b;
702
+ if (!containerRef.current || !raycasterRef.current || !cameraRef.current) return;
703
+ const rect = containerRef.current.getBoundingClientRect();
704
+ mouseRef.current.set(
705
+ (event.clientX - rect.left) / rect.width * 2 - 1,
706
+ -((event.clientY - rect.top) / rect.height) * 2 + 1
707
+ );
708
+ raycasterRef.current.setFromCamera(mouseRef.current, cameraRef.current);
709
+ const selectables = [];
710
+ (_a = objectsGroupRef.current) == null ? void 0 : _a.traverse((obj) => {
711
+ var _a2;
712
+ if ((_a2 = obj.userData) == null ? void 0 : _a2.isSelectable) selectables.push(obj);
713
+ });
714
+ const intersects = raycasterRef.current.intersectObjects(selectables, true);
715
+ if (intersects.length > 0) {
716
+ let hit = intersects[0].object;
717
+ while (hit && !((_b = hit.userData) == null ? void 0 : _b.objectId)) hit = hit.parent;
718
+ if (hit == null ? void 0 : hit.userData) {
719
+ setHoverTooltip({ visible: true, x: event.clientX - rect.left, y: event.clientY - rect.top, object: hit.userData.data });
720
+ if (containerRef.current) containerRef.current.style.cursor = "pointer";
721
+ return;
722
+ }
723
+ }
724
+ setHoverTooltip({ visible: false, x: 0, y: 0, object: null });
725
+ if (containerRef.current) containerRef.current.style.cursor = "grab";
726
+ }, []);
727
+ const handleClick = useCallback((event) => {
728
+ var _a, _b, _c, _d, _e, _f;
729
+ if (!containerRef.current || !raycasterRef.current || !cameraRef.current) return;
730
+ const rect = containerRef.current.getBoundingClientRect();
731
+ mouseRef.current.set(
732
+ (event.clientX - rect.left) / rect.width * 2 - 1,
733
+ -((event.clientY - rect.top) / rect.height) * 2 + 1
734
+ );
735
+ raycasterRef.current.setFromCamera(mouseRef.current, cameraRef.current);
736
+ const selectables = [];
737
+ (_a = objectsGroupRef.current) == null ? void 0 : _a.traverse((obj) => {
738
+ var _a2;
739
+ if ((_a2 = obj.userData) == null ? void 0 : _a2.isSelectable) selectables.push(obj);
740
+ });
741
+ (_b = planetsGroupRef.current) == null ? void 0 : _b.traverse((obj) => {
742
+ var _a2;
743
+ if ((_a2 = obj.userData) == null ? void 0 : _a2.isSelectable) selectables.push(obj);
744
+ });
745
+ const intersects = raycasterRef.current.intersectObjects(selectables, true);
746
+ if (intersects.length > 0) {
747
+ let hit = intersects[0].object;
748
+ while (hit && !((_c = hit.userData) == null ? void 0 : _c.objectId)) hit = hit.parent;
749
+ if ((_d = hit == null ? void 0 : hit.userData) == null ? void 0 : _d.objectId) {
750
+ const objectId = hit.userData.objectId;
751
+ setSelection({ selectedId: objectId, selectedObject: hit.userData.data, hoveredId: null });
752
+ (_e = callbacks == null ? void 0 : callbacks.onSelect) == null ? void 0 : _e.call(callbacks, hit.userData.data);
753
+ flyTo(objectId, { animate: true });
754
+ return;
755
+ }
756
+ }
757
+ setSelection({ selectedId: null, selectedObject: null, hoveredId: null });
758
+ (_f = callbacks == null ? void 0 : callbacks.onSelect) == null ? void 0 : _f.call(callbacks, null);
759
+ }, [callbacks]);
760
+ const flyTo = useCallback((objectId, options) => {
761
+ var _a, _b, _c, _d, _e, _f;
762
+ if (!THREE || !cameraRef.current || !controlsRef.current) return;
763
+ const mesh = objectMeshMapRef.current.get(objectId);
764
+ if (!mesh) {
765
+ return;
766
+ }
767
+ const targetPos = mesh.position.clone();
768
+ const camera = cameraRef.current;
769
+ const controls = controlsRef.current;
770
+ const category = (_a = mesh.userData) == null ? void 0 : _a.category;
771
+ const isGroundStation = category === "groundStation";
772
+ let viewDistance;
773
+ if (options == null ? void 0 : options.distance) {
774
+ viewDistance = options.distance;
775
+ } else {
776
+ const isPlanet = (_b = mesh.userData) == null ? void 0 : _b.isPlanet;
777
+ const targetDist = targetPos.length();
778
+ if (isPlanet) {
779
+ const planetRadius = ((_d = (_c = mesh.geometry) == null ? void 0 : _c.parameters) == null ? void 0 : _d.radius) || 100;
780
+ viewDistance = planetRadius * 5;
781
+ } else if (isGroundStation) {
782
+ viewDistance = 200;
783
+ } else if (category === "station") {
784
+ viewDistance = 80;
785
+ } else if (category === "satellite") {
786
+ viewDistance = 60;
787
+ } else if (targetDist > SCENE_EARTH_RADIUS * 2) {
788
+ viewDistance = 150;
789
+ } else {
790
+ viewDistance = 100 * ((options == null ? void 0 : options.zoom) || 1);
791
+ }
792
+ }
793
+ let cameraTargetPos;
794
+ if (options == null ? void 0 : options.offset) {
795
+ cameraTargetPos = targetPos.clone().add(new THREE.Vector3(options.offset.x, options.offset.y, options.offset.z));
796
+ } else if (isGroundStation) {
797
+ const upDir = targetPos.clone().normalize();
798
+ cameraTargetPos = targetPos.clone().add(upDir.multiplyScalar(viewDistance));
799
+ } else {
800
+ const dir = targetPos.clone().normalize();
801
+ const side = new THREE.Vector3(-dir.z, 0.2, dir.x).normalize();
802
+ cameraTargetPos = targetPos.clone().add(side.multiplyScalar(viewDistance));
803
+ }
804
+ if ((options == null ? void 0 : options.animate) !== false) {
805
+ focusTransitionRef.current = {
806
+ startTime: Date.now(),
807
+ startPos: { x: camera.position.x, y: camera.position.y, z: camera.position.z },
808
+ targetPos: { x: cameraTargetPos.x, y: cameraTargetPos.y, z: cameraTargetPos.z },
809
+ startTarget: { x: controls.target.x, y: controls.target.y, z: controls.target.z },
810
+ targetTarget: { x: targetPos.x, y: targetPos.y, z: targetPos.z },
811
+ duration: CAMERA_TRANSITION_DURATION,
812
+ isActive: true
813
+ };
814
+ } else {
815
+ camera.position.copy(cameraTargetPos);
816
+ controls.target.copy(targetPos);
817
+ controls.update();
818
+ }
819
+ (_f = callbacks == null ? void 0 : callbacks.onFocus) == null ? void 0 : _f.call(callbacks, ((_e = mesh.userData) == null ? void 0 : _e.data) || { id: objectId });
820
+ }, [THREE, callbacks]);
821
+ const focusOn = flyTo;
822
+ const animate = useCallback(() => {
823
+ var _a;
824
+ if (!isAnimatingRef.current) return;
825
+ if (!rendererRef.current || !sceneRef.current || !cameraRef.current) return;
826
+ const camera = cameraRef.current;
827
+ const controls = controlsRef.current;
828
+ if ((_a = focusTransitionRef.current) == null ? void 0 : _a.isActive) {
829
+ const t = focusTransitionRef.current;
830
+ const progress = Math.min((Date.now() - t.startTime) / t.duration, 1);
831
+ const e = easeOutCubic(progress);
832
+ camera.position.x = t.startPos.x + (t.targetPos.x - t.startPos.x) * e;
833
+ camera.position.y = t.startPos.y + (t.targetPos.y - t.startPos.y) * e;
834
+ camera.position.z = t.startPos.z + (t.targetPos.z - t.startPos.z) * e;
835
+ controls.target.x = t.startTarget.x + (t.targetTarget.x - t.startTarget.x) * e;
836
+ controls.target.y = t.startTarget.y + (t.targetTarget.y - t.startTarget.y) * e;
837
+ controls.target.z = t.startTarget.z + (t.targetTarget.z - t.startTarget.z) * e;
838
+ if (progress >= 1) t.isActive = false;
839
+ }
840
+ if (controls) controls.update();
841
+ timeRef.current += 0.016;
842
+ if (starsMaterialRef.current) starsMaterialRef.current.uniforms.time.value = timeRef.current;
843
+ if (linksGroupRef.current) {
844
+ linksGroupRef.current.children.forEach((child) => {
845
+ var _a2;
846
+ if (((_a2 = child.userData) == null ? void 0 : _a2.isAnimatedLink) && child.material) {
847
+ child.material.dashOffset -= 1.5;
848
+ }
849
+ });
850
+ }
851
+ rendererRef.current.render(sceneRef.current, camera);
852
+ animationRef.current = requestAnimationFrame(animate);
853
+ }, []);
854
+ useImperativeHandle(ref, () => ({
855
+ focusOn,
856
+ flyTo,
857
+ setView: (mode) => setCurrentView(mode),
858
+ setTime: () => {
859
+ },
860
+ setTimeScale: () => {
861
+ },
862
+ togglePlay: () => {
863
+ },
864
+ setLayerVisibility: (layer, visible) => setLayers((prev) => ({ ...prev, [layer]: visible })),
865
+ getCameraState: () => {
866
+ var _a, _b, _c;
867
+ if (!cameraRef.current) return { position: { x: 0, y: 0, z: 1e4 }, target: { x: 0, y: 0, z: 0 }, up: { x: 0, y: 1, z: 0 }, fov: 45, near: 0.1, far: 1e5 };
868
+ const c = cameraRef.current;
869
+ return { position: { x: c.position.x, y: c.position.y, z: c.position.z }, target: { x: ((_a = controlsRef.current) == null ? void 0 : _a.target.x) || 0, y: ((_b = controlsRef.current) == null ? void 0 : _b.target.y) || 0, z: ((_c = controlsRef.current) == null ? void 0 : _c.target.z) || 0 }, up: { x: c.up.x, y: c.up.y, z: c.up.z }, fov: c.fov, near: c.near, far: c.far };
870
+ },
871
+ setCameraState: (state) => {
872
+ if (!cameraRef.current) return;
873
+ if (state.position) cameraRef.current.position.set(state.position.x, state.position.y, state.position.z);
874
+ if (state.target && controlsRef.current) controlsRef.current.target.set(state.target.x, state.target.y, state.target.z);
875
+ },
876
+ exportImage: (format = "png") => {
877
+ var _a;
878
+ return ((_a = rendererRef.current) == null ? void 0 : _a.domElement.toDataURL(`image/${format}`)) || "";
879
+ },
880
+ exportScene: () => new Blob(),
881
+ getVisibleObjects: () => [...satellites, ...debris, ...stations, ...customObjects],
882
+ findObjects: (q) => [...satellites, ...debris, ...stations, ...customObjects].filter((obj) => obj.name.toLowerCase().includes(q.toLowerCase()) || obj.id.toLowerCase().includes(q.toLowerCase())),
883
+ addObject: () => {
884
+ },
885
+ removeObject: () => {
886
+ },
887
+ updateObject: () => {
888
+ },
889
+ measureDistance: (_from, _to) => 0
890
+ }), [satellites, debris, stations, customObjects, focusOn]);
891
+ useEffect(() => {
892
+ if (THREE && OrbitControlsClass) return initScene();
893
+ }, [THREE, OrbitControlsClass, initScene]);
894
+ useEffect(() => {
895
+ if (!isLoading && THREE) {
896
+ if (animationRef.current) {
897
+ cancelAnimationFrame(animationRef.current);
898
+ animationRef.current = null;
899
+ }
900
+ isAnimatingRef.current = true;
901
+ animate();
902
+ return () => {
903
+ isAnimatingRef.current = false;
904
+ if (animationRef.current) {
905
+ cancelAnimationFrame(animationRef.current);
906
+ animationRef.current = null;
907
+ }
908
+ };
909
+ }
910
+ }, [isLoading, THREE, animate]);
911
+ useEffect(() => {
912
+ if (THREE && !isLoading) {
913
+ updateObjects();
914
+ updateGroundStations();
915
+ updateCoverage();
916
+ updateOrbits();
917
+ updateLinks();
918
+ }
919
+ }, [THREE, isLoading, updateObjects, updateGroundStations, updateCoverage, updateOrbits, updateLinks]);
920
+ useEffect(() => {
921
+ const container = containerRef.current;
922
+ if (!container || isLoading) return;
923
+ container.addEventListener("mousemove", handleMouseMove);
924
+ container.addEventListener("click", handleClick);
925
+ return () => {
926
+ container.removeEventListener("mousemove", handleMouseMove);
927
+ container.removeEventListener("click", handleClick);
928
+ };
929
+ }, [isLoading, handleMouseMove, handleClick]);
930
+ useEffect(() => {
931
+ const handleResize = () => {
932
+ if (!containerRef.current || !rendererRef.current || !cameraRef.current) return;
933
+ const rect = containerRef.current.getBoundingClientRect();
934
+ const h = typeof height === "number" ? height : rect.height;
935
+ cameraRef.current.aspect = rect.width / h;
936
+ cameraRef.current.updateProjectionMatrix();
937
+ rendererRef.current.setSize(rect.width, h);
938
+ };
939
+ window.addEventListener("resize", handleResize);
940
+ return () => window.removeEventListener("resize", handleResize);
941
+ }, [height]);
942
+ useEffect(() => {
943
+ if (focusedObjectId) focusOn(focusedObjectId, { animate: true });
944
+ }, [focusedObjectId, focusOn]);
945
+ useEffect(() => {
946
+ if (THREE && atmosphereMaterialRef.current && sunDirection) {
947
+ const dir = new THREE.Vector3(sunDirection.x, sunDirection.y, sunDirection.z).normalize();
948
+ atmosphereMaterialRef.current.uniforms.uSunDirection.value = dir;
949
+ }
950
+ }, [THREE, sunDirection]);
951
+ useEffect(() => {
952
+ if (!THREE || !planetPositions || !planetsGroupRef.current) return;
953
+ planetsGroupRef.current.traverse((child) => {
954
+ var _a;
955
+ if (((_a = child.userData) == null ? void 0 : _a.planetId) && planetPositions[child.userData.planetId]) {
956
+ const pos = planetPositions[child.userData.planetId];
957
+ child.position.set(pos.x, pos.y, pos.z);
958
+ }
959
+ });
960
+ }, [THREE, planetPositions]);
961
+ if (!webglCapabilities.available) {
962
+ return /* @__PURE__ */ jsxs("div", { className: `zenspace3d-fallback ${className}`, style: { width, height: typeof height === "number" ? height : 600, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", backgroundColor: "#030508", borderRadius: 8, fontFamily: "ui-monospace, monospace", ...style }, children: [
963
+ /* @__PURE__ */ jsx("div", { style: { marginBottom: 16 }, children: /* @__PURE__ */ jsx(AstroIcon, { name: "public", size: "large", label: "Globe", style: { color: "#22d3ee", opacity: 0.5 } }) }),
964
+ /* @__PURE__ */ jsx("div", { style: { color: "#b8bcc8", fontSize: 14 }, children: "WebGL is not available" })
965
+ ] });
966
+ }
967
+ if (error) return /* @__PURE__ */ jsx("div", { style: { width, height, display: "flex", alignItems: "center", justifyContent: "center", backgroundColor: "#0a0a0f", color: "#ff4444" }, children: /* @__PURE__ */ jsxs("p", { children: [
968
+ "Error: ",
969
+ error
970
+ ] }) });
971
+ return /* @__PURE__ */ jsxs("div", { className: `zenspace3d ${className}`, style: { width, height: typeof height === "number" ? height : "100%", position: "relative", backgroundColor: "#000", borderRadius: 8, overflow: "hidden", cursor: "grab", ...style }, children: [
972
+ /* @__PURE__ */ jsx("div", { ref: containerRef, style: { width: "100%", height: "100%" } }),
973
+ isLoading && /* @__PURE__ */ jsx("div", { style: { position: "absolute", top: 0, left: 0, right: 0, bottom: 0, display: "flex", alignItems: "center", justifyContent: "center", backgroundColor: "rgba(0,0,0,0.9)" }, children: /* @__PURE__ */ jsx("div", { style: { color: "#22d3ee", fontSize: 14 }, children: "Loading..." }) }),
974
+ showTools && !isLoading && /* @__PURE__ */ jsxs("div", { style: { position: "absolute", top: 12, left: 12, display: "flex", flexDirection: "column", gap: 4 }, children: [
975
+ /* @__PURE__ */ jsx(LayerBtn, { icon: "satellite", label: "Satellites", active: layers.satellites, onClick: () => setLayers((l) => ({ ...l, satellites: !l.satellites })) }),
976
+ /* @__PURE__ */ jsx(LayerBtn, { icon: "home", label: "Stations", active: layers.stations, onClick: () => setLayers((l) => ({ ...l, stations: !l.stations })) }),
977
+ /* @__PURE__ */ jsx(LayerBtn, { icon: "antenna", label: "Ground", active: layers.groundStations, onClick: () => setLayers((l) => ({ ...l, groundStations: !l.groundStations })) }),
978
+ /* @__PURE__ */ jsx(LayerBtn, { icon: "timeline", label: "Orbits", active: layers.orbits, onClick: () => setLayers((l) => ({ ...l, orbits: !l.orbits })) }),
979
+ /* @__PURE__ */ jsx(LayerBtn, { icon: "signal-cellular-4-bar", label: "Coverage", active: layers.coverage, onClick: () => setLayers((l) => ({ ...l, coverage: !l.coverage })) })
980
+ ] }),
981
+ !isLoading && /* @__PURE__ */ jsx(
982
+ ObjectLegend,
983
+ {
984
+ currentView,
985
+ satellites,
986
+ stations,
987
+ groundStations,
988
+ onObjectClick: (id, objectData) => {
989
+ var _a;
990
+ setSelection({ selectedId: id, selectedObject: objectData, hoveredId: null });
991
+ (_a = callbacks == null ? void 0 : callbacks.onSelect) == null ? void 0 : _a.call(callbacks, objectData);
992
+ flyTo(id, { animate: true });
993
+ }
994
+ }
995
+ ),
996
+ hoverTooltip.visible && hoverTooltip.object && /* @__PURE__ */ jsxs("div", { style: {
997
+ position: "absolute",
998
+ left: hoverTooltip.x + 15,
999
+ top: hoverTooltip.y - 10,
1000
+ backgroundColor: "rgba(20, 25, 35, 0.95)",
1001
+ color: "#e4e4e7",
1002
+ padding: "10px 14px",
1003
+ borderRadius: 4,
1004
+ fontSize: 12,
1005
+ fontFamily: "system-ui, -apple-system, sans-serif",
1006
+ boxShadow: "0 4px 20px rgba(0,0,0,0.5)",
1007
+ pointerEvents: "none",
1008
+ zIndex: 100,
1009
+ minWidth: 140
1010
+ }, children: [
1011
+ /* @__PURE__ */ jsx("div", { style: { color: "#ffffff", fontWeight: 500, marginBottom: 4 }, children: hoverTooltip.object.name || hoverTooltip.object.targetName || "Unknown" }),
1012
+ hoverTooltip.object.category && /* @__PURE__ */ jsx("div", { style: { color: "#9ca3af", fontSize: 11 }, children: hoverTooltip.object.category }),
1013
+ hoverTooltip.object.overpassTime && /* @__PURE__ */ jsxs("div", { style: { color: "#9ca3af", fontSize: 11, marginTop: 4 }, children: [
1014
+ "Overpass time: ",
1015
+ hoverTooltip.object.overpassTime
1016
+ ] })
1017
+ ] }),
1018
+ showInfoPanel && selection.selectedObject && /* @__PURE__ */ jsxs("div", { style: { position: "absolute", bottom: 16, right: 12, backgroundColor: "rgba(0,0,0,0.9)", color: "#e4e4e7", padding: "14px 18px", borderRadius: 10, fontSize: 12, fontFamily: "ui-monospace, monospace", border: "1px solid rgba(34,211,238,0.3)", minWidth: 180 }, children: [
1019
+ /* @__PURE__ */ jsx("div", { style: { color: "#22d3ee", fontWeight: 500, marginBottom: 8 }, children: selection.selectedObject.name }),
1020
+ /* @__PURE__ */ jsx("div", { style: { color: "#b8bcc8", fontSize: 10 }, children: selection.selectedObject.category }),
1021
+ /* @__PURE__ */ jsx("button", { onClick: () => setSelection({ selectedId: null, selectedObject: null, hoveredId: null }), style: { marginTop: 10, padding: "5px 10px", backgroundColor: "rgba(255,255,255,0.1)", border: "none", borderRadius: 4, color: "#a1a1aa", cursor: "pointer", fontSize: 10, width: "100%" }, children: "Close" })
1022
+ ] }),
1023
+ !isLoading && /* @__PURE__ */ jsx("div", { style: { position: "absolute", bottom: 12, left: 12, backgroundColor: "rgba(0,0,0,0.5)", color: "#b8bcc8", padding: "5px 10px", borderRadius: 6, fontSize: 10 }, children: "Drag to rotate • Scroll to zoom" })
1024
+ ] });
1025
+ }
1026
+ );
1027
+ function LayerBtn({ icon, label, active, onClick }) {
1028
+ return /* @__PURE__ */ jsxs(
1029
+ "button",
1030
+ {
1031
+ onClick,
1032
+ "aria-label": `Toggle ${label} layer`,
1033
+ "aria-pressed": active,
1034
+ style: {
1035
+ display: "flex",
1036
+ alignItems: "center",
1037
+ gap: 6,
1038
+ padding: "6px 10px",
1039
+ backgroundColor: active ? "rgba(34,211,238,0.15)" : "rgba(0,0,0,0.7)",
1040
+ border: active ? "1px solid rgba(34,211,238,0.4)" : "1px solid rgba(255,255,255,0.1)",
1041
+ borderRadius: 6,
1042
+ cursor: "pointer",
1043
+ fontSize: "0.6875rem",
1044
+ // 11px in rem
1045
+ color: active ? "#22d3ee" : "#b8bcc8",
1046
+ // WCAG: improved contrast from #71717a
1047
+ minWidth: 110
1048
+ },
1049
+ children: [
1050
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: /* @__PURE__ */ jsx(AstroIcon, { name: icon, size: "extra-small", label: "" }) }),
1051
+ /* @__PURE__ */ jsx("span", { children: label })
1052
+ ]
1053
+ }
1054
+ );
1055
+ }
1056
+ function ObjectLegend({ currentView, satellites, stations, groundStations, onObjectClick }) {
1057
+ const [hoveredSection, setHoveredSection] = React.useState(null);
1058
+ const [pinnedSection, setPinnedSection] = React.useState(null);
1059
+ const isExpanded = (section) => hoveredSection === section || pinnedSection === section;
1060
+ const togglePin = (section) => {
1061
+ setPinnedSection((prev) => prev === section ? null : section);
1062
+ };
1063
+ const sectionStyle = {
1064
+ cursor: "pointer",
1065
+ padding: "4px 0",
1066
+ display: "flex",
1067
+ alignItems: "center",
1068
+ justifyContent: "space-between",
1069
+ transition: "background-color 0.15s"
1070
+ };
1071
+ const itemStyle = {
1072
+ padding: "3px 8px",
1073
+ marginLeft: 12,
1074
+ cursor: "pointer",
1075
+ borderRadius: 4,
1076
+ fontSize: 10,
1077
+ color: "#a1a1aa",
1078
+ transition: "all 0.15s"
1079
+ };
1080
+ return /* @__PURE__ */ jsxs("div", { style: {
1081
+ position: "absolute",
1082
+ top: 12,
1083
+ right: 12,
1084
+ backgroundColor: "rgba(0,0,0,0.85)",
1085
+ color: "#e4e4e7",
1086
+ padding: "10px 14px",
1087
+ borderRadius: 8,
1088
+ fontSize: 11,
1089
+ fontFamily: "ui-monospace, monospace",
1090
+ minWidth: 160,
1091
+ maxHeight: 400,
1092
+ overflowY: "auto"
1093
+ }, children: [
1094
+ /* @__PURE__ */ jsx("div", { style: { color: "#b8bcc8", fontSize: 10, marginBottom: 8 }, children: currentView === "solar-system" ? "Solar System" : "Earth Orbit" }),
1095
+ satellites.length > 0 && /* @__PURE__ */ jsxs(
1096
+ "div",
1097
+ {
1098
+ onMouseEnter: () => setHoveredSection("satellites"),
1099
+ onMouseLeave: () => setHoveredSection(null),
1100
+ children: [
1101
+ /* @__PURE__ */ jsxs(
1102
+ "div",
1103
+ {
1104
+ style: {
1105
+ ...sectionStyle,
1106
+ backgroundColor: isExpanded("satellites") ? "rgba(255,255,255,0.05)" : "transparent"
1107
+ },
1108
+ onClick: () => togglePin("satellites"),
1109
+ children: [
1110
+ /* @__PURE__ */ jsxs("span", { children: [
1111
+ /* @__PURE__ */ jsx("span", { style: { color: "#00ff88" }, children: "●" }),
1112
+ " ",
1113
+ satellites.length,
1114
+ " Satellites"
1115
+ ] }),
1116
+ /* @__PURE__ */ jsxs("span", { style: { color: pinnedSection === "satellites" ? "#00ff88" : "#666", fontSize: 9 }, children: [
1117
+ isExpanded("satellites") ? "▼" : "▶",
1118
+ pinnedSection === "satellites" ? /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(AstroIcon, { name: "bookmark", size: "extra-small", label: "", style: { marginLeft: 4, verticalAlign: "middle" } }) }) : null
1119
+ ] })
1120
+ ]
1121
+ }
1122
+ ),
1123
+ isExpanded("satellites") && /* @__PURE__ */ jsx("div", { style: { marginBottom: 4, paddingBottom: 4 }, children: satellites.map((sat) => /* @__PURE__ */ jsx(
1124
+ "div",
1125
+ {
1126
+ style: itemStyle,
1127
+ onClick: (e) => {
1128
+ e.stopPropagation();
1129
+ onObjectClick(sat.id, sat);
1130
+ },
1131
+ onMouseEnter: (e) => {
1132
+ e.currentTarget.style.backgroundColor = "rgba(0,255,136,0.15)";
1133
+ e.currentTarget.style.color = "#00ff88";
1134
+ },
1135
+ onMouseLeave: (e) => {
1136
+ e.currentTarget.style.backgroundColor = "transparent";
1137
+ e.currentTarget.style.color = "#a1a1aa";
1138
+ },
1139
+ children: sat.name || sat.id
1140
+ },
1141
+ sat.id
1142
+ )) })
1143
+ ]
1144
+ }
1145
+ ),
1146
+ stations.length > 0 && /* @__PURE__ */ jsxs(
1147
+ "div",
1148
+ {
1149
+ onMouseEnter: () => setHoveredSection("stations"),
1150
+ onMouseLeave: () => setHoveredSection(null),
1151
+ children: [
1152
+ /* @__PURE__ */ jsxs(
1153
+ "div",
1154
+ {
1155
+ style: {
1156
+ ...sectionStyle,
1157
+ backgroundColor: isExpanded("stations") ? "rgba(255,255,255,0.05)" : "transparent"
1158
+ },
1159
+ onClick: () => togglePin("stations"),
1160
+ children: [
1161
+ /* @__PURE__ */ jsxs("span", { children: [
1162
+ /* @__PURE__ */ jsx("span", { style: { color: "#ffd700" }, children: "●" }),
1163
+ " ",
1164
+ stations.length,
1165
+ " Stations"
1166
+ ] }),
1167
+ /* @__PURE__ */ jsxs("span", { style: { color: pinnedSection === "stations" ? "#ffd700" : "#666", fontSize: 9 }, children: [
1168
+ isExpanded("stations") ? "▼" : "▶",
1169
+ pinnedSection === "stations" ? /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(AstroIcon, { name: "bookmark", size: "extra-small", label: "", style: { marginLeft: 4, verticalAlign: "middle" } }) }) : null
1170
+ ] })
1171
+ ]
1172
+ }
1173
+ ),
1174
+ isExpanded("stations") && /* @__PURE__ */ jsx("div", { style: { marginBottom: 4, paddingBottom: 4 }, children: stations.map((st) => /* @__PURE__ */ jsx(
1175
+ "div",
1176
+ {
1177
+ style: itemStyle,
1178
+ onClick: (e) => {
1179
+ e.stopPropagation();
1180
+ onObjectClick(st.id, st);
1181
+ },
1182
+ onMouseEnter: (e) => {
1183
+ e.currentTarget.style.backgroundColor = "rgba(255,215,0,0.15)";
1184
+ e.currentTarget.style.color = "#ffd700";
1185
+ },
1186
+ onMouseLeave: (e) => {
1187
+ e.currentTarget.style.backgroundColor = "transparent";
1188
+ e.currentTarget.style.color = "#a1a1aa";
1189
+ },
1190
+ children: st.name || st.id
1191
+ },
1192
+ st.id
1193
+ )) })
1194
+ ]
1195
+ }
1196
+ ),
1197
+ groundStations.length > 0 && /* @__PURE__ */ jsxs(
1198
+ "div",
1199
+ {
1200
+ onMouseEnter: () => setHoveredSection("groundStations"),
1201
+ onMouseLeave: () => setHoveredSection(null),
1202
+ children: [
1203
+ /* @__PURE__ */ jsxs(
1204
+ "div",
1205
+ {
1206
+ style: {
1207
+ ...sectionStyle,
1208
+ backgroundColor: isExpanded("groundStations") ? "rgba(255,255,255,0.05)" : "transparent"
1209
+ },
1210
+ onClick: () => togglePin("groundStations"),
1211
+ children: [
1212
+ /* @__PURE__ */ jsxs("span", { children: [
1213
+ /* @__PURE__ */ jsx("span", { style: { color: "#00ffff" }, children: "▲" }),
1214
+ " ",
1215
+ groundStations.length,
1216
+ " Ground Stations"
1217
+ ] }),
1218
+ /* @__PURE__ */ jsxs("span", { style: { color: pinnedSection === "groundStations" ? "#00ffff" : "#666", fontSize: 9 }, children: [
1219
+ isExpanded("groundStations") ? "▼" : "▶",
1220
+ pinnedSection === "groundStations" ? /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(AstroIcon, { name: "bookmark", size: "extra-small", label: "", style: { marginLeft: 4, verticalAlign: "middle" } }) }) : null
1221
+ ] })
1222
+ ]
1223
+ }
1224
+ ),
1225
+ isExpanded("groundStations") && /* @__PURE__ */ jsx("div", { style: { marginBottom: 4, paddingBottom: 4 }, children: groundStations.map((gs) => /* @__PURE__ */ jsx(
1226
+ "div",
1227
+ {
1228
+ style: itemStyle,
1229
+ onClick: (e) => {
1230
+ e.stopPropagation();
1231
+ onObjectClick(gs.id, gs);
1232
+ },
1233
+ onMouseEnter: (e) => {
1234
+ e.currentTarget.style.backgroundColor = "rgba(0,255,255,0.15)";
1235
+ e.currentTarget.style.color = "#00ffff";
1236
+ },
1237
+ onMouseLeave: (e) => {
1238
+ e.currentTarget.style.backgroundColor = "transparent";
1239
+ e.currentTarget.style.color = "#a1a1aa";
1240
+ },
1241
+ children: gs.name || gs.id
1242
+ },
1243
+ gs.id
1244
+ )) })
1245
+ ]
1246
+ }
1247
+ )
1248
+ ] });
1249
+ }
1250
+ export {
1251
+ ZenSpace3D
1252
+ };
1253
+ //# sourceMappingURL=ZenSpace3D.js.map