@zendir/ui 0.2.20 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (260) hide show
  1. package/CHANGELOG.md +192 -1
  2. package/README.md +70 -28
  3. package/dist/index.d.ts +1 -1
  4. package/dist/index.js +57 -41
  5. package/dist/index.js.map +1 -1
  6. package/dist/react/3d/CesiumCaptureSource.d.ts +119 -0
  7. package/dist/react/3d/CesiumCaptureSource.js +307 -0
  8. package/dist/react/3d/CesiumCaptureSource.js.map +1 -0
  9. package/dist/react/3d/ZenSpace3D.js +1253 -0
  10. package/dist/react/3d/ZenSpace3D.js.map +1 -0
  11. package/dist/react/3d/ZenSpace3DCesium.js +579 -0
  12. package/dist/react/3d/ZenSpace3DCesium.js.map +1 -0
  13. package/dist/react/3d/ZenSpace3DTypes.d.ts +28 -1
  14. package/dist/react/3d/ZenSpace3DUtils.d.ts +17 -173
  15. package/dist/react/3d/ZenSpace3DUtils.js +28 -0
  16. package/dist/react/3d/ZenSpace3DUtils.js.map +1 -0
  17. package/dist/react/3d/capturePngAnalysis.d.ts +16 -0
  18. package/dist/react/3d/index.d.ts +10 -12
  19. package/dist/react/3d/threeLoader.js +18 -0
  20. package/dist/react/3d/threeLoader.js.map +1 -0
  21. package/dist/react/astro/MonitoringIcon.js +1 -1
  22. package/dist/react/astro/MonitoringIcon.js.map +1 -1
  23. package/dist/react/astro/SimulationControls.js +2 -2
  24. package/dist/react/astro/SimulationControls.js.map +1 -1
  25. package/dist/react/astro/UnifiedTimeline.js +4 -4
  26. package/dist/react/astro/UnifiedTimeline.js.map +1 -1
  27. package/dist/react/charts/GroundTrackMap.d.ts +2 -15
  28. package/dist/react/charts/GroundTrackMap.js +1 -1
  29. package/dist/react/charts/GroundTrackMap.js.map +1 -1
  30. package/dist/react/charts/unified/AstroChart.js +34 -13
  31. package/dist/react/charts/unified/AstroChart.js.map +1 -1
  32. package/dist/react/chatgpt/AppCard.d.ts +0 -4
  33. package/dist/react/chatgpt/index.d.ts +0 -19
  34. package/dist/react/context/SpatialSelectionContext.d.ts +40 -0
  35. package/dist/react/context/SpatialSelectionContext.js +10 -0
  36. package/dist/react/context/SpatialSelectionContext.js.map +1 -0
  37. package/dist/react/context/index.d.ts +2 -0
  38. package/dist/react/core/{DataTable.d.ts → data/DataTable.d.ts} +1 -1
  39. package/dist/react/core/{DataTable.js → data/DataTable.js} +4 -4
  40. package/dist/react/core/data/DataTable.js.map +1 -0
  41. package/dist/react/core/{DataValue.d.ts → data/DataValue.d.ts} +2 -2
  42. package/dist/react/core/{DataValue.js → data/DataValue.js} +2 -2
  43. package/dist/react/core/data/DataValue.js.map +1 -0
  44. package/dist/react/core/{propertyConfig.d.ts → data/propertyConfig.d.ts} +2 -2
  45. package/dist/react/core/data/propertyConfig.js.map +1 -0
  46. package/dist/react/core/{AstroIcon.js → display/AstroIcon.js} +1 -1
  47. package/dist/react/core/display/AstroIcon.js.map +1 -0
  48. package/dist/react/core/{Badge.d.ts → display/Badge.d.ts} +1 -1
  49. package/dist/react/core/{Badge.js → display/Badge.js} +2 -2
  50. package/dist/react/core/display/Badge.js.map +1 -0
  51. package/dist/react/core/{CardHeader.d.ts → display/CardHeader.d.ts} +1 -1
  52. package/dist/react/core/{CardHeader.js → display/CardHeader.js} +2 -2
  53. package/dist/react/core/display/CardHeader.js.map +1 -0
  54. package/dist/react/core/{Container.d.ts → display/Container.d.ts} +1 -1
  55. package/dist/react/core/{Container.js → display/Container.js} +3 -3
  56. package/dist/react/core/display/Container.js.map +1 -0
  57. package/dist/react/core/{CopyButton.js → display/CopyButton.js} +1 -1
  58. package/dist/react/core/display/CopyButton.js.map +1 -0
  59. package/dist/react/core/{GlassCard.d.ts → display/GlassCard.d.ts} +1 -1
  60. package/dist/react/core/{GlassCard.js → display/GlassCard.js} +2 -2
  61. package/dist/react/core/display/GlassCard.js.map +1 -0
  62. package/dist/react/core/{HeaderIconWithStatus.d.ts → display/HeaderIconWithStatus.d.ts} +1 -1
  63. package/dist/react/core/{HeaderIconWithStatus.js → display/HeaderIconWithStatus.js} +1 -1
  64. package/dist/react/core/display/HeaderIconWithStatus.js.map +1 -0
  65. package/dist/react/core/{Icon.d.ts → display/Icon.d.ts} +1 -1
  66. package/dist/react/core/{Icon.js → display/Icon.js} +1 -1
  67. package/dist/react/core/display/Icon.js.map +1 -0
  68. package/dist/react/core/{Typography.d.ts → display/Typography.d.ts} +13 -4
  69. package/dist/react/core/{Typography.js → display/Typography.js} +1 -1
  70. package/dist/react/core/display/Typography.js.map +1 -0
  71. package/dist/react/core/{ConfirmDialog.js → feedback/ConfirmDialog.js} +1 -1
  72. package/dist/react/core/feedback/ConfirmDialog.js.map +1 -0
  73. package/dist/react/core/{Dialog.js → feedback/Dialog.js} +2 -2
  74. package/dist/react/core/feedback/Dialog.js.map +1 -0
  75. package/dist/react/core/{Toast.js → feedback/Toast.js} +3 -3
  76. package/dist/react/core/feedback/Toast.js.map +1 -0
  77. package/dist/react/core/index.d.ts +85 -83
  78. package/dist/react/core/{Button.js → inputs/Button.js} +2 -2
  79. package/dist/react/core/inputs/Button.js.map +1 -0
  80. package/dist/react/core/{Checkbox.js → inputs/Checkbox.js} +2 -2
  81. package/dist/react/core/inputs/Checkbox.js.map +1 -0
  82. package/dist/react/core/{Input.d.ts → inputs/Input.d.ts} +1 -1
  83. package/dist/react/core/{Input.js → inputs/Input.js} +3 -3
  84. package/dist/react/core/inputs/Input.js.map +1 -0
  85. package/dist/react/core/{LimitsBar.js → inputs/LimitsBar.js} +1 -1
  86. package/dist/react/core/inputs/LimitsBar.js.map +1 -0
  87. package/dist/react/core/{NumberInput.d.ts → inputs/NumberInput.d.ts} +2 -2
  88. package/dist/react/core/{NumberInput.js → inputs/NumberInput.js} +3 -3
  89. package/dist/react/core/inputs/NumberInput.js.map +1 -0
  90. package/dist/react/core/{PinInput.js → inputs/PinInput.js} +2 -2
  91. package/dist/react/core/inputs/PinInput.js.map +1 -0
  92. package/dist/react/core/{Select.js → inputs/Select.js} +3 -3
  93. package/dist/react/core/inputs/Select.js.map +1 -0
  94. package/dist/react/core/{Toggle.js → inputs/Toggle.js} +2 -2
  95. package/dist/react/core/inputs/Toggle.js.map +1 -0
  96. package/dist/react/core/{AppBar.d.ts → navigation/AppBar.d.ts} +1 -1
  97. package/dist/react/core/{AppBar.js → navigation/AppBar.js} +7 -7
  98. package/dist/react/core/navigation/AppBar.js.map +1 -0
  99. package/dist/react/core/{Pagination.js → navigation/Pagination.js} +2 -2
  100. package/dist/react/core/navigation/Pagination.js.map +1 -0
  101. package/dist/react/core/{SideNav.d.ts → navigation/SideNav.d.ts} +1 -1
  102. package/dist/react/core/{SideNav.js → navigation/SideNav.js} +8 -9
  103. package/dist/react/core/navigation/SideNav.js.map +1 -0
  104. package/dist/react/core/{Tabs.js → navigation/Tabs.js} +2 -2
  105. package/dist/react/core/navigation/Tabs.js.map +1 -0
  106. package/dist/react/core/{Popover.js → overlays/Popover.js} +1 -1
  107. package/dist/react/core/overlays/Popover.js.map +1 -0
  108. package/dist/react/core/{SidePanel.js → overlays/SidePanel.js} +7 -7
  109. package/dist/react/core/overlays/SidePanel.js.map +1 -0
  110. package/dist/react/core/{Tooltip.js → overlays/Tooltip.js} +2 -2
  111. package/dist/react/core/overlays/Tooltip.js.map +1 -0
  112. package/dist/react/core/{ActivityPlanner.js → widgets/ActivityPlanner.js} +1 -1
  113. package/dist/react/core/widgets/ActivityPlanner.js.map +1 -0
  114. package/dist/react/core/widgets/Capture.d.ts +140 -0
  115. package/dist/react/core/widgets/Capture.js +804 -0
  116. package/dist/react/core/widgets/Capture.js.map +1 -0
  117. package/dist/react/core/{ChatPanel.d.ts → widgets/ChatPanel.d.ts} +1 -1
  118. package/dist/react/core/{ChatPanel.js → widgets/ChatPanel.js} +5 -4
  119. package/dist/react/core/widgets/ChatPanel.js.map +1 -0
  120. package/dist/react/core/{ColorPickerPanel.d.ts → widgets/ColorPickerPanel.d.ts} +1 -1
  121. package/dist/react/core/{ColorPickerPanel.js → widgets/ColorPickerPanel.js} +3 -3
  122. package/dist/react/core/widgets/ColorPickerPanel.js.map +1 -0
  123. package/dist/react/core/{CommandBuilder.js → widgets/CommandBuilder.js} +1 -1
  124. package/dist/react/core/widgets/CommandBuilder.js.map +1 -0
  125. package/dist/react/core/{ConnectionForm.d.ts → widgets/ConnectionForm.d.ts} +1 -1
  126. package/dist/react/core/{ConnectionForm.js → widgets/ConnectionForm.js} +2 -2
  127. package/dist/react/core/widgets/ConnectionForm.js.map +1 -0
  128. package/dist/react/core/{FileExplorer.js → widgets/FileExplorer.js} +2 -2
  129. package/dist/react/core/widgets/FileExplorer.js.map +1 -0
  130. package/dist/react/core/{HexViewer.js → widgets/HexViewer.js} +1 -1
  131. package/dist/react/core/widgets/HexViewer.js.map +1 -0
  132. package/dist/react/core/{ImageGallery.d.ts → widgets/ImageGallery.d.ts} +1 -1
  133. package/dist/react/core/{ImageGallery.js → widgets/ImageGallery.js} +3 -3
  134. package/dist/react/core/widgets/ImageGallery.js.map +1 -0
  135. package/dist/react/core/{LogViewer.d.ts → widgets/LogViewer.d.ts} +13 -3
  136. package/dist/react/core/{LogViewer.js → widgets/LogViewer.js} +28 -8
  137. package/dist/react/core/widgets/LogViewer.js.map +1 -0
  138. package/dist/react/core/{MessageStream.d.ts → widgets/MessageStream.d.ts} +2 -2
  139. package/dist/react/core/{MessageStream.js → widgets/MessageStream.js} +4 -4
  140. package/dist/react/core/widgets/MessageStream.js.map +1 -0
  141. package/dist/react/core/{MissionCalendar.js → widgets/MissionCalendar.js} +2 -2
  142. package/dist/react/core/widgets/MissionCalendar.js.map +1 -0
  143. package/dist/react/core/{PacketViewer.js → widgets/PacketViewer.js} +1 -1
  144. package/dist/react/core/widgets/PacketViewer.js.map +1 -0
  145. package/dist/react/core/widgets/capture-placeholder.png.js +5 -0
  146. package/dist/react/core/widgets/capture-placeholder.png.js.map +1 -0
  147. package/dist/react/hooks/index.d.ts +9 -11
  148. package/dist/react/hooks/useAccessWindows.d.ts +15 -19
  149. package/dist/react/hooks/useGroundTrackHistory.d.ts +34 -0
  150. package/dist/react/hooks/useSimulationScene.d.ts +141 -0
  151. package/dist/react/hooks/useSimulationScene.js +401 -0
  152. package/dist/react/hooks/useSimulationScene.js.map +1 -0
  153. package/dist/react/hooks/useZendirSession.d.ts +44 -69
  154. package/dist/react/index.d.ts +10 -5
  155. package/dist/react/panels/LayerControlPanel.d.ts +54 -0
  156. package/dist/react/panels/LayerControlPanel.js +184 -0
  157. package/dist/react/panels/LayerControlPanel.js.map +1 -0
  158. package/dist/react/panels/ObjectInventoryPanel.d.ts +57 -0
  159. package/dist/react/panels/ObjectInventoryPanel.js +261 -0
  160. package/dist/react/panels/ObjectInventoryPanel.js.map +1 -0
  161. package/dist/react/panels/index.d.ts +15 -0
  162. package/dist/react/theme/ThemeProvider.d.ts +2 -0
  163. package/dist/react/theme/ThemeProvider.js +50 -72
  164. package/dist/react/theme/ThemeProvider.js.map +1 -1
  165. package/dist/react/types.d.ts +32 -3
  166. package/dist/react/types.js.map +1 -1
  167. package/dist/react.js +57 -41
  168. package/dist/react.js.map +1 -1
  169. package/dist/shaders/atmosphere.frag.js +5 -0
  170. package/dist/shaders/atmosphere.frag.js.map +1 -0
  171. package/dist/shaders/atmosphere.vert.js +5 -0
  172. package/dist/shaders/atmosphere.vert.js.map +1 -0
  173. package/dist/shaders/stars.frag.js +5 -0
  174. package/dist/shaders/stars.frag.js.map +1 -0
  175. package/dist/shaders/stars.vert.js +5 -0
  176. package/dist/shaders/stars.vert.js.map +1 -0
  177. package/dist/style.css +6 -4
  178. package/dist/tokens/css-vars.d.ts +91 -0
  179. package/dist/tokens/css-vars.js +228 -0
  180. package/dist/tokens/css-vars.js.map +1 -0
  181. package/dist/tokens/index.d.ts +71 -18
  182. package/dist/tokens/index.js +206 -97
  183. package/dist/tokens/index.js.map +1 -1
  184. package/dist/tokens/tokens.css +50 -50
  185. package/package.json +27 -22
  186. package/sdk-stub.js +10 -5
  187. package/dist/react/3d/EarthViewer.d.ts +0 -46
  188. package/dist/react/3d/SolarSystemViewer.d.ts +0 -43
  189. package/dist/react/chatgpt/ChatGPTCard.d.ts +0 -6
  190. package/dist/react/core/ActivityPlanner.js.map +0 -1
  191. package/dist/react/core/AppBar.js.map +0 -1
  192. package/dist/react/core/AstroIcon.js.map +0 -1
  193. package/dist/react/core/Badge.js.map +0 -1
  194. package/dist/react/core/Button.js.map +0 -1
  195. package/dist/react/core/CardHeader.js.map +0 -1
  196. package/dist/react/core/ChatPanel.js.map +0 -1
  197. package/dist/react/core/Checkbox.js.map +0 -1
  198. package/dist/react/core/ColorPickerPanel.js.map +0 -1
  199. package/dist/react/core/CommandBuilder.js.map +0 -1
  200. package/dist/react/core/ConfirmDialog.js.map +0 -1
  201. package/dist/react/core/ConnectionForm.js.map +0 -1
  202. package/dist/react/core/Container.js.map +0 -1
  203. package/dist/react/core/CopyButton.js.map +0 -1
  204. package/dist/react/core/DataTable.js.map +0 -1
  205. package/dist/react/core/DataValue.js.map +0 -1
  206. package/dist/react/core/Dialog.js.map +0 -1
  207. package/dist/react/core/FileExplorer.js.map +0 -1
  208. package/dist/react/core/GlassCard.js.map +0 -1
  209. package/dist/react/core/HeaderIconWithStatus.js.map +0 -1
  210. package/dist/react/core/HexViewer.js.map +0 -1
  211. package/dist/react/core/Icon.js.map +0 -1
  212. package/dist/react/core/ImageGallery.js.map +0 -1
  213. package/dist/react/core/Input.js.map +0 -1
  214. package/dist/react/core/LimitsBar.js.map +0 -1
  215. package/dist/react/core/LogViewer.js.map +0 -1
  216. package/dist/react/core/MessageStream.js.map +0 -1
  217. package/dist/react/core/MissionCalendar.js.map +0 -1
  218. package/dist/react/core/NumberInput.js.map +0 -1
  219. package/dist/react/core/PacketViewer.js.map +0 -1
  220. package/dist/react/core/Pagination.js.map +0 -1
  221. package/dist/react/core/PinInput.js.map +0 -1
  222. package/dist/react/core/Popover.js.map +0 -1
  223. package/dist/react/core/Select.js.map +0 -1
  224. package/dist/react/core/SideNav.js.map +0 -1
  225. package/dist/react/core/SidePanel.js.map +0 -1
  226. package/dist/react/core/Tabs.js.map +0 -1
  227. package/dist/react/core/Toast.js.map +0 -1
  228. package/dist/react/core/Toggle.js.map +0 -1
  229. package/dist/react/core/Tooltip.js.map +0 -1
  230. package/dist/react/core/Typography.js.map +0 -1
  231. package/dist/react/core/propertyConfig.js.map +0 -1
  232. package/dist/react/hooks/useSimulationTime.d.ts +0 -61
  233. package/dist/react/hooks/useSpacecraftPosition.d.ts +0 -50
  234. package/dist/react/hooks/useTelemetry.d.ts +0 -55
  235. package/dist/types.d.ts +0 -1
  236. package/dist/types.js +0 -2
  237. package/dist/types.js.map +0 -1
  238. /package/dist/react/core/{propertyConfig.js → data/propertyConfig.js} +0 -0
  239. /package/dist/react/core/{AstroIcon.d.ts → display/AstroIcon.d.ts} +0 -0
  240. /package/dist/react/core/{CopyButton.d.ts → display/CopyButton.d.ts} +0 -0
  241. /package/dist/react/core/{ConfirmDialog.d.ts → feedback/ConfirmDialog.d.ts} +0 -0
  242. /package/dist/react/core/{Dialog.d.ts → feedback/Dialog.d.ts} +0 -0
  243. /package/dist/react/core/{Toast.d.ts → feedback/Toast.d.ts} +0 -0
  244. /package/dist/react/core/{Button.d.ts → inputs/Button.d.ts} +0 -0
  245. /package/dist/react/core/{Checkbox.d.ts → inputs/Checkbox.d.ts} +0 -0
  246. /package/dist/react/core/{LimitsBar.d.ts → inputs/LimitsBar.d.ts} +0 -0
  247. /package/dist/react/core/{PinInput.d.ts → inputs/PinInput.d.ts} +0 -0
  248. /package/dist/react/core/{Select.d.ts → inputs/Select.d.ts} +0 -0
  249. /package/dist/react/core/{Toggle.d.ts → inputs/Toggle.d.ts} +0 -0
  250. /package/dist/react/core/{Pagination.d.ts → navigation/Pagination.d.ts} +0 -0
  251. /package/dist/react/core/{Tabs.d.ts → navigation/Tabs.d.ts} +0 -0
  252. /package/dist/react/core/{Popover.d.ts → overlays/Popover.d.ts} +0 -0
  253. /package/dist/react/core/{SidePanel.d.ts → overlays/SidePanel.d.ts} +0 -0
  254. /package/dist/react/core/{Tooltip.d.ts → overlays/Tooltip.d.ts} +0 -0
  255. /package/dist/react/core/{ActivityPlanner.d.ts → widgets/ActivityPlanner.d.ts} +0 -0
  256. /package/dist/react/core/{CommandBuilder.d.ts → widgets/CommandBuilder.d.ts} +0 -0
  257. /package/dist/react/core/{FileExplorer.d.ts → widgets/FileExplorer.d.ts} +0 -0
  258. /package/dist/react/core/{HexViewer.d.ts → widgets/HexViewer.d.ts} +0 -0
  259. /package/dist/react/core/{MissionCalendar.d.ts → widgets/MissionCalendar.d.ts} +0 -0
  260. /package/dist/react/core/{PacketViewer.d.ts → widgets/PacketViewer.d.ts} +0 -0
@@ -0,0 +1,57 @@
1
+ import { CSSProperties, ReactNode } from 'react';
2
+ import { SimulationSceneObject, StructureDiag, PositionTickDiag } from '../hooks/useSimulationScene';
3
+
4
+ export type ObjectInventoryKind = SimulationSceneObject['kind'];
5
+ export interface ObjectInventoryPanelProps {
6
+ /** Objects to list. Typically `useSimulationScene().objects`. */
7
+ objects: SimulationSceneObject[];
8
+ /** Click handler — fires with the clicked object. Pure forward; the
9
+ * panel does not navigate, focus, or fly anywhere itself. */
10
+ onSelect?: (object: SimulationSceneObject) => void;
11
+ /** Currently selected object id (highlights the row). Pass `null` for none. */
12
+ selectedId?: string | null;
13
+ /** Where to anchor the panel on its parent. Default: `top-left`. */
14
+ position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
15
+ /** Pixel offsets from the anchor. Defaults to (16, 56) so a top bar fits above. */
16
+ offset?: {
17
+ x?: number;
18
+ y?: number;
19
+ };
20
+ /** Hide the panel without unmounting (preserves `selectedId` state). */
21
+ hidden?: boolean;
22
+ /** Width in pixels. Default 330. */
23
+ width?: number;
24
+ /** Maximum height (CSS value). Default `'50vh'`. */
25
+ maxHeight?: string | number;
26
+ /** Allow the user to collapse / expand the panel. Default true. */
27
+ collapsible?: boolean;
28
+ /** Initial collapsed state. Default false. */
29
+ defaultCollapsed?: boolean;
30
+ /** Optional title override. Default: `"API Objects · N"`. */
31
+ title?: ReactNode;
32
+ /**
33
+ * Optional structure diagnostic block (raw keys + counts from
34
+ * getSimulationStructure). Useful in dev / debug builds; omit in
35
+ * production for a cleaner look.
36
+ */
37
+ structureDiag?: StructureDiag | null;
38
+ /** Optional position-tick diagnostic block. */
39
+ positionTickDiag?: PositionTickDiag | null;
40
+ /** Custom labels per kind (defaults: "Spacecraft", "Ground Stations", "Celestial Bodies"). */
41
+ kindLabels?: Partial<Record<ObjectInventoryKind, string>>;
42
+ /** Inline style override on the outer container. */
43
+ style?: CSSProperties;
44
+ }
45
+ export declare const ObjectInventoryPanel: import('react').NamedExoticComponent<ObjectInventoryPanelProps>;
46
+ /**
47
+ * Sensible default fly-to distance per object kind, for use with
48
+ * `<ZenSpace3D />`'s imperative `focusOn(id, { zoom })` handle.
49
+ *
50
+ * spacecraft: 2 000 000 m (≈ 2 000 km — close orbital view)
51
+ * groundStation: 1 500 000 m (≈ 1 500 km — high-altitude angled view)
52
+ * celestialBody: 50 000 000 m (≈ 50 000 km — full-planet view)
53
+ *
54
+ * Override per call if the consumer wants tighter/looser framing.
55
+ */
56
+ export declare function recommendedFlyDistance(kind: ObjectInventoryKind): number;
57
+ export default ObjectInventoryPanel;
@@ -0,0 +1,261 @@
1
+ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
+ import { memo, useState, useCallback } from "react";
3
+ import { useThemeTokens } from "../theme/ThemeProvider.js";
4
+ import { useSpatialSelection } from "../context/SpatialSelectionContext.js";
5
+ function shortId(id) {
6
+ return id.length <= 8 ? id : id.slice(0, 8);
7
+ }
8
+ const DEFAULT_KIND_LABELS = {
9
+ spacecraft: "Spacecraft",
10
+ groundStation: "Ground Stations",
11
+ celestialBody: "Celestial Bodies"
12
+ };
13
+ const KIND_ORDER = ["spacecraft", "groundStation", "celestialBody"];
14
+ const ObjectInventoryPanel = memo(function ObjectInventoryPanel2({
15
+ objects,
16
+ onSelect,
17
+ selectedId,
18
+ position = "top-left",
19
+ offset,
20
+ hidden = false,
21
+ width = 330,
22
+ maxHeight = "50vh",
23
+ collapsible = true,
24
+ defaultCollapsed = false,
25
+ title,
26
+ structureDiag,
27
+ positionTickDiag,
28
+ kindLabels,
29
+ style
30
+ }) {
31
+ var _a;
32
+ const tokens = useThemeTokens();
33
+ const [collapsed, setCollapsed] = useState(defaultCollapsed);
34
+ const ctx = useSpatialSelection();
35
+ const effectiveSelectedId = selectedId !== void 0 ? selectedId : (ctx == null ? void 0 : ctx.selectedId) ?? null;
36
+ const effectiveOnSelect = useCallback(
37
+ (obj) => {
38
+ onSelect == null ? void 0 : onSelect(obj);
39
+ if (!onSelect) ctx == null ? void 0 : ctx.setSelectedId(obj.id);
40
+ },
41
+ [onSelect, ctx]
42
+ );
43
+ const labels = { ...DEFAULT_KIND_LABELS, ...kindLabels };
44
+ const entries = Array.isArray(objects) ? objects : [];
45
+ const offX = (offset == null ? void 0 : offset.x) ?? 16;
46
+ const offY = (offset == null ? void 0 : offset.y) ?? 56;
47
+ const anchor = (() => {
48
+ switch (position) {
49
+ case "top-right":
50
+ return { top: offY, right: offX };
51
+ case "bottom-left":
52
+ return { bottom: offY, left: offX };
53
+ case "bottom-right":
54
+ return { bottom: offY, right: offX };
55
+ case "top-left":
56
+ default:
57
+ return { top: offY, left: offX };
58
+ }
59
+ })();
60
+ if (hidden) return null;
61
+ const dim = { opacity: 0.55, fontSize: "0.9em" };
62
+ return /* @__PURE__ */ jsxs(
63
+ "div",
64
+ {
65
+ "data-testid": "zendir-object-inventory",
66
+ style: {
67
+ position: "absolute",
68
+ zIndex: 2,
69
+ width,
70
+ maxHeight,
71
+ overflow: "auto",
72
+ padding: tokens.spacing.sm,
73
+ borderRadius: ((_a = tokens.borderRadius) == null ? void 0 : _a.md) ?? 6,
74
+ border: "1px solid rgba(255,255,255,0.12)",
75
+ background: "rgba(0,0,0,0.72)",
76
+ color: tokens.colors.text.primary,
77
+ fontSize: tokens.typography.fontSize.xs,
78
+ lineHeight: 1.35,
79
+ ...anchor,
80
+ ...style
81
+ },
82
+ children: [
83
+ /* @__PURE__ */ jsx(
84
+ PanelHeader,
85
+ {
86
+ title: title ?? `API Objects · ${entries.length}`,
87
+ collapsible,
88
+ collapsed,
89
+ onToggle: () => setCollapsed((c) => !c)
90
+ }
91
+ ),
92
+ !collapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
93
+ KIND_ORDER.map((kind) => {
94
+ const items = entries.filter((o) => o.kind === kind);
95
+ return /* @__PURE__ */ jsx(
96
+ ObjectInventoryGroup,
97
+ {
98
+ title: `${labels[kind]} (${items.length})`,
99
+ items,
100
+ selectedId: effectiveSelectedId,
101
+ onSelect: onSelect != null ? onSelect : ctx ? effectiveOnSelect : void 0
102
+ },
103
+ kind
104
+ );
105
+ }),
106
+ structureDiag !== void 0 && /* @__PURE__ */ jsx(StructureDiagBlock, { diag: structureDiag, dim }),
107
+ positionTickDiag !== void 0 && /* @__PURE__ */ jsx(PositionTickDiagBlock, { diag: positionTickDiag, dim })
108
+ ] })
109
+ ]
110
+ }
111
+ );
112
+ });
113
+ const PanelHeader = ({
114
+ title,
115
+ collapsible,
116
+ collapsed,
117
+ onToggle
118
+ }) => {
119
+ if (!collapsible) {
120
+ return /* @__PURE__ */ jsx("div", { style: { marginBottom: 6, fontWeight: 600 }, children: title });
121
+ }
122
+ return /* @__PURE__ */ jsxs(
123
+ "button",
124
+ {
125
+ type: "button",
126
+ onClick: onToggle,
127
+ "aria-expanded": !collapsed,
128
+ style: {
129
+ display: "flex",
130
+ width: "100%",
131
+ alignItems: "center",
132
+ justifyContent: "space-between",
133
+ marginBottom: 6,
134
+ padding: 0,
135
+ background: "none",
136
+ border: "none",
137
+ color: "inherit",
138
+ font: "inherit",
139
+ fontWeight: 600,
140
+ cursor: "pointer",
141
+ textAlign: "left"
142
+ },
143
+ children: [
144
+ /* @__PURE__ */ jsx("span", { children: title }),
145
+ /* @__PURE__ */ jsx("span", { "aria-hidden": true, style: { opacity: 0.7, fontSize: "0.85em" }, children: collapsed ? "▸" : "▾" })
146
+ ]
147
+ }
148
+ );
149
+ };
150
+ const ObjectInventoryGroup = ({
151
+ title,
152
+ items,
153
+ selectedId,
154
+ onSelect
155
+ }) => /* @__PURE__ */ jsxs("div", { style: { marginBottom: 6 }, children: [
156
+ /* @__PURE__ */ jsx("div", { style: { opacity: 0.85 }, children: title }),
157
+ items.length === 0 ? /* @__PURE__ */ jsx("div", { style: { opacity: 0.6 }, children: "—" }) : items.map((item) => /* @__PURE__ */ jsx(
158
+ ObjectRow,
159
+ {
160
+ object: item,
161
+ selected: item.id === selectedId,
162
+ onSelect
163
+ },
164
+ item.id
165
+ ))
166
+ ] });
167
+ const ObjectRow = ({
168
+ object,
169
+ selected,
170
+ onSelect
171
+ }) => {
172
+ const handleClick = useCallback(() => onSelect == null ? void 0 : onSelect(object), [onSelect, object]);
173
+ const interactive = onSelect != null;
174
+ return /* @__PURE__ */ jsxs(
175
+ "div",
176
+ {
177
+ role: interactive ? "button" : void 0,
178
+ tabIndex: interactive ? 0 : void 0,
179
+ onClick: interactive ? handleClick : void 0,
180
+ onKeyDown: interactive ? (e) => {
181
+ if (e.key === "Enter" || e.key === " ") {
182
+ e.preventDefault();
183
+ handleClick();
184
+ }
185
+ } : void 0,
186
+ style: {
187
+ opacity: 0.95,
188
+ padding: "1px 4px",
189
+ borderRadius: 3,
190
+ cursor: interactive ? "pointer" : "default",
191
+ background: selected ? "rgba(255,255,255,0.12)" : "transparent"
192
+ },
193
+ children: [
194
+ object.name,
195
+ " ",
196
+ /* @__PURE__ */ jsxs("span", { style: { opacity: 0.6 }, children: [
197
+ "(",
198
+ shortId(object.id),
199
+ ")"
200
+ ] })
201
+ ]
202
+ }
203
+ );
204
+ };
205
+ const StructureDiagBlock = ({
206
+ diag,
207
+ dim
208
+ }) => /* @__PURE__ */ jsx("div", { style: { marginTop: 8, borderTop: "1px solid rgba(255,255,255,0.1)", paddingTop: 6 }, children: diag ? /* @__PURE__ */ jsxs(Fragment, { children: [
209
+ /* @__PURE__ */ jsx("div", { style: { ...dim, marginBottom: 2 }, children: "Raw structure keys:" }),
210
+ /* @__PURE__ */ jsx("div", { style: dim, children: diag.topLevelKeys.join(", ") || "—" }),
211
+ /* @__PURE__ */ jsxs("div", { style: { ...dim, marginTop: 2 }, children: [
212
+ "Objects[]: ",
213
+ diag.rawObjectCount < 0 ? "absent" : diag.rawObjectCount,
214
+ " · ",
215
+ "Systems[]: ",
216
+ diag.rawSystemCount < 0 ? "absent" : diag.rawSystemCount
217
+ ] })
218
+ ] }) : /* @__PURE__ */ jsx("div", { style: { ...dim, marginTop: 4 }, children: "Waiting for structure…" }) });
219
+ const PositionTickDiagBlock = ({
220
+ diag,
221
+ dim
222
+ }) => {
223
+ if (!diag) return null;
224
+ return /* @__PURE__ */ jsxs("div", { style: { marginTop: 8, borderTop: "1px solid rgba(255,255,255,0.1)", paddingTop: 6 }, children: [
225
+ /* @__PURE__ */ jsx("div", { style: { ...dim, marginBottom: 2 }, children: "Last position tick:" }),
226
+ /* @__PURE__ */ jsxs("div", { style: dim, children: [
227
+ "ok ",
228
+ diag.parsedOk,
229
+ "/",
230
+ diag.requested,
231
+ diag.parseFailed > 0 && ` · parseFail ${diag.parseFailed}`,
232
+ diag.requestFailed > 0 && ` · reqFail ${diag.requestFailed}`
233
+ ] }),
234
+ /* @__PURE__ */ jsxs("div", { style: dim, children: [
235
+ "asked for:",
236
+ " ",
237
+ diag.requestedProps ? diag.requestedProps.join(", ") : "getAllProperties (fallback)"
238
+ ] }),
239
+ diag.firstResponseKeys.length > 0 && /* @__PURE__ */ jsxs("div", { style: { ...dim, wordBreak: "break-all" }, children: [
240
+ "got keys: ",
241
+ diag.firstResponseKeys.slice(0, 12).join(", "),
242
+ diag.firstResponseKeys.length > 12 && " …"
243
+ ] }),
244
+ diag.firstResponseSample && /* @__PURE__ */ jsx("div", { style: { ...dim, wordBreak: "break-all" }, children: diag.firstResponseSample })
245
+ ] });
246
+ };
247
+ function recommendedFlyDistance(kind) {
248
+ switch (kind) {
249
+ case "spacecraft":
250
+ return 2e6;
251
+ case "groundStation":
252
+ return 15e5;
253
+ case "celestialBody":
254
+ return 5e7;
255
+ }
256
+ }
257
+ export {
258
+ ObjectInventoryPanel,
259
+ recommendedFlyDistance
260
+ };
261
+ //# sourceMappingURL=ObjectInventoryPanel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ObjectInventoryPanel.js","sources":["../../../src/react/panels/ObjectInventoryPanel.tsx"],"sourcesContent":["/**\n * @zendir/ui - ObjectInventoryPanel\n *\n * Themed list of simulation objects, grouped by `kind` (spacecraft /\n * ground stations / celestial bodies). Pair with `useSimulationScene`:\n *\n * ```tsx\n * const scene = useSimulationScene({ client, containerId, simulationId });\n * <ObjectInventoryPanel\n * objects={scene.objects}\n * onSelect={(obj) => zenRef.current?.focusOn(obj.id, {\n * zoom: recommendedFlyDistance(obj.kind),\n * })}\n * />\n * ```\n *\n * The panel renders the toggle UI only — it has no opinion about WHAT\n * happens on select. The consumer wires `onSelect` to its 3D viewer's\n * imperative handle (or anywhere else — the panel is reusable in any\n * dashboard or debug context).\n */\n\nimport { memo, useCallback, useState } from 'react';\nimport type { CSSProperties, ReactNode } from 'react';\nimport { useThemeTokens } from '../theme/ThemeProvider';\nimport { useSpatialSelection } from '../context/SpatialSelectionContext';\nimport type { SimulationSceneObject, StructureDiag, PositionTickDiag } from '../hooks/useSimulationScene';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type ObjectInventoryKind = SimulationSceneObject['kind'];\n\nexport interface ObjectInventoryPanelProps {\n /** Objects to list. Typically `useSimulationScene().objects`. */\n objects: SimulationSceneObject[];\n /** Click handler — fires with the clicked object. Pure forward; the\n * panel does not navigate, focus, or fly anywhere itself. */\n onSelect?: (object: SimulationSceneObject) => void;\n /** Currently selected object id (highlights the row). Pass `null` for none. */\n selectedId?: string | null;\n\n /** Where to anchor the panel on its parent. Default: `top-left`. */\n position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';\n /** Pixel offsets from the anchor. Defaults to (16, 56) so a top bar fits above. */\n offset?: { x?: number; y?: number };\n /** Hide the panel without unmounting (preserves `selectedId` state). */\n hidden?: boolean;\n /** Width in pixels. Default 330. */\n width?: number;\n /** Maximum height (CSS value). Default `'50vh'`. */\n maxHeight?: string | number;\n\n /** Allow the user to collapse / expand the panel. Default true. */\n collapsible?: boolean;\n /** Initial collapsed state. Default false. */\n defaultCollapsed?: boolean;\n\n /** Optional title override. Default: `\"API Objects · N\"`. */\n title?: ReactNode;\n\n /**\n * Optional structure diagnostic block (raw keys + counts from\n * getSimulationStructure). Useful in dev / debug builds; omit in\n * production for a cleaner look.\n */\n structureDiag?: StructureDiag | null;\n /** Optional position-tick diagnostic block. */\n positionTickDiag?: PositionTickDiag | null;\n\n /** Custom labels per kind (defaults: \"Spacecraft\", \"Ground Stations\", \"Celestial Bodies\"). */\n kindLabels?: Partial<Record<ObjectInventoryKind, string>>;\n\n /** Inline style override on the outer container. */\n style?: CSSProperties;\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\n/** Stable short-form of a GUID for inline display. */\nfunction shortId(id: string): string {\n return id.length <= 8 ? id : id.slice(0, 8);\n}\n\nconst DEFAULT_KIND_LABELS: Record<ObjectInventoryKind, string> = {\n spacecraft: 'Spacecraft',\n groundStation: 'Ground Stations',\n celestialBody: 'Celestial Bodies',\n};\n\nconst KIND_ORDER: ObjectInventoryKind[] = ['spacecraft', 'groundStation', 'celestialBody'];\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\nexport const ObjectInventoryPanel = memo(function ObjectInventoryPanel({\n objects,\n onSelect,\n selectedId,\n position = 'top-left',\n offset,\n hidden = false,\n width = 330,\n maxHeight = '50vh',\n collapsible = true,\n defaultCollapsed = false,\n title,\n structureDiag,\n positionTickDiag,\n kindLabels,\n style,\n}: ObjectInventoryPanelProps) {\n const tokens = useThemeTokens();\n const [collapsed, setCollapsed] = useState(defaultCollapsed);\n\n // Optional <SpatialSelectionProvider> integration. When present, the\n // panel uses the shared selection state so other components (3D\n // viewer, GroundTrackMap, etc.) stay synchronised. Explicit props\n // always win — consumers can still override per-instance.\n const ctx = useSpatialSelection();\n const effectiveSelectedId = selectedId !== undefined ? selectedId : ctx?.selectedId ?? null;\n const effectiveOnSelect = useCallback(\n (obj: SimulationSceneObject) => {\n onSelect?.(obj);\n // Also push into the shared context if no explicit onSelect wins it.\n if (!onSelect) ctx?.setSelectedId(obj.id);\n },\n [onSelect, ctx],\n );\n\n const labels = { ...DEFAULT_KIND_LABELS, ...kindLabels };\n const entries = Array.isArray(objects) ? objects : [];\n\n // Position anchor — translate the prop into top/left/right/bottom CSS.\n const offX = offset?.x ?? 16;\n const offY = offset?.y ?? 56;\n const anchor: CSSProperties = (() => {\n switch (position) {\n case 'top-right': return { top: offY, right: offX };\n case 'bottom-left': return { bottom: offY, left: offX };\n case 'bottom-right': return { bottom: offY, right: offX };\n case 'top-left':\n default: return { top: offY, left: offX };\n }\n })();\n\n if (hidden) return null;\n\n const dim: CSSProperties = { opacity: 0.55, fontSize: '0.9em' };\n\n return (\n <div\n data-testid=\"zendir-object-inventory\"\n style={{\n position: 'absolute',\n zIndex: 2,\n width,\n maxHeight,\n overflow: 'auto',\n padding: tokens.spacing.sm,\n borderRadius: tokens.borderRadius?.md ?? 6,\n border: '1px solid rgba(255,255,255,0.12)',\n background: 'rgba(0,0,0,0.72)',\n color: tokens.colors.text.primary,\n fontSize: tokens.typography.fontSize.xs,\n lineHeight: 1.35,\n ...anchor,\n ...style,\n }}\n >\n <PanelHeader\n title={title ?? `API Objects · ${entries.length}`}\n collapsible={collapsible}\n collapsed={collapsed}\n onToggle={() => setCollapsed((c) => !c)}\n />\n\n {!collapsed && (\n <>\n {KIND_ORDER.map((kind) => {\n const items = entries.filter((o) => o.kind === kind);\n return (\n <ObjectInventoryGroup\n key={kind}\n title={`${labels[kind]} (${items.length})`}\n items={items}\n selectedId={effectiveSelectedId}\n onSelect={onSelect != null ? onSelect : (ctx ? effectiveOnSelect : undefined)}\n />\n );\n })}\n\n {structureDiag !== undefined && (\n <StructureDiagBlock diag={structureDiag} dim={dim} />\n )}\n\n {positionTickDiag !== undefined && (\n <PositionTickDiagBlock diag={positionTickDiag} dim={dim} />\n )}\n </>\n )}\n </div>\n );\n});\n\n// ─── Sub-components ───────────────────────────────────────────────────────────\n\nconst PanelHeader = ({\n title,\n collapsible,\n collapsed,\n onToggle,\n}: {\n title: ReactNode;\n collapsible: boolean;\n collapsed: boolean;\n onToggle: () => void;\n}) => {\n if (!collapsible) {\n return <div style={{ marginBottom: 6, fontWeight: 600 }}>{title}</div>;\n }\n return (\n <button\n type=\"button\"\n onClick={onToggle}\n aria-expanded={!collapsed}\n style={{\n display: 'flex',\n width: '100%',\n alignItems: 'center',\n justifyContent: 'space-between',\n marginBottom: 6,\n padding: 0,\n background: 'none',\n border: 'none',\n color: 'inherit',\n font: 'inherit',\n fontWeight: 600,\n cursor: 'pointer',\n textAlign: 'left',\n }}\n >\n <span>{title}</span>\n <span aria-hidden style={{ opacity: 0.7, fontSize: '0.85em' }}>\n {collapsed ? '▸' : '▾'}\n </span>\n </button>\n );\n};\n\nconst ObjectInventoryGroup = ({\n title,\n items,\n selectedId,\n onSelect,\n}: {\n title: string;\n items: SimulationSceneObject[];\n selectedId: string | null;\n onSelect?: (obj: SimulationSceneObject) => void;\n}) => (\n <div style={{ marginBottom: 6 }}>\n <div style={{ opacity: 0.85 }}>{title}</div>\n {items.length === 0 ? (\n <div style={{ opacity: 0.6 }}>—</div>\n ) : (\n items.map((item) => (\n <ObjectRow\n key={item.id}\n object={item}\n selected={item.id === selectedId}\n onSelect={onSelect}\n />\n ))\n )}\n </div>\n);\n\nconst ObjectRow = ({\n object,\n selected,\n onSelect,\n}: {\n object: SimulationSceneObject;\n selected: boolean;\n onSelect?: (obj: SimulationSceneObject) => void;\n}) => {\n const handleClick = useCallback(() => onSelect?.(object), [onSelect, object]);\n const interactive = onSelect != null;\n return (\n <div\n role={interactive ? 'button' : undefined}\n tabIndex={interactive ? 0 : undefined}\n onClick={interactive ? handleClick : undefined}\n onKeyDown={\n interactive\n ? (e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n handleClick();\n }\n }\n : undefined\n }\n style={{\n opacity: 0.95,\n padding: '1px 4px',\n borderRadius: 3,\n cursor: interactive ? 'pointer' : 'default',\n background: selected ? 'rgba(255,255,255,0.12)' : 'transparent',\n }}\n >\n {object.name} <span style={{ opacity: 0.6 }}>({shortId(object.id)})</span>\n </div>\n );\n};\n\nconst StructureDiagBlock = ({\n diag,\n dim,\n}: {\n diag: StructureDiag | null | undefined;\n dim: CSSProperties;\n}) => (\n <div style={{ marginTop: 8, borderTop: '1px solid rgba(255,255,255,0.1)', paddingTop: 6 }}>\n {diag ? (\n <>\n <div style={{ ...dim, marginBottom: 2 }}>Raw structure keys:</div>\n <div style={dim}>{diag.topLevelKeys.join(', ') || '—'}</div>\n <div style={{ ...dim, marginTop: 2 }}>\n Objects[]: {diag.rawObjectCount < 0 ? 'absent' : diag.rawObjectCount}\n {' · '}\n Systems[]: {diag.rawSystemCount < 0 ? 'absent' : diag.rawSystemCount}\n </div>\n </>\n ) : (\n <div style={{ ...dim, marginTop: 4 }}>Waiting for structure…</div>\n )}\n </div>\n);\n\nconst PositionTickDiagBlock = ({\n diag,\n dim,\n}: {\n diag: PositionTickDiag | null | undefined;\n dim: CSSProperties;\n}) => {\n if (!diag) return null;\n return (\n <div style={{ marginTop: 8, borderTop: '1px solid rgba(255,255,255,0.1)', paddingTop: 6 }}>\n <div style={{ ...dim, marginBottom: 2 }}>Last position tick:</div>\n <div style={dim}>\n ok {diag.parsedOk}/{diag.requested}\n {diag.parseFailed > 0 && ` · parseFail ${diag.parseFailed}`}\n {diag.requestFailed > 0 && ` · reqFail ${diag.requestFailed}`}\n </div>\n <div style={dim}>\n asked for:{' '}\n {diag.requestedProps\n ? diag.requestedProps.join(', ')\n : 'getAllProperties (fallback)'}\n </div>\n {diag.firstResponseKeys.length > 0 && (\n <div style={{ ...dim, wordBreak: 'break-all' }}>\n got keys: {diag.firstResponseKeys.slice(0, 12).join(', ')}\n {diag.firstResponseKeys.length > 12 && ' …'}\n </div>\n )}\n {diag.firstResponseSample && (\n <div style={{ ...dim, wordBreak: 'break-all' }}>{diag.firstResponseSample}</div>\n )}\n </div>\n );\n};\n\n// ─── Companion helper ─────────────────────────────────────────────────────────\n\n/**\n * Sensible default fly-to distance per object kind, for use with\n * `<ZenSpace3D />`'s imperative `focusOn(id, { zoom })` handle.\n *\n * spacecraft: 2 000 000 m (≈ 2 000 km — close orbital view)\n * groundStation: 1 500 000 m (≈ 1 500 km — high-altitude angled view)\n * celestialBody: 50 000 000 m (≈ 50 000 km — full-planet view)\n *\n * Override per call if the consumer wants tighter/looser framing.\n */\nexport function recommendedFlyDistance(kind: ObjectInventoryKind): number {\n switch (kind) {\n case 'spacecraft': return 2_000_000;\n case 'groundStation': return 1_500_000;\n case 'celestialBody': return 50_000_000;\n }\n}\n\nexport default ObjectInventoryPanel;\n"],"names":["ObjectInventoryPanel"],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @zendir/ui - Floating UI panels for live 3D dashboards.
3
+ *
4
+ * Both panels are **renderless** w.r.t. their data:
5
+ * • `<ObjectInventoryPanel />` lists objects and emits `onSelect`.
6
+ * • `<LayerControlPanel />` emits visibility / layer-flag changes.
7
+ *
8
+ * The consumer wires the callbacks to `<ZenSpace3D />`'s imperative
9
+ * handle and props. Same separation of concerns as `<GroundTrackMap />`'s
10
+ * `MapLayerDef` pattern — UI here, rendering there.
11
+ */
12
+ export { ObjectInventoryPanel, recommendedFlyDistance } from './ObjectInventoryPanel';
13
+ export type { ObjectInventoryPanelProps, ObjectInventoryKind, } from './ObjectInventoryPanel';
14
+ export { LayerControlPanel } from './LayerControlPanel';
15
+ export type { LayerControlPanelProps, RenderLayers, GroupVisibility, GroupCounts, } from './LayerControlPanel';
@@ -157,6 +157,8 @@ export interface ThemeTokens {
157
157
  typography: {
158
158
  fontFamily: {
159
159
  primary: string;
160
+ /** Optional heading face. Defaults to Sora, falls back to primary. */
161
+ heading?: string;
160
162
  mono: string;
161
163
  };
162
164
  display: {
@@ -1,5 +1,6 @@
1
1
  import { jsxs, jsx } from "react/jsx-runtime";
2
- import { createContext, useMemo, useState, useEffect, useContext } from "react";
2
+ import { useState, useEffect, useMemo, useContext, createContext } from "react";
3
+ import { typography, borderRadius, spacing } from "../../tokens/index.js";
3
4
  function adjustHexBrightness(hex, factor) {
4
5
  const normalized = hex.replace("#", "").trim();
5
6
  const expanded = normalized.length === 3 ? normalized.split("").map((ch) => ch + ch).join("") : normalized;
@@ -58,6 +59,14 @@ const animationTokens = {
58
59
  slow: 400
59
60
  }
60
61
  };
62
+ const animationTokensReduced = {
63
+ fast: "all 0.01ms linear",
64
+ normal: "all 0.01ms linear",
65
+ slow: "all 0.01ms linear",
66
+ spring: "all 0.01ms linear",
67
+ easing: animationTokens.easing,
68
+ duration: { instant: 0, fast: 0, normal: 0, slow: 0 }
69
+ };
61
70
  const focusTokensDark = {
62
71
  color: "#4dacff",
63
72
  width: "2px",
@@ -190,26 +199,10 @@ const astroThemeDark = {
190
199
  disabled: "#6b7280"
191
200
  }
192
201
  },
193
- spacing: {
194
- xxs: "2px",
195
- xs: "4px",
196
- sm: "8px",
197
- smd: "12px",
198
- md: "16px",
199
- mdl: "20px",
200
- lg: "24px",
201
- xl: "32px",
202
- xxl: "48px"
203
- },
204
- borderRadius: {
205
- none: "0",
206
- xs: "1px",
207
- sm: "2px",
208
- md: "4px",
209
- lg: "8px",
210
- xl: "12px",
211
- full: "9999px"
212
- },
202
+ // spacing + borderRadius are sourced from tokens/index.ts (single source
203
+ // of truth — see comment at the top of this file).
204
+ spacing: { ...spacing },
205
+ borderRadius: { ...borderRadius },
213
206
  elementSize: {
214
207
  sm: "28px",
215
208
  md: "36px",
@@ -217,15 +210,13 @@ const astroThemeDark = {
217
210
  },
218
211
  typography: {
219
212
  /**
220
- * Font families per AstroUXDS specification
213
+ * Font families sourced from src/tokens/index.ts. Canonical stacks:
214
+ * primary: Public Sans → Roboto → system
215
+ * heading: Sora → Roboto → system
216
+ * mono: Roboto Mono → system mono
221
217
  * @see https://www.astrouxds.com/foundations/typography/
222
218
  */
223
- fontFamily: {
224
- /** Primary font: Roboto with comprehensive system fallbacks */
225
- primary: '"Roboto", -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, sans-serif',
226
- /** Monospace font: Roboto Mono for data/code with system fallbacks */
227
- mono: '"Roboto Mono", "SF Mono", "Consolas", "Liberation Mono", monospace'
228
- },
219
+ fontFamily: { ...typography.fontFamily },
229
220
  // Astro UX Display Styles
230
221
  // https://www.astrouxds.com/foundations/typography/
231
222
  display: {
@@ -256,30 +247,11 @@ const astroThemeDark = {
256
247
  1: { fontSize: "1rem", fontWeight: 400, letterSpacing: "0.5px", lineHeight: "1.25rem" },
257
248
  "1Bold": { fontSize: "1rem", fontWeight: 700, letterSpacing: "0.5px", lineHeight: "1.25rem" }
258
249
  },
259
- // Legacy convenience tokens (rem-based for Astro compliance)
260
- // Note: Use Typography component for new code
261
- fontSize: {
262
- micro: "0.5625rem",
263
- // 9px - very tight spaces
264
- xxs: "0.625rem",
265
- // 10px - compact labels (xs in old system)
266
- xs: "0.625rem",
267
- // 10px - compact (kept for backward compatibility)
268
- sm: "0.75rem",
269
- // 12px (Body 3)
270
- base: "0.875rem",
271
- // 14px (Body 2)
272
- md: "0.875rem",
273
- // 14px (Body 2)
274
- lg: "1rem",
275
- // 16px (Body 1)
276
- xl: "1.25rem",
277
- // 20px (Heading 3/4)
278
- xxl: "1.5rem",
279
- // 24px (Heading 2)
280
- xxxl: "2.125rem"
281
- // 34px (Heading 1)
282
- },
250
+ // Convenience numeric scale sourced from tokens/index.ts. Prefer the
251
+ // semantically-named display/heading/body/control style objects above
252
+ // for new code; this scale is kept for the ~300 existing components
253
+ // that consume `tokens.typography.fontSize.*` directly.
254
+ fontSize: { ...typography.fontSize },
283
255
  /**
284
256
  * Font weights per AstroUXDS specification
285
257
  * Only 300, 400, 500, 700 are officially supported
@@ -986,13 +958,19 @@ function ThemeProvider({
986
958
  return {
987
959
  ...base,
988
960
  colors: resolvedColors,
989
- borders: computeBorderTokens(resolvedColors)
961
+ borders: computeBorderTokens(resolvedColors),
962
+ // Honor prefers-reduced-motion in the JS token surface too. The
963
+ // global CSS @media rule below already strips CSS animations; this
964
+ // ensures JS-driven animation libraries (Framer Motion etc.) that
965
+ // read `tokens.animation.*` get the collapsed variant without each
966
+ // developer having to branch on `useTheme().prefersReducedMotion`.
967
+ animation: prefersReducedMotion ? animationTokensReduced : base.animation
990
968
  };
991
- }, [theme, mode, accentColor]);
969
+ }, [theme, mode, accentColor, prefersReducedMotion]);
992
970
  useEffect(() => {
993
971
  if (typeof document === "undefined") return;
994
972
  const root = document.documentElement;
995
- const { colors, typography, spacing, borderRadius, animation, focus, shadows } = tokens;
973
+ const { colors, typography: typography2, spacing: spacing2, borderRadius: borderRadius2, animation, focus, shadows } = tokens;
996
974
  root.setAttribute("data-theme", mode);
997
975
  root.setAttribute("data-variant", theme);
998
976
  root.style.setProperty("--color-background-base", colors.background.base);
@@ -1019,22 +997,22 @@ function ThemeProvider({
1019
997
  root.style.setProperty("--color-accent-primary", colors.accent.primary);
1020
998
  root.style.setProperty("--color-accent-secondary", colors.accent.secondary);
1021
999
  root.style.setProperty("--color-accent-tertiary", colors.accent.tertiary);
1022
- root.style.setProperty("--font-family-heading", typography.fontFamily.heading || typography.fontFamily.primary);
1023
- root.style.setProperty("--font-family-primary", typography.fontFamily.primary);
1024
- root.style.setProperty("--font-family-mono", typography.fontFamily.mono);
1025
- root.style.setProperty("--spacing-xxs", spacing.xxs);
1026
- root.style.setProperty("--spacing-xs", spacing.xs);
1027
- root.style.setProperty("--spacing-sm", spacing.sm);
1028
- root.style.setProperty("--spacing-smd", spacing.smd);
1029
- root.style.setProperty("--spacing-md", spacing.md);
1030
- root.style.setProperty("--spacing-mdl", spacing.mdl);
1031
- root.style.setProperty("--spacing-lg", spacing.lg);
1032
- root.style.setProperty("--spacing-xl", spacing.xl);
1033
- root.style.setProperty("--spacing-xxl", spacing.xxl);
1034
- root.style.setProperty("--radius-sm", borderRadius.sm);
1035
- root.style.setProperty("--radius-md", borderRadius.md);
1036
- root.style.setProperty("--radius-lg", borderRadius.lg);
1037
- root.style.setProperty("--radius-xl", borderRadius.xl);
1000
+ root.style.setProperty("--font-family-heading", typography2.fontFamily.heading || typography2.fontFamily.primary);
1001
+ root.style.setProperty("--font-family-primary", typography2.fontFamily.primary);
1002
+ root.style.setProperty("--font-family-mono", typography2.fontFamily.mono);
1003
+ root.style.setProperty("--spacing-xxs", spacing2.xxs);
1004
+ root.style.setProperty("--spacing-xs", spacing2.xs);
1005
+ root.style.setProperty("--spacing-sm", spacing2.sm);
1006
+ root.style.setProperty("--spacing-smd", spacing2.smd);
1007
+ root.style.setProperty("--spacing-md", spacing2.md);
1008
+ root.style.setProperty("--spacing-mdl", spacing2.mdl);
1009
+ root.style.setProperty("--spacing-lg", spacing2.lg);
1010
+ root.style.setProperty("--spacing-xl", spacing2.xl);
1011
+ root.style.setProperty("--spacing-xxl", spacing2.xxl);
1012
+ root.style.setProperty("--radius-sm", borderRadius2.sm);
1013
+ root.style.setProperty("--radius-md", borderRadius2.md);
1014
+ root.style.setProperty("--radius-lg", borderRadius2.lg);
1015
+ root.style.setProperty("--radius-xl", borderRadius2.xl);
1038
1016
  const motionMultiplier = prefersReducedMotion ? 0 : 1;
1039
1017
  root.style.setProperty("--animation-fast", prefersReducedMotion ? "none" : animation.fast);
1040
1018
  root.style.setProperty("--animation-normal", prefersReducedMotion ? "none" : animation.normal);
@@ -1051,7 +1029,7 @@ function ThemeProvider({
1051
1029
  root.style.setProperty("--shadow-xl", shadows.xl);
1052
1030
  document.body.style.backgroundColor = colors.background.base;
1053
1031
  document.body.style.color = colors.text.primary;
1054
- document.body.style.fontFamily = typography.fontFamily.primary;
1032
+ document.body.style.fontFamily = typography2.fontFamily.primary;
1055
1033
  }, [tokens, theme, mode, prefersReducedMotion]);
1056
1034
  const scrollbarCSS = useMemo(() => buildScrollbarCSS(tokens, mode, prefersReducedMotion), [tokens, mode, prefersReducedMotion]);
1057
1035
  const value = useMemo(