@zendir/ui 0.6.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/CHANGELOG.md +419 -406
  2. package/dist/react/3d/CesiumCaptureSource.js.map +1 -1
  3. package/dist/react/3d/ZenSpace3D.js.map +1 -1
  4. package/dist/react/3d/ZenSpace3DCesium.js.map +1 -1
  5. package/dist/react/3d/ZenSpace3DShaders.js.map +1 -1
  6. package/dist/react/3d/ZenSpace3DUtils.js.map +1 -1
  7. package/dist/react/3d/capturePngAnalysis.js.map +1 -1
  8. package/dist/react/3d/threeLoader.js.map +1 -1
  9. package/dist/react/astro/ClassificationBanner.js.map +1 -1
  10. package/dist/react/astro/GlobalStatusBar.js.map +1 -1
  11. package/dist/react/astro/MissionClock.js.map +1 -1
  12. package/dist/react/astro/MonitoringIcon.js.map +1 -1
  13. package/dist/react/astro/Notification.js.map +1 -1
  14. package/dist/react/astro/Progress.js.map +1 -1
  15. package/dist/react/astro/SimulationControls.js.map +1 -1
  16. package/dist/react/astro/StatusIndicator.js.map +1 -1
  17. package/dist/react/astro/UnifiedTimeline.js.map +1 -1
  18. package/dist/react/cards/AccessCard.js.map +1 -1
  19. package/dist/react/cards/OrbitCard.js.map +1 -1
  20. package/dist/react/cards/SpacecraftCard.js.map +1 -1
  21. package/dist/react/cards/TelemetryCard.js.map +1 -1
  22. package/dist/react/cards/TelemetryStreamCard.js.map +1 -1
  23. package/dist/react/charts/GroundTrackMap.js.map +1 -1
  24. package/dist/react/charts/GroundTrackMapLeaflet.js.map +1 -1
  25. package/dist/react/charts/groundTrackMapLeafletTiles.js.map +1 -1
  26. package/dist/react/charts/groundTrackMapLeafletUtils.js.map +1 -1
  27. package/dist/react/charts/unified/AstroChart.js.map +1 -1
  28. package/dist/react/charts/unified/PowerOverviewChart.js.map +1 -1
  29. package/dist/react/charts/unified/domain.js.map +1 -1
  30. package/dist/react/charts/unified/generators.js.map +1 -1
  31. package/dist/react/charts/unified/presets.js.map +1 -1
  32. package/dist/react/charts/unified/sync.js.map +1 -1
  33. package/dist/react/charts/unified/theme.js.map +1 -1
  34. package/dist/react/charts/unified/useChartStream.js.map +1 -1
  35. package/dist/react/chatgpt/index.js.map +1 -1
  36. package/dist/react/context/CategoryContext.js.map +1 -1
  37. package/dist/react/context/DisplaySettingsContext.js.map +1 -1
  38. package/dist/react/context/SpatialSelectionContext.js.map +1 -1
  39. package/dist/react/core/data/DataTable.js.map +1 -1
  40. package/dist/react/core/data/DataValue.js.map +1 -1
  41. package/dist/react/core/data/propertyConfig.js.map +1 -1
  42. package/dist/react/core/display/AstroIcon.js.map +1 -1
  43. package/dist/react/core/display/Badge.js.map +1 -1
  44. package/dist/react/core/display/CardHeader.js.map +1 -1
  45. package/dist/react/core/display/Container.js.map +1 -1
  46. package/dist/react/core/display/CopyButton.js.map +1 -1
  47. package/dist/react/core/display/GlassCard.js.map +1 -1
  48. package/dist/react/core/display/HeaderIconWithStatus.js.map +1 -1
  49. package/dist/react/core/display/Icon.js.map +1 -1
  50. package/dist/react/core/display/Typography.js.map +1 -1
  51. package/dist/react/core/feedback/ConfirmDialog.js.map +1 -1
  52. package/dist/react/core/feedback/Dialog.js.map +1 -1
  53. package/dist/react/core/feedback/Toast.js.map +1 -1
  54. package/dist/react/core/inputs/Button.js.map +1 -1
  55. package/dist/react/core/inputs/Checkbox.js.map +1 -1
  56. package/dist/react/core/inputs/Input.js.map +1 -1
  57. package/dist/react/core/inputs/LimitsBar.js.map +1 -1
  58. package/dist/react/core/inputs/NumberInput.js.map +1 -1
  59. package/dist/react/core/inputs/PinInput.js.map +1 -1
  60. package/dist/react/core/inputs/Select.js.map +1 -1
  61. package/dist/react/core/inputs/Toggle.js.map +1 -1
  62. package/dist/react/core/layout/Box.js.map +1 -1
  63. package/dist/react/core/layout/Center.js.map +1 -1
  64. package/dist/react/core/layout/Divider.js.map +1 -1
  65. package/dist/react/core/layout/Flex.js.map +1 -1
  66. package/dist/react/core/layout/Grid.js.map +1 -1
  67. package/dist/react/core/layout/Spacer.js.map +1 -1
  68. package/dist/react/core/layout/Stack.js.map +1 -1
  69. package/dist/react/core/layout/responsive.js.map +1 -1
  70. package/dist/react/core/layout/useBreakpoint.js.map +1 -1
  71. package/dist/react/core/navigation/AppBar.js.map +1 -1
  72. package/dist/react/core/navigation/Pagination.js.map +1 -1
  73. package/dist/react/core/navigation/SideNav.js.map +1 -1
  74. package/dist/react/core/navigation/Tabs.js.map +1 -1
  75. package/dist/react/core/overlays/Popover.js.map +1 -1
  76. package/dist/react/core/overlays/SidePanel.js.map +1 -1
  77. package/dist/react/core/overlays/Tooltip.js.map +1 -1
  78. package/dist/react/core/widgets/ActivityPlanner.js.map +1 -1
  79. package/dist/react/core/widgets/Capture.js.map +1 -1
  80. package/dist/react/core/widgets/ChatPanel.js.map +1 -1
  81. package/dist/react/core/widgets/ColorPickerPanel.js.map +1 -1
  82. package/dist/react/core/widgets/CommandBuilder.js.map +1 -1
  83. package/dist/react/core/widgets/ConnectionForm.js.map +1 -1
  84. package/dist/react/core/widgets/FileExplorer.js.map +1 -1
  85. package/dist/react/core/widgets/HexViewer.js.map +1 -1
  86. package/dist/react/core/widgets/ImageFrame.js.map +1 -1
  87. package/dist/react/core/widgets/ImageGallery.js.map +1 -1
  88. package/dist/react/core/widgets/ImagePanel.js.map +1 -1
  89. package/dist/react/core/widgets/LogViewer.js.map +1 -1
  90. package/dist/react/core/widgets/MessageStream.js.map +1 -1
  91. package/dist/react/core/widgets/MissionCalendar.js.map +1 -1
  92. package/dist/react/core/widgets/PacketViewer.js.map +1 -1
  93. package/dist/react/hooks/useCompactMode.js.map +1 -1
  94. package/dist/react/hooks/useSimulationScene.js.map +1 -1
  95. package/dist/react/panels/LayerControlPanel.js.map +1 -1
  96. package/dist/react/panels/ObjectInventoryPanel.js.map +1 -1
  97. package/dist/react/shared/ErrorBoundary.js.map +1 -1
  98. package/dist/react/shared/Skeleton.js.map +1 -1
  99. package/dist/react/theme/ThemeProvider.js +2 -1
  100. package/dist/react/theme/ThemeProvider.js.map +1 -1
  101. package/dist/react/theme/cardAccent.js.map +1 -1
  102. package/dist/react/theme/config.js.map +1 -1
  103. package/dist/react/theme/types.js.map +1 -1
  104. package/dist/react/types.js.map +1 -1
  105. package/dist/react/utils/categoryPalette.js.map +1 -1
  106. package/dist/react/utils/index.js.map +1 -1
  107. package/dist/style.css +216 -216
  108. package/dist/tokens/core.js.map +1 -1
  109. package/dist/tokens/index.js.map +1 -1
  110. package/dist/tokens/palettes/astro.js.map +1 -1
  111. package/dist/tokens/palettes/index.js.map +1 -1
  112. package/dist/tokens/palettes/tailwind.js.map +1 -1
  113. package/dist/tokens/palettes/zendir.js.map +1 -1
  114. package/dist/tokens/themes.js.map +1 -1
  115. package/dist/tokens/tokens.css +533 -533
  116. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"ZenSpace3DCesium.js","names":[],"sources":["../../../src/react/3d/ZenSpace3DCesium.tsx"],"sourcesContent":["/**\n * @zendir/ui - ZenSpace3D Cesium Backend\n *\n * Cesium-based 3D Earth/orbit view. Entities are reconciled **in-place** on\n * each data update (position changes only) to avoid the WebGL context churn\n * that occurs when entities are removed and re-added every polling tick.\n *\n * Layer visibility is controlled via the `layers` prop\n * (all default to \"sensible\" if the key is absent):\n *\n * layers.labels — spacecraft / GS name labels (default: false)\n * layers.coverage — coverage footprint ellipses + cones (default: false)\n * layers.orbits — orbit-path polylines (default: true)\n *\n * Coverage geometry sources (checked in order per satellite):\n * 1. Explicit `satelliteCoverages[].footprintPolygon` — ground polygon from SDK\n * 2. Explicit `satelliteCoverages[].halfAngle` — circular cone from half-angle\n * 3. Auto-computed circular ellipse from altitude (when layers.coverage = true)\n *\n * Additional explicit coverage options per `SatelliteCoverage`:\n * showCone — 3-D sensor cone from satellite to footprint\n * showNadirLine — dashed line from satellite to sub-satellite point\n */\n\nimport React, {\n useRef,\n useEffect,\n useImperativeHandle,\n forwardRef,\n useCallback,\n} from \"react\";\nimport type {\n ZenSpace3DProps,\n ZenSpace3DHandle,\n ViewMode,\n Vector3D,\n SatelliteCoverage,\n} from \"./ZenSpace3DTypes\";\n\n// ── Constants ─────────────────────────────────────────────────────────────────\n\nconst EARTH_RADIUS_KM = 6371;\n\n// ── Coordinate helpers ────────────────────────────────────────────────────────\n\n/**\n * Convert an ECI / J2000 position (km, Earth-centred) into a Cesium\n * `Cartesian3`. Cesium's Cartesian3 lives in the **ECEF (Earth-Fixed)**\n * frame — the same frame Cesium for Unreal exposes when its\n * `CesiumGeoreference` origin is set to `TrueOrigin (0,0,0)`. We rotate\n * ECI → ECEF using `Transforms.computeTemeToPseudoFixedMatrix(jd)`,\n * which is GMST-based and accurate to sub-arcsec for visualisation\n * (no IERS data required, fully synchronous).\n *\n * Returns `null` for non-finite inputs so callers can skip stale ticks\n * without crashing Cesium's Cartesian primitives.\n *\n * @param positionKm ECI position in km (Earth-centred).\n * @param julianDate Cesium JulianDate at which to evaluate Earth's rotation.\n * Caller is responsible for advancing this with sim time.\n */\nfunction eciKmToFixedCartesian3(\n Cesium: any,\n positionKm: Vector3D,\n julianDate: any,\n): any | null {\n if (\n !positionKm ||\n !Number.isFinite(positionKm.x) ||\n !Number.isFinite(positionKm.y) ||\n !Number.isFinite(positionKm.z)\n ) {\n return null;\n }\n // ECI position in metres — Cesium works in metres throughout.\n const eciM = new Cesium.Cartesian3(\n positionKm.x * 1000,\n positionKm.y * 1000,\n positionKm.z * 1000,\n );\n const rot = Cesium.Transforms.computeTemeToPseudoFixedMatrix(julianDate);\n if (!rot) {\n // Should never happen, but Transforms can return undefined for invalid\n // dates — fall back to treating the input as already-fixed so we never\n // crash the viewer on a stray NaN date.\n return eciM;\n }\n return Cesium.Matrix3.multiplyByVector(rot, eciM, new Cesium.Cartesian3());\n}\n\n/**\n * Sub-satellite (lat/lon) point + altitude (km) of an ECI position at a\n * given epoch. Used by coverage/cone code that needs to draw on Earth's\n * surface beneath the satellite.\n */\nfunction eciKmToSubSatellite(\n Cesium: any,\n positionKm: Vector3D,\n julianDate: any,\n): { latitude: number; longitude: number; altitude: number } | null {\n const fixed = eciKmToFixedCartesian3(Cesium, positionKm, julianDate);\n if (!fixed) return null;\n const carto = Cesium.Cartographic.fromCartesian(fixed);\n if (!carto || !Number.isFinite(carto.height)) return null;\n return {\n latitude: Cesium.Math.toDegrees(carto.latitude),\n longitude: Cesium.Math.toDegrees(carto.longitude),\n altitude: carto.height / 1000, // m → km\n };\n}\n\n// ── Coverage geometry ─────────────────────────────────────────────────────────\n\n/**\n * Circular coverage footprint radius (m) on Earth surface.\n *\n * Spherical geometry:\n * α = arcsin(R·cos(ε) / (R+h)) — nadir angle\n * λ = π/2 − ε − α — Earth central angle\n * radius = R · λ — arc length on surface\n */\nfunction autoFootprintRadiusM(altitudeKm: number, minElevDeg: number): number {\n if (altitudeKm <= 0) return 0;\n const R = EARTH_RADIUS_KM;\n const eps = (minElevDeg * Math.PI) / 180;\n const sinAlpha = (R * Math.cos(eps)) / (R + altitudeKm);\n if (sinAlpha >= 1) return 0;\n const alpha = Math.asin(sinAlpha);\n const lambda = Math.PI / 2 - eps - alpha;\n return R * 1000 * lambda;\n}\n\n/** Radius (m) of the footprint circle for a sensor with the given half-angle. */\nfunction halfAngleFootprintRadiusM(\n altitudeKm: number,\n halfAngleDeg: number,\n): number {\n return altitudeKm * 1000 * Math.tan((halfAngleDeg * Math.PI) / 180);\n}\n\n// ── Entity helpers ────────────────────────────────────────────────────────────\n\ntype EntityMap = { current: Map<string, any> };\n\nfunction removeFromMap(viewer: any, id: string, map: EntityMap): void {\n if (map.current.has(id)) {\n try {\n viewer.entities.removeById(id);\n } catch (_) {\n /* already gone */\n }\n map.current.delete(id);\n }\n}\n\nfunction makePointGraphics(Cesium: any, color: any, pixelSize = 8) {\n return {\n pixelSize,\n color,\n outlineColor: Cesium.Color.WHITE,\n outlineWidth: 1,\n scaleByDistance: new Cesium.NearFarScalar(1e6, 1.5, 5e7, 0.5),\n // Always render on top so satellites hidden behind Earth are still visible\n disableDepthTestDistance: Number.POSITIVE_INFINITY,\n };\n}\n\nfunction makeLabelOpts(Cesium: any, text: string, fillColor?: any) {\n return {\n text,\n font: \"12px monospace\",\n fillColor: fillColor ?? Cesium.Color.WHITE,\n outlineColor: Cesium.Color.BLACK,\n outlineWidth: 2,\n style: Cesium.LabelStyle.FILL_AND_OUTLINE,\n pixelOffset: new Cesium.Cartesian2(12, 0),\n scaleByDistance: new Cesium.NearFarScalar(1e6, 1.0, 5e7, 0.3),\n // Fade out label when camera is very far away\n distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 3e7),\n disableDepthTestDistance: Number.POSITIVE_INFINITY,\n };\n}\n\n/** Add or update a circular ellipse entity on the Earth surface. */\nfunction upsertEllipse(\n viewer: any,\n Cesium: any,\n id: string,\n position: any,\n radiusM: number,\n color: any,\n map: EntityMap,\n): void {\n const existing = map.current.get(id);\n if (existing) {\n existing.position = new Cesium.ConstantPositionProperty(position);\n existing.ellipse.semiMajorAxis = new Cesium.ConstantProperty(radiusM);\n existing.ellipse.semiMinorAxis = new Cesium.ConstantProperty(radiusM);\n } else {\n const entity = viewer.entities.add({\n id,\n position,\n ellipse: {\n semiMajorAxis: radiusM,\n semiMinorAxis: radiusM,\n material: color.withAlpha(0.08),\n outline: true,\n outlineColor: color.withAlpha(0.5),\n outlineWidth: 1,\n height: 0,\n },\n });\n map.current.set(id, entity);\n }\n}\n\n/**\n * Render (or update in-place) the full coverage geometry for one satellite\n * from an explicit `SatelliteCoverage` record supplied by the Zendir SDK.\n */\nfunction renderExplicitCoverage(\n viewer: any,\n Cesium: any,\n satId: string,\n satPos3: Vector3D,\n cov: SatelliteCoverage,\n julianDate: any,\n covMap: EntityMap,\n coneMap: EntityMap,\n nadirMap: EntityMap,\n): void {\n const baseColor = cov.color\n ? Cesium.Color.fromCssColorString(cov.color)\n : Cesium.Color.CYAN;\n const opacity = cov.opacity ?? 0.2;\n\n const resolvedPos = cov.satellitePosition ?? satPos3;\n const satCesPos = eciKmToFixedCartesian3(Cesium, resolvedPos, julianDate);\n const subSat = eciKmToSubSatellite(Cesium, resolvedPos, julianDate);\n const latitude = subSat?.latitude ?? NaN;\n const longitude = subSat?.longitude ?? NaN;\n const altitude = subSat?.altitude ?? NaN;\n\n const nadirLat = cov.nadirPoint?.latitude ?? latitude;\n const nadirLon = cov.nadirPoint?.longitude ?? longitude;\n\n // ── Footprint polygon or circular ellipse ──────────────────────────────────\n const covId = `cov-${satId}`;\n if (\n cov.showFootprint !== false &&\n cov.footprintPolygon &&\n cov.footprintPolygon.length >= 3\n ) {\n const positions = Cesium.Cartesian3.fromDegreesArray(\n cov.footprintPolygon.flatMap((pt) => [pt.longitude, pt.latitude]),\n );\n const existing = covMap.current.get(covId);\n if (existing) {\n existing.polygon.hierarchy = new Cesium.ConstantProperty(\n new Cesium.PolygonHierarchy(positions),\n );\n } else {\n const entity = viewer.entities.add({\n id: covId,\n polygon: {\n hierarchy: new Cesium.PolygonHierarchy(positions),\n material: baseColor.withAlpha(opacity),\n outline: true,\n outlineColor: baseColor.withAlpha(opacity * 3),\n height: 0,\n clampToGround: true,\n },\n });\n covMap.current.set(covId, entity);\n }\n } else if (\n cov.showFootprint !== false &&\n cov.halfAngle != null &&\n Number.isFinite(altitude) &&\n altitude > 0\n ) {\n const subSatPos = Cesium.Cartesian3.fromDegrees(nadirLon, nadirLat, 0);\n const radius = halfAngleFootprintRadiusM(altitude, cov.halfAngle);\n upsertEllipse(viewer, Cesium, covId, subSatPos, radius, baseColor, covMap);\n } else {\n removeFromMap(viewer, covId, covMap);\n }\n\n // ── Sensor cone (satellite → footprint, rendered as a Cesium cylinder) ─────\n const coneId = `cone-${satId}`;\n if (cov.showCone && satCesPos && Number.isFinite(altitude) && altitude > 0) {\n const radius =\n cov.halfAngle != null\n ? halfAngleFootprintRadiusM(altitude, cov.halfAngle)\n : autoFootprintRadiusM(altitude, 5);\n const coneLength = altitude * 1000; // km → m\n // Cylinder centered at mid-altitude between satellite and ground point\n const midPos = Cesium.Cartesian3.fromDegrees(\n nadirLon,\n nadirLat,\n coneLength / 2,\n );\n const existing = coneMap.current.get(coneId);\n if (existing) {\n existing.position = new Cesium.ConstantPositionProperty(midPos);\n existing.cylinder.length = new Cesium.ConstantProperty(coneLength);\n existing.cylinder.bottomRadius = new Cesium.ConstantProperty(radius);\n } else {\n const entity = viewer.entities.add({\n id: coneId,\n position: midPos,\n cylinder: {\n length: coneLength,\n topRadius: 0,\n bottomRadius: radius,\n material: baseColor.withAlpha(0.06),\n outline: true,\n outlineColor: baseColor.withAlpha(0.25),\n numberOfVerticalLines: 12,\n },\n });\n coneMap.current.set(coneId, entity);\n }\n } else {\n removeFromMap(viewer, coneId, coneMap);\n }\n\n // ── Nadir line (satellite → sub-satellite point) ───────────────────────────\n const nadirId = `nadir-${satId}`;\n if (cov.showNadirLine && satCesPos) {\n const groundPos = Cesium.Cartesian3.fromDegrees(nadirLon, nadirLat, 0);\n const existing = nadirMap.current.get(nadirId);\n if (existing) {\n existing.polyline.positions = new Cesium.ConstantProperty([\n satCesPos,\n groundPos,\n ]);\n } else {\n const entity = viewer.entities.add({\n id: nadirId,\n polyline: {\n positions: [satCesPos, groundPos],\n width: 1,\n material: new Cesium.PolylineDashMaterialProperty({\n color: baseColor.withAlpha(0.5),\n dashLength: 8,\n }),\n },\n });\n nadirMap.current.set(nadirId, entity);\n }\n } else {\n removeFromMap(viewer, nadirId, nadirMap);\n }\n}\n\n// ── Component ─────────────────────────────────────────────────────────────────\n\nexport interface ZenSpace3DCesiumProps extends ZenSpace3DProps {\n /** Cesium module from dynamic import('cesium') */\n cesium: any;\n}\n\nexport const ZenSpace3DCesium = forwardRef<\n ZenSpace3DHandle,\n ZenSpace3DCesiumProps\n>(function ZenSpace3DCesium(\n {\n satellites = [],\n stations = [],\n groundStations = [],\n customObjects = [],\n orbitPaths = [],\n satelliteCoverages = [],\n view = \"earth-orbit\",\n height = 600,\n callbacks,\n showTools = false,\n layers,\n visibility,\n referenceTime,\n cesium: Cesium,\n },\n ref,\n) {\n const containerRef = useRef<HTMLDivElement>(null);\n const viewerRef = useRef<any>(null);\n\n // Stash callbacks in a ref so click/hover handlers never close over a stale\n // function reference — and never make the mount effect depend on them.\n const callbacksRef = useRef(callbacks);\n useEffect(() => {\n callbacksRef.current = callbacks;\n }, [callbacks]);\n\n // ── Entity maps for in-place reconciliation ────────────────────────────────\n // Each map: entity-id → Cesium entity object (only live entities live here).\n const satMapRef = useRef<Map<string, any>>(new Map()); // spacecraft + station dots\n const gsMapRef = useRef<Map<string, any>>(new Map()); // ground station dots\n const covMapRef = useRef<Map<string, any>>(new Map()); // coverage ellipses / polygons\n const coneMapRef = useRef<Map<string, any>>(new Map()); // sensor cones\n const nadirMapRef = useRef<Map<string, any>>(new Map()); // nadir lines\n const orbitMapRef = useRef<Map<string, any>>(new Map()); // orbit polylines\n\n // ── Latest ECI positions for satellite callback-properties ─────────────────\n // Satellites use CallbackPositionProperty so Cesium re-derives ECEF from the\n // stored ECI position on every rendered frame (using the current JulianDate).\n // This makes satellites track Earth's rotation smoothly between API poll ticks,\n // fixing the \"satellite frozen while Earth rotates\" bug.\n const eciPositionsRef = useRef<Map<string, Vector3D>>(new Map());\n\n // ── Layer visibility ──────────────────────────────────────────────────────\n // Render-flag layers (always-on by default for orbits; off by default for\n // labels/coverage). Derive primitives so the sync effect deps are cheap.\n const showLabels = layers?.labels === true; // off by default\n const showCoverage = layers?.coverage === true; // off by default\n const showOrbits = layers?.orbits !== false; // on by default\n\n // Object-group visibility — toggle whole categories of markers off without\n // unmounting them. Defaults to \"everything visible\" so existing consumers\n // who never set `visibility` are unaffected.\n const showSpacecraft = visibility?.spacecraft !== false;\n const showGroundStations = visibility?.groundStations !== false;\n const showCelestialBodies = visibility?.celestialBodies !== false;\n\n // ── Imperative handle ──────────────────────────────────────────────────────\n const focusOn = useCallback(\n (objectId: string, options?: { zoom?: number; animate?: boolean }) => {\n const viewer = viewerRef.current;\n if (!viewer) return;\n const entity = viewer.entities.getById(objectId);\n if (entity) {\n viewer.zoomTo(entity, {\n duration: options?.animate !== false ? 1.5 : 0,\n });\n }\n },\n [],\n );\n\n const setView = useCallback(\n (mode: ViewMode) => {\n const viewer = viewerRef.current;\n if (!viewer) return;\n if (mode === \"earth-orbit\") {\n viewer.camera.flyTo({\n destination: Cesium.Cartesian3.fromDegrees(0, 0, 2e7),\n });\n } else if (mode === \"solar-system\") {\n viewer.camera.flyTo({\n destination: Cesium.Cartesian3.fromDegrees(0, 0, 5e7),\n });\n }\n },\n [Cesium],\n );\n\n useImperativeHandle(\n ref,\n (): ZenSpace3DHandle => ({\n focusOn,\n flyTo: focusOn,\n setView,\n setTime: () => {},\n setTimeScale: () => {},\n togglePlay: () => {},\n setLayerVisibility: () => {},\n getCameraState: () => ({}) as any,\n setCameraState: () => {},\n exportImage: () => \"\",\n exportScene: () => new Blob(),\n getVisibleObjects: () => [],\n findObjects: () => [],\n addObject: () => {},\n removeObject: () => {},\n updateObject: () => {},\n measureDistance: () => 0,\n }),\n [focusOn, setView],\n );\n\n // ── Mount effect: create Cesium Viewer + event handlers ONCE ──────────────\n //\n // Does NOT depend on the data arrays (satellites, orbitPaths, etc.) or on\n // `callbacks`. Both produce fresh references on every parent render / poll\n // tick. Including them would tear down + rebuild Cesium every 2 s, leaking\n // WebGL contexts until Chrome hits its hard cap (~16).\n useEffect(() => {\n if (!containerRef.current || !Cesium) return;\n\n // Clear maps in case this is a hot-reload remount\n satMapRef.current.clear();\n gsMapRef.current.clear();\n covMapRef.current.clear();\n coneMapRef.current.clear();\n nadirMapRef.current.clear();\n orbitMapRef.current.clear();\n eciPositionsRef.current.clear();\n\n const viewer = new Cesium.Viewer(containerRef.current, {\n baseLayerPicker: false,\n fullscreenButton: false,\n vrButton: false,\n geocoder: false,\n homeButton: !!showTools,\n sceneModePicker: false,\n timeline: false,\n navigationHelpButton: !!showTools,\n animation: false,\n scene3DOnly: true,\n useDefaultRenderLoop: true,\n requestRenderMode: false,\n });\n\n viewer.scene.globe.enableLighting = true;\n viewer.scene.backgroundColor = Cesium.Color.BLACK;\n\n // Surface the actual exception text on render-loop crashes. Without\n // this listener Cesium only shows \"An error occurred while rendering.\"\n // and the stack trace is buried.\n viewer.scene.renderError.addEventListener((scene: any, err: unknown) => {\n // eslint-disable-next-line no-console\n console.error(\"[ZenSpace3D] Cesium render error:\", err);\n });\n\n // Advance the clock at 1× real time so Cesium continuously evaluates\n // ECI→ECEF for the correct current moment. Without this, the JulianDate\n // passed to satellite CallbackPositionProperty callbacks would be frozen\n // at the viewer-creation instant and satellites would drift relative to\n // Earth as wall-clock time advanced.\n viewer.clock.currentTime = Cesium.JulianDate.now();\n viewer.clock.multiplier = 1;\n viewer.clock.shouldAnimate = true;\n\n viewerRef.current = viewer;\n\n const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);\n\n // Click → select\n handler.setInputAction((movement: any) => {\n const picked = viewer.scene.pick(movement.position);\n if (\n Cesium.defined(picked) &&\n picked.id &&\n callbacksRef.current?.onSelect\n ) {\n callbacksRef.current.onSelect({\n id: picked.id.id,\n name: picked.id.name,\n category: \"satellite\",\n position: { x: 0, y: 0, z: 0 },\n visualRadius: 0,\n });\n }\n }, Cesium.ScreenSpaceEventType.LEFT_CLICK);\n\n // Hover → onHover callback\n handler.setInputAction((movement: any) => {\n const picked = viewer.scene.pick(movement.endPosition);\n if (Cesium.defined(picked) && picked.id) {\n callbacksRef.current?.onHover?.({\n id: picked.id.id,\n name: picked.id.name,\n category: \"satellite\",\n position: { x: 0, y: 0, z: 0 },\n visualRadius: 0,\n });\n } else {\n callbacksRef.current?.onHover?.(null);\n }\n }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);\n\n return () => {\n handler.destroy();\n satMapRef.current.clear();\n gsMapRef.current.clear();\n covMapRef.current.clear();\n coneMapRef.current.clear();\n nadirMapRef.current.clear();\n orbitMapRef.current.clear();\n eciPositionsRef.current.clear();\n viewer.destroy();\n viewerRef.current = null;\n };\n }, [Cesium, showTools]);\n\n // ── Sync effect: reconcile entities on data / layer changes ───────────────\n //\n // For existing satellites (same ID, new position): entity.position is\n // updated in-place via ConstantPositionProperty — no remove/add required.\n // Only genuinely new or removed satellites trigger entity creation/deletion.\n useEffect(() => {\n const viewer = viewerRef.current;\n if (!viewer || !Cesium) return;\n\n // JulianDate used for static ECI→ECEF snapshots in coverage/orbit\n // geometry. Satellite dot positions use CallbackPositionProperty and\n // re-evaluate this conversion per-frame from the viewer's live clock,\n // so they don't depend on `jd` here.\n const jd = referenceTime\n ? Cesium.JulianDate.fromDate(referenceTime)\n : Cesium.JulianDate.now();\n\n // ── Satellites + stations ────────────────────────────────────────────\n // When `visibility.spacecraft === false` the consumer wants the whole\n // group hidden — feed the loop an empty list so the cleanup pass below\n // sweeps existing entities without us having to duplicate the removal\n // logic here.\n const seenSats = new Set<string>();\n const renderedSats = showSpacecraft ? [...satellites, ...stations] : [];\n\n renderedSats.forEach((obj) => {\n const id = obj.id;\n const pos3 = (obj as any).position as Vector3D;\n if (!pos3) return;\n // Guard: reject placeholder/zero positions that haven't been seeded yet.\n if (\n !Number.isFinite(pos3.x) ||\n !Number.isFinite(pos3.y) ||\n !Number.isFinite(pos3.z)\n )\n return;\n\n // Store the latest ECI km position so the per-frame callback can\n // convert it to ECEF at the actual render time rather than at the\n // (stale) poll-tick time.\n eciPositionsRef.current.set(id, pos3);\n seenSats.add(id);\n\n const name = (obj as any).name ?? id;\n const existing = satMapRef.current.get(id);\n\n if (existing) {\n // ECI position already written to eciPositionsRef above — the\n // CallbackPositionProperty will pick it up on the next frame.\n // Only sync label visibility here.\n if (existing.label) {\n existing.label.show = new Cesium.ConstantProperty(showLabels);\n } else if (showLabels) {\n existing.label = new Cesium.LabelGraphics(\n makeLabelOpts(Cesium, name),\n );\n }\n } else {\n // Use CallbackPositionProperty so Cesium re-evaluates the ECI→ECEF\n // transform on every rendered frame using the viewer's current\n // JulianDate. This keeps the satellite locked to the correct point\n // on the rotating Earth rather than drifting in inertial space.\n const capturedId = id; // capture for closure\n // The callback runs inside Cesium's render loop on every frame.\n // ANY exception here halts rendering (\"An error occurred while\n // rendering. Rendering has stopped.\"), so we defensively catch\n // and return undefined — Cesium treats that as \"position\n // unavailable\" and skips the entity for that frame.\n const posProperty = new Cesium.CallbackPositionProperty(\n (time: any) => {\n try {\n const eciKm = eciPositionsRef.current.get(capturedId);\n if (!eciKm) return undefined;\n const fixed = eciKmToFixedCartesian3(Cesium, eciKm, time);\n if (\n !fixed ||\n !Number.isFinite(fixed.x) ||\n !Number.isFinite(fixed.y) ||\n !Number.isFinite(fixed.z)\n ) {\n return undefined;\n }\n return fixed;\n } catch {\n return undefined;\n }\n },\n false, // not constant — must be re-evaluated every frame\n Cesium.ReferenceFrame.FIXED,\n );\n const entity = viewer.entities.add({\n id,\n name,\n position: posProperty,\n point: makePointGraphics(Cesium, Cesium.Color.CYAN),\n label: showLabels\n ? new Cesium.LabelGraphics(makeLabelOpts(Cesium, name))\n : undefined,\n });\n satMapRef.current.set(id, entity);\n }\n\n // ── Coverage ─────────────────────────────────────────────────────────\n const explicitCov = (satelliteCoverages ?? []).find(\n (c) => c.satelliteId === id,\n );\n if (explicitCov) {\n renderExplicitCoverage(\n viewer,\n Cesium,\n id,\n pos3,\n explicitCov,\n jd,\n covMapRef,\n coneMapRef,\n nadirMapRef,\n );\n } else if (showCoverage) {\n // Auto coverage: circular ellipse computed from sub-satellite point\n const subSat = eciKmToSubSatellite(Cesium, pos3, jd);\n if (subSat && subSat.altitude > 0) {\n const subSatPos = Cesium.Cartesian3.fromDegrees(\n subSat.longitude,\n subSat.latitude,\n 0,\n );\n const radius = autoFootprintRadiusM(subSat.altitude, 5);\n upsertEllipse(\n viewer,\n Cesium,\n `cov-${id}`,\n subSatPos,\n radius,\n Cesium.Color.CYAN,\n covMapRef,\n );\n }\n } else {\n removeFromMap(viewer, `cov-${id}`, covMapRef);\n removeFromMap(viewer, `cone-${id}`, coneMapRef);\n removeFromMap(viewer, `nadir-${id}`, nadirMapRef);\n }\n });\n\n // Remove stale satellite entities (+ associated coverage geometry + ECI position).\n satMapRef.current.forEach((_, id) => {\n if (!seenSats.has(id)) {\n eciPositionsRef.current.delete(id);\n removeFromMap(viewer, id, satMapRef);\n removeFromMap(viewer, `cov-${id}`, covMapRef);\n removeFromMap(viewer, `cone-${id}`, coneMapRef);\n removeFromMap(viewer, `nadir-${id}`, nadirMapRef);\n }\n });\n\n // ── Custom objects (planets, celestial bodies, etc.) ────────────────\n // Earth is already rendered by Cesium's globe — skip it by name.\n // Other celestial bodies (Moon, etc.) are rendered as orange dots.\n // When `visibility.celestialBodies === false`, feed an empty list so\n // the cleanup pass removes any existing custom-object entities.\n const seenCustom = new Set<string>();\n const renderedCustom = showCelestialBodies ? (customObjects ?? []) : [];\n renderedCustom.forEach((obj) => {\n if (/^earth$/i.test(obj.name ?? \"\")) return; // skip Earth\n const id = obj.id;\n const cesPos = eciKmToFixedCartesian3(Cesium, obj.position, jd);\n if (!cesPos) return;\n seenCustom.add(id);\n\n const existing = satMapRef.current.get(`custom-${id}`);\n if (existing) {\n existing.position = new Cesium.ConstantPositionProperty(cesPos);\n if (existing.label) {\n existing.label.show = new Cesium.ConstantProperty(showLabels);\n } else if (showLabels) {\n existing.label = new Cesium.LabelGraphics(\n makeLabelOpts(Cesium, obj.name ?? id, Cesium.Color.ORANGE),\n );\n }\n } else {\n const entity = viewer.entities.add({\n id: `custom-${id}`,\n name: obj.name,\n position: cesPos,\n point: makePointGraphics(Cesium, Cesium.Color.ORANGE, 10),\n label: showLabels\n ? new Cesium.LabelGraphics(\n makeLabelOpts(Cesium, obj.name ?? id, Cesium.Color.ORANGE),\n )\n : undefined,\n });\n satMapRef.current.set(`custom-${id}`, entity);\n }\n });\n // Remove stale custom objects\n satMapRef.current.forEach((_, key) => {\n if (key.startsWith(\"custom-\") && !seenCustom.has(key.slice(7))) {\n removeFromMap(viewer, key, satMapRef);\n }\n });\n\n // ── Ground stations ──────────────────────────────────────────────────\n // When `visibility.groundStations === false`, feed an empty list so\n // the cleanup pass removes any existing ground-station entities.\n const seenGs = new Set<string>();\n const renderedGs = showGroundStations ? (groundStations ?? []) : [];\n renderedGs.forEach((gs) => {\n if (!Number.isFinite(gs.longitude) || !Number.isFinite(gs.latitude))\n return;\n seenGs.add(gs.id);\n const heightM = Number.isFinite((gs as any).elevation)\n ? (gs as any).elevation\n : 0;\n const gsPos = Cesium.Cartesian3.fromDegrees(\n gs.longitude,\n gs.latitude,\n heightM,\n );\n const name = gs.name ?? gs.id;\n\n const existing = gsMapRef.current.get(gs.id);\n if (existing) {\n existing.position = new Cesium.ConstantPositionProperty(gsPos);\n if (existing.label) {\n existing.label.show = new Cesium.ConstantProperty(showLabels);\n } else if (showLabels) {\n existing.label = new Cesium.LabelGraphics(\n makeLabelOpts(Cesium, name, Cesium.Color.YELLOW),\n );\n }\n } else {\n const entity = viewer.entities.add({\n id: gs.id,\n name,\n position: gsPos,\n point: makePointGraphics(Cesium, Cesium.Color.YELLOW, 6),\n label: showLabels\n ? new Cesium.LabelGraphics(\n makeLabelOpts(Cesium, name, Cesium.Color.YELLOW),\n )\n : undefined,\n });\n gsMapRef.current.set(gs.id, entity);\n }\n });\n\n gsMapRef.current.forEach((_, id) => {\n if (!seenGs.has(id)) removeFromMap(viewer, id, gsMapRef);\n });\n\n // ── Orbit paths ──────────────────────────────────────────────────────\n if (showOrbits) {\n const seenOrbits = new Set<string>();\n\n (orbitPaths ?? []).forEach((path) => {\n const orbitId = `orbit-${path.objectId}`;\n seenOrbits.add(orbitId);\n const positions = path.points\n .map((p) => eciKmToFixedCartesian3(Cesium, p, jd))\n .filter((p): p is NonNullable<typeof p> => p != null);\n if (positions.length < 2) return;\n\n const orbitColor = path.color\n ? Cesium.Color.fromCssColorString(path.color).withAlpha(0.65)\n : Cesium.Color.YELLOW.withAlpha(0.55);\n\n const existing = orbitMapRef.current.get(orbitId);\n if (existing) {\n existing.polyline.positions = new Cesium.ConstantProperty(positions);\n } else {\n const entity = viewer.entities.add({\n id: orbitId,\n name: path.objectId,\n polyline: {\n positions,\n width: 1.5,\n material: new Cesium.PolylineDashMaterialProperty({\n color: orbitColor,\n dashLength: 16,\n }),\n },\n });\n orbitMapRef.current.set(orbitId, entity);\n }\n });\n\n orbitMapRef.current.forEach((_, id) => {\n if (!seenOrbits.has(id)) removeFromMap(viewer, id, orbitMapRef);\n });\n } else {\n // Orbits turned off: remove all\n orbitMapRef.current.forEach((_, id) =>\n removeFromMap(viewer, id, orbitMapRef),\n );\n }\n }, [\n Cesium,\n satellites,\n stations,\n customObjects,\n groundStations,\n orbitPaths,\n satelliteCoverages,\n showLabels,\n showCoverage,\n showOrbits,\n showSpacecraft,\n showGroundStations,\n showCelestialBodies,\n referenceTime,\n ]);\n\n // ── React to `view` prop changes ──────────────────────────────────────────\n // The imperative `setView()` handle is exposed for ad-hoc fly-tos. When\n // the consumer drives the camera declaratively via the `view` prop\n // instead, fly to the matching default destination on each change.\n useEffect(() => {\n setView(view);\n // setView itself depends on Cesium (constant once mounted), so it's\n // stable enough to omit from the dep array — but include `view` itself.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [view]);\n\n const h = typeof height === \"number\" ? `${height}px` : height;\n return (\n <div\n ref={containerRef}\n className=\"zendir-zenspace3d-cesium\"\n style={{ width: \"100%\", height: h, minHeight: 200 }}\n />\n );\n});\n\nexport default ZenSpace3DCesium;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAyCA,IAAM,kBAAkB;;;;;;;;;;;;;;;;;AAoBxB,SAAS,uBACP,QACA,YACA,YACY;CACZ,IACE,CAAC,cACD,CAAC,OAAO,SAAS,WAAW,CAAC,KAC7B,CAAC,OAAO,SAAS,WAAW,CAAC,KAC7B,CAAC,OAAO,SAAS,WAAW,CAAC,GAE7B,OAAO;CAGT,MAAM,OAAO,IAAI,OAAO,WACtB,WAAW,IAAI,KACf,WAAW,IAAI,KACf,WAAW,IAAI,GACjB;CACA,MAAM,MAAM,OAAO,WAAW,+BAA+B,UAAU;CACvE,IAAI,CAAC,KAIH,OAAO;CAET,OAAO,OAAO,QAAQ,iBAAiB,KAAK,MAAM,IAAI,OAAO,WAAW,CAAC;AAC3E;;;;;;AAOA,SAAS,oBACP,QACA,YACA,YACkE;CAClE,MAAM,QAAQ,uBAAuB,QAAQ,YAAY,UAAU;CACnE,IAAI,CAAC,OAAO,OAAO;CACnB,MAAM,QAAQ,OAAO,aAAa,cAAc,KAAK;CACrD,IAAI,CAAC,SAAS,CAAC,OAAO,SAAS,MAAM,MAAM,GAAG,OAAO;CACrD,OAAO;EACL,UAAU,OAAO,KAAK,UAAU,MAAM,QAAQ;EAC9C,WAAW,OAAO,KAAK,UAAU,MAAM,SAAS;EAChD,UAAU,MAAM,SAAS;CAC3B;AACF;;;;;;;;;AAYA,SAAS,qBAAqB,YAAoB,YAA4B;CAC5E,IAAI,cAAc,GAAG,OAAO;CAC5B,MAAM,IAAI;CACV,MAAM,MAAO,aAAa,KAAK,KAAM;CACrC,MAAM,WAAY,IAAI,KAAK,IAAI,GAAG,KAAM,IAAI;CAC5C,IAAI,YAAY,GAAG,OAAO;CAC1B,MAAM,QAAQ,KAAK,KAAK,QAAQ;CAChC,MAAM,SAAS,KAAK,KAAK,IAAI,MAAM;CACnC,OAAO,IAAI,MAAO;AACpB;;AAGA,SAAS,0BACP,YACA,cACQ;CACR,OAAO,aAAa,MAAO,KAAK,IAAK,eAAe,KAAK,KAAM,GAAG;AACpE;AAMA,SAAS,cAAc,QAAa,IAAY,KAAsB;CACpE,IAAI,IAAI,QAAQ,IAAI,EAAE,GAAG;EACvB,IAAI;GACF,OAAO,SAAS,WAAW,EAAE;EAC/B,SAAS,GAAG,CAEZ;EACA,IAAI,QAAQ,OAAO,EAAE;CACvB;AACF;AAEA,SAAS,kBAAkB,QAAa,OAAY,YAAY,GAAG;CACjE,OAAO;EACL;EACA;EACA,cAAc,OAAO,MAAM;EAC3B,cAAc;EACd,iBAAiB,IAAI,OAAO,cAAc,KAAK,KAAK,KAAK,EAAG;EAE5D,0BAA0B,OAAO;CACnC;AACF;AAEA,SAAS,cAAc,QAAa,MAAc,WAAiB;CACjE,OAAO;EACL;EACA,MAAM;EACN,WAAW,aAAa,OAAO,MAAM;EACrC,cAAc,OAAO,MAAM;EAC3B,cAAc;EACd,OAAO,OAAO,WAAW;EACzB,aAAa,IAAI,OAAO,WAAW,IAAI,CAAC;EACxC,iBAAiB,IAAI,OAAO,cAAc,KAAK,GAAK,KAAK,EAAG;EAE5D,0BAA0B,IAAI,OAAO,yBAAyB,GAAG,GAAG;EACpE,0BAA0B,OAAO;CACnC;AACF;;AAGA,SAAS,cACP,QACA,QACA,IACA,UACA,SACA,OACA,KACM;CACN,MAAM,WAAW,IAAI,QAAQ,IAAI,EAAE;CACnC,IAAI,UAAU;EACZ,SAAS,WAAW,IAAI,OAAO,yBAAyB,QAAQ;EAChE,SAAS,QAAQ,gBAAgB,IAAI,OAAO,iBAAiB,OAAO;EACpE,SAAS,QAAQ,gBAAgB,IAAI,OAAO,iBAAiB,OAAO;CACtE,OAAO;EACL,MAAM,SAAS,OAAO,SAAS,IAAI;GACjC;GACA;GACA,SAAS;IACP,eAAe;IACf,eAAe;IACf,UAAU,MAAM,UAAU,GAAI;IAC9B,SAAS;IACT,cAAc,MAAM,UAAU,EAAG;IACjC,cAAc;IACd,QAAQ;GACV;EACF,CAAC;EACD,IAAI,QAAQ,IAAI,IAAI,MAAM;CAC5B;AACF;;;;;AAMA,SAAS,uBACP,QACA,QACA,OACA,SACA,KACA,YACA,QACA,SACA,UACM;CACN,MAAM,YAAY,IAAI,QAClB,OAAO,MAAM,mBAAmB,IAAI,KAAK,IACzC,OAAO,MAAM;CACjB,MAAM,UAAU,IAAI,WAAW;CAE/B,MAAM,cAAc,IAAI,qBAAqB;CAC7C,MAAM,YAAY,uBAAuB,QAAQ,aAAa,UAAU;CACxE,MAAM,SAAS,oBAAoB,QAAQ,aAAa,UAAU;CAClE,MAAM,WAAW,QAAQ,YAAY;CACrC,MAAM,YAAY,QAAQ,aAAa;CACvC,MAAM,WAAW,QAAQ,YAAY;CAErC,MAAM,WAAW,IAAI,YAAY,YAAY;CAC7C,MAAM,WAAW,IAAI,YAAY,aAAa;CAG9C,MAAM,QAAQ,OAAO;CACrB,IACE,IAAI,kBAAkB,SACtB,IAAI,oBACJ,IAAI,iBAAiB,UAAU,GAC/B;EACA,MAAM,YAAY,OAAO,WAAW,iBAClC,IAAI,iBAAiB,SAAS,OAAO,CAAC,GAAG,WAAW,GAAG,QAAQ,CAAC,CAClE;EACA,MAAM,WAAW,OAAO,QAAQ,IAAI,KAAK;EACzC,IAAI,UACF,SAAS,QAAQ,YAAY,IAAI,OAAO,iBACtC,IAAI,OAAO,iBAAiB,SAAS,CACvC;OACK;GACL,MAAM,SAAS,OAAO,SAAS,IAAI;IACjC,IAAI;IACJ,SAAS;KACP,WAAW,IAAI,OAAO,iBAAiB,SAAS;KAChD,UAAU,UAAU,UAAU,OAAO;KACrC,SAAS;KACT,cAAc,UAAU,UAAU,UAAU,CAAC;KAC7C,QAAQ;KACR,eAAe;IACjB;GACF,CAAC;GACD,OAAO,QAAQ,IAAI,OAAO,MAAM;EAClC;CACF,OAAO,IACL,IAAI,kBAAkB,SACtB,IAAI,aAAa,QACjB,OAAO,SAAS,QAAQ,KACxB,WAAW,GAIX,cAAc,QAAQ,QAAQ,OAFZ,OAAO,WAAW,YAAY,UAAU,UAAU,CAE/B,GADtB,0BAA0B,UAAU,IAAI,SACP,GAAQ,WAAW,MAAM;MAEzE,cAAc,QAAQ,OAAO,MAAM;CAIrC,MAAM,SAAS,QAAQ;CACvB,IAAI,IAAI,YAAY,aAAa,OAAO,SAAS,QAAQ,KAAK,WAAW,GAAG;EAC1E,MAAM,SACJ,IAAI,aAAa,OACb,0BAA0B,UAAU,IAAI,SAAS,IACjD,qBAAqB,UAAU,CAAC;EACtC,MAAM,aAAa,WAAW;EAE9B,MAAM,SAAS,OAAO,WAAW,YAC/B,UACA,UACA,aAAa,CACf;EACA,MAAM,WAAW,QAAQ,QAAQ,IAAI,MAAM;EAC3C,IAAI,UAAU;GACZ,SAAS,WAAW,IAAI,OAAO,yBAAyB,MAAM;GAC9D,SAAS,SAAS,SAAS,IAAI,OAAO,iBAAiB,UAAU;GACjE,SAAS,SAAS,eAAe,IAAI,OAAO,iBAAiB,MAAM;EACrE,OAAO;GACL,MAAM,SAAS,OAAO,SAAS,IAAI;IACjC,IAAI;IACJ,UAAU;IACV,UAAU;KACR,QAAQ;KACR,WAAW;KACX,cAAc;KACd,UAAU,UAAU,UAAU,GAAI;KAClC,SAAS;KACT,cAAc,UAAU,UAAU,GAAI;KACtC,uBAAuB;IACzB;GACF,CAAC;GACD,QAAQ,QAAQ,IAAI,QAAQ,MAAM;EACpC;CACF,OACE,cAAc,QAAQ,QAAQ,OAAO;CAIvC,MAAM,UAAU,SAAS;CACzB,IAAI,IAAI,iBAAiB,WAAW;EAClC,MAAM,YAAY,OAAO,WAAW,YAAY,UAAU,UAAU,CAAC;EACrE,MAAM,WAAW,SAAS,QAAQ,IAAI,OAAO;EAC7C,IAAI,UACF,SAAS,SAAS,YAAY,IAAI,OAAO,iBAAiB,CACxD,WACA,SACF,CAAC;OACI;GACL,MAAM,SAAS,OAAO,SAAS,IAAI;IACjC,IAAI;IACJ,UAAU;KACR,WAAW,CAAC,WAAW,SAAS;KAChC,OAAO;KACP,UAAU,IAAI,OAAO,6BAA6B;MAChD,OAAO,UAAU,UAAU,EAAG;MAC9B,YAAY;KACd,CAAC;IACH;GACF,CAAC;GACD,SAAS,QAAQ,IAAI,SAAS,MAAM;EACtC;CACF,OACE,cAAc,QAAQ,SAAS,QAAQ;AAE3C;AASA,IAAa,mBAAmB,WAG9B,SAAS,iBACT,EACE,aAAa,CAAC,GACd,WAAW,CAAC,GACZ,iBAAiB,CAAC,GAClB,gBAAgB,CAAC,GACjB,aAAa,CAAC,GACd,qBAAqB,CAAC,GACtB,OAAO,eACP,SAAS,KACT,WACA,YAAY,OACZ,QACA,YACA,eACA,QAAQ,UAEV,KACA;CACA,MAAM,eAAe,OAAuB,IAAI;CAChD,MAAM,YAAY,OAAY,IAAI;CAIlC,MAAM,eAAe,OAAO,SAAS;CACrC,gBAAgB;EACd,aAAa,UAAU;CACzB,GAAG,CAAC,SAAS,CAAC;CAId,MAAM,YAAY,uBAAyB,IAAI,IAAI,CAAC;CACpD,MAAM,WAAW,uBAAyB,IAAI,IAAI,CAAC;CACnD,MAAM,YAAY,uBAAyB,IAAI,IAAI,CAAC;CACpD,MAAM,aAAa,uBAAyB,IAAI,IAAI,CAAC;CACrD,MAAM,cAAc,uBAAyB,IAAI,IAAI,CAAC;CACtD,MAAM,cAAc,uBAAyB,IAAI,IAAI,CAAC;CAOtD,MAAM,kBAAkB,uBAA8B,IAAI,IAAI,CAAC;CAK/D,MAAM,aAAa,QAAQ,WAAW;CACtC,MAAM,eAAe,QAAQ,aAAa;CAC1C,MAAM,aAAa,QAAQ,WAAW;CAKtC,MAAM,iBAAiB,YAAY,eAAe;CAClD,MAAM,qBAAqB,YAAY,mBAAmB;CAC1D,MAAM,sBAAsB,YAAY,oBAAoB;CAG5D,MAAM,UAAU,aACb,UAAkB,YAAmD;EACpE,MAAM,SAAS,UAAU;EACzB,IAAI,CAAC,QAAQ;EACb,MAAM,SAAS,OAAO,SAAS,QAAQ,QAAQ;EAC/C,IAAI,QACF,OAAO,OAAO,QAAQ,EACpB,UAAU,SAAS,YAAY,QAAQ,MAAM,EAC/C,CAAC;CAEL,GACA,CAAC,CACH;CAEA,MAAM,UAAU,aACb,SAAmB;EAClB,MAAM,SAAS,UAAU;EACzB,IAAI,CAAC,QAAQ;EACb,IAAI,SAAS,eACX,OAAO,OAAO,MAAM,EAClB,aAAa,OAAO,WAAW,YAAY,GAAG,GAAG,GAAG,EACtD,CAAC;OACI,IAAI,SAAS,gBAClB,OAAO,OAAO,MAAM,EAClB,aAAa,OAAO,WAAW,YAAY,GAAG,GAAG,GAAG,EACtD,CAAC;CAEL,GACA,CAAC,MAAM,CACT;CAEA,oBACE,YACyB;EACvB;EACA,OAAO;EACP;EACA,eAAe,CAAC;EAChB,oBAAoB,CAAC;EACrB,kBAAkB,CAAC;EACnB,0BAA0B,CAAC;EAC3B,uBAAuB,CAAC;EACxB,sBAAsB,CAAC;EACvB,mBAAmB;EACnB,mBAAmB,IAAI,KAAK;EAC5B,yBAAyB,CAAC;EAC1B,mBAAmB,CAAC;EACpB,iBAAiB,CAAC;EAClB,oBAAoB,CAAC;EACrB,oBAAoB,CAAC;EACrB,uBAAuB;CACzB,IACA,CAAC,SAAS,OAAO,CACnB;CAQA,gBAAgB;EACd,IAAI,CAAC,aAAa,WAAW,CAAC,QAAQ;EAGtC,UAAU,QAAQ,MAAM;EACxB,SAAS,QAAQ,MAAM;EACvB,UAAU,QAAQ,MAAM;EACxB,WAAW,QAAQ,MAAM;EACzB,YAAY,QAAQ,MAAM;EAC1B,YAAY,QAAQ,MAAM;EAC1B,gBAAgB,QAAQ,MAAM;EAE9B,MAAM,SAAS,IAAI,OAAO,OAAO,aAAa,SAAS;GACrD,iBAAiB;GACjB,kBAAkB;GAClB,UAAU;GACV,UAAU;GACV,YAAY,CAAC,CAAC;GACd,iBAAiB;GACjB,UAAU;GACV,sBAAsB,CAAC,CAAC;GACxB,WAAW;GACX,aAAa;GACb,sBAAsB;GACtB,mBAAmB;EACrB,CAAC;EAED,OAAO,MAAM,MAAM,iBAAiB;EACpC,OAAO,MAAM,kBAAkB,OAAO,MAAM;EAK5C,OAAO,MAAM,YAAY,kBAAkB,OAAY,QAAiB;GAEtE,QAAQ,MAAM,qCAAqC,GAAG;EACxD,CAAC;EAOD,OAAO,MAAM,cAAc,OAAO,WAAW,IAAI;EACjD,OAAO,MAAM,aAAa;EAC1B,OAAO,MAAM,gBAAgB;EAE7B,UAAU,UAAU;EAEpB,MAAM,UAAU,IAAI,OAAO,wBAAwB,OAAO,MAAM,MAAM;EAGtE,QAAQ,gBAAgB,aAAkB;GACxC,MAAM,SAAS,OAAO,MAAM,KAAK,SAAS,QAAQ;GAClD,IACE,OAAO,QAAQ,MAAM,KACrB,OAAO,MACP,aAAa,SAAS,UAEtB,aAAa,QAAQ,SAAS;IAC5B,IAAI,OAAO,GAAG;IACd,MAAM,OAAO,GAAG;IAChB,UAAU;IACV,UAAU;KAAE,GAAG;KAAG,GAAG;KAAG,GAAG;IAAE;IAC7B,cAAc;GAChB,CAAC;EAEL,GAAG,OAAO,qBAAqB,UAAU;EAGzC,QAAQ,gBAAgB,aAAkB;GACxC,MAAM,SAAS,OAAO,MAAM,KAAK,SAAS,WAAW;GACrD,IAAI,OAAO,QAAQ,MAAM,KAAK,OAAO,IACnC,aAAa,SAAS,UAAU;IAC9B,IAAI,OAAO,GAAG;IACd,MAAM,OAAO,GAAG;IAChB,UAAU;IACV,UAAU;KAAE,GAAG;KAAG,GAAG;KAAG,GAAG;IAAE;IAC7B,cAAc;GAChB,CAAC;QAED,aAAa,SAAS,UAAU,IAAI;EAExC,GAAG,OAAO,qBAAqB,UAAU;EAEzC,aAAa;GACX,QAAQ,QAAQ;GAChB,UAAU,QAAQ,MAAM;GACxB,SAAS,QAAQ,MAAM;GACvB,UAAU,QAAQ,MAAM;GACxB,WAAW,QAAQ,MAAM;GACzB,YAAY,QAAQ,MAAM;GAC1B,YAAY,QAAQ,MAAM;GAC1B,gBAAgB,QAAQ,MAAM;GAC9B,OAAO,QAAQ;GACf,UAAU,UAAU;EACtB;CACF,GAAG,CAAC,QAAQ,SAAS,CAAC;CAOtB,gBAAgB;EACd,MAAM,SAAS,UAAU;EACzB,IAAI,CAAC,UAAU,CAAC,QAAQ;EAMxB,MAAM,KAAK,gBACP,OAAO,WAAW,SAAS,aAAa,IACxC,OAAO,WAAW,IAAI;EAO1B,MAAM,2BAAW,IAAI,IAAY;EAGjC,CAFqB,iBAAiB,CAAC,GAAG,YAAY,GAAG,QAAQ,IAAI,CAAC,EAAA,CAEzD,SAAS,QAAQ;GAC5B,MAAM,KAAK,IAAI;GACf,MAAM,OAAQ,IAAY;GAC1B,IAAI,CAAC,MAAM;GAEX,IACE,CAAC,OAAO,SAAS,KAAK,CAAC,KACvB,CAAC,OAAO,SAAS,KAAK,CAAC,KACvB,CAAC,OAAO,SAAS,KAAK,CAAC,GAEvB;GAKF,gBAAgB,QAAQ,IAAI,IAAI,IAAI;GACpC,SAAS,IAAI,EAAE;GAEf,MAAM,OAAQ,IAAY,QAAQ;GAClC,MAAM,WAAW,UAAU,QAAQ,IAAI,EAAE;GAEzC,IAAI;QAIE,SAAS,OACX,SAAS,MAAM,OAAO,IAAI,OAAO,iBAAiB,UAAU;SACvD,IAAI,YACT,SAAS,QAAQ,IAAI,OAAO,cAC1B,cAAc,QAAQ,IAAI,CAC5B;GAAA,OAEG;IAKL,MAAM,aAAa;IAMnB,MAAM,cAAc,IAAI,OAAO,0BAC5B,SAAc;KACb,IAAI;MACF,MAAM,QAAQ,gBAAgB,QAAQ,IAAI,UAAU;MACpD,IAAI,CAAC,OAAO,OAAO,KAAA;MACnB,MAAM,QAAQ,uBAAuB,QAAQ,OAAO,IAAI;MACxD,IACE,CAAC,SACD,CAAC,OAAO,SAAS,MAAM,CAAC,KACxB,CAAC,OAAO,SAAS,MAAM,CAAC,KACxB,CAAC,OAAO,SAAS,MAAM,CAAC,GAExB;MAEF,OAAO;KACT,QAAQ;MACN;KACF;IACF,GACA,OACA,OAAO,eAAe,KACxB;IACA,MAAM,SAAS,OAAO,SAAS,IAAI;KACjC;KACA;KACA,UAAU;KACV,OAAO,kBAAkB,QAAQ,OAAO,MAAM,IAAI;KAClD,OAAO,aACH,IAAI,OAAO,cAAc,cAAc,QAAQ,IAAI,CAAC,IACpD,KAAA;IACN,CAAC;IACD,UAAU,QAAQ,IAAI,IAAI,MAAM;GAClC;GAGA,MAAM,eAAe,sBAAsB,CAAC,EAAA,CAAG,MAC5C,MAAM,EAAE,gBAAgB,EAC3B;GACA,IAAI,aACF,uBACE,QACA,QACA,IACA,MACA,aACA,IACA,WACA,YACA,WACF;QACK,IAAI,cAAc;IAEvB,MAAM,SAAS,oBAAoB,QAAQ,MAAM,EAAE;IACnD,IAAI,UAAU,OAAO,WAAW,GAAG;KACjC,MAAM,YAAY,OAAO,WAAW,YAClC,OAAO,WACP,OAAO,UACP,CACF;KACA,MAAM,SAAS,qBAAqB,OAAO,UAAU,CAAC;KACtD,cACE,QACA,QACA,OAAO,MACP,WACA,QACA,OAAO,MAAM,MACb,SACF;IACF;GACF,OAAO;IACL,cAAc,QAAQ,OAAO,MAAM,SAAS;IAC5C,cAAc,QAAQ,QAAQ,MAAM,UAAU;IAC9C,cAAc,QAAQ,SAAS,MAAM,WAAW;GAClD;EACF,CAAC;EAGD,UAAU,QAAQ,SAAS,GAAG,OAAO;GACnC,IAAI,CAAC,SAAS,IAAI,EAAE,GAAG;IACrB,gBAAgB,QAAQ,OAAO,EAAE;IACjC,cAAc,QAAQ,IAAI,SAAS;IACnC,cAAc,QAAQ,OAAO,MAAM,SAAS;IAC5C,cAAc,QAAQ,QAAQ,MAAM,UAAU;IAC9C,cAAc,QAAQ,SAAS,MAAM,WAAW;GAClD;EACF,CAAC;EAOD,MAAM,6BAAa,IAAI,IAAY;EAEnC,CADuB,sBAAuB,iBAAiB,CAAC,IAAK,CAAC,EAAA,CACvD,SAAS,QAAQ;GAC9B,IAAI,WAAW,KAAK,IAAI,QAAQ,EAAE,GAAG;GACrC,MAAM,KAAK,IAAI;GACf,MAAM,SAAS,uBAAuB,QAAQ,IAAI,UAAU,EAAE;GAC9D,IAAI,CAAC,QAAQ;GACb,WAAW,IAAI,EAAE;GAEjB,MAAM,WAAW,UAAU,QAAQ,IAAI,UAAU,IAAI;GACrD,IAAI,UAAU;IACZ,SAAS,WAAW,IAAI,OAAO,yBAAyB,MAAM;IAC9D,IAAI,SAAS,OACX,SAAS,MAAM,OAAO,IAAI,OAAO,iBAAiB,UAAU;SACvD,IAAI,YACT,SAAS,QAAQ,IAAI,OAAO,cAC1B,cAAc,QAAQ,IAAI,QAAQ,IAAI,OAAO,MAAM,MAAM,CAC3D;GAEJ,OAAO;IACL,MAAM,SAAS,OAAO,SAAS,IAAI;KACjC,IAAI,UAAU;KACd,MAAM,IAAI;KACV,UAAU;KACV,OAAO,kBAAkB,QAAQ,OAAO,MAAM,QAAQ,EAAE;KACxD,OAAO,aACH,IAAI,OAAO,cACT,cAAc,QAAQ,IAAI,QAAQ,IAAI,OAAO,MAAM,MAAM,CAC3D,IACA,KAAA;IACN,CAAC;IACD,UAAU,QAAQ,IAAI,UAAU,MAAM,MAAM;GAC9C;EACF,CAAC;EAED,UAAU,QAAQ,SAAS,GAAG,QAAQ;GACpC,IAAI,IAAI,WAAW,SAAS,KAAK,CAAC,WAAW,IAAI,IAAI,MAAM,CAAC,CAAC,GAC3D,cAAc,QAAQ,KAAK,SAAS;EAExC,CAAC;EAKD,MAAM,yBAAS,IAAI,IAAY;EAE/B,CADmB,qBAAsB,kBAAkB,CAAC,IAAK,CAAC,EAAA,CACvD,SAAS,OAAO;GACzB,IAAI,CAAC,OAAO,SAAS,GAAG,SAAS,KAAK,CAAC,OAAO,SAAS,GAAG,QAAQ,GAChE;GACF,OAAO,IAAI,GAAG,EAAE;GAChB,MAAM,UAAU,OAAO,SAAU,GAAW,SAAS,IAChD,GAAW,YACZ;GACJ,MAAM,QAAQ,OAAO,WAAW,YAC9B,GAAG,WACH,GAAG,UACH,OACF;GACA,MAAM,OAAO,GAAG,QAAQ,GAAG;GAE3B,MAAM,WAAW,SAAS,QAAQ,IAAI,GAAG,EAAE;GAC3C,IAAI,UAAU;IACZ,SAAS,WAAW,IAAI,OAAO,yBAAyB,KAAK;IAC7D,IAAI,SAAS,OACX,SAAS,MAAM,OAAO,IAAI,OAAO,iBAAiB,UAAU;SACvD,IAAI,YACT,SAAS,QAAQ,IAAI,OAAO,cAC1B,cAAc,QAAQ,MAAM,OAAO,MAAM,MAAM,CACjD;GAEJ,OAAO;IACL,MAAM,SAAS,OAAO,SAAS,IAAI;KACjC,IAAI,GAAG;KACP;KACA,UAAU;KACV,OAAO,kBAAkB,QAAQ,OAAO,MAAM,QAAQ,CAAC;KACvD,OAAO,aACH,IAAI,OAAO,cACT,cAAc,QAAQ,MAAM,OAAO,MAAM,MAAM,CACjD,IACA,KAAA;IACN,CAAC;IACD,SAAS,QAAQ,IAAI,GAAG,IAAI,MAAM;GACpC;EACF,CAAC;EAED,SAAS,QAAQ,SAAS,GAAG,OAAO;GAClC,IAAI,CAAC,OAAO,IAAI,EAAE,GAAG,cAAc,QAAQ,IAAI,QAAQ;EACzD,CAAC;EAGD,IAAI,YAAY;GACd,MAAM,6BAAa,IAAI,IAAY;GAEnC,CAAC,cAAc,CAAC,EAAA,CAAG,SAAS,SAAS;IACnC,MAAM,UAAU,SAAS,KAAK;IAC9B,WAAW,IAAI,OAAO;IACtB,MAAM,YAAY,KAAK,OACpB,KAAK,MAAM,uBAAuB,QAAQ,GAAG,EAAE,CAAC,CAAC,CACjD,QAAQ,MAAkC,KAAK,IAAI;IACtD,IAAI,UAAU,SAAS,GAAG;IAE1B,MAAM,aAAa,KAAK,QACpB,OAAO,MAAM,mBAAmB,KAAK,KAAK,CAAC,CAAC,UAAU,GAAI,IAC1D,OAAO,MAAM,OAAO,UAAU,GAAI;IAEtC,MAAM,WAAW,YAAY,QAAQ,IAAI,OAAO;IAChD,IAAI,UACF,SAAS,SAAS,YAAY,IAAI,OAAO,iBAAiB,SAAS;SAC9D;KACL,MAAM,SAAS,OAAO,SAAS,IAAI;MACjC,IAAI;MACJ,MAAM,KAAK;MACX,UAAU;OACR;OACA,OAAO;OACP,UAAU,IAAI,OAAO,6BAA6B;QAChD,OAAO;QACP,YAAY;OACd,CAAC;MACH;KACF,CAAC;KACD,YAAY,QAAQ,IAAI,SAAS,MAAM;IACzC;GACF,CAAC;GAED,YAAY,QAAQ,SAAS,GAAG,OAAO;IACrC,IAAI,CAAC,WAAW,IAAI,EAAE,GAAG,cAAc,QAAQ,IAAI,WAAW;GAChE,CAAC;EACH,OAEE,YAAY,QAAQ,SAAS,GAAG,OAC9B,cAAc,QAAQ,IAAI,WAAW,CACvC;CAEJ,GAAG;EACD;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC;CAMD,gBAAgB;EACd,QAAQ,IAAI;CAId,GAAG,CAAC,IAAI,CAAC;CAGT,OACE,oBAAC,OAAD;EACE,KAAK;EACL,WAAU;EACV,OAAO;GAAE,OAAO;GAAQ,QALlB,OAAO,WAAW,WAAW,GAAG,OAAO,MAAM;GAKhB,WAAW;EAAI;CACnD,CAAA;AAEL,CAAC"}
1
+ {"version":3,"file":"ZenSpace3DCesium.js","names":[],"sources":["../../../src/react/3d/ZenSpace3DCesium.tsx"],"sourcesContent":["/**\r\n * @zendir/ui - ZenSpace3D Cesium Backend\r\n *\r\n * Cesium-based 3D Earth/orbit view. Entities are reconciled **in-place** on\r\n * each data update (position changes only) to avoid the WebGL context churn\r\n * that occurs when entities are removed and re-added every polling tick.\r\n *\r\n * Layer visibility is controlled via the `layers` prop\r\n * (all default to \"sensible\" if the key is absent):\r\n *\r\n * layers.labels — spacecraft / GS name labels (default: false)\r\n * layers.coverage — coverage footprint ellipses + cones (default: false)\r\n * layers.orbits — orbit-path polylines (default: true)\r\n *\r\n * Coverage geometry sources (checked in order per satellite):\r\n * 1. Explicit `satelliteCoverages[].footprintPolygon` — ground polygon from SDK\r\n * 2. Explicit `satelliteCoverages[].halfAngle` — circular cone from half-angle\r\n * 3. Auto-computed circular ellipse from altitude (when layers.coverage = true)\r\n *\r\n * Additional explicit coverage options per `SatelliteCoverage`:\r\n * showCone — 3-D sensor cone from satellite to footprint\r\n * showNadirLine — dashed line from satellite to sub-satellite point\r\n */\r\n\r\nimport React, {\r\n useRef,\r\n useEffect,\r\n useImperativeHandle,\r\n forwardRef,\r\n useCallback,\r\n} from \"react\";\r\nimport type {\r\n ZenSpace3DProps,\r\n ZenSpace3DHandle,\r\n ViewMode,\r\n Vector3D,\r\n SatelliteCoverage,\r\n} from \"./ZenSpace3DTypes\";\r\n\r\n// ── Constants ─────────────────────────────────────────────────────────────────\r\n\r\nconst EARTH_RADIUS_KM = 6371;\r\n\r\n// ── Coordinate helpers ────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Convert an ECI / J2000 position (km, Earth-centred) into a Cesium\r\n * `Cartesian3`. Cesium's Cartesian3 lives in the **ECEF (Earth-Fixed)**\r\n * frame — the same frame Cesium for Unreal exposes when its\r\n * `CesiumGeoreference` origin is set to `TrueOrigin (0,0,0)`. We rotate\r\n * ECI → ECEF using `Transforms.computeTemeToPseudoFixedMatrix(jd)`,\r\n * which is GMST-based and accurate to sub-arcsec for visualisation\r\n * (no IERS data required, fully synchronous).\r\n *\r\n * Returns `null` for non-finite inputs so callers can skip stale ticks\r\n * without crashing Cesium's Cartesian primitives.\r\n *\r\n * @param positionKm ECI position in km (Earth-centred).\r\n * @param julianDate Cesium JulianDate at which to evaluate Earth's rotation.\r\n * Caller is responsible for advancing this with sim time.\r\n */\r\nfunction eciKmToFixedCartesian3(\r\n Cesium: any,\r\n positionKm: Vector3D,\r\n julianDate: any,\r\n): any | null {\r\n if (\r\n !positionKm ||\r\n !Number.isFinite(positionKm.x) ||\r\n !Number.isFinite(positionKm.y) ||\r\n !Number.isFinite(positionKm.z)\r\n ) {\r\n return null;\r\n }\r\n // ECI position in metres — Cesium works in metres throughout.\r\n const eciM = new Cesium.Cartesian3(\r\n positionKm.x * 1000,\r\n positionKm.y * 1000,\r\n positionKm.z * 1000,\r\n );\r\n const rot = Cesium.Transforms.computeTemeToPseudoFixedMatrix(julianDate);\r\n if (!rot) {\r\n // Should never happen, but Transforms can return undefined for invalid\r\n // dates — fall back to treating the input as already-fixed so we never\r\n // crash the viewer on a stray NaN date.\r\n return eciM;\r\n }\r\n return Cesium.Matrix3.multiplyByVector(rot, eciM, new Cesium.Cartesian3());\r\n}\r\n\r\n/**\r\n * Sub-satellite (lat/lon) point + altitude (km) of an ECI position at a\r\n * given epoch. Used by coverage/cone code that needs to draw on Earth's\r\n * surface beneath the satellite.\r\n */\r\nfunction eciKmToSubSatellite(\r\n Cesium: any,\r\n positionKm: Vector3D,\r\n julianDate: any,\r\n): { latitude: number; longitude: number; altitude: number } | null {\r\n const fixed = eciKmToFixedCartesian3(Cesium, positionKm, julianDate);\r\n if (!fixed) return null;\r\n const carto = Cesium.Cartographic.fromCartesian(fixed);\r\n if (!carto || !Number.isFinite(carto.height)) return null;\r\n return {\r\n latitude: Cesium.Math.toDegrees(carto.latitude),\r\n longitude: Cesium.Math.toDegrees(carto.longitude),\r\n altitude: carto.height / 1000, // m → km\r\n };\r\n}\r\n\r\n// ── Coverage geometry ─────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Circular coverage footprint radius (m) on Earth surface.\r\n *\r\n * Spherical geometry:\r\n * α = arcsin(R·cos(ε) / (R+h)) — nadir angle\r\n * λ = π/2 − ε − α — Earth central angle\r\n * radius = R · λ — arc length on surface\r\n */\r\nfunction autoFootprintRadiusM(altitudeKm: number, minElevDeg: number): number {\r\n if (altitudeKm <= 0) return 0;\r\n const R = EARTH_RADIUS_KM;\r\n const eps = (minElevDeg * Math.PI) / 180;\r\n const sinAlpha = (R * Math.cos(eps)) / (R + altitudeKm);\r\n if (sinAlpha >= 1) return 0;\r\n const alpha = Math.asin(sinAlpha);\r\n const lambda = Math.PI / 2 - eps - alpha;\r\n return R * 1000 * lambda;\r\n}\r\n\r\n/** Radius (m) of the footprint circle for a sensor with the given half-angle. */\r\nfunction halfAngleFootprintRadiusM(\r\n altitudeKm: number,\r\n halfAngleDeg: number,\r\n): number {\r\n return altitudeKm * 1000 * Math.tan((halfAngleDeg * Math.PI) / 180);\r\n}\r\n\r\n// ── Entity helpers ────────────────────────────────────────────────────────────\r\n\r\ntype EntityMap = { current: Map<string, any> };\r\n\r\nfunction removeFromMap(viewer: any, id: string, map: EntityMap): void {\r\n if (map.current.has(id)) {\r\n try {\r\n viewer.entities.removeById(id);\r\n } catch (_) {\r\n /* already gone */\r\n }\r\n map.current.delete(id);\r\n }\r\n}\r\n\r\nfunction makePointGraphics(Cesium: any, color: any, pixelSize = 8) {\r\n return {\r\n pixelSize,\r\n color,\r\n outlineColor: Cesium.Color.WHITE,\r\n outlineWidth: 1,\r\n scaleByDistance: new Cesium.NearFarScalar(1e6, 1.5, 5e7, 0.5),\r\n // Always render on top so satellites hidden behind Earth are still visible\r\n disableDepthTestDistance: Number.POSITIVE_INFINITY,\r\n };\r\n}\r\n\r\nfunction makeLabelOpts(Cesium: any, text: string, fillColor?: any) {\r\n return {\r\n text,\r\n font: \"12px monospace\",\r\n fillColor: fillColor ?? Cesium.Color.WHITE,\r\n outlineColor: Cesium.Color.BLACK,\r\n outlineWidth: 2,\r\n style: Cesium.LabelStyle.FILL_AND_OUTLINE,\r\n pixelOffset: new Cesium.Cartesian2(12, 0),\r\n scaleByDistance: new Cesium.NearFarScalar(1e6, 1.0, 5e7, 0.3),\r\n // Fade out label when camera is very far away\r\n distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 3e7),\r\n disableDepthTestDistance: Number.POSITIVE_INFINITY,\r\n };\r\n}\r\n\r\n/** Add or update a circular ellipse entity on the Earth surface. */\r\nfunction upsertEllipse(\r\n viewer: any,\r\n Cesium: any,\r\n id: string,\r\n position: any,\r\n radiusM: number,\r\n color: any,\r\n map: EntityMap,\r\n): void {\r\n const existing = map.current.get(id);\r\n if (existing) {\r\n existing.position = new Cesium.ConstantPositionProperty(position);\r\n existing.ellipse.semiMajorAxis = new Cesium.ConstantProperty(radiusM);\r\n existing.ellipse.semiMinorAxis = new Cesium.ConstantProperty(radiusM);\r\n } else {\r\n const entity = viewer.entities.add({\r\n id,\r\n position,\r\n ellipse: {\r\n semiMajorAxis: radiusM,\r\n semiMinorAxis: radiusM,\r\n material: color.withAlpha(0.08),\r\n outline: true,\r\n outlineColor: color.withAlpha(0.5),\r\n outlineWidth: 1,\r\n height: 0,\r\n },\r\n });\r\n map.current.set(id, entity);\r\n }\r\n}\r\n\r\n/**\r\n * Render (or update in-place) the full coverage geometry for one satellite\r\n * from an explicit `SatelliteCoverage` record supplied by the Zendir SDK.\r\n */\r\nfunction renderExplicitCoverage(\r\n viewer: any,\r\n Cesium: any,\r\n satId: string,\r\n satPos3: Vector3D,\r\n cov: SatelliteCoverage,\r\n julianDate: any,\r\n covMap: EntityMap,\r\n coneMap: EntityMap,\r\n nadirMap: EntityMap,\r\n): void {\r\n const baseColor = cov.color\r\n ? Cesium.Color.fromCssColorString(cov.color)\r\n : Cesium.Color.CYAN;\r\n const opacity = cov.opacity ?? 0.2;\r\n\r\n const resolvedPos = cov.satellitePosition ?? satPos3;\r\n const satCesPos = eciKmToFixedCartesian3(Cesium, resolvedPos, julianDate);\r\n const subSat = eciKmToSubSatellite(Cesium, resolvedPos, julianDate);\r\n const latitude = subSat?.latitude ?? NaN;\r\n const longitude = subSat?.longitude ?? NaN;\r\n const altitude = subSat?.altitude ?? NaN;\r\n\r\n const nadirLat = cov.nadirPoint?.latitude ?? latitude;\r\n const nadirLon = cov.nadirPoint?.longitude ?? longitude;\r\n\r\n // ── Footprint polygon or circular ellipse ──────────────────────────────────\r\n const covId = `cov-${satId}`;\r\n if (\r\n cov.showFootprint !== false &&\r\n cov.footprintPolygon &&\r\n cov.footprintPolygon.length >= 3\r\n ) {\r\n const positions = Cesium.Cartesian3.fromDegreesArray(\r\n cov.footprintPolygon.flatMap((pt) => [pt.longitude, pt.latitude]),\r\n );\r\n const existing = covMap.current.get(covId);\r\n if (existing) {\r\n existing.polygon.hierarchy = new Cesium.ConstantProperty(\r\n new Cesium.PolygonHierarchy(positions),\r\n );\r\n } else {\r\n const entity = viewer.entities.add({\r\n id: covId,\r\n polygon: {\r\n hierarchy: new Cesium.PolygonHierarchy(positions),\r\n material: baseColor.withAlpha(opacity),\r\n outline: true,\r\n outlineColor: baseColor.withAlpha(opacity * 3),\r\n height: 0,\r\n clampToGround: true,\r\n },\r\n });\r\n covMap.current.set(covId, entity);\r\n }\r\n } else if (\r\n cov.showFootprint !== false &&\r\n cov.halfAngle != null &&\r\n Number.isFinite(altitude) &&\r\n altitude > 0\r\n ) {\r\n const subSatPos = Cesium.Cartesian3.fromDegrees(nadirLon, nadirLat, 0);\r\n const radius = halfAngleFootprintRadiusM(altitude, cov.halfAngle);\r\n upsertEllipse(viewer, Cesium, covId, subSatPos, radius, baseColor, covMap);\r\n } else {\r\n removeFromMap(viewer, covId, covMap);\r\n }\r\n\r\n // ── Sensor cone (satellite → footprint, rendered as a Cesium cylinder) ─────\r\n const coneId = `cone-${satId}`;\r\n if (cov.showCone && satCesPos && Number.isFinite(altitude) && altitude > 0) {\r\n const radius =\r\n cov.halfAngle != null\r\n ? halfAngleFootprintRadiusM(altitude, cov.halfAngle)\r\n : autoFootprintRadiusM(altitude, 5);\r\n const coneLength = altitude * 1000; // km → m\r\n // Cylinder centered at mid-altitude between satellite and ground point\r\n const midPos = Cesium.Cartesian3.fromDegrees(\r\n nadirLon,\r\n nadirLat,\r\n coneLength / 2,\r\n );\r\n const existing = coneMap.current.get(coneId);\r\n if (existing) {\r\n existing.position = new Cesium.ConstantPositionProperty(midPos);\r\n existing.cylinder.length = new Cesium.ConstantProperty(coneLength);\r\n existing.cylinder.bottomRadius = new Cesium.ConstantProperty(radius);\r\n } else {\r\n const entity = viewer.entities.add({\r\n id: coneId,\r\n position: midPos,\r\n cylinder: {\r\n length: coneLength,\r\n topRadius: 0,\r\n bottomRadius: radius,\r\n material: baseColor.withAlpha(0.06),\r\n outline: true,\r\n outlineColor: baseColor.withAlpha(0.25),\r\n numberOfVerticalLines: 12,\r\n },\r\n });\r\n coneMap.current.set(coneId, entity);\r\n }\r\n } else {\r\n removeFromMap(viewer, coneId, coneMap);\r\n }\r\n\r\n // ── Nadir line (satellite → sub-satellite point) ───────────────────────────\r\n const nadirId = `nadir-${satId}`;\r\n if (cov.showNadirLine && satCesPos) {\r\n const groundPos = Cesium.Cartesian3.fromDegrees(nadirLon, nadirLat, 0);\r\n const existing = nadirMap.current.get(nadirId);\r\n if (existing) {\r\n existing.polyline.positions = new Cesium.ConstantProperty([\r\n satCesPos,\r\n groundPos,\r\n ]);\r\n } else {\r\n const entity = viewer.entities.add({\r\n id: nadirId,\r\n polyline: {\r\n positions: [satCesPos, groundPos],\r\n width: 1,\r\n material: new Cesium.PolylineDashMaterialProperty({\r\n color: baseColor.withAlpha(0.5),\r\n dashLength: 8,\r\n }),\r\n },\r\n });\r\n nadirMap.current.set(nadirId, entity);\r\n }\r\n } else {\r\n removeFromMap(viewer, nadirId, nadirMap);\r\n }\r\n}\r\n\r\n// ── Component ─────────────────────────────────────────────────────────────────\r\n\r\nexport interface ZenSpace3DCesiumProps extends ZenSpace3DProps {\r\n /** Cesium module from dynamic import('cesium') */\r\n cesium: any;\r\n}\r\n\r\nexport const ZenSpace3DCesium = forwardRef<\r\n ZenSpace3DHandle,\r\n ZenSpace3DCesiumProps\r\n>(function ZenSpace3DCesium(\r\n {\r\n satellites = [],\r\n stations = [],\r\n groundStations = [],\r\n customObjects = [],\r\n orbitPaths = [],\r\n satelliteCoverages = [],\r\n view = \"earth-orbit\",\r\n height = 600,\r\n callbacks,\r\n showTools = false,\r\n layers,\r\n visibility,\r\n referenceTime,\r\n cesium: Cesium,\r\n },\r\n ref,\r\n) {\r\n const containerRef = useRef<HTMLDivElement>(null);\r\n const viewerRef = useRef<any>(null);\r\n\r\n // Stash callbacks in a ref so click/hover handlers never close over a stale\r\n // function reference — and never make the mount effect depend on them.\r\n const callbacksRef = useRef(callbacks);\r\n useEffect(() => {\r\n callbacksRef.current = callbacks;\r\n }, [callbacks]);\r\n\r\n // ── Entity maps for in-place reconciliation ────────────────────────────────\r\n // Each map: entity-id → Cesium entity object (only live entities live here).\r\n const satMapRef = useRef<Map<string, any>>(new Map()); // spacecraft + station dots\r\n const gsMapRef = useRef<Map<string, any>>(new Map()); // ground station dots\r\n const covMapRef = useRef<Map<string, any>>(new Map()); // coverage ellipses / polygons\r\n const coneMapRef = useRef<Map<string, any>>(new Map()); // sensor cones\r\n const nadirMapRef = useRef<Map<string, any>>(new Map()); // nadir lines\r\n const orbitMapRef = useRef<Map<string, any>>(new Map()); // orbit polylines\r\n\r\n // ── Latest ECI positions for satellite callback-properties ─────────────────\r\n // Satellites use CallbackPositionProperty so Cesium re-derives ECEF from the\r\n // stored ECI position on every rendered frame (using the current JulianDate).\r\n // This makes satellites track Earth's rotation smoothly between API poll ticks,\r\n // fixing the \"satellite frozen while Earth rotates\" bug.\r\n const eciPositionsRef = useRef<Map<string, Vector3D>>(new Map());\r\n\r\n // ── Layer visibility ──────────────────────────────────────────────────────\r\n // Render-flag layers (always-on by default for orbits; off by default for\r\n // labels/coverage). Derive primitives so the sync effect deps are cheap.\r\n const showLabels = layers?.labels === true; // off by default\r\n const showCoverage = layers?.coverage === true; // off by default\r\n const showOrbits = layers?.orbits !== false; // on by default\r\n\r\n // Object-group visibility — toggle whole categories of markers off without\r\n // unmounting them. Defaults to \"everything visible\" so existing consumers\r\n // who never set `visibility` are unaffected.\r\n const showSpacecraft = visibility?.spacecraft !== false;\r\n const showGroundStations = visibility?.groundStations !== false;\r\n const showCelestialBodies = visibility?.celestialBodies !== false;\r\n\r\n // ── Imperative handle ──────────────────────────────────────────────────────\r\n const focusOn = useCallback(\r\n (objectId: string, options?: { zoom?: number; animate?: boolean }) => {\r\n const viewer = viewerRef.current;\r\n if (!viewer) return;\r\n const entity = viewer.entities.getById(objectId);\r\n if (entity) {\r\n viewer.zoomTo(entity, {\r\n duration: options?.animate !== false ? 1.5 : 0,\r\n });\r\n }\r\n },\r\n [],\r\n );\r\n\r\n const setView = useCallback(\r\n (mode: ViewMode) => {\r\n const viewer = viewerRef.current;\r\n if (!viewer) return;\r\n if (mode === \"earth-orbit\") {\r\n viewer.camera.flyTo({\r\n destination: Cesium.Cartesian3.fromDegrees(0, 0, 2e7),\r\n });\r\n } else if (mode === \"solar-system\") {\r\n viewer.camera.flyTo({\r\n destination: Cesium.Cartesian3.fromDegrees(0, 0, 5e7),\r\n });\r\n }\r\n },\r\n [Cesium],\r\n );\r\n\r\n useImperativeHandle(\r\n ref,\r\n (): ZenSpace3DHandle => ({\r\n focusOn,\r\n flyTo: focusOn,\r\n setView,\r\n setTime: () => {},\r\n setTimeScale: () => {},\r\n togglePlay: () => {},\r\n setLayerVisibility: () => {},\r\n getCameraState: () => ({}) as any,\r\n setCameraState: () => {},\r\n exportImage: () => \"\",\r\n exportScene: () => new Blob(),\r\n getVisibleObjects: () => [],\r\n findObjects: () => [],\r\n addObject: () => {},\r\n removeObject: () => {},\r\n updateObject: () => {},\r\n measureDistance: () => 0,\r\n }),\r\n [focusOn, setView],\r\n );\r\n\r\n // ── Mount effect: create Cesium Viewer + event handlers ONCE ──────────────\r\n //\r\n // Does NOT depend on the data arrays (satellites, orbitPaths, etc.) or on\r\n // `callbacks`. Both produce fresh references on every parent render / poll\r\n // tick. Including them would tear down + rebuild Cesium every 2 s, leaking\r\n // WebGL contexts until Chrome hits its hard cap (~16).\r\n useEffect(() => {\r\n if (!containerRef.current || !Cesium) return;\r\n\r\n // Clear maps in case this is a hot-reload remount\r\n satMapRef.current.clear();\r\n gsMapRef.current.clear();\r\n covMapRef.current.clear();\r\n coneMapRef.current.clear();\r\n nadirMapRef.current.clear();\r\n orbitMapRef.current.clear();\r\n eciPositionsRef.current.clear();\r\n\r\n const viewer = new Cesium.Viewer(containerRef.current, {\r\n baseLayerPicker: false,\r\n fullscreenButton: false,\r\n vrButton: false,\r\n geocoder: false,\r\n homeButton: !!showTools,\r\n sceneModePicker: false,\r\n timeline: false,\r\n navigationHelpButton: !!showTools,\r\n animation: false,\r\n scene3DOnly: true,\r\n useDefaultRenderLoop: true,\r\n requestRenderMode: false,\r\n });\r\n\r\n viewer.scene.globe.enableLighting = true;\r\n viewer.scene.backgroundColor = Cesium.Color.BLACK;\r\n\r\n // Surface the actual exception text on render-loop crashes. Without\r\n // this listener Cesium only shows \"An error occurred while rendering.\"\r\n // and the stack trace is buried.\r\n viewer.scene.renderError.addEventListener((scene: any, err: unknown) => {\r\n // eslint-disable-next-line no-console\r\n console.error(\"[ZenSpace3D] Cesium render error:\", err);\r\n });\r\n\r\n // Advance the clock at 1× real time so Cesium continuously evaluates\r\n // ECI→ECEF for the correct current moment. Without this, the JulianDate\r\n // passed to satellite CallbackPositionProperty callbacks would be frozen\r\n // at the viewer-creation instant and satellites would drift relative to\r\n // Earth as wall-clock time advanced.\r\n viewer.clock.currentTime = Cesium.JulianDate.now();\r\n viewer.clock.multiplier = 1;\r\n viewer.clock.shouldAnimate = true;\r\n\r\n viewerRef.current = viewer;\r\n\r\n const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);\r\n\r\n // Click → select\r\n handler.setInputAction((movement: any) => {\r\n const picked = viewer.scene.pick(movement.position);\r\n if (\r\n Cesium.defined(picked) &&\r\n picked.id &&\r\n callbacksRef.current?.onSelect\r\n ) {\r\n callbacksRef.current.onSelect({\r\n id: picked.id.id,\r\n name: picked.id.name,\r\n category: \"satellite\",\r\n position: { x: 0, y: 0, z: 0 },\r\n visualRadius: 0,\r\n });\r\n }\r\n }, Cesium.ScreenSpaceEventType.LEFT_CLICK);\r\n\r\n // Hover → onHover callback\r\n handler.setInputAction((movement: any) => {\r\n const picked = viewer.scene.pick(movement.endPosition);\r\n if (Cesium.defined(picked) && picked.id) {\r\n callbacksRef.current?.onHover?.({\r\n id: picked.id.id,\r\n name: picked.id.name,\r\n category: \"satellite\",\r\n position: { x: 0, y: 0, z: 0 },\r\n visualRadius: 0,\r\n });\r\n } else {\r\n callbacksRef.current?.onHover?.(null);\r\n }\r\n }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);\r\n\r\n return () => {\r\n handler.destroy();\r\n satMapRef.current.clear();\r\n gsMapRef.current.clear();\r\n covMapRef.current.clear();\r\n coneMapRef.current.clear();\r\n nadirMapRef.current.clear();\r\n orbitMapRef.current.clear();\r\n eciPositionsRef.current.clear();\r\n viewer.destroy();\r\n viewerRef.current = null;\r\n };\r\n }, [Cesium, showTools]);\r\n\r\n // ── Sync effect: reconcile entities on data / layer changes ───────────────\r\n //\r\n // For existing satellites (same ID, new position): entity.position is\r\n // updated in-place via ConstantPositionProperty — no remove/add required.\r\n // Only genuinely new or removed satellites trigger entity creation/deletion.\r\n useEffect(() => {\r\n const viewer = viewerRef.current;\r\n if (!viewer || !Cesium) return;\r\n\r\n // JulianDate used for static ECI→ECEF snapshots in coverage/orbit\r\n // geometry. Satellite dot positions use CallbackPositionProperty and\r\n // re-evaluate this conversion per-frame from the viewer's live clock,\r\n // so they don't depend on `jd` here.\r\n const jd = referenceTime\r\n ? Cesium.JulianDate.fromDate(referenceTime)\r\n : Cesium.JulianDate.now();\r\n\r\n // ── Satellites + stations ────────────────────────────────────────────\r\n // When `visibility.spacecraft === false` the consumer wants the whole\r\n // group hidden — feed the loop an empty list so the cleanup pass below\r\n // sweeps existing entities without us having to duplicate the removal\r\n // logic here.\r\n const seenSats = new Set<string>();\r\n const renderedSats = showSpacecraft ? [...satellites, ...stations] : [];\r\n\r\n renderedSats.forEach((obj) => {\r\n const id = obj.id;\r\n const pos3 = (obj as any).position as Vector3D;\r\n if (!pos3) return;\r\n // Guard: reject placeholder/zero positions that haven't been seeded yet.\r\n if (\r\n !Number.isFinite(pos3.x) ||\r\n !Number.isFinite(pos3.y) ||\r\n !Number.isFinite(pos3.z)\r\n )\r\n return;\r\n\r\n // Store the latest ECI km position so the per-frame callback can\r\n // convert it to ECEF at the actual render time rather than at the\r\n // (stale) poll-tick time.\r\n eciPositionsRef.current.set(id, pos3);\r\n seenSats.add(id);\r\n\r\n const name = (obj as any).name ?? id;\r\n const existing = satMapRef.current.get(id);\r\n\r\n if (existing) {\r\n // ECI position already written to eciPositionsRef above — the\r\n // CallbackPositionProperty will pick it up on the next frame.\r\n // Only sync label visibility here.\r\n if (existing.label) {\r\n existing.label.show = new Cesium.ConstantProperty(showLabels);\r\n } else if (showLabels) {\r\n existing.label = new Cesium.LabelGraphics(\r\n makeLabelOpts(Cesium, name),\r\n );\r\n }\r\n } else {\r\n // Use CallbackPositionProperty so Cesium re-evaluates the ECI→ECEF\r\n // transform on every rendered frame using the viewer's current\r\n // JulianDate. This keeps the satellite locked to the correct point\r\n // on the rotating Earth rather than drifting in inertial space.\r\n const capturedId = id; // capture for closure\r\n // The callback runs inside Cesium's render loop on every frame.\r\n // ANY exception here halts rendering (\"An error occurred while\r\n // rendering. Rendering has stopped.\"), so we defensively catch\r\n // and return undefined — Cesium treats that as \"position\r\n // unavailable\" and skips the entity for that frame.\r\n const posProperty = new Cesium.CallbackPositionProperty(\r\n (time: any) => {\r\n try {\r\n const eciKm = eciPositionsRef.current.get(capturedId);\r\n if (!eciKm) return undefined;\r\n const fixed = eciKmToFixedCartesian3(Cesium, eciKm, time);\r\n if (\r\n !fixed ||\r\n !Number.isFinite(fixed.x) ||\r\n !Number.isFinite(fixed.y) ||\r\n !Number.isFinite(fixed.z)\r\n ) {\r\n return undefined;\r\n }\r\n return fixed;\r\n } catch {\r\n return undefined;\r\n }\r\n },\r\n false, // not constant — must be re-evaluated every frame\r\n Cesium.ReferenceFrame.FIXED,\r\n );\r\n const entity = viewer.entities.add({\r\n id,\r\n name,\r\n position: posProperty,\r\n point: makePointGraphics(Cesium, Cesium.Color.CYAN),\r\n label: showLabels\r\n ? new Cesium.LabelGraphics(makeLabelOpts(Cesium, name))\r\n : undefined,\r\n });\r\n satMapRef.current.set(id, entity);\r\n }\r\n\r\n // ── Coverage ─────────────────────────────────────────────────────────\r\n const explicitCov = (satelliteCoverages ?? []).find(\r\n (c) => c.satelliteId === id,\r\n );\r\n if (explicitCov) {\r\n renderExplicitCoverage(\r\n viewer,\r\n Cesium,\r\n id,\r\n pos3,\r\n explicitCov,\r\n jd,\r\n covMapRef,\r\n coneMapRef,\r\n nadirMapRef,\r\n );\r\n } else if (showCoverage) {\r\n // Auto coverage: circular ellipse computed from sub-satellite point\r\n const subSat = eciKmToSubSatellite(Cesium, pos3, jd);\r\n if (subSat && subSat.altitude > 0) {\r\n const subSatPos = Cesium.Cartesian3.fromDegrees(\r\n subSat.longitude,\r\n subSat.latitude,\r\n 0,\r\n );\r\n const radius = autoFootprintRadiusM(subSat.altitude, 5);\r\n upsertEllipse(\r\n viewer,\r\n Cesium,\r\n `cov-${id}`,\r\n subSatPos,\r\n radius,\r\n Cesium.Color.CYAN,\r\n covMapRef,\r\n );\r\n }\r\n } else {\r\n removeFromMap(viewer, `cov-${id}`, covMapRef);\r\n removeFromMap(viewer, `cone-${id}`, coneMapRef);\r\n removeFromMap(viewer, `nadir-${id}`, nadirMapRef);\r\n }\r\n });\r\n\r\n // Remove stale satellite entities (+ associated coverage geometry + ECI position).\r\n satMapRef.current.forEach((_, id) => {\r\n if (!seenSats.has(id)) {\r\n eciPositionsRef.current.delete(id);\r\n removeFromMap(viewer, id, satMapRef);\r\n removeFromMap(viewer, `cov-${id}`, covMapRef);\r\n removeFromMap(viewer, `cone-${id}`, coneMapRef);\r\n removeFromMap(viewer, `nadir-${id}`, nadirMapRef);\r\n }\r\n });\r\n\r\n // ── Custom objects (planets, celestial bodies, etc.) ────────────────\r\n // Earth is already rendered by Cesium's globe — skip it by name.\r\n // Other celestial bodies (Moon, etc.) are rendered as orange dots.\r\n // When `visibility.celestialBodies === false`, feed an empty list so\r\n // the cleanup pass removes any existing custom-object entities.\r\n const seenCustom = new Set<string>();\r\n const renderedCustom = showCelestialBodies ? (customObjects ?? []) : [];\r\n renderedCustom.forEach((obj) => {\r\n if (/^earth$/i.test(obj.name ?? \"\")) return; // skip Earth\r\n const id = obj.id;\r\n const cesPos = eciKmToFixedCartesian3(Cesium, obj.position, jd);\r\n if (!cesPos) return;\r\n seenCustom.add(id);\r\n\r\n const existing = satMapRef.current.get(`custom-${id}`);\r\n if (existing) {\r\n existing.position = new Cesium.ConstantPositionProperty(cesPos);\r\n if (existing.label) {\r\n existing.label.show = new Cesium.ConstantProperty(showLabels);\r\n } else if (showLabels) {\r\n existing.label = new Cesium.LabelGraphics(\r\n makeLabelOpts(Cesium, obj.name ?? id, Cesium.Color.ORANGE),\r\n );\r\n }\r\n } else {\r\n const entity = viewer.entities.add({\r\n id: `custom-${id}`,\r\n name: obj.name,\r\n position: cesPos,\r\n point: makePointGraphics(Cesium, Cesium.Color.ORANGE, 10),\r\n label: showLabels\r\n ? new Cesium.LabelGraphics(\r\n makeLabelOpts(Cesium, obj.name ?? id, Cesium.Color.ORANGE),\r\n )\r\n : undefined,\r\n });\r\n satMapRef.current.set(`custom-${id}`, entity);\r\n }\r\n });\r\n // Remove stale custom objects\r\n satMapRef.current.forEach((_, key) => {\r\n if (key.startsWith(\"custom-\") && !seenCustom.has(key.slice(7))) {\r\n removeFromMap(viewer, key, satMapRef);\r\n }\r\n });\r\n\r\n // ── Ground stations ──────────────────────────────────────────────────\r\n // When `visibility.groundStations === false`, feed an empty list so\r\n // the cleanup pass removes any existing ground-station entities.\r\n const seenGs = new Set<string>();\r\n const renderedGs = showGroundStations ? (groundStations ?? []) : [];\r\n renderedGs.forEach((gs) => {\r\n if (!Number.isFinite(gs.longitude) || !Number.isFinite(gs.latitude))\r\n return;\r\n seenGs.add(gs.id);\r\n const heightM = Number.isFinite((gs as any).elevation)\r\n ? (gs as any).elevation\r\n : 0;\r\n const gsPos = Cesium.Cartesian3.fromDegrees(\r\n gs.longitude,\r\n gs.latitude,\r\n heightM,\r\n );\r\n const name = gs.name ?? gs.id;\r\n\r\n const existing = gsMapRef.current.get(gs.id);\r\n if (existing) {\r\n existing.position = new Cesium.ConstantPositionProperty(gsPos);\r\n if (existing.label) {\r\n existing.label.show = new Cesium.ConstantProperty(showLabels);\r\n } else if (showLabels) {\r\n existing.label = new Cesium.LabelGraphics(\r\n makeLabelOpts(Cesium, name, Cesium.Color.YELLOW),\r\n );\r\n }\r\n } else {\r\n const entity = viewer.entities.add({\r\n id: gs.id,\r\n name,\r\n position: gsPos,\r\n point: makePointGraphics(Cesium, Cesium.Color.YELLOW, 6),\r\n label: showLabels\r\n ? new Cesium.LabelGraphics(\r\n makeLabelOpts(Cesium, name, Cesium.Color.YELLOW),\r\n )\r\n : undefined,\r\n });\r\n gsMapRef.current.set(gs.id, entity);\r\n }\r\n });\r\n\r\n gsMapRef.current.forEach((_, id) => {\r\n if (!seenGs.has(id)) removeFromMap(viewer, id, gsMapRef);\r\n });\r\n\r\n // ── Orbit paths ──────────────────────────────────────────────────────\r\n if (showOrbits) {\r\n const seenOrbits = new Set<string>();\r\n\r\n (orbitPaths ?? []).forEach((path) => {\r\n const orbitId = `orbit-${path.objectId}`;\r\n seenOrbits.add(orbitId);\r\n const positions = path.points\r\n .map((p) => eciKmToFixedCartesian3(Cesium, p, jd))\r\n .filter((p): p is NonNullable<typeof p> => p != null);\r\n if (positions.length < 2) return;\r\n\r\n const orbitColor = path.color\r\n ? Cesium.Color.fromCssColorString(path.color).withAlpha(0.65)\r\n : Cesium.Color.YELLOW.withAlpha(0.55);\r\n\r\n const existing = orbitMapRef.current.get(orbitId);\r\n if (existing) {\r\n existing.polyline.positions = new Cesium.ConstantProperty(positions);\r\n } else {\r\n const entity = viewer.entities.add({\r\n id: orbitId,\r\n name: path.objectId,\r\n polyline: {\r\n positions,\r\n width: 1.5,\r\n material: new Cesium.PolylineDashMaterialProperty({\r\n color: orbitColor,\r\n dashLength: 16,\r\n }),\r\n },\r\n });\r\n orbitMapRef.current.set(orbitId, entity);\r\n }\r\n });\r\n\r\n orbitMapRef.current.forEach((_, id) => {\r\n if (!seenOrbits.has(id)) removeFromMap(viewer, id, orbitMapRef);\r\n });\r\n } else {\r\n // Orbits turned off: remove all\r\n orbitMapRef.current.forEach((_, id) =>\r\n removeFromMap(viewer, id, orbitMapRef),\r\n );\r\n }\r\n }, [\r\n Cesium,\r\n satellites,\r\n stations,\r\n customObjects,\r\n groundStations,\r\n orbitPaths,\r\n satelliteCoverages,\r\n showLabels,\r\n showCoverage,\r\n showOrbits,\r\n showSpacecraft,\r\n showGroundStations,\r\n showCelestialBodies,\r\n referenceTime,\r\n ]);\r\n\r\n // ── React to `view` prop changes ──────────────────────────────────────────\r\n // The imperative `setView()` handle is exposed for ad-hoc fly-tos. When\r\n // the consumer drives the camera declaratively via the `view` prop\r\n // instead, fly to the matching default destination on each change.\r\n useEffect(() => {\r\n setView(view);\r\n // setView itself depends on Cesium (constant once mounted), so it's\r\n // stable enough to omit from the dep array — but include `view` itself.\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [view]);\r\n\r\n const h = typeof height === \"number\" ? `${height}px` : height;\r\n return (\r\n <div\r\n ref={containerRef}\r\n className=\"zendir-zenspace3d-cesium\"\r\n style={{ width: \"100%\", height: h, minHeight: 200 }}\r\n />\r\n );\r\n});\r\n\r\nexport default ZenSpace3DCesium;\r\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ZenSpace3DShaders.js","names":[],"sources":["../../../src/react/3d/ZenSpace3DShaders.ts"],"sourcesContent":["/**\n * @zendir/ui - ZenSpace3D Shaders\n *\n * Advanced GLSL shaders for atmosphere, glow effects, coverage visualization,\n * and other space rendering effects.\n */\n\n// =============================================================================\n// Atmosphere Shader (Enhanced from original)\n// =============================================================================\n\nexport const atmosphereVertexShader = `\nvarying vec3 vNormal;\nvarying vec3 vPosition;\nvarying float vIntensity;\n\nvoid main() {\n vNormal = normalize(normalMatrix * normal);\n vPosition = position;\n \n // Calculate intensity based on view angle\n vec4 worldPosition = modelMatrix * vec4(position, 1.0);\n vec3 viewDirection = normalize(cameraPosition - worldPosition.xyz);\n vIntensity = pow(0.65 - dot(vNormal, viewDirection), 2.0);\n \n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n}\n`;\n\nexport const atmosphereFragmentShader = `\nvarying vec3 vNormal;\nvarying vec3 vPosition;\nvarying float vIntensity;\n\nuniform vec3 glowColor;\nuniform float glowIntensity;\n\nvoid main() {\n // Fresnel-based glow\n float intensity = vIntensity * glowIntensity;\n \n // Add subtle color variation\n vec3 color = glowColor * intensity;\n \n // Fade at edges for smoother blending\n float alpha = intensity * 0.8;\n \n gl_FragColor = vec4(color, alpha);\n}\n`;\n\n// =============================================================================\n// Enhanced Stars Shader (Twinkling with nebula)\n// =============================================================================\n\nexport const starsVertexShader = `\nattribute float opacity;\nattribute float size;\nattribute vec3 customColor;\n\nvarying float vOpacity;\nvarying vec3 vColor;\n\nuniform float time;\nuniform float pointSize;\n\nvoid main() {\n vOpacity = opacity;\n vColor = customColor;\n \n // Twinkle effect\n float twinkle = sin(time * (0.5 + opacity * 2.0) + opacity * 100.0) * 0.5 + 0.5;\n vOpacity = opacity * (0.6 + twinkle * 0.4);\n \n vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);\n \n // Size attenuation\n float sizeMultiplier = size > 0.0 ? size : 1.0;\n gl_PointSize = pointSize * sizeMultiplier * (300.0 / -mvPosition.z);\n gl_PointSize = clamp(gl_PointSize, 0.5, 8.0);\n \n gl_Position = projectionMatrix * mvPosition;\n}\n`;\n\nexport const starsFragmentShader = `\nvarying float vOpacity;\nvarying vec3 vColor;\n\nvoid main() {\n // Circular point with soft edge\n vec2 center = gl_PointCoord - vec2(0.5);\n float dist = length(center);\n \n if (dist > 0.5) discard;\n \n // Soft glow falloff\n float alpha = smoothstep(0.5, 0.0, dist) * vOpacity;\n \n // Apply color\n vec3 color = vColor.r > 0.0 ? vColor : vec3(1.0, 1.0, 1.0);\n \n gl_FragColor = vec4(color, alpha);\n}\n`;\n\n// =============================================================================\n// Coverage Heatmap Shader\n// =============================================================================\n\nexport const coverageVertexShader = `\nvarying vec2 vUv;\nvarying vec3 vNormal;\nvarying vec3 vPosition;\n\nvoid main() {\n vUv = uv;\n vNormal = normalize(normalMatrix * normal);\n vPosition = (modelMatrix * vec4(position, 1.0)).xyz;\n \n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n}\n`;\n\nexport const coverageFragmentShader = `\nuniform sampler2D coverageMap;\nuniform vec3 lowColor;\nuniform vec3 highColor;\nuniform float opacity;\nuniform float time;\n\nvarying vec2 vUv;\nvarying vec3 vNormal;\nvarying vec3 vPosition;\n\nvoid main() {\n // Sample coverage value\n float coverage = texture2D(coverageMap, vUv).r;\n \n // Animated pulse for active areas\n float pulse = sin(time * 2.0) * 0.1 + 0.9;\n coverage *= coverage > 0.5 ? pulse : 1.0;\n \n // Color gradient\n vec3 color = mix(lowColor, highColor, coverage);\n \n // Fresnel edge highlight\n vec3 viewDir = normalize(cameraPosition - vPosition);\n float fresnel = pow(1.0 - dot(vNormal, viewDir), 2.0);\n color += vec3(0.1, 0.3, 0.5) * fresnel * coverage;\n \n // Hexagon pattern overlay (subtle)\n float hexPattern = fract(vUv.x * 20.0 + vUv.y * 10.0);\n color *= 0.95 + hexPattern * 0.05;\n \n gl_FragColor = vec4(color, coverage * opacity);\n}\n`;\n\n// =============================================================================\n// Visibility Cone Shader\n// =============================================================================\n\nexport const visibilityConeVertexShader = `\nvarying vec3 vPosition;\nvarying vec3 vNormal;\nvarying float vDistFromApex;\n\nuniform vec3 apexPosition;\n\nvoid main() {\n vPosition = (modelMatrix * vec4(position, 1.0)).xyz;\n vNormal = normalize(normalMatrix * normal);\n vDistFromApex = distance(position, apexPosition);\n \n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n}\n`;\n\nexport const visibilityConeFragmentShader = `\nuniform vec3 coneColor;\nuniform float coneOpacity;\nuniform float time;\n\nvarying vec3 vPosition;\nvarying vec3 vNormal;\nvarying float vDistFromApex;\n\nvoid main() {\n // Fresnel effect for edge visibility\n vec3 viewDir = normalize(cameraPosition - vPosition);\n float fresnel = pow(1.0 - abs(dot(vNormal, viewDir)), 2.0);\n \n // Animated scan line effect\n float scan = fract(vDistFromApex * 0.001 - time * 0.5);\n scan = smoothstep(0.0, 0.1, scan) * smoothstep(0.2, 0.1, scan);\n \n // Distance-based opacity (fade towards apex)\n float distFade = smoothstep(0.0, 500.0, vDistFromApex);\n \n // Combine effects\n float alpha = (fresnel * 0.3 + 0.1 + scan * 0.2) * coneOpacity * distFade;\n \n gl_FragColor = vec4(coneColor, alpha);\n}\n`;\n\n// =============================================================================\n// Orbit Line Shader\n// =============================================================================\n\nexport const orbitLineVertexShader = `\nattribute float lineDistance;\nvarying float vLineDistance;\nvarying float vOpacity;\n\nuniform float totalLength;\nuniform float time;\nuniform float dashScale;\n\nvoid main() {\n vLineDistance = lineDistance;\n \n // Animated dash effect\n float animOffset = mod(time * 0.5, 1.0) * totalLength;\n float dashPos = mod(lineDistance + animOffset, dashScale);\n vOpacity = smoothstep(0.0, dashScale * 0.3, dashPos) * \n smoothstep(dashScale, dashScale * 0.7, dashPos);\n \n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n}\n`;\n\nexport const orbitLineFragmentShader = `\nuniform vec3 lineColor;\nuniform float lineOpacity;\n\nvarying float vLineDistance;\nvarying float vOpacity;\n\nvoid main() {\n gl_FragColor = vec4(lineColor, lineOpacity * vOpacity);\n}\n`;\n\n// =============================================================================\n// Satellite/Object Glow Shader\n// =============================================================================\n\nexport const objectGlowVertexShader = `\nvarying vec3 vNormal;\nvarying vec3 vViewPosition;\n\nvoid main() {\n vNormal = normalize(normalMatrix * normal);\n vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);\n vViewPosition = -mvPosition.xyz;\n \n gl_Position = projectionMatrix * mvPosition;\n}\n`;\n\nexport const objectGlowFragmentShader = `\nuniform vec3 glowColor;\nuniform float glowIntensity;\nuniform float glowPower;\n\nvarying vec3 vNormal;\nvarying vec3 vViewPosition;\n\nvoid main() {\n vec3 viewDir = normalize(vViewPosition);\n float fresnel = pow(1.0 - abs(dot(vNormal, viewDir)), glowPower);\n \n vec3 color = glowColor * glowIntensity * fresnel;\n float alpha = fresnel * glowIntensity;\n \n gl_FragColor = vec4(color, alpha);\n}\n`;\n\n// =============================================================================\n// Selection Ring Shader\n// =============================================================================\n\nexport const selectionRingVertexShader = `\nvarying vec2 vUv;\n\nvoid main() {\n vUv = uv;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n}\n`;\n\nexport const selectionRingFragmentShader = `\nuniform vec3 ringColor;\nuniform float time;\nuniform float ringWidth;\nuniform float ringRadius;\n\nvarying vec2 vUv;\n\nvoid main() {\n vec2 center = vUv - vec2(0.5);\n float dist = length(center);\n \n // Ring shape\n float ring = smoothstep(ringRadius - ringWidth, ringRadius, dist) *\n smoothstep(ringRadius + ringWidth, ringRadius, dist);\n \n // Animated rotation\n float angle = atan(center.y, center.x);\n float rotatingDash = sin(angle * 8.0 - time * 3.0) * 0.5 + 0.5;\n \n // Pulse effect\n float pulse = sin(time * 2.0) * 0.2 + 0.8;\n \n float alpha = ring * rotatingDash * pulse;\n \n gl_FragColor = vec4(ringColor, alpha);\n}\n`;\n\n// =============================================================================\n// Terminator (Day/Night) Shader\n// =============================================================================\n\nexport const terminatorVertexShader = `\nvarying vec3 vWorldPosition;\nvarying vec3 vNormal;\n\nvoid main() {\n vWorldPosition = (modelMatrix * vec4(position, 1.0)).xyz;\n vNormal = normalize(normalMatrix * normal);\n \n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n}\n`;\n\nexport const terminatorFragmentShader = `\nuniform vec3 sunDirection;\nuniform sampler2D dayTexture;\nuniform sampler2D nightTexture;\n\nvarying vec3 vWorldPosition;\nvarying vec3 vNormal;\n\nvoid main() {\n // Calculate sun illumination\n float sunDot = dot(vNormal, sunDirection);\n \n // Smooth transition at terminator\n float dayFactor = smoothstep(-0.1, 0.2, sunDot);\n \n // Sample textures (simplified - using world position for UV)\n vec2 uv = vec2(\n atan(vWorldPosition.z, vWorldPosition.x) / (2.0 * 3.14159) + 0.5,\n asin(vWorldPosition.y / length(vWorldPosition)) / 3.14159 + 0.5\n );\n \n vec4 dayColor = texture2D(dayTexture, uv);\n vec4 nightColor = texture2D(nightTexture, uv);\n \n // Blend day and night\n vec4 color = mix(nightColor, dayColor, dayFactor);\n \n // Add city lights glow on night side\n float nightGlow = (1.0 - dayFactor) * nightColor.r * 0.5;\n color.rgb += vec3(1.0, 0.9, 0.7) * nightGlow;\n \n gl_FragColor = color;\n}\n`;\n\n// =============================================================================\n// Grid Shader (Lat/Lon lines)\n// =============================================================================\n\nexport const gridVertexShader = `\nvarying vec3 vPosition;\n\nvoid main() {\n vPosition = position;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n}\n`;\n\nexport const gridFragmentShader = `\nuniform vec3 gridColor;\nuniform float gridOpacity;\nuniform float gridSpacing;\nuniform float lineWidth;\n\nvarying vec3 vPosition;\n\nvoid main() {\n // Convert position to lat/lon\n float lat = asin(vPosition.y / length(vPosition)) * 57.2958; // rad to deg\n float lon = atan(vPosition.z, vPosition.x) * 57.2958;\n \n // Create grid lines\n float latLine = abs(mod(lat + 90.0, gridSpacing) - gridSpacing * 0.5);\n float lonLine = abs(mod(lon + 180.0, gridSpacing) - gridSpacing * 0.5);\n \n float line = min(latLine, lonLine);\n float alpha = smoothstep(lineWidth, 0.0, line) * gridOpacity;\n \n // Highlight equator and prime meridian\n float equator = smoothstep(1.0, 0.0, abs(lat));\n float primeMeridian = smoothstep(1.0, 0.0, abs(lon));\n alpha += (equator + primeMeridian) * 0.3;\n \n gl_FragColor = vec4(gridColor, alpha);\n}\n`;\n\n// =============================================================================\n// Planet Ring Shader (Saturn-like)\n// =============================================================================\n\nexport const planetRingVertexShader = `\nvarying vec2 vUv;\nvarying vec3 vWorldPosition;\n\nvoid main() {\n vUv = uv;\n vWorldPosition = (modelMatrix * vec4(position, 1.0)).xyz;\n \n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n}\n`;\n\nexport const planetRingFragmentShader = `\nuniform vec3 ringColor;\nuniform float innerRadius;\nuniform float outerRadius;\nuniform float ringOpacity;\nuniform vec3 sunDirection;\n\nvarying vec2 vUv;\nvarying vec3 vWorldPosition;\n\nvoid main() {\n vec2 center = vUv - vec2(0.5);\n float dist = length(center) * 2.0;\n \n // Ring bands\n float normalizedDist = (dist - innerRadius) / (outerRadius - innerRadius);\n \n if (normalizedDist < 0.0 || normalizedDist > 1.0) discard;\n \n // Create ring bands pattern\n float bands = sin(normalizedDist * 50.0) * 0.3 + 0.7;\n float gaps = step(0.3, fract(normalizedDist * 5.0));\n \n // Simple shadow from planet\n float shadow = smoothstep(0.3, 0.5, dist);\n \n float alpha = bands * gaps * ringOpacity * shadow;\n \n gl_FragColor = vec4(ringColor, alpha);\n}\n`;\n\n// =============================================================================\n// Debris Field Shader (for instanced rendering)\n// =============================================================================\n\nexport const debrisVertexShader = `\nattribute vec3 instancePosition;\nattribute float instanceSize;\nattribute vec3 instanceColor;\n\nvarying vec3 vColor;\nvarying float vSize;\n\nuniform float time;\n\nvoid main() {\n vColor = instanceColor;\n vSize = instanceSize;\n \n // Apply instance transform\n vec3 transformed = position * instanceSize + instancePosition;\n \n // Slight rotation animation\n float angle = time * 0.5 + instancePosition.x * 0.01;\n mat2 rot = mat2(cos(angle), -sin(angle), sin(angle), cos(angle));\n transformed.xz = rot * transformed.xz;\n \n gl_Position = projectionMatrix * modelViewMatrix * vec4(transformed, 1.0);\n}\n`;\n\nexport const debrisFragmentShader = `\nvarying vec3 vColor;\nvarying float vSize;\n\nvoid main() {\n gl_FragColor = vec4(vColor, 1.0);\n}\n`;\n\n// =============================================================================\n// Shader Uniforms Helpers\n// =============================================================================\n\nexport interface AtmosphereUniforms {\n glowColor: { value: [number, number, number] };\n glowIntensity: { value: number };\n}\n\nexport interface StarsUniforms {\n time: { value: number };\n pointSize: { value: number };\n}\n\nexport interface CoverageUniforms {\n coverageMap: { value: unknown };\n lowColor: { value: [number, number, number] };\n highColor: { value: [number, number, number] };\n opacity: { value: number };\n time: { value: number };\n}\n\nexport interface VisibilityConeUniforms {\n coneColor: { value: [number, number, number] };\n coneOpacity: { value: number };\n time: { value: number };\n apexPosition: { value: [number, number, number] };\n}\n\nexport function createAtmosphereUniforms(\n color: [number, number, number] = [0.3, 0.6, 1.0],\n intensity: number = 1.5,\n): AtmosphereUniforms {\n return {\n glowColor: { value: color },\n glowIntensity: { value: intensity },\n };\n}\n\nexport function createStarsUniforms(pointSize: number = 2.0): StarsUniforms {\n return {\n time: { value: 0 },\n pointSize: { value: pointSize },\n };\n}\n\nexport function createVisibilityConeUniforms(\n color: [number, number, number] = [0.2, 0.8, 0.9],\n opacity: number = 0.3,\n): VisibilityConeUniforms {\n return {\n coneColor: { value: color },\n coneOpacity: { value: opacity },\n time: { value: 0 },\n apexPosition: { value: [0, 0, 0] },\n };\n}\n"],"mappings":";;;;;;;AAWA,IAAa,yBAAyB;;;;;;;;;;;;;;;;;AAkBtC,IAAa,2BAA2B;;;;;;;;;;;;;;;;;;;;;AA0BxC,IAAa,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BjC,IAAa,sBAAsB;;;;;;;;;;;;;;;;;;;;AAyBnC,IAAa,uBAAuB;;;;;;;;;;;;;AAcpC,IAAa,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuCtC,IAAa,6BAA6B;;;;;;;;;;;;;;;AAgB1C,IAAa,+BAA+B;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgC5C,IAAa,wBAAwB;;;;;;;;;;;;;;;;;;;;;AAsBrC,IAAa,0BAA0B;;;;;;;;;;;AAgBvC,IAAa,yBAAyB;;;;;;;;;;;;AAatC,IAAa,2BAA2B;;;;;;;;;;;;;;;;;;AAuBxC,IAAa,4BAA4B;;;;;;;;AASzC,IAAa,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiC3C,IAAa,yBAAyB;;;;;;;;;;;AAYtC,IAAa,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuCxC,IAAa,mBAAmB;;;;;;;;AAShC,IAAa,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiClC,IAAa,yBAAyB;;;;;;;;;;;AAYtC,IAAa,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCxC,IAAa,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;AA0BlC,IAAa,uBAAuB;;;;;;;;AAsCpC,SAAgB,yBACd,QAAkC;CAAC;CAAK;CAAK;AAAG,GAChD,YAAoB,KACA;CACpB,OAAO;EACL,WAAW,EAAE,OAAO,MAAM;EAC1B,eAAe,EAAE,OAAO,UAAU;CACpC;AACF;AAEA,SAAgB,oBAAoB,YAAoB,GAAoB;CAC1E,OAAO;EACL,MAAM,EAAE,OAAO,EAAE;EACjB,WAAW,EAAE,OAAO,UAAU;CAChC;AACF;AAEA,SAAgB,6BACd,QAAkC;CAAC;CAAK;CAAK;AAAG,GAChD,UAAkB,IACM;CACxB,OAAO;EACL,WAAW,EAAE,OAAO,MAAM;EAC1B,aAAa,EAAE,OAAO,QAAQ;EAC9B,MAAM,EAAE,OAAO,EAAE;EACjB,cAAc,EAAE,OAAO;GAAC;GAAG;GAAG;EAAC,EAAE;CACnC;AACF"}
1
+ {"version":3,"file":"ZenSpace3DShaders.js","names":[],"sources":["../../../src/react/3d/ZenSpace3DShaders.ts"],"sourcesContent":["/**\r\n * @zendir/ui - ZenSpace3D Shaders\r\n *\r\n * Advanced GLSL shaders for atmosphere, glow effects, coverage visualization,\r\n * and other space rendering effects.\r\n */\r\n\r\n// =============================================================================\r\n// Atmosphere Shader (Enhanced from original)\r\n// =============================================================================\r\n\r\nexport const atmosphereVertexShader = `\r\nvarying vec3 vNormal;\r\nvarying vec3 vPosition;\r\nvarying float vIntensity;\r\n\r\nvoid main() {\r\n vNormal = normalize(normalMatrix * normal);\r\n vPosition = position;\r\n \r\n // Calculate intensity based on view angle\r\n vec4 worldPosition = modelMatrix * vec4(position, 1.0);\r\n vec3 viewDirection = normalize(cameraPosition - worldPosition.xyz);\r\n vIntensity = pow(0.65 - dot(vNormal, viewDirection), 2.0);\r\n \r\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\r\n}\r\n`;\r\n\r\nexport const atmosphereFragmentShader = `\r\nvarying vec3 vNormal;\r\nvarying vec3 vPosition;\r\nvarying float vIntensity;\r\n\r\nuniform vec3 glowColor;\r\nuniform float glowIntensity;\r\n\r\nvoid main() {\r\n // Fresnel-based glow\r\n float intensity = vIntensity * glowIntensity;\r\n \r\n // Add subtle color variation\r\n vec3 color = glowColor * intensity;\r\n \r\n // Fade at edges for smoother blending\r\n float alpha = intensity * 0.8;\r\n \r\n gl_FragColor = vec4(color, alpha);\r\n}\r\n`;\r\n\r\n// =============================================================================\r\n// Enhanced Stars Shader (Twinkling with nebula)\r\n// =============================================================================\r\n\r\nexport const starsVertexShader = `\r\nattribute float opacity;\r\nattribute float size;\r\nattribute vec3 customColor;\r\n\r\nvarying float vOpacity;\r\nvarying vec3 vColor;\r\n\r\nuniform float time;\r\nuniform float pointSize;\r\n\r\nvoid main() {\r\n vOpacity = opacity;\r\n vColor = customColor;\r\n \r\n // Twinkle effect\r\n float twinkle = sin(time * (0.5 + opacity * 2.0) + opacity * 100.0) * 0.5 + 0.5;\r\n vOpacity = opacity * (0.6 + twinkle * 0.4);\r\n \r\n vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);\r\n \r\n // Size attenuation\r\n float sizeMultiplier = size > 0.0 ? size : 1.0;\r\n gl_PointSize = pointSize * sizeMultiplier * (300.0 / -mvPosition.z);\r\n gl_PointSize = clamp(gl_PointSize, 0.5, 8.0);\r\n \r\n gl_Position = projectionMatrix * mvPosition;\r\n}\r\n`;\r\n\r\nexport const starsFragmentShader = `\r\nvarying float vOpacity;\r\nvarying vec3 vColor;\r\n\r\nvoid main() {\r\n // Circular point with soft edge\r\n vec2 center = gl_PointCoord - vec2(0.5);\r\n float dist = length(center);\r\n \r\n if (dist > 0.5) discard;\r\n \r\n // Soft glow falloff\r\n float alpha = smoothstep(0.5, 0.0, dist) * vOpacity;\r\n \r\n // Apply color\r\n vec3 color = vColor.r > 0.0 ? vColor : vec3(1.0, 1.0, 1.0);\r\n \r\n gl_FragColor = vec4(color, alpha);\r\n}\r\n`;\r\n\r\n// =============================================================================\r\n// Coverage Heatmap Shader\r\n// =============================================================================\r\n\r\nexport const coverageVertexShader = `\r\nvarying vec2 vUv;\r\nvarying vec3 vNormal;\r\nvarying vec3 vPosition;\r\n\r\nvoid main() {\r\n vUv = uv;\r\n vNormal = normalize(normalMatrix * normal);\r\n vPosition = (modelMatrix * vec4(position, 1.0)).xyz;\r\n \r\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\r\n}\r\n`;\r\n\r\nexport const coverageFragmentShader = `\r\nuniform sampler2D coverageMap;\r\nuniform vec3 lowColor;\r\nuniform vec3 highColor;\r\nuniform float opacity;\r\nuniform float time;\r\n\r\nvarying vec2 vUv;\r\nvarying vec3 vNormal;\r\nvarying vec3 vPosition;\r\n\r\nvoid main() {\r\n // Sample coverage value\r\n float coverage = texture2D(coverageMap, vUv).r;\r\n \r\n // Animated pulse for active areas\r\n float pulse = sin(time * 2.0) * 0.1 + 0.9;\r\n coverage *= coverage > 0.5 ? pulse : 1.0;\r\n \r\n // Color gradient\r\n vec3 color = mix(lowColor, highColor, coverage);\r\n \r\n // Fresnel edge highlight\r\n vec3 viewDir = normalize(cameraPosition - vPosition);\r\n float fresnel = pow(1.0 - dot(vNormal, viewDir), 2.0);\r\n color += vec3(0.1, 0.3, 0.5) * fresnel * coverage;\r\n \r\n // Hexagon pattern overlay (subtle)\r\n float hexPattern = fract(vUv.x * 20.0 + vUv.y * 10.0);\r\n color *= 0.95 + hexPattern * 0.05;\r\n \r\n gl_FragColor = vec4(color, coverage * opacity);\r\n}\r\n`;\r\n\r\n// =============================================================================\r\n// Visibility Cone Shader\r\n// =============================================================================\r\n\r\nexport const visibilityConeVertexShader = `\r\nvarying vec3 vPosition;\r\nvarying vec3 vNormal;\r\nvarying float vDistFromApex;\r\n\r\nuniform vec3 apexPosition;\r\n\r\nvoid main() {\r\n vPosition = (modelMatrix * vec4(position, 1.0)).xyz;\r\n vNormal = normalize(normalMatrix * normal);\r\n vDistFromApex = distance(position, apexPosition);\r\n \r\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\r\n}\r\n`;\r\n\r\nexport const visibilityConeFragmentShader = `\r\nuniform vec3 coneColor;\r\nuniform float coneOpacity;\r\nuniform float time;\r\n\r\nvarying vec3 vPosition;\r\nvarying vec3 vNormal;\r\nvarying float vDistFromApex;\r\n\r\nvoid main() {\r\n // Fresnel effect for edge visibility\r\n vec3 viewDir = normalize(cameraPosition - vPosition);\r\n float fresnel = pow(1.0 - abs(dot(vNormal, viewDir)), 2.0);\r\n \r\n // Animated scan line effect\r\n float scan = fract(vDistFromApex * 0.001 - time * 0.5);\r\n scan = smoothstep(0.0, 0.1, scan) * smoothstep(0.2, 0.1, scan);\r\n \r\n // Distance-based opacity (fade towards apex)\r\n float distFade = smoothstep(0.0, 500.0, vDistFromApex);\r\n \r\n // Combine effects\r\n float alpha = (fresnel * 0.3 + 0.1 + scan * 0.2) * coneOpacity * distFade;\r\n \r\n gl_FragColor = vec4(coneColor, alpha);\r\n}\r\n`;\r\n\r\n// =============================================================================\r\n// Orbit Line Shader\r\n// =============================================================================\r\n\r\nexport const orbitLineVertexShader = `\r\nattribute float lineDistance;\r\nvarying float vLineDistance;\r\nvarying float vOpacity;\r\n\r\nuniform float totalLength;\r\nuniform float time;\r\nuniform float dashScale;\r\n\r\nvoid main() {\r\n vLineDistance = lineDistance;\r\n \r\n // Animated dash effect\r\n float animOffset = mod(time * 0.5, 1.0) * totalLength;\r\n float dashPos = mod(lineDistance + animOffset, dashScale);\r\n vOpacity = smoothstep(0.0, dashScale * 0.3, dashPos) * \r\n smoothstep(dashScale, dashScale * 0.7, dashPos);\r\n \r\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\r\n}\r\n`;\r\n\r\nexport const orbitLineFragmentShader = `\r\nuniform vec3 lineColor;\r\nuniform float lineOpacity;\r\n\r\nvarying float vLineDistance;\r\nvarying float vOpacity;\r\n\r\nvoid main() {\r\n gl_FragColor = vec4(lineColor, lineOpacity * vOpacity);\r\n}\r\n`;\r\n\r\n// =============================================================================\r\n// Satellite/Object Glow Shader\r\n// =============================================================================\r\n\r\nexport const objectGlowVertexShader = `\r\nvarying vec3 vNormal;\r\nvarying vec3 vViewPosition;\r\n\r\nvoid main() {\r\n vNormal = normalize(normalMatrix * normal);\r\n vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);\r\n vViewPosition = -mvPosition.xyz;\r\n \r\n gl_Position = projectionMatrix * mvPosition;\r\n}\r\n`;\r\n\r\nexport const objectGlowFragmentShader = `\r\nuniform vec3 glowColor;\r\nuniform float glowIntensity;\r\nuniform float glowPower;\r\n\r\nvarying vec3 vNormal;\r\nvarying vec3 vViewPosition;\r\n\r\nvoid main() {\r\n vec3 viewDir = normalize(vViewPosition);\r\n float fresnel = pow(1.0 - abs(dot(vNormal, viewDir)), glowPower);\r\n \r\n vec3 color = glowColor * glowIntensity * fresnel;\r\n float alpha = fresnel * glowIntensity;\r\n \r\n gl_FragColor = vec4(color, alpha);\r\n}\r\n`;\r\n\r\n// =============================================================================\r\n// Selection Ring Shader\r\n// =============================================================================\r\n\r\nexport const selectionRingVertexShader = `\r\nvarying vec2 vUv;\r\n\r\nvoid main() {\r\n vUv = uv;\r\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\r\n}\r\n`;\r\n\r\nexport const selectionRingFragmentShader = `\r\nuniform vec3 ringColor;\r\nuniform float time;\r\nuniform float ringWidth;\r\nuniform float ringRadius;\r\n\r\nvarying vec2 vUv;\r\n\r\nvoid main() {\r\n vec2 center = vUv - vec2(0.5);\r\n float dist = length(center);\r\n \r\n // Ring shape\r\n float ring = smoothstep(ringRadius - ringWidth, ringRadius, dist) *\r\n smoothstep(ringRadius + ringWidth, ringRadius, dist);\r\n \r\n // Animated rotation\r\n float angle = atan(center.y, center.x);\r\n float rotatingDash = sin(angle * 8.0 - time * 3.0) * 0.5 + 0.5;\r\n \r\n // Pulse effect\r\n float pulse = sin(time * 2.0) * 0.2 + 0.8;\r\n \r\n float alpha = ring * rotatingDash * pulse;\r\n \r\n gl_FragColor = vec4(ringColor, alpha);\r\n}\r\n`;\r\n\r\n// =============================================================================\r\n// Terminator (Day/Night) Shader\r\n// =============================================================================\r\n\r\nexport const terminatorVertexShader = `\r\nvarying vec3 vWorldPosition;\r\nvarying vec3 vNormal;\r\n\r\nvoid main() {\r\n vWorldPosition = (modelMatrix * vec4(position, 1.0)).xyz;\r\n vNormal = normalize(normalMatrix * normal);\r\n \r\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\r\n}\r\n`;\r\n\r\nexport const terminatorFragmentShader = `\r\nuniform vec3 sunDirection;\r\nuniform sampler2D dayTexture;\r\nuniform sampler2D nightTexture;\r\n\r\nvarying vec3 vWorldPosition;\r\nvarying vec3 vNormal;\r\n\r\nvoid main() {\r\n // Calculate sun illumination\r\n float sunDot = dot(vNormal, sunDirection);\r\n \r\n // Smooth transition at terminator\r\n float dayFactor = smoothstep(-0.1, 0.2, sunDot);\r\n \r\n // Sample textures (simplified - using world position for UV)\r\n vec2 uv = vec2(\r\n atan(vWorldPosition.z, vWorldPosition.x) / (2.0 * 3.14159) + 0.5,\r\n asin(vWorldPosition.y / length(vWorldPosition)) / 3.14159 + 0.5\r\n );\r\n \r\n vec4 dayColor = texture2D(dayTexture, uv);\r\n vec4 nightColor = texture2D(nightTexture, uv);\r\n \r\n // Blend day and night\r\n vec4 color = mix(nightColor, dayColor, dayFactor);\r\n \r\n // Add city lights glow on night side\r\n float nightGlow = (1.0 - dayFactor) * nightColor.r * 0.5;\r\n color.rgb += vec3(1.0, 0.9, 0.7) * nightGlow;\r\n \r\n gl_FragColor = color;\r\n}\r\n`;\r\n\r\n// =============================================================================\r\n// Grid Shader (Lat/Lon lines)\r\n// =============================================================================\r\n\r\nexport const gridVertexShader = `\r\nvarying vec3 vPosition;\r\n\r\nvoid main() {\r\n vPosition = position;\r\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\r\n}\r\n`;\r\n\r\nexport const gridFragmentShader = `\r\nuniform vec3 gridColor;\r\nuniform float gridOpacity;\r\nuniform float gridSpacing;\r\nuniform float lineWidth;\r\n\r\nvarying vec3 vPosition;\r\n\r\nvoid main() {\r\n // Convert position to lat/lon\r\n float lat = asin(vPosition.y / length(vPosition)) * 57.2958; // rad to deg\r\n float lon = atan(vPosition.z, vPosition.x) * 57.2958;\r\n \r\n // Create grid lines\r\n float latLine = abs(mod(lat + 90.0, gridSpacing) - gridSpacing * 0.5);\r\n float lonLine = abs(mod(lon + 180.0, gridSpacing) - gridSpacing * 0.5);\r\n \r\n float line = min(latLine, lonLine);\r\n float alpha = smoothstep(lineWidth, 0.0, line) * gridOpacity;\r\n \r\n // Highlight equator and prime meridian\r\n float equator = smoothstep(1.0, 0.0, abs(lat));\r\n float primeMeridian = smoothstep(1.0, 0.0, abs(lon));\r\n alpha += (equator + primeMeridian) * 0.3;\r\n \r\n gl_FragColor = vec4(gridColor, alpha);\r\n}\r\n`;\r\n\r\n// =============================================================================\r\n// Planet Ring Shader (Saturn-like)\r\n// =============================================================================\r\n\r\nexport const planetRingVertexShader = `\r\nvarying vec2 vUv;\r\nvarying vec3 vWorldPosition;\r\n\r\nvoid main() {\r\n vUv = uv;\r\n vWorldPosition = (modelMatrix * vec4(position, 1.0)).xyz;\r\n \r\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\r\n}\r\n`;\r\n\r\nexport const planetRingFragmentShader = `\r\nuniform vec3 ringColor;\r\nuniform float innerRadius;\r\nuniform float outerRadius;\r\nuniform float ringOpacity;\r\nuniform vec3 sunDirection;\r\n\r\nvarying vec2 vUv;\r\nvarying vec3 vWorldPosition;\r\n\r\nvoid main() {\r\n vec2 center = vUv - vec2(0.5);\r\n float dist = length(center) * 2.0;\r\n \r\n // Ring bands\r\n float normalizedDist = (dist - innerRadius) / (outerRadius - innerRadius);\r\n \r\n if (normalizedDist < 0.0 || normalizedDist > 1.0) discard;\r\n \r\n // Create ring bands pattern\r\n float bands = sin(normalizedDist * 50.0) * 0.3 + 0.7;\r\n float gaps = step(0.3, fract(normalizedDist * 5.0));\r\n \r\n // Simple shadow from planet\r\n float shadow = smoothstep(0.3, 0.5, dist);\r\n \r\n float alpha = bands * gaps * ringOpacity * shadow;\r\n \r\n gl_FragColor = vec4(ringColor, alpha);\r\n}\r\n`;\r\n\r\n// =============================================================================\r\n// Debris Field Shader (for instanced rendering)\r\n// =============================================================================\r\n\r\nexport const debrisVertexShader = `\r\nattribute vec3 instancePosition;\r\nattribute float instanceSize;\r\nattribute vec3 instanceColor;\r\n\r\nvarying vec3 vColor;\r\nvarying float vSize;\r\n\r\nuniform float time;\r\n\r\nvoid main() {\r\n vColor = instanceColor;\r\n vSize = instanceSize;\r\n \r\n // Apply instance transform\r\n vec3 transformed = position * instanceSize + instancePosition;\r\n \r\n // Slight rotation animation\r\n float angle = time * 0.5 + instancePosition.x * 0.01;\r\n mat2 rot = mat2(cos(angle), -sin(angle), sin(angle), cos(angle));\r\n transformed.xz = rot * transformed.xz;\r\n \r\n gl_Position = projectionMatrix * modelViewMatrix * vec4(transformed, 1.0);\r\n}\r\n`;\r\n\r\nexport const debrisFragmentShader = `\r\nvarying vec3 vColor;\r\nvarying float vSize;\r\n\r\nvoid main() {\r\n gl_FragColor = vec4(vColor, 1.0);\r\n}\r\n`;\r\n\r\n// =============================================================================\r\n// Shader Uniforms Helpers\r\n// =============================================================================\r\n\r\nexport interface AtmosphereUniforms {\r\n glowColor: { value: [number, number, number] };\r\n glowIntensity: { value: number };\r\n}\r\n\r\nexport interface StarsUniforms {\r\n time: { value: number };\r\n pointSize: { value: number };\r\n}\r\n\r\nexport interface CoverageUniforms {\r\n coverageMap: { value: unknown };\r\n lowColor: { value: [number, number, number] };\r\n highColor: { value: [number, number, number] };\r\n opacity: { value: number };\r\n time: { value: number };\r\n}\r\n\r\nexport interface VisibilityConeUniforms {\r\n coneColor: { value: [number, number, number] };\r\n coneOpacity: { value: number };\r\n time: { value: number };\r\n apexPosition: { value: [number, number, number] };\r\n}\r\n\r\nexport function createAtmosphereUniforms(\r\n color: [number, number, number] = [0.3, 0.6, 1.0],\r\n intensity: number = 1.5,\r\n): AtmosphereUniforms {\r\n return {\r\n glowColor: { value: color },\r\n glowIntensity: { value: intensity },\r\n };\r\n}\r\n\r\nexport function createStarsUniforms(pointSize: number = 2.0): StarsUniforms {\r\n return {\r\n time: { value: 0 },\r\n pointSize: { value: pointSize },\r\n };\r\n}\r\n\r\nexport function createVisibilityConeUniforms(\r\n color: [number, number, number] = [0.2, 0.8, 0.9],\r\n opacity: number = 0.3,\r\n): VisibilityConeUniforms {\r\n return {\r\n coneColor: { value: color },\r\n coneOpacity: { value: opacity },\r\n time: { value: 0 },\r\n apexPosition: { value: [0, 0, 0] },\r\n };\r\n}\r\n"],"mappings":";;;;;;;AAWA,IAAa,yBAAyB;;;;;;;;;;;;;;;;;AAkBtC,IAAa,2BAA2B;;;;;;;;;;;;;;;;;;;;;AA0BxC,IAAa,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BjC,IAAa,sBAAsB;;;;;;;;;;;;;;;;;;;;AAyBnC,IAAa,uBAAuB;;;;;;;;;;;;;AAcpC,IAAa,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuCtC,IAAa,6BAA6B;;;;;;;;;;;;;;;AAgB1C,IAAa,+BAA+B;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgC5C,IAAa,wBAAwB;;;;;;;;;;;;;;;;;;;;;AAsBrC,IAAa,0BAA0B;;;;;;;;;;;AAgBvC,IAAa,yBAAyB;;;;;;;;;;;;AAatC,IAAa,2BAA2B;;;;;;;;;;;;;;;;;;AAuBxC,IAAa,4BAA4B;;;;;;;;AASzC,IAAa,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiC3C,IAAa,yBAAyB;;;;;;;;;;;AAYtC,IAAa,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuCxC,IAAa,mBAAmB;;;;;;;;AAShC,IAAa,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiClC,IAAa,yBAAyB;;;;;;;;;;;AAYtC,IAAa,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCxC,IAAa,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;AA0BlC,IAAa,uBAAuB;;;;;;;;AAsCpC,SAAgB,yBACd,QAAkC;CAAC;CAAK;CAAK;AAAG,GAChD,YAAoB,KACA;CACpB,OAAO;EACL,WAAW,EAAE,OAAO,MAAM;EAC1B,eAAe,EAAE,OAAO,UAAU;CACpC;AACF;AAEA,SAAgB,oBAAoB,YAAoB,GAAoB;CAC1E,OAAO;EACL,MAAM,EAAE,OAAO,EAAE;EACjB,WAAW,EAAE,OAAO,UAAU;CAChC;AACF;AAEA,SAAgB,6BACd,QAAkC;CAAC;CAAK;CAAK;AAAG,GAChD,UAAkB,IACM;CACxB,OAAO;EACL,WAAW,EAAE,OAAO,MAAM;EAC1B,aAAa,EAAE,OAAO,QAAQ;EAC9B,MAAM,EAAE,OAAO,EAAE;EACjB,cAAc,EAAE,OAAO;GAAC;GAAG;GAAG;EAAC,EAAE;CACnC;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"ZenSpace3DUtils.js","names":[],"sources":["../../../src/react/3d/ZenSpace3DUtils.ts"],"sourcesContent":["/**\n * @zendir/ui - ZenSpace3D rendering math\n *\n * The minimal helper set needed by the live 3D backends:\n * • Earth + scene scaling constants used by the Three.js path\n * (`ZenSpace3D.tsx`) and the Cesium capture source\n * (`CesiumCaptureSource.ts`)\n * • `latLonAltToCartesian` — lat/lon/alt → render-space Cartesian\n * • `easeOutCubic` — camera-fly-to easing\n *\n * Higher-level orbital math (TLE/SGP4 propagation, GMST, visibility cones,\n * hex-grid coverage) was removed — those weren't consumed by either\n * backend. The Cesium backend uses Cesium's own `Transforms` for\n * ECI→ECEF, and the Three.js backend draws coverage from explicit\n * `SatelliteCoverage` props rather than computing it locally.\n */\n\nimport type { Vector3D } from \"./ZenSpace3DTypes\";\n\n// ── Constants ─────────────────────────────────────────────────────────────────\n\n/** Earth's mean radius in km. */\nexport const EARTH_RADIUS_KM = 6371;\n\n/** Degrees → radians. */\nexport const DEG_TO_RAD = Math.PI / 180;\n\n/** Radians → degrees. */\nexport const RAD_TO_DEG = 180 / Math.PI;\n\n/** Earth radius in scene units (Three.js render space). */\nexport const SCENE_EARTH_RADIUS = 3000;\n\n/** Conversion factor: 1 km → scene units. */\nexport const KM_TO_SCENE = SCENE_EARTH_RADIUS / EARTH_RADIUS_KM;\n\n// ── Coordinate conversion ─────────────────────────────────────────────────────\n\n/**\n * Convert lat/lon/alt to a Cartesian point in scene space.\n *\n * Result is in *render units* (scaled by `KM_TO_SCENE`), suitable for\n * direct use in the Three.js scene graph. For ECI→ECEF on the Cesium\n * backend, use `Cesium.Transforms.computeTemeToPseudoFixedMatrix`.\n *\n * @param lat Latitude in degrees.\n * @param lon Longitude in degrees.\n * @param altKm Altitude above Earth surface in km (default 0).\n * @param earthRadius Scene radius for Earth (default `SCENE_EARTH_RADIUS`).\n */\nexport function latLonAltToCartesian(\n lat: number,\n lon: number,\n altKm: number = 0,\n earthRadius: number = SCENE_EARTH_RADIUS,\n): Vector3D {\n const r = earthRadius + altKm * KM_TO_SCENE;\n const phi = (90 - lat) * DEG_TO_RAD;\n const theta = (lon + 180) * DEG_TO_RAD;\n return {\n x: -r * Math.sin(phi) * Math.cos(theta),\n y: r * Math.cos(phi),\n z: r * Math.sin(phi) * Math.sin(theta),\n };\n}\n\n// ── Easing ────────────────────────────────────────────────────────────────────\n\n/** Cubic ease-out curve, used by camera fly-to animations. */\nexport function easeOutCubic(t: number): number {\n return 1 - Math.pow(1 - t, 3);\n}\n"],"mappings":";;AAsBA,IAAa,kBAAkB;;AAG/B,IAAa,aAAa,KAAK,KAAK;;AAGpC,IAAa,aAAa,MAAM,KAAK;;AAGrC,IAAa,qBAAqB;;AAGlC,IAAa,cAAc,qBAAqB;;;;;;;;;;;;;AAgBhD,SAAgB,qBACd,KACA,KACA,QAAgB,GAChB,cAAsB,oBACZ;CACV,MAAM,IAAI,cAAc,QAAQ;CAChC,MAAM,OAAO,KAAK,OAAO;CACzB,MAAM,SAAS,MAAM,OAAO;CAC5B,OAAO;EACL,GAAG,CAAC,IAAI,KAAK,IAAI,GAAG,IAAI,KAAK,IAAI,KAAK;EACtC,GAAG,IAAI,KAAK,IAAI,GAAG;EACnB,GAAG,IAAI,KAAK,IAAI,GAAG,IAAI,KAAK,IAAI,KAAK;CACvC;AACF;;AAKA,SAAgB,aAAa,GAAmB;CAC9C,OAAO,IAAI,KAAK,IAAI,IAAI,GAAG,CAAC;AAC9B"}
1
+ {"version":3,"file":"ZenSpace3DUtils.js","names":[],"sources":["../../../src/react/3d/ZenSpace3DUtils.ts"],"sourcesContent":["/**\r\n * @zendir/ui - ZenSpace3D rendering math\r\n *\r\n * The minimal helper set needed by the live 3D backends:\r\n * • Earth + scene scaling constants used by the Three.js path\r\n * (`ZenSpace3D.tsx`) and the Cesium capture source\r\n * (`CesiumCaptureSource.ts`)\r\n * • `latLonAltToCartesian` — lat/lon/alt → render-space Cartesian\r\n * • `easeOutCubic` — camera-fly-to easing\r\n *\r\n * Higher-level orbital math (TLE/SGP4 propagation, GMST, visibility cones,\r\n * hex-grid coverage) was removed — those weren't consumed by either\r\n * backend. The Cesium backend uses Cesium's own `Transforms` for\r\n * ECI→ECEF, and the Three.js backend draws coverage from explicit\r\n * `SatelliteCoverage` props rather than computing it locally.\r\n */\r\n\r\nimport type { Vector3D } from \"./ZenSpace3DTypes\";\r\n\r\n// ── Constants ─────────────────────────────────────────────────────────────────\r\n\r\n/** Earth's mean radius in km. */\r\nexport const EARTH_RADIUS_KM = 6371;\r\n\r\n/** Degrees → radians. */\r\nexport const DEG_TO_RAD = Math.PI / 180;\r\n\r\n/** Radians → degrees. */\r\nexport const RAD_TO_DEG = 180 / Math.PI;\r\n\r\n/** Earth radius in scene units (Three.js render space). */\r\nexport const SCENE_EARTH_RADIUS = 3000;\r\n\r\n/** Conversion factor: 1 km → scene units. */\r\nexport const KM_TO_SCENE = SCENE_EARTH_RADIUS / EARTH_RADIUS_KM;\r\n\r\n// ── Coordinate conversion ─────────────────────────────────────────────────────\r\n\r\n/**\r\n * Convert lat/lon/alt to a Cartesian point in scene space.\r\n *\r\n * Result is in *render units* (scaled by `KM_TO_SCENE`), suitable for\r\n * direct use in the Three.js scene graph. For ECI→ECEF on the Cesium\r\n * backend, use `Cesium.Transforms.computeTemeToPseudoFixedMatrix`.\r\n *\r\n * @param lat Latitude in degrees.\r\n * @param lon Longitude in degrees.\r\n * @param altKm Altitude above Earth surface in km (default 0).\r\n * @param earthRadius Scene radius for Earth (default `SCENE_EARTH_RADIUS`).\r\n */\r\nexport function latLonAltToCartesian(\r\n lat: number,\r\n lon: number,\r\n altKm: number = 0,\r\n earthRadius: number = SCENE_EARTH_RADIUS,\r\n): Vector3D {\r\n const r = earthRadius + altKm * KM_TO_SCENE;\r\n const phi = (90 - lat) * DEG_TO_RAD;\r\n const theta = (lon + 180) * DEG_TO_RAD;\r\n return {\r\n x: -r * Math.sin(phi) * Math.cos(theta),\r\n y: r * Math.cos(phi),\r\n z: r * Math.sin(phi) * Math.sin(theta),\r\n };\r\n}\r\n\r\n// ── Easing ────────────────────────────────────────────────────────────────────\r\n\r\n/** Cubic ease-out curve, used by camera fly-to animations. */\r\nexport function easeOutCubic(t: number): number {\r\n return 1 - Math.pow(1 - t, 3);\r\n}\r\n"],"mappings":";;AAsBA,IAAa,kBAAkB;;AAG/B,IAAa,aAAa,KAAK,KAAK;;AAGpC,IAAa,aAAa,MAAM,KAAK;;AAGrC,IAAa,qBAAqB;;AAGlC,IAAa,cAAc,qBAAqB;;;;;;;;;;;;;AAgBhD,SAAgB,qBACd,KACA,KACA,QAAgB,GAChB,cAAsB,oBACZ;CACV,MAAM,IAAI,cAAc,QAAQ;CAChC,MAAM,OAAO,KAAK,OAAO;CACzB,MAAM,SAAS,MAAM,OAAO;CAC5B,OAAO;EACL,GAAG,CAAC,IAAI,KAAK,IAAI,GAAG,IAAI,KAAK,IAAI,KAAK;EACtC,GAAG,IAAI,KAAK,IAAI,GAAG;EACnB,GAAG,IAAI,KAAK,IAAI,GAAG,IAAI,KAAK,IAAI,KAAK;CACvC;AACF;;AAKA,SAAgB,aAAa,GAAmB;CAC9C,OAAO,IAAI,KAAK,IAAI,IAAI,GAAG,CAAC;AAC9B"}
@@ -1 +1 @@
1
- {"version":3,"file":"capturePngAnalysis.js","names":[],"sources":["../../../src/react/3d/capturePngAnalysis.ts"],"sourcesContent":["/**\n * Lightweight PNG analysis for capture QA (brightness / \"all black\" detection).\n * Used by tests and optional runtime checks — keep dependency-free (no pngjs).\n */\n\n/** ITU-R BT.709 luma from RGB */\nexport function rgbToLuma(r: number, g: number, b: number): number {\n return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n}\n\n/**\n * Sample mean luma of PNG bytes by drawing to a small canvas (browser / happy-dom).\n * Returns 0 if decode fails. For very large images, samples a grid (max ~4096 samples).\n */\nexport async function meanLuminanceFromPngBytes(\n png: Uint8Array,\n): Promise<number> {\n if (\n typeof createImageBitmap !== \"function\" ||\n typeof document === \"undefined\"\n ) {\n return 0;\n }\n try {\n const blob = new Blob([png as unknown as BlobPart], { type: \"image/png\" });\n const bmp = await createImageBitmap(blob);\n const w = bmp.width;\n const h = bmp.height;\n const canvas = document.createElement(\"canvas\");\n const step = Math.max(1, Math.floor(Math.sqrt((w * h) / 4096)));\n canvas.width = Math.ceil(w / step);\n canvas.height = Math.ceil(h / step);\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return 0;\n ctx.drawImage(bmp, 0, 0, w, h, 0, 0, canvas.width, canvas.height);\n const { data } = ctx.getImageData(0, 0, canvas.width, canvas.height);\n let sum = 0;\n const n = data.length / 4;\n for (let i = 0; i < data.length; i += 4) {\n sum += rgbToLuma(data[i], data[i + 1], data[i + 2]);\n }\n bmp.close?.();\n return n > 0 ? sum / n : 0;\n } catch {\n return 0;\n }\n}\n\n/**\n * True if the image is uniformly dark (likely failed render / empty GL buffer).\n * Threshold is mean luma on 0–255 scale (default 5 ≈ pure black).\n */\nexport async function isPngMostlyBlack(\n png: Uint8Array,\n threshold = 5,\n): Promise<boolean> {\n const m = await meanLuminanceFromPngBytes(png);\n return m < threshold;\n}\n"],"mappings":";;;;;;AAMA,SAAgB,UAAU,GAAW,GAAW,GAAmB;CACjE,OAAO,QAAS,IAAI,QAAS,IAAI,QAAS;AAC5C;;;;;AAMA,eAAsB,0BACpB,KACiB;CACjB,IACE,OAAO,sBAAsB,cAC7B,OAAO,aAAa,aAEpB,OAAO;CAET,IAAI;EACF,MAAM,OAAO,IAAI,KAAK,CAAC,GAA0B,GAAG,EAAE,MAAM,YAAY,CAAC;EACzE,MAAM,MAAM,MAAM,kBAAkB,IAAI;EACxC,MAAM,IAAI,IAAI;EACd,MAAM,IAAI,IAAI;EACd,MAAM,SAAS,SAAS,cAAc,QAAQ;EAC9C,MAAM,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,KAAM,IAAI,IAAK,IAAI,CAAC,CAAC;EAC9D,OAAO,QAAQ,KAAK,KAAK,IAAI,IAAI;EACjC,OAAO,SAAS,KAAK,KAAK,IAAI,IAAI;EAClC,MAAM,MAAM,OAAO,WAAW,IAAI;EAClC,IAAI,CAAC,KAAK,OAAO;EACjB,IAAI,UAAU,KAAK,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,OAAO,OAAO,OAAO,MAAM;EAChE,MAAM,EAAE,SAAS,IAAI,aAAa,GAAG,GAAG,OAAO,OAAO,OAAO,MAAM;EACnE,IAAI,MAAM;EACV,MAAM,IAAI,KAAK,SAAS;EACxB,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,GACpC,OAAO,UAAU,KAAK,IAAI,KAAK,IAAI,IAAI,KAAK,IAAI,EAAE;EAEpD,IAAI,QAAQ;EACZ,OAAO,IAAI,IAAI,MAAM,IAAI;CAC3B,QAAQ;EACN,OAAO;CACT;AACF;;;;;AAMA,eAAsB,iBACpB,KACA,YAAY,GACM;CAElB,OAAO,MADS,0BAA0B,GAAG,IAClC;AACb"}
1
+ {"version":3,"file":"capturePngAnalysis.js","names":[],"sources":["../../../src/react/3d/capturePngAnalysis.ts"],"sourcesContent":["/**\r\n * Lightweight PNG analysis for capture QA (brightness / \"all black\" detection).\r\n * Used by tests and optional runtime checks — keep dependency-free (no pngjs).\r\n */\r\n\r\n/** ITU-R BT.709 luma from RGB */\r\nexport function rgbToLuma(r: number, g: number, b: number): number {\r\n return 0.2126 * r + 0.7152 * g + 0.0722 * b;\r\n}\r\n\r\n/**\r\n * Sample mean luma of PNG bytes by drawing to a small canvas (browser / happy-dom).\r\n * Returns 0 if decode fails. For very large images, samples a grid (max ~4096 samples).\r\n */\r\nexport async function meanLuminanceFromPngBytes(\r\n png: Uint8Array,\r\n): Promise<number> {\r\n if (\r\n typeof createImageBitmap !== \"function\" ||\r\n typeof document === \"undefined\"\r\n ) {\r\n return 0;\r\n }\r\n try {\r\n const blob = new Blob([png as unknown as BlobPart], { type: \"image/png\" });\r\n const bmp = await createImageBitmap(blob);\r\n const w = bmp.width;\r\n const h = bmp.height;\r\n const canvas = document.createElement(\"canvas\");\r\n const step = Math.max(1, Math.floor(Math.sqrt((w * h) / 4096)));\r\n canvas.width = Math.ceil(w / step);\r\n canvas.height = Math.ceil(h / step);\r\n const ctx = canvas.getContext(\"2d\");\r\n if (!ctx) return 0;\r\n ctx.drawImage(bmp, 0, 0, w, h, 0, 0, canvas.width, canvas.height);\r\n const { data } = ctx.getImageData(0, 0, canvas.width, canvas.height);\r\n let sum = 0;\r\n const n = data.length / 4;\r\n for (let i = 0; i < data.length; i += 4) {\r\n sum += rgbToLuma(data[i], data[i + 1], data[i + 2]);\r\n }\r\n bmp.close?.();\r\n return n > 0 ? sum / n : 0;\r\n } catch {\r\n return 0;\r\n }\r\n}\r\n\r\n/**\r\n * True if the image is uniformly dark (likely failed render / empty GL buffer).\r\n * Threshold is mean luma on 0–255 scale (default 5 ≈ pure black).\r\n */\r\nexport async function isPngMostlyBlack(\r\n png: Uint8Array,\r\n threshold = 5,\r\n): Promise<boolean> {\r\n const m = await meanLuminanceFromPngBytes(png);\r\n return m < threshold;\r\n}\r\n"],"mappings":";;;;;;AAMA,SAAgB,UAAU,GAAW,GAAW,GAAmB;CACjE,OAAO,QAAS,IAAI,QAAS,IAAI,QAAS;AAC5C;;;;;AAMA,eAAsB,0BACpB,KACiB;CACjB,IACE,OAAO,sBAAsB,cAC7B,OAAO,aAAa,aAEpB,OAAO;CAET,IAAI;EACF,MAAM,OAAO,IAAI,KAAK,CAAC,GAA0B,GAAG,EAAE,MAAM,YAAY,CAAC;EACzE,MAAM,MAAM,MAAM,kBAAkB,IAAI;EACxC,MAAM,IAAI,IAAI;EACd,MAAM,IAAI,IAAI;EACd,MAAM,SAAS,SAAS,cAAc,QAAQ;EAC9C,MAAM,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,KAAM,IAAI,IAAK,IAAI,CAAC,CAAC;EAC9D,OAAO,QAAQ,KAAK,KAAK,IAAI,IAAI;EACjC,OAAO,SAAS,KAAK,KAAK,IAAI,IAAI;EAClC,MAAM,MAAM,OAAO,WAAW,IAAI;EAClC,IAAI,CAAC,KAAK,OAAO;EACjB,IAAI,UAAU,KAAK,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,OAAO,OAAO,OAAO,MAAM;EAChE,MAAM,EAAE,SAAS,IAAI,aAAa,GAAG,GAAG,OAAO,OAAO,OAAO,MAAM;EACnE,IAAI,MAAM;EACV,MAAM,IAAI,KAAK,SAAS;EACxB,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,GACpC,OAAO,UAAU,KAAK,IAAI,KAAK,IAAI,IAAI,KAAK,IAAI,EAAE;EAEpD,IAAI,QAAQ;EACZ,OAAO,IAAI,IAAI,MAAM,IAAI;CAC3B,QAAQ;EACN,OAAO;CACT;AACF;;;;;AAMA,eAAsB,iBACpB,KACA,YAAY,GACM;CAElB,OAAO,MADS,0BAA0B,GAAG,IAClC;AACb"}
@@ -1 +1 @@
1
- {"version":3,"file":"threeLoader.js","names":[],"sources":["../../../src/react/3d/threeLoader.ts"],"sourcesContent":["/**\n * Shared Three.js loader\n *\n * Ensures only one instance of Three.js is loaded across all 3D components.\n * This prevents the \"Multiple instances of Three.js being imported\" warning.\n */\n\nexport type ThreeModule = typeof import(\"three\");\nexport type OrbitControlsType =\n typeof import(\"three/examples/jsm/controls/OrbitControls.js\").OrbitControls;\n\ninterface ThreeLoaderResult {\n THREE: ThreeModule;\n OrbitControls: OrbitControlsType;\n}\n\n// Cached promise to ensure single load\nlet loadPromise: Promise<ThreeLoaderResult> | null = null;\n\n/**\n * Load Three.js and OrbitControls\n * Returns cached instance if already loaded\n */\nexport async function loadThree(): Promise<ThreeLoaderResult> {\n if (loadPromise) {\n return loadPromise;\n }\n\n loadPromise = (async () => {\n const [THREE, { OrbitControls }] = await Promise.all([\n import(\"three\"),\n import(\"three/examples/jsm/controls/OrbitControls.js\"),\n ]);\n\n return { THREE, OrbitControls };\n })();\n\n return loadPromise;\n}\n\n/**\n * Check if Three.js is already loaded\n */\nexport function isThreeLoaded(): boolean {\n return loadPromise !== null;\n}\n"],"mappings":";AAiBA,IAAI,cAAiD;;;;;AAMrD,eAAsB,YAAwC;CAC5D,IAAI,aACF,OAAO;CAGT,eAAe,YAAY;EACzB,MAAM,CAAC,OAAO,EAAE,mBAAmB,MAAM,QAAQ,IAAI,CACnD,OAAO,UACP,OAAO,+CACT,CAAC;EAED,OAAO;GAAE;GAAO;EAAc;CAChC,EAAA,CAAG;CAEH,OAAO;AACT;;;;AAKA,SAAgB,gBAAyB;CACvC,OAAO,gBAAgB;AACzB"}
1
+ {"version":3,"file":"threeLoader.js","names":[],"sources":["../../../src/react/3d/threeLoader.ts"],"sourcesContent":["/**\r\n * Shared Three.js loader\r\n *\r\n * Ensures only one instance of Three.js is loaded across all 3D components.\r\n * This prevents the \"Multiple instances of Three.js being imported\" warning.\r\n */\r\n\r\nexport type ThreeModule = typeof import(\"three\");\r\nexport type OrbitControlsType =\r\n typeof import(\"three/examples/jsm/controls/OrbitControls.js\").OrbitControls;\r\n\r\ninterface ThreeLoaderResult {\r\n THREE: ThreeModule;\r\n OrbitControls: OrbitControlsType;\r\n}\r\n\r\n// Cached promise to ensure single load\r\nlet loadPromise: Promise<ThreeLoaderResult> | null = null;\r\n\r\n/**\r\n * Load Three.js and OrbitControls\r\n * Returns cached instance if already loaded\r\n */\r\nexport async function loadThree(): Promise<ThreeLoaderResult> {\r\n if (loadPromise) {\r\n return loadPromise;\r\n }\r\n\r\n loadPromise = (async () => {\r\n const [THREE, { OrbitControls }] = await Promise.all([\r\n import(\"three\"),\r\n import(\"three/examples/jsm/controls/OrbitControls.js\"),\r\n ]);\r\n\r\n return { THREE, OrbitControls };\r\n })();\r\n\r\n return loadPromise;\r\n}\r\n\r\n/**\r\n * Check if Three.js is already loaded\r\n */\r\nexport function isThreeLoaded(): boolean {\r\n return loadPromise !== null;\r\n}\r\n"],"mappings":";AAiBA,IAAI,cAAiD;;;;;AAMrD,eAAsB,YAAwC;CAC5D,IAAI,aACF,OAAO;CAGT,eAAe,YAAY;EACzB,MAAM,CAAC,OAAO,EAAE,mBAAmB,MAAM,QAAQ,IAAI,CACnD,OAAO,UACP,OAAO,+CACT,CAAC;EAED,OAAO;GAAE;GAAO;EAAc;CAChC,EAAA,CAAG;CAEH,OAAO;AACT;;;;AAKA,SAAgB,gBAAyB;CACvC,OAAO,gBAAgB;AACzB"}
@@ -1 +1 @@
1
- {"version":3,"file":"ClassificationBanner.js","names":[],"sources":["../../../src/react/astro/ClassificationBanner.tsx"],"sourcesContent":["/**\n * @zendir/ui - ClassificationBanner Component (Pure React)\n *\n * Security classification banner following Astro UX Design System.\n * Required for U.S. Space Force applications to display security classification.\n *\n * Features:\n * - Theme-integrated via useTheme()\n * - Official classification colors per DoD standards\n * - Top/bottom positioning\n * - Accessible (screen reader announces classification)\n */\n\nimport React, { memo } from \"react\";\nimport { useTheme } from \"../theme\";\nimport { classNames } from \"../utils\";\n\nexport type ClassificationLevel =\n | \"unclassified\"\n | \"cui\"\n | \"controlled\"\n | \"confidential\"\n | \"secret\"\n | \"top-secret\"\n | \"top-secret-sci\";\n\nexport interface ClassificationBannerProps {\n /** Classification level */\n classification: ClassificationLevel;\n /** Custom label (overrides default text) */\n label?: string;\n /** Position of the banner */\n position?: \"top\" | \"bottom\";\n /** Custom className */\n className?: string;\n}\n\n// Official DoD classification colors\nconst CLASSIFICATION_STYLES: Record<\n ClassificationLevel,\n { bg: string; text: string; label: string }\n> = {\n unclassified: {\n bg: \"#007a33\",\n text: \"#ffffff\",\n label: \"UNCLASSIFIED\",\n },\n cui: {\n bg: \"#502b85\",\n text: \"#ffffff\",\n label: \"CUI\",\n },\n controlled: {\n bg: \"#502b85\",\n text: \"#ffffff\",\n label: \"CONTROLLED\",\n },\n confidential: {\n bg: \"#0033a0\",\n text: \"#ffffff\",\n label: \"CONFIDENTIAL\",\n },\n secret: {\n bg: \"#c8102e\",\n text: \"#ffffff\",\n label: \"SECRET\",\n },\n \"top-secret\": {\n bg: \"#ff8c00\",\n text: \"#000000\",\n label: \"TOP SECRET\",\n },\n \"top-secret-sci\": {\n bg: \"#fce83a\",\n text: \"#000000\",\n label: \"TOP SECRET//SCI\",\n },\n};\n\n/**\n * ClassificationBanner - Pure React classification banner\n *\n * Displays security classification level. Required for U.S. Space Force applications.\n *\n * @example\n * ```tsx\n * <ClassificationBanner classification=\"unclassified\" />\n * <ClassificationBanner classification=\"secret\" position=\"bottom\" />\n * ```\n */\nexport const ClassificationBanner = memo(function ClassificationBanner({\n classification,\n label,\n position = \"top\",\n className = \"\",\n}: ClassificationBannerProps): React.ReactElement {\n const { tokens } = useTheme();\n const style =\n CLASSIFICATION_STYLES[classification] ?? CLASSIFICATION_STYLES.unclassified;\n const displayLabel = label ?? style.label;\n\n return (\n <div\n className={classNames(\"zendir-classification-banner\", className)}\n role=\"banner\"\n aria-label={`Security classification: ${displayLabel}`}\n style={{\n position: \"fixed\",\n left: 0,\n right: 0,\n [position]: 0,\n zIndex: 9999,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n height: 20,\n backgroundColor: style.bg,\n color: style.text,\n fontSize: tokens.typography.fontSize.xs,\n fontWeight: tokens.typography.fontWeight.bold,\n letterSpacing: tokens.typography.letterSpacing.wide,\n textTransform: \"uppercase\",\n fontFamily: tokens.typography.fontFamily.primary,\n userSelect: \"none\",\n }}\n >\n {displayLabel}\n </div>\n );\n});\n"],"mappings":";;;;;;;;;;;;;;;;;AAsCA,IAAM,wBAGF;CACF,cAAc;EACZ,IAAI;EACJ,MAAM;EACN,OAAO;CACT;CACA,KAAK;EACH,IAAI;EACJ,MAAM;EACN,OAAO;CACT;CACA,YAAY;EACV,IAAI;EACJ,MAAM;EACN,OAAO;CACT;CACA,cAAc;EACZ,IAAI;EACJ,MAAM;EACN,OAAO;CACT;CACA,QAAQ;EACN,IAAI;EACJ,MAAM;EACN,OAAO;CACT;CACA,cAAc;EACZ,IAAI;EACJ,MAAM;EACN,OAAO;CACT;CACA,kBAAkB;EAChB,IAAI;EACJ,MAAM;EACN,OAAO;CACT;AACF;;;;;;;;;;;;AAaA,IAAa,uBAAuB,KAAK,SAAS,qBAAqB,EACrE,gBACA,OACA,WAAW,OACX,YAAY,MACoC;CAChD,MAAM,EAAE,WAAW,SAAS;CAC5B,MAAM,QACJ,sBAAsB,mBAAmB,sBAAsB;CACjE,MAAM,eAAe,SAAS,MAAM;CAEpC,OACE,oBAAC,OAAD;EACE,WAAW,WAAW,gCAAgC,SAAS;EAC/D,MAAK;EACL,cAAY,4BAA4B;EACxC,OAAO;GACL,UAAU;GACV,MAAM;GACN,OAAO;IACN,WAAW;GACZ,QAAQ;GACR,SAAS;GACT,YAAY;GACZ,gBAAgB;GAChB,QAAQ;GACR,iBAAiB,MAAM;GACvB,OAAO,MAAM;GACb,UAAU,OAAO,WAAW,SAAS;GACrC,YAAY,OAAO,WAAW,WAAW;GACzC,eAAe,OAAO,WAAW,cAAc;GAC/C,eAAe;GACf,YAAY,OAAO,WAAW,WAAW;GACzC,YAAY;EACd;YAEC;CACE,CAAA;AAET,CAAC"}
1
+ {"version":3,"file":"ClassificationBanner.js","names":[],"sources":["../../../src/react/astro/ClassificationBanner.tsx"],"sourcesContent":["/**\r\n * @zendir/ui - ClassificationBanner Component (Pure React)\r\n *\r\n * Security classification banner following Astro UX Design System.\r\n * Required for U.S. Space Force applications to display security classification.\r\n *\r\n * Features:\r\n * - Theme-integrated via useTheme()\r\n * - Official classification colors per DoD standards\r\n * - Top/bottom positioning\r\n * - Accessible (screen reader announces classification)\r\n */\r\n\r\nimport React, { memo } from \"react\";\r\nimport { useTheme } from \"../theme\";\r\nimport { classNames } from \"../utils\";\r\n\r\nexport type ClassificationLevel =\r\n | \"unclassified\"\r\n | \"cui\"\r\n | \"controlled\"\r\n | \"confidential\"\r\n | \"secret\"\r\n | \"top-secret\"\r\n | \"top-secret-sci\";\r\n\r\nexport interface ClassificationBannerProps {\r\n /** Classification level */\r\n classification: ClassificationLevel;\r\n /** Custom label (overrides default text) */\r\n label?: string;\r\n /** Position of the banner */\r\n position?: \"top\" | \"bottom\";\r\n /** Custom className */\r\n className?: string;\r\n}\r\n\r\n// Official DoD classification colors\r\nconst CLASSIFICATION_STYLES: Record<\r\n ClassificationLevel,\r\n { bg: string; text: string; label: string }\r\n> = {\r\n unclassified: {\r\n bg: \"#007a33\",\r\n text: \"#ffffff\",\r\n label: \"UNCLASSIFIED\",\r\n },\r\n cui: {\r\n bg: \"#502b85\",\r\n text: \"#ffffff\",\r\n label: \"CUI\",\r\n },\r\n controlled: {\r\n bg: \"#502b85\",\r\n text: \"#ffffff\",\r\n label: \"CONTROLLED\",\r\n },\r\n confidential: {\r\n bg: \"#0033a0\",\r\n text: \"#ffffff\",\r\n label: \"CONFIDENTIAL\",\r\n },\r\n secret: {\r\n bg: \"#c8102e\",\r\n text: \"#ffffff\",\r\n label: \"SECRET\",\r\n },\r\n \"top-secret\": {\r\n bg: \"#ff8c00\",\r\n text: \"#000000\",\r\n label: \"TOP SECRET\",\r\n },\r\n \"top-secret-sci\": {\r\n bg: \"#fce83a\",\r\n text: \"#000000\",\r\n label: \"TOP SECRET//SCI\",\r\n },\r\n};\r\n\r\n/**\r\n * ClassificationBanner - Pure React classification banner\r\n *\r\n * Displays security classification level. Required for U.S. Space Force applications.\r\n *\r\n * @example\r\n * ```tsx\r\n * <ClassificationBanner classification=\"unclassified\" />\r\n * <ClassificationBanner classification=\"secret\" position=\"bottom\" />\r\n * ```\r\n */\r\nexport const ClassificationBanner = memo(function ClassificationBanner({\r\n classification,\r\n label,\r\n position = \"top\",\r\n className = \"\",\r\n}: ClassificationBannerProps): React.ReactElement {\r\n const { tokens } = useTheme();\r\n const style =\r\n CLASSIFICATION_STYLES[classification] ?? CLASSIFICATION_STYLES.unclassified;\r\n const displayLabel = label ?? style.label;\r\n\r\n return (\r\n <div\r\n className={classNames(\"zendir-classification-banner\", className)}\r\n role=\"banner\"\r\n aria-label={`Security classification: ${displayLabel}`}\r\n style={{\r\n position: \"fixed\",\r\n left: 0,\r\n right: 0,\r\n [position]: 0,\r\n zIndex: 9999,\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n height: 20,\r\n backgroundColor: style.bg,\r\n color: style.text,\r\n fontSize: tokens.typography.fontSize.xs,\r\n fontWeight: tokens.typography.fontWeight.bold,\r\n letterSpacing: tokens.typography.letterSpacing.wide,\r\n textTransform: \"uppercase\",\r\n fontFamily: tokens.typography.fontFamily.primary,\r\n userSelect: \"none\",\r\n }}\r\n >\r\n {displayLabel}\r\n </div>\r\n );\r\n});\r\n"],"mappings":";;;;;;;;;;;;;;;;;AAsCA,IAAM,wBAGF;CACF,cAAc;EACZ,IAAI;EACJ,MAAM;EACN,OAAO;CACT;CACA,KAAK;EACH,IAAI;EACJ,MAAM;EACN,OAAO;CACT;CACA,YAAY;EACV,IAAI;EACJ,MAAM;EACN,OAAO;CACT;CACA,cAAc;EACZ,IAAI;EACJ,MAAM;EACN,OAAO;CACT;CACA,QAAQ;EACN,IAAI;EACJ,MAAM;EACN,OAAO;CACT;CACA,cAAc;EACZ,IAAI;EACJ,MAAM;EACN,OAAO;CACT;CACA,kBAAkB;EAChB,IAAI;EACJ,MAAM;EACN,OAAO;CACT;AACF;;;;;;;;;;;;AAaA,IAAa,uBAAuB,KAAK,SAAS,qBAAqB,EACrE,gBACA,OACA,WAAW,OACX,YAAY,MACoC;CAChD,MAAM,EAAE,WAAW,SAAS;CAC5B,MAAM,QACJ,sBAAsB,mBAAmB,sBAAsB;CACjE,MAAM,eAAe,SAAS,MAAM;CAEpC,OACE,oBAAC,OAAD;EACE,WAAW,WAAW,gCAAgC,SAAS;EAC/D,MAAK;EACL,cAAY,4BAA4B;EACxC,OAAO;GACL,UAAU;GACV,MAAM;GACN,OAAO;IACN,WAAW;GACZ,QAAQ;GACR,SAAS;GACT,YAAY;GACZ,gBAAgB;GAChB,QAAQ;GACR,iBAAiB,MAAM;GACvB,OAAO,MAAM;GACb,UAAU,OAAO,WAAW,SAAS;GACrC,YAAY,OAAO,WAAW,WAAW;GACzC,eAAe,OAAO,WAAW,cAAc;GAC/C,eAAe;GACf,YAAY,OAAO,WAAW,WAAW;GACzC,YAAY;EACd;YAEC;CACE,CAAA;AAET,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"GlobalStatusBar.js","names":[],"sources":["../../../src/react/astro/GlobalStatusBar.tsx"],"sourcesContent":["/**\n * @zendir/ui - GlobalStatusBar Component (Pure React)\n *\n * Primary header component for space applications following Astro UX patterns.\n *\n * Features:\n * - Theme-integrated via useTheme()\n * - App identity (name, domain, version)\n * - Clock slot\n * - Monitoring icons slot\n * - Username display\n * - Responsive design\n */\n\nimport React, { memo, ReactNode } from \"react\";\nimport { useTheme } from \"../theme\";\nimport { classNames, safeAccentText } from \"../utils\";\n\nexport interface GlobalStatusBarProps {\n /** Application name */\n appName?: string;\n /** Application domain (organization/mission) */\n appDomain?: string;\n /** Application version */\n appVersion?: string;\n /** Username to display */\n username?: string;\n /** Left slot content (app switcher, etc.) */\n leftSlot?: ReactNode;\n /** Center slot content (typically clock) */\n centerSlot?: ReactNode;\n /** Right slot content (monitoring icons, controls) */\n rightSlot?: ReactNode;\n /** Additional children */\n children?: ReactNode;\n /** Custom className */\n className?: string;\n}\n\n/**\n * GlobalStatusBar - Pure React global status bar\n *\n * Primary header component for space applications. Includes slots for:\n * - App switcher (left)\n * - Global clock (center)\n * - Monitoring icons (right)\n *\n * @example\n * ```tsx\n * <GlobalStatusBar\n * appName=\"Mission Control\"\n * appDomain=\"Zendir\"\n * username=\"Demo User\"\n * centerSlot={<MissionClock />}\n * rightSlot={<MonitoringIcons />}\n * />\n * ```\n */\nexport const GlobalStatusBar = memo(function GlobalStatusBar({\n appName = \"Zendir\",\n appDomain,\n appVersion,\n username,\n leftSlot,\n centerSlot,\n rightSlot,\n children,\n className = \"\",\n}: GlobalStatusBarProps): React.ReactElement {\n const { tokens, theme } = useTheme();\n const isTransparentTheme =\n theme === \"transparent\" ||\n theme === \"transparent-bold\" ||\n theme === \"transparent-minimal\";\n return (\n <header\n className={classNames(\"zendir-global-status-bar\", className)}\n role=\"banner\"\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n height: 56,\n padding: `0 ${tokens.spacing.lg}`,\n backgroundColor: isTransparentTheme\n ? \"transparent\"\n : tokens.colors.background.surface,\n ...(isTransparentTheme && {\n backdropFilter: \"blur(12px)\",\n WebkitBackdropFilter: \"blur(12px)\" as const,\n }),\n borderBottom: `1px solid ${tokens.colors.border.muted}`,\n color: tokens.colors.text.primary,\n fontFamily: tokens.typography.fontFamily.primary,\n }}\n >\n {/* Left section: App identity */}\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: tokens.spacing.lg,\n flex: \"1 1 0\",\n }}\n >\n {leftSlot}\n\n <div\n style={{\n display: \"flex\",\n alignItems: \"baseline\",\n gap: tokens.spacing.sm,\n }}\n >\n {appDomain && (\n <span\n style={{\n fontSize: tokens.typography.fontSize.sm,\n fontWeight: tokens.typography.fontWeight.normal,\n color: tokens.colors.text.tertiary,\n textTransform: \"uppercase\",\n letterSpacing: tokens.typography.letterSpacing.wide,\n }}\n >\n {appDomain}\n </span>\n )}\n <span\n style={{\n fontSize: tokens.typography.fontSize.xl,\n fontWeight: tokens.typography.fontWeight.semibold,\n letterSpacing: tokens.typography.letterSpacing.tight,\n color: tokens.colors.text.primary,\n }}\n >\n {appName}\n </span>\n {appVersion && (\n <span\n style={{\n fontSize: tokens.typography.fontSize.xs,\n fontWeight: tokens.typography.fontWeight.normal,\n color: tokens.colors.text.tertiary,\n }}\n >\n v{appVersion}\n </span>\n )}\n </div>\n </div>\n\n {/* Center section: Clock */}\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n flex: \"0 0 auto\",\n }}\n >\n {centerSlot}\n {children}\n </div>\n\n {/* Right section: Monitoring + User */}\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"flex-end\",\n gap: tokens.spacing.lg,\n flex: \"1 1 0\",\n }}\n >\n {rightSlot}\n\n {username && (\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: tokens.spacing.sm,\n paddingLeft: tokens.spacing.lg,\n borderLeft: `1px solid ${tokens.colors.border.muted}`,\n }}\n >\n {/* User avatar */}\n <div\n style={{\n width: 28,\n height: 28,\n borderRadius: tokens.borderRadius.full,\n backgroundColor: `${tokens.colors.accent.primary}20`,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n fontSize: tokens.typography.fontSize.sm,\n fontWeight: tokens.typography.fontWeight.semibold,\n color: safeAccentText(tokens.colors.accent.primary),\n }}\n >\n {username.charAt(0).toUpperCase()}\n </div>\n <span\n style={{\n fontSize: tokens.typography.fontSize.sm,\n fontWeight: tokens.typography.fontWeight.medium,\n color: tokens.colors.text.primary,\n }}\n >\n {username}\n </span>\n </div>\n )}\n </div>\n </header>\n );\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0DA,IAAa,kBAAkB,KAAK,SAAS,gBAAgB,EAC3D,UAAU,UACV,WACA,YACA,UACA,UACA,YACA,WACA,UACA,YAAY,MAC+B;CAC3C,MAAM,EAAE,QAAQ,UAAU,SAAS;CACnC,MAAM,qBACJ,UAAU,iBACV,UAAU,sBACV,UAAU;CACZ,OACE,qBAAC,UAAD;EACE,WAAW,WAAW,4BAA4B,SAAS;EAC3D,MAAK;EACL,OAAO;GACL,SAAS;GACT,YAAY;GACZ,gBAAgB;GAChB,QAAQ;GACR,SAAS,KAAK,OAAO,QAAQ;GAC7B,iBAAiB,qBACb,gBACA,OAAO,OAAO,WAAW;GAC7B,GAAI,sBAAsB;IACxB,gBAAgB;IAChB,sBAAsB;GACxB;GACA,cAAc,aAAa,OAAO,OAAO,OAAO;GAChD,OAAO,OAAO,OAAO,KAAK;GAC1B,YAAY,OAAO,WAAW,WAAW;EAC3C;YAnBF;GAsBE,qBAAC,OAAD;IACE,OAAO;KACL,SAAS;KACT,YAAY;KACZ,KAAK,OAAO,QAAQ;KACpB,MAAM;IACR;cANF,CAQG,UAED,qBAAC,OAAD;KACE,OAAO;MACL,SAAS;MACT,YAAY;MACZ,KAAK,OAAO,QAAQ;KACtB;eALF;MAOG,aACC,oBAAC,QAAD;OACE,OAAO;QACL,UAAU,OAAO,WAAW,SAAS;QACrC,YAAY,OAAO,WAAW,WAAW;QACzC,OAAO,OAAO,OAAO,KAAK;QAC1B,eAAe;QACf,eAAe,OAAO,WAAW,cAAc;OACjD;iBAEC;MACG,CAAA;MAER,oBAAC,QAAD;OACE,OAAO;QACL,UAAU,OAAO,WAAW,SAAS;QACrC,YAAY,OAAO,WAAW,WAAW;QACzC,eAAe,OAAO,WAAW,cAAc;QAC/C,OAAO,OAAO,OAAO,KAAK;OAC5B;iBAEC;MACG,CAAA;MACL,cACC,qBAAC,QAAD;OACE,OAAO;QACL,UAAU,OAAO,WAAW,SAAS;QACrC,YAAY,OAAO,WAAW,WAAW;QACzC,OAAO,OAAO,OAAO,KAAK;OAC5B;iBALF,CAMC,KACG,UACE;;KAEL;MACF;;GAGL,qBAAC,OAAD;IACE,OAAO;KACL,SAAS;KACT,YAAY;KACZ,gBAAgB;KAChB,MAAM;IACR;cANF,CAQG,YACA,QACE;;GAGL,qBAAC,OAAD;IACE,OAAO;KACL,SAAS;KACT,YAAY;KACZ,gBAAgB;KAChB,KAAK,OAAO,QAAQ;KACpB,MAAM;IACR;cAPF,CASG,WAEA,YACC,qBAAC,OAAD;KACE,OAAO;MACL,SAAS;MACT,YAAY;MACZ,KAAK,OAAO,QAAQ;MACpB,aAAa,OAAO,QAAQ;MAC5B,YAAY,aAAa,OAAO,OAAO,OAAO;KAChD;eAPF,CAUE,oBAAC,OAAD;MACE,OAAO;OACL,OAAO;OACP,QAAQ;OACR,cAAc,OAAO,aAAa;OAClC,iBAAiB,GAAG,OAAO,OAAO,OAAO,QAAQ;OACjD,SAAS;OACT,YAAY;OACZ,gBAAgB;OAChB,UAAU,OAAO,WAAW,SAAS;OACrC,YAAY,OAAO,WAAW,WAAW;OACzC,OAAO,eAAe,OAAO,OAAO,OAAO,OAAO;MACpD;gBAEC,SAAS,OAAO,CAAC,CAAC,CAAC,YAAY;KAC7B,CAAA,GACL,oBAAC,QAAD;MACE,OAAO;OACL,UAAU,OAAO,WAAW,SAAS;OACrC,YAAY,OAAO,WAAW,WAAW;OACzC,OAAO,OAAO,OAAO,KAAK;MAC5B;gBAEC;KACG,CAAA,CACH;MAEJ;;EACC;;AAEZ,CAAC"}
1
+ {"version":3,"file":"GlobalStatusBar.js","names":[],"sources":["../../../src/react/astro/GlobalStatusBar.tsx"],"sourcesContent":["/**\r\n * @zendir/ui - GlobalStatusBar Component (Pure React)\r\n *\r\n * Primary header component for space applications following Astro UX patterns.\r\n *\r\n * Features:\r\n * - Theme-integrated via useTheme()\r\n * - App identity (name, domain, version)\r\n * - Clock slot\r\n * - Monitoring icons slot\r\n * - Username display\r\n * - Responsive design\r\n */\r\n\r\nimport React, { memo, ReactNode } from \"react\";\r\nimport { useTheme } from \"../theme\";\r\nimport { classNames, safeAccentText } from \"../utils\";\r\n\r\nexport interface GlobalStatusBarProps {\r\n /** Application name */\r\n appName?: string;\r\n /** Application domain (organization/mission) */\r\n appDomain?: string;\r\n /** Application version */\r\n appVersion?: string;\r\n /** Username to display */\r\n username?: string;\r\n /** Left slot content (app switcher, etc.) */\r\n leftSlot?: ReactNode;\r\n /** Center slot content (typically clock) */\r\n centerSlot?: ReactNode;\r\n /** Right slot content (monitoring icons, controls) */\r\n rightSlot?: ReactNode;\r\n /** Additional children */\r\n children?: ReactNode;\r\n /** Custom className */\r\n className?: string;\r\n}\r\n\r\n/**\r\n * GlobalStatusBar - Pure React global status bar\r\n *\r\n * Primary header component for space applications. Includes slots for:\r\n * - App switcher (left)\r\n * - Global clock (center)\r\n * - Monitoring icons (right)\r\n *\r\n * @example\r\n * ```tsx\r\n * <GlobalStatusBar\r\n * appName=\"Mission Control\"\r\n * appDomain=\"Zendir\"\r\n * username=\"Demo User\"\r\n * centerSlot={<MissionClock />}\r\n * rightSlot={<MonitoringIcons />}\r\n * />\r\n * ```\r\n */\r\nexport const GlobalStatusBar = memo(function GlobalStatusBar({\r\n appName = \"Zendir\",\r\n appDomain,\r\n appVersion,\r\n username,\r\n leftSlot,\r\n centerSlot,\r\n rightSlot,\r\n children,\r\n className = \"\",\r\n}: GlobalStatusBarProps): React.ReactElement {\r\n const { tokens, theme } = useTheme();\r\n const isTransparentTheme =\r\n theme === \"transparent\" ||\r\n theme === \"transparent-bold\" ||\r\n theme === \"transparent-minimal\";\r\n return (\r\n <header\r\n className={classNames(\"zendir-global-status-bar\", className)}\r\n role=\"banner\"\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"space-between\",\r\n height: 56,\r\n padding: `0 ${tokens.spacing.lg}`,\r\n backgroundColor: isTransparentTheme\r\n ? \"transparent\"\r\n : tokens.colors.background.surface,\r\n ...(isTransparentTheme && {\r\n backdropFilter: \"blur(12px)\",\r\n WebkitBackdropFilter: \"blur(12px)\" as const,\r\n }),\r\n borderBottom: `1px solid ${tokens.colors.border.muted}`,\r\n color: tokens.colors.text.primary,\r\n fontFamily: tokens.typography.fontFamily.primary,\r\n }}\r\n >\r\n {/* Left section: App identity */}\r\n <div\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: tokens.spacing.lg,\r\n flex: \"1 1 0\",\r\n }}\r\n >\r\n {leftSlot}\r\n\r\n <div\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"baseline\",\r\n gap: tokens.spacing.sm,\r\n }}\r\n >\r\n {appDomain && (\r\n <span\r\n style={{\r\n fontSize: tokens.typography.fontSize.sm,\r\n fontWeight: tokens.typography.fontWeight.normal,\r\n color: tokens.colors.text.tertiary,\r\n textTransform: \"uppercase\",\r\n letterSpacing: tokens.typography.letterSpacing.wide,\r\n }}\r\n >\r\n {appDomain}\r\n </span>\r\n )}\r\n <span\r\n style={{\r\n fontSize: tokens.typography.fontSize.xl,\r\n fontWeight: tokens.typography.fontWeight.semibold,\r\n letterSpacing: tokens.typography.letterSpacing.tight,\r\n color: tokens.colors.text.primary,\r\n }}\r\n >\r\n {appName}\r\n </span>\r\n {appVersion && (\r\n <span\r\n style={{\r\n fontSize: tokens.typography.fontSize.xs,\r\n fontWeight: tokens.typography.fontWeight.normal,\r\n color: tokens.colors.text.tertiary,\r\n }}\r\n >\r\n v{appVersion}\r\n </span>\r\n )}\r\n </div>\r\n </div>\r\n\r\n {/* Center section: Clock */}\r\n <div\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n flex: \"0 0 auto\",\r\n }}\r\n >\r\n {centerSlot}\r\n {children}\r\n </div>\r\n\r\n {/* Right section: Monitoring + User */}\r\n <div\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"flex-end\",\r\n gap: tokens.spacing.lg,\r\n flex: \"1 1 0\",\r\n }}\r\n >\r\n {rightSlot}\r\n\r\n {username && (\r\n <div\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: tokens.spacing.sm,\r\n paddingLeft: tokens.spacing.lg,\r\n borderLeft: `1px solid ${tokens.colors.border.muted}`,\r\n }}\r\n >\r\n {/* User avatar */}\r\n <div\r\n style={{\r\n width: 28,\r\n height: 28,\r\n borderRadius: tokens.borderRadius.full,\r\n backgroundColor: `${tokens.colors.accent.primary}20`,\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n fontSize: tokens.typography.fontSize.sm,\r\n fontWeight: tokens.typography.fontWeight.semibold,\r\n color: safeAccentText(tokens.colors.accent.primary),\r\n }}\r\n >\r\n {username.charAt(0).toUpperCase()}\r\n </div>\r\n <span\r\n style={{\r\n fontSize: tokens.typography.fontSize.sm,\r\n fontWeight: tokens.typography.fontWeight.medium,\r\n color: tokens.colors.text.primary,\r\n }}\r\n >\r\n {username}\r\n </span>\r\n </div>\r\n )}\r\n </div>\r\n </header>\r\n );\r\n});\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0DA,IAAa,kBAAkB,KAAK,SAAS,gBAAgB,EAC3D,UAAU,UACV,WACA,YACA,UACA,UACA,YACA,WACA,UACA,YAAY,MAC+B;CAC3C,MAAM,EAAE,QAAQ,UAAU,SAAS;CACnC,MAAM,qBACJ,UAAU,iBACV,UAAU,sBACV,UAAU;CACZ,OACE,qBAAC,UAAD;EACE,WAAW,WAAW,4BAA4B,SAAS;EAC3D,MAAK;EACL,OAAO;GACL,SAAS;GACT,YAAY;GACZ,gBAAgB;GAChB,QAAQ;GACR,SAAS,KAAK,OAAO,QAAQ;GAC7B,iBAAiB,qBACb,gBACA,OAAO,OAAO,WAAW;GAC7B,GAAI,sBAAsB;IACxB,gBAAgB;IAChB,sBAAsB;GACxB;GACA,cAAc,aAAa,OAAO,OAAO,OAAO;GAChD,OAAO,OAAO,OAAO,KAAK;GAC1B,YAAY,OAAO,WAAW,WAAW;EAC3C;YAnBF;GAsBE,qBAAC,OAAD;IACE,OAAO;KACL,SAAS;KACT,YAAY;KACZ,KAAK,OAAO,QAAQ;KACpB,MAAM;IACR;cANF,CAQG,UAED,qBAAC,OAAD;KACE,OAAO;MACL,SAAS;MACT,YAAY;MACZ,KAAK,OAAO,QAAQ;KACtB;eALF;MAOG,aACC,oBAAC,QAAD;OACE,OAAO;QACL,UAAU,OAAO,WAAW,SAAS;QACrC,YAAY,OAAO,WAAW,WAAW;QACzC,OAAO,OAAO,OAAO,KAAK;QAC1B,eAAe;QACf,eAAe,OAAO,WAAW,cAAc;OACjD;iBAEC;MACG,CAAA;MAER,oBAAC,QAAD;OACE,OAAO;QACL,UAAU,OAAO,WAAW,SAAS;QACrC,YAAY,OAAO,WAAW,WAAW;QACzC,eAAe,OAAO,WAAW,cAAc;QAC/C,OAAO,OAAO,OAAO,KAAK;OAC5B;iBAEC;MACG,CAAA;MACL,cACC,qBAAC,QAAD;OACE,OAAO;QACL,UAAU,OAAO,WAAW,SAAS;QACrC,YAAY,OAAO,WAAW,WAAW;QACzC,OAAO,OAAO,OAAO,KAAK;OAC5B;iBALF,CAMC,KACG,UACE;;KAEL;MACF;;GAGL,qBAAC,OAAD;IACE,OAAO;KACL,SAAS;KACT,YAAY;KACZ,gBAAgB;KAChB,MAAM;IACR;cANF,CAQG,YACA,QACE;;GAGL,qBAAC,OAAD;IACE,OAAO;KACL,SAAS;KACT,YAAY;KACZ,gBAAgB;KAChB,KAAK,OAAO,QAAQ;KACpB,MAAM;IACR;cAPF,CASG,WAEA,YACC,qBAAC,OAAD;KACE,OAAO;MACL,SAAS;MACT,YAAY;MACZ,KAAK,OAAO,QAAQ;MACpB,aAAa,OAAO,QAAQ;MAC5B,YAAY,aAAa,OAAO,OAAO,OAAO;KAChD;eAPF,CAUE,oBAAC,OAAD;MACE,OAAO;OACL,OAAO;OACP,QAAQ;OACR,cAAc,OAAO,aAAa;OAClC,iBAAiB,GAAG,OAAO,OAAO,OAAO,QAAQ;OACjD,SAAS;OACT,YAAY;OACZ,gBAAgB;OAChB,UAAU,OAAO,WAAW,SAAS;OACrC,YAAY,OAAO,WAAW,WAAW;OACzC,OAAO,eAAe,OAAO,OAAO,OAAO,OAAO;MACpD;gBAEC,SAAS,OAAO,CAAC,CAAC,CAAC,YAAY;KAC7B,CAAA,GACL,oBAAC,QAAD;MACE,OAAO;OACL,UAAU,OAAO,WAAW,SAAS;OACrC,YAAY,OAAO,WAAW,WAAW;OACzC,OAAO,OAAO,OAAO,KAAK;MAC5B;gBAEC;KACG,CAAA,CACH;MAEJ;;EACC;;AAEZ,CAAC"}