@zendir/ui 0.1.8 → 0.1.10

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 (92) hide show
  1. package/dist/index.js +0 -169
  2. package/dist/index.js.map +1 -1
  3. package/dist/react/context/DisplaySettingsContext.js +0 -12
  4. package/dist/react/context/DisplaySettingsContext.js.map +1 -1
  5. package/dist/react/core/AstroIcon.js +1 -816
  6. package/dist/react/core/AstroIcon.js.map +1 -1
  7. package/dist/react/core/index.d.ts +0 -1
  8. package/dist/react/index.d.ts +2 -42
  9. package/dist/react/utils/index.js +0 -8
  10. package/dist/react/utils/index.js.map +1 -1
  11. package/dist/react.js +0 -169
  12. package/dist/react.js.map +1 -1
  13. package/package.json +1 -1
  14. package/dist/react/3d/EarthViewer.js +0 -836
  15. package/dist/react/3d/EarthViewer.js.map +0 -1
  16. package/dist/react/3d/SolarSystemViewer.js +0 -372
  17. package/dist/react/3d/SolarSystemViewer.js.map +0 -1
  18. package/dist/react/3d/ZenSpace3D.js +0 -1253
  19. package/dist/react/3d/ZenSpace3D.js.map +0 -1
  20. package/dist/react/3d/ZenSpace3DCesium.js +0 -186
  21. package/dist/react/3d/ZenSpace3DCesium.js.map +0 -1
  22. package/dist/react/3d/ZenSpace3DShaders.js +0 -94
  23. package/dist/react/3d/ZenSpace3DShaders.js.map +0 -1
  24. package/dist/react/3d/ZenSpace3DUtils.js +0 -213
  25. package/dist/react/3d/ZenSpace3DUtils.js.map +0 -1
  26. package/dist/react/3d/threeLoader.js +0 -18
  27. package/dist/react/3d/threeLoader.js.map +0 -1
  28. package/dist/react/cards/AccessCard.js +0 -410
  29. package/dist/react/cards/AccessCard.js.map +0 -1
  30. package/dist/react/cards/OrbitCard.js +0 -372
  31. package/dist/react/cards/OrbitCard.js.map +0 -1
  32. package/dist/react/cards/SpacecraftCard.js +0 -941
  33. package/dist/react/cards/SpacecraftCard.js.map +0 -1
  34. package/dist/react/cards/TelemetryCard.js +0 -742
  35. package/dist/react/cards/TelemetryCard.js.map +0 -1
  36. package/dist/react/cards/TelemetryStreamCard.js +0 -309
  37. package/dist/react/cards/TelemetryStreamCard.js.map +0 -1
  38. package/dist/react/charts/GroundTrackMap.js +0 -1123
  39. package/dist/react/charts/GroundTrackMap.js.map +0 -1
  40. package/dist/react/charts/GroundTrackMapLeaflet.js +0 -571
  41. package/dist/react/charts/GroundTrackMapLeaflet.js.map +0 -1
  42. package/dist/react/charts/groundTrackMapLeafletTiles.js +0 -11
  43. package/dist/react/charts/groundTrackMapLeafletTiles.js.map +0 -1
  44. package/dist/react/charts/groundTrackMapLeafletUtils.js +0 -109
  45. package/dist/react/charts/groundTrackMapLeafletUtils.js.map +0 -1
  46. package/dist/react/charts/unified/AstroChart.js +0 -1405
  47. package/dist/react/charts/unified/AstroChart.js.map +0 -1
  48. package/dist/react/charts/unified/PowerOverviewChart.js +0 -488
  49. package/dist/react/charts/unified/PowerOverviewChart.js.map +0 -1
  50. package/dist/react/charts/unified/domain.js +0 -3168
  51. package/dist/react/charts/unified/domain.js.map +0 -1
  52. package/dist/react/charts/unified/generators.js +0 -518
  53. package/dist/react/charts/unified/generators.js.map +0 -1
  54. package/dist/react/charts/unified/presets.js +0 -999
  55. package/dist/react/charts/unified/presets.js.map +0 -1
  56. package/dist/react/charts/unified/sync.js +0 -219
  57. package/dist/react/charts/unified/sync.js.map +0 -1
  58. package/dist/react/charts/unified/theme.js +0 -562
  59. package/dist/react/charts/unified/theme.js.map +0 -1
  60. package/dist/react/charts/unified/useChartStream.js +0 -226
  61. package/dist/react/charts/unified/useChartStream.js.map +0 -1
  62. package/dist/react/chatgpt/AppCard.js +0 -306
  63. package/dist/react/chatgpt/AppCard.js.map +0 -1
  64. package/dist/react/chatgpt/index.js +0 -166
  65. package/dist/react/chatgpt/index.js.map +0 -1
  66. package/dist/react/hooks/useSpacecraftPosition.js +0 -89
  67. package/dist/react/hooks/useSpacecraftPosition.js.map +0 -1
  68. package/dist/react/hooks/useTelemetry.js +0 -73
  69. package/dist/react/hooks/useTelemetry.js.map +0 -1
  70. package/dist/react/hooks/useZendirSession.js +0 -148
  71. package/dist/react/hooks/useZendirSession.js.map +0 -1
  72. package/dist/react/visualizations/EclipseTimerCard.js +0 -250
  73. package/dist/react/visualizations/EclipseTimerCard.js.map +0 -1
  74. package/dist/react/visualizations/LinkBudgetCard.js +0 -444
  75. package/dist/react/visualizations/LinkBudgetCard.js.map +0 -1
  76. package/dist/react/visualizations/NavBallCard.js +0 -243
  77. package/dist/react/visualizations/NavBallCard.js.map +0 -1
  78. package/dist/react/visualizations/PropulsionCard.js +0 -298
  79. package/dist/react/visualizations/PropulsionCard.js.map +0 -1
  80. package/dist/react/visualizations/SensorFootprintCard.js +0 -326
  81. package/dist/react/visualizations/SensorFootprintCard.js.map +0 -1
  82. package/dist/react/visualizations/ThermalHeatmapCard.js +0 -372
  83. package/dist/react/visualizations/ThermalHeatmapCard.js.map +0 -1
  84. package/dist/shaders/atmosphere.frag.js +0 -5
  85. package/dist/shaders/atmosphere.frag.js.map +0 -1
  86. package/dist/shaders/atmosphere.vert.js +0 -5
  87. package/dist/shaders/atmosphere.vert.js.map +0 -1
  88. package/dist/shaders/stars.frag.js +0 -5
  89. package/dist/shaders/stars.frag.js.map +0 -1
  90. package/dist/shaders/stars.vert.js +0 -5
  91. package/dist/shaders/stars.vert.js.map +0 -1
  92. package/dist/style.css +0 -143
@@ -1,1253 +0,0 @@
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