@zendir/ui 0.5.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 (391) hide show
  1. package/CHANGELOG.md +419 -386
  2. package/README.md +640 -631
  3. package/dist/_virtual/_rolldown/runtime.js +13 -0
  4. package/dist/index.js +17 -221
  5. package/dist/react/3d/CesiumCaptureSource.d.ts +0 -1
  6. package/dist/react/3d/CesiumCaptureSource.js +364 -283
  7. package/dist/react/3d/CesiumCaptureSource.js.map +1 -1
  8. package/dist/react/3d/ZenSpace3D.d.ts +2 -3
  9. package/dist/react/3d/ZenSpace3D.js +1681 -1237
  10. package/dist/react/3d/ZenSpace3D.js.map +1 -1
  11. package/dist/react/3d/ZenSpace3DCesium.d.ts +0 -1
  12. package/dist/react/3d/ZenSpace3DCesium.js +513 -560
  13. package/dist/react/3d/ZenSpace3DCesium.js.map +1 -1
  14. package/dist/react/3d/ZenSpace3DShaders.js +61 -66
  15. package/dist/react/3d/ZenSpace3DShaders.js.map +1 -1
  16. package/dist/react/3d/ZenSpace3DTypes.d.ts +19 -20
  17. package/dist/react/3d/ZenSpace3DUtils.d.ts +0 -1
  18. package/dist/react/3d/ZenSpace3DUtils.js +37 -24
  19. package/dist/react/3d/ZenSpace3DUtils.js.map +1 -1
  20. package/dist/react/3d/capturePngAnalysis.js +42 -36
  21. package/dist/react/3d/capturePngAnalysis.js.map +1 -1
  22. package/dist/react/3d/index.d.ts +3 -3
  23. package/dist/react/3d/index.js +5 -53
  24. package/dist/react/3d/threeLoader.d.ts +1 -1
  25. package/dist/react/3d/threeLoader.js +23 -18
  26. package/dist/react/3d/threeLoader.js.map +1 -1
  27. package/dist/react/astro/ClassificationBanner.d.ts +2 -3
  28. package/dist/react/astro/ClassificationBanner.js +95 -79
  29. package/dist/react/astro/ClassificationBanner.js.map +1 -1
  30. package/dist/react/astro/GlobalStatusBar.d.ts +0 -1
  31. package/dist/react/astro/GlobalStatusBar.js +157 -163
  32. package/dist/react/astro/GlobalStatusBar.js.map +1 -1
  33. package/dist/react/astro/MissionClock.d.ts +7 -8
  34. package/dist/react/astro/MissionClock.js +377 -400
  35. package/dist/react/astro/MissionClock.js.map +1 -1
  36. package/dist/react/astro/MonitoringIcon.d.ts +1 -2
  37. package/dist/react/astro/MonitoringIcon.js +422 -358
  38. package/dist/react/astro/MonitoringIcon.js.map +1 -1
  39. package/dist/react/astro/Notification.d.ts +1 -2
  40. package/dist/react/astro/Notification.js +229 -126
  41. package/dist/react/astro/Notification.js.map +1 -1
  42. package/dist/react/astro/Progress.d.ts +0 -1
  43. package/dist/react/astro/Progress.js +199 -141
  44. package/dist/react/astro/Progress.js.map +1 -1
  45. package/dist/react/astro/SimulationControls.d.ts +3 -4
  46. package/dist/react/astro/SimulationControls.js +596 -650
  47. package/dist/react/astro/SimulationControls.js.map +1 -1
  48. package/dist/react/astro/StatusIndicator.d.ts +0 -1
  49. package/dist/react/astro/StatusIndicator.js +236 -170
  50. package/dist/react/astro/StatusIndicator.js.map +1 -1
  51. package/dist/react/astro/UnifiedTimeline.d.ts +9 -10
  52. package/dist/react/astro/UnifiedTimeline.js +2345 -2392
  53. package/dist/react/astro/UnifiedTimeline.js.map +1 -1
  54. package/dist/react/astro/index.d.ts +5 -5
  55. package/dist/react/astro/index.js +1 -25
  56. package/dist/react/cards/AccessCard.d.ts +0 -1
  57. package/dist/react/cards/AccessCard.js +471 -405
  58. package/dist/react/cards/AccessCard.js.map +1 -1
  59. package/dist/react/cards/OrbitCard.d.ts +0 -1
  60. package/dist/react/cards/OrbitCard.js +487 -364
  61. package/dist/react/cards/OrbitCard.js.map +1 -1
  62. package/dist/react/cards/SpacecraftCard.d.ts +0 -1
  63. package/dist/react/cards/SpacecraftCard.js +813 -925
  64. package/dist/react/cards/SpacecraftCard.js.map +1 -1
  65. package/dist/react/cards/TelemetryCard.d.ts +0 -1
  66. package/dist/react/cards/TelemetryCard.js +1055 -732
  67. package/dist/react/cards/TelemetryCard.js.map +1 -1
  68. package/dist/react/cards/TelemetryStreamCard.d.ts +0 -1
  69. package/dist/react/cards/TelemetryStreamCard.js +323 -297
  70. package/dist/react/cards/TelemetryStreamCard.js.map +1 -1
  71. package/dist/react/cards/index.d.ts +1 -1
  72. package/dist/react/cards/index.js +1 -8
  73. package/dist/react/charts/GroundTrackMap.d.ts +6 -7
  74. package/dist/react/charts/GroundTrackMap.js +1377 -1256
  75. package/dist/react/charts/GroundTrackMap.js.map +1 -1
  76. package/dist/react/charts/GroundTrackMapLeaflet.d.ts +1 -2
  77. package/dist/react/charts/GroundTrackMapLeaflet.js +1065 -987
  78. package/dist/react/charts/GroundTrackMapLeaflet.js.map +1 -1
  79. package/dist/react/charts/groundTrackMapLeafletTiles.js +34 -25
  80. package/dist/react/charts/groundTrackMapLeafletTiles.js.map +1 -1
  81. package/dist/react/charts/groundTrackMapLeafletUtils.js +70 -61
  82. package/dist/react/charts/groundTrackMapLeafletUtils.js.map +1 -1
  83. package/dist/react/charts/index.d.ts +1 -1
  84. package/dist/react/charts/index.js +7 -100
  85. package/dist/react/charts/unified/AstroChart.d.ts +1 -2
  86. package/dist/react/charts/unified/AstroChart.js +1331 -1411
  87. package/dist/react/charts/unified/AstroChart.js.map +1 -1
  88. package/dist/react/charts/unified/PowerOverviewChart.d.ts +3 -3
  89. package/dist/react/charts/unified/PowerOverviewChart.js +495 -478
  90. package/dist/react/charts/unified/PowerOverviewChart.js.map +1 -1
  91. package/dist/react/charts/unified/domain.d.ts +12 -13
  92. package/dist/react/charts/unified/domain.js +3999 -3145
  93. package/dist/react/charts/unified/domain.js.map +1 -1
  94. package/dist/react/charts/unified/generators.d.ts +3 -4
  95. package/dist/react/charts/unified/generators.js +561 -498
  96. package/dist/react/charts/unified/generators.js.map +1 -1
  97. package/dist/react/charts/unified/index.d.ts +1 -1
  98. package/dist/react/charts/unified/presets.d.ts +14 -15
  99. package/dist/react/charts/unified/presets.js +857 -994
  100. package/dist/react/charts/unified/presets.js.map +1 -1
  101. package/dist/react/charts/unified/sync.d.ts +2 -3
  102. package/dist/react/charts/unified/sync.js +229 -195
  103. package/dist/react/charts/unified/sync.js.map +1 -1
  104. package/dist/react/charts/unified/theme.d.ts +11 -12
  105. package/dist/react/charts/unified/theme.js +606 -642
  106. package/dist/react/charts/unified/theme.js.map +1 -1
  107. package/dist/react/charts/unified/types.d.ts +32 -33
  108. package/dist/react/charts/unified/useChartStream.d.ts +1 -2
  109. package/dist/react/charts/unified/useChartStream.js +203 -216
  110. package/dist/react/charts/unified/useChartStream.js.map +1 -1
  111. package/dist/react/chatgpt/AppCard.d.ts +1 -2
  112. package/dist/react/chatgpt/index.d.ts +7 -7
  113. package/dist/react/chatgpt/index.js +160 -139
  114. package/dist/react/chatgpt/index.js.map +1 -1
  115. package/dist/react/context/CategoryContext.d.ts +0 -1
  116. package/dist/react/context/CategoryContext.js +78 -29
  117. package/dist/react/context/CategoryContext.js.map +1 -1
  118. package/dist/react/context/DisplaySettingsContext.d.ts +1 -2
  119. package/dist/react/context/DisplaySettingsContext.js +205 -156
  120. package/dist/react/context/DisplaySettingsContext.js.map +1 -1
  121. package/dist/react/context/SpatialSelectionContext.d.ts +0 -1
  122. package/dist/react/context/SpatialSelectionContext.js +36 -7
  123. package/dist/react/context/SpatialSelectionContext.js.map +1 -1
  124. package/dist/react/core/data/DataTable.d.ts +3 -4
  125. package/dist/react/core/data/DataTable.js +473 -440
  126. package/dist/react/core/data/DataTable.js.map +1 -1
  127. package/dist/react/core/data/DataValue.d.ts +4 -5
  128. package/dist/react/core/data/DataValue.js +605 -571
  129. package/dist/react/core/data/DataValue.js.map +1 -1
  130. package/dist/react/core/data/propertyConfig.d.ts +1 -2
  131. package/dist/react/core/data/propertyConfig.js +446 -382
  132. package/dist/react/core/data/propertyConfig.js.map +1 -1
  133. package/dist/react/core/display/AstroIcon.d.ts +4 -5
  134. package/dist/react/core/display/AstroIcon.js +339 -429
  135. package/dist/react/core/display/AstroIcon.js.map +1 -1
  136. package/dist/react/core/display/Badge.d.ts +2 -3
  137. package/dist/react/core/display/Badge.js +131 -131
  138. package/dist/react/core/display/Badge.js.map +1 -1
  139. package/dist/react/core/display/CardHeader.d.ts +0 -1
  140. package/dist/react/core/display/CardHeader.js +398 -311
  141. package/dist/react/core/display/CardHeader.js.map +1 -1
  142. package/dist/react/core/display/Container.d.ts +2 -3
  143. package/dist/react/core/display/Container.js +640 -668
  144. package/dist/react/core/display/Container.js.map +1 -1
  145. package/dist/react/core/display/CopyButton.d.ts +1 -2
  146. package/dist/react/core/display/CopyButton.js +158 -101
  147. package/dist/react/core/display/CopyButton.js.map +1 -1
  148. package/dist/react/core/display/GlassCard.d.ts +3 -4
  149. package/dist/react/core/display/GlassCard.js +416 -369
  150. package/dist/react/core/display/GlassCard.js.map +1 -1
  151. package/dist/react/core/display/HeaderIconWithStatus.d.ts +0 -1
  152. package/dist/react/core/display/HeaderIconWithStatus.js +152 -152
  153. package/dist/react/core/display/HeaderIconWithStatus.js.map +1 -1
  154. package/dist/react/core/display/Icon.d.ts +2 -3
  155. package/dist/react/core/display/Icon.js +172 -143
  156. package/dist/react/core/display/Icon.js.map +1 -1
  157. package/dist/react/core/display/Typography.d.ts +24 -25
  158. package/dist/react/core/display/Typography.js +360 -178
  159. package/dist/react/core/display/Typography.js.map +1 -1
  160. package/dist/react/core/feedback/ConfirmDialog.d.ts +1 -2
  161. package/dist/react/core/feedback/ConfirmDialog.js +343 -304
  162. package/dist/react/core/feedback/ConfirmDialog.js.map +1 -1
  163. package/dist/react/core/feedback/Dialog.d.ts +2 -3
  164. package/dist/react/core/feedback/Dialog.js +189 -197
  165. package/dist/react/core/feedback/Dialog.js.map +1 -1
  166. package/dist/react/core/feedback/Toast.d.ts +2 -3
  167. package/dist/react/core/feedback/Toast.js +346 -207
  168. package/dist/react/core/feedback/Toast.js.map +1 -1
  169. package/dist/react/core/index.d.ts +26 -26
  170. package/dist/react/core/index.js +7 -119
  171. package/dist/react/core/inputs/Button.d.ts +2 -3
  172. package/dist/react/core/inputs/Button.js +281 -304
  173. package/dist/react/core/inputs/Button.js.map +1 -1
  174. package/dist/react/core/inputs/Checkbox.d.ts +2 -3
  175. package/dist/react/core/inputs/Checkbox.js +139 -127
  176. package/dist/react/core/inputs/Checkbox.js.map +1 -1
  177. package/dist/react/core/inputs/Input.d.ts +3 -4
  178. package/dist/react/core/inputs/Input.js +277 -285
  179. package/dist/react/core/inputs/Input.js.map +1 -1
  180. package/dist/react/core/inputs/LimitsBar.d.ts +1 -2
  181. package/dist/react/core/inputs/LimitsBar.js +271 -196
  182. package/dist/react/core/inputs/LimitsBar.js.map +1 -1
  183. package/dist/react/core/inputs/NumberInput.d.ts +2 -3
  184. package/dist/react/core/inputs/NumberInput.js +531 -503
  185. package/dist/react/core/inputs/NumberInput.js.map +1 -1
  186. package/dist/react/core/inputs/PinInput.d.ts +2 -3
  187. package/dist/react/core/inputs/PinInput.js +224 -207
  188. package/dist/react/core/inputs/PinInput.js.map +1 -1
  189. package/dist/react/core/inputs/Select.d.ts +1 -2
  190. package/dist/react/core/inputs/Select.js +298 -299
  191. package/dist/react/core/inputs/Select.js.map +1 -1
  192. package/dist/react/core/inputs/Toggle.d.ts +2 -3
  193. package/dist/react/core/inputs/Toggle.js +142 -148
  194. package/dist/react/core/inputs/Toggle.js.map +1 -1
  195. package/dist/react/core/layout/Box.d.ts +3 -4
  196. package/dist/react/core/layout/Box.js +118 -122
  197. package/dist/react/core/layout/Box.js.map +1 -1
  198. package/dist/react/core/layout/Center.d.ts +0 -1
  199. package/dist/react/core/layout/Center.js +45 -32
  200. package/dist/react/core/layout/Center.js.map +1 -1
  201. package/dist/react/core/layout/Divider.d.ts +1 -2
  202. package/dist/react/core/layout/Divider.js +112 -106
  203. package/dist/react/core/layout/Divider.js.map +1 -1
  204. package/dist/react/core/layout/Flex.d.ts +4 -5
  205. package/dist/react/core/layout/Flex.js +120 -124
  206. package/dist/react/core/layout/Flex.js.map +1 -1
  207. package/dist/react/core/layout/Grid.d.ts +5 -6
  208. package/dist/react/core/layout/Grid.js +131 -138
  209. package/dist/react/core/layout/Grid.js.map +1 -1
  210. package/dist/react/core/layout/Spacer.d.ts +0 -1
  211. package/dist/react/core/layout/Spacer.js +36 -29
  212. package/dist/react/core/layout/Spacer.js.map +1 -1
  213. package/dist/react/core/layout/Stack.d.ts +4 -5
  214. package/dist/react/core/layout/Stack.js +127 -70
  215. package/dist/react/core/layout/Stack.js.map +1 -1
  216. package/dist/react/core/layout/responsive.d.ts +1 -2
  217. package/dist/react/core/layout/responsive.js +25 -19
  218. package/dist/react/core/layout/responsive.js.map +1 -1
  219. package/dist/react/core/layout/useBreakpoint.d.ts +1 -1
  220. package/dist/react/core/layout/useBreakpoint.js +119 -66
  221. package/dist/react/core/layout/useBreakpoint.js.map +1 -1
  222. package/dist/react/core/navigation/AppBar.d.ts +1 -2
  223. package/dist/react/core/navigation/AppBar.js +827 -805
  224. package/dist/react/core/navigation/AppBar.js.map +1 -1
  225. package/dist/react/core/navigation/Pagination.d.ts +2 -3
  226. package/dist/react/core/navigation/Pagination.js +207 -187
  227. package/dist/react/core/navigation/Pagination.js.map +1 -1
  228. package/dist/react/core/navigation/SideNav.d.ts +5 -6
  229. package/dist/react/core/navigation/SideNav.js +855 -629
  230. package/dist/react/core/navigation/SideNav.js.map +1 -1
  231. package/dist/react/core/navigation/Tabs.d.ts +0 -1
  232. package/dist/react/core/navigation/Tabs.js +113 -119
  233. package/dist/react/core/navigation/Tabs.js.map +1 -1
  234. package/dist/react/core/overlays/Popover.d.ts +2 -3
  235. package/dist/react/core/overlays/Popover.js +303 -282
  236. package/dist/react/core/overlays/Popover.js.map +1 -1
  237. package/dist/react/core/overlays/SidePanel.d.ts +1 -2
  238. package/dist/react/core/overlays/SidePanel.js +217 -212
  239. package/dist/react/core/overlays/SidePanel.js.map +1 -1
  240. package/dist/react/core/overlays/Tooltip.d.ts +1 -2
  241. package/dist/react/core/overlays/Tooltip.js +170 -176
  242. package/dist/react/core/overlays/Tooltip.js.map +1 -1
  243. package/dist/react/core/widgets/ActivityPlanner.d.ts +0 -1
  244. package/dist/react/core/widgets/ActivityPlanner.js +855 -517
  245. package/dist/react/core/widgets/ActivityPlanner.js.map +1 -1
  246. package/dist/react/core/widgets/Capture.d.ts +2 -3
  247. package/dist/react/core/widgets/Capture.js +965 -779
  248. package/dist/react/core/widgets/Capture.js.map +1 -1
  249. package/dist/react/core/widgets/ChatPanel.d.ts +16 -17
  250. package/dist/react/core/widgets/ChatPanel.js +1734 -1034
  251. package/dist/react/core/widgets/ChatPanel.js.map +1 -1
  252. package/dist/react/core/widgets/ColorPickerPanel.d.ts +0 -1
  253. package/dist/react/core/widgets/ColorPickerPanel.js +298 -287
  254. package/dist/react/core/widgets/ColorPickerPanel.js.map +1 -1
  255. package/dist/react/core/widgets/CommandBuilder.d.ts +2 -3
  256. package/dist/react/core/widgets/CommandBuilder.js +624 -516
  257. package/dist/react/core/widgets/CommandBuilder.js.map +1 -1
  258. package/dist/react/core/widgets/ConnectionForm.d.ts +2 -3
  259. package/dist/react/core/widgets/ConnectionForm.js +528 -492
  260. package/dist/react/core/widgets/ConnectionForm.js.map +1 -1
  261. package/dist/react/core/widgets/FileExplorer.d.ts +3 -4
  262. package/dist/react/core/widgets/FileExplorer.js +617 -505
  263. package/dist/react/core/widgets/FileExplorer.js.map +1 -1
  264. package/dist/react/core/widgets/HexViewer.d.ts +2 -3
  265. package/dist/react/core/widgets/HexViewer.js +1353 -1091
  266. package/dist/react/core/widgets/HexViewer.js.map +1 -1
  267. package/dist/react/core/widgets/ImageFrame.d.ts +1 -2
  268. package/dist/react/core/widgets/ImageFrame.js +230 -193
  269. package/dist/react/core/widgets/ImageFrame.js.map +1 -1
  270. package/dist/react/core/widgets/ImageGallery.d.ts +2 -1
  271. package/dist/react/core/widgets/ImageGallery.js +380 -314
  272. package/dist/react/core/widgets/ImageGallery.js.map +1 -1
  273. package/dist/react/core/widgets/ImagePanel.d.ts +0 -1
  274. package/dist/react/core/widgets/ImagePanel.js +110 -101
  275. package/dist/react/core/widgets/ImagePanel.js.map +1 -1
  276. package/dist/react/core/widgets/LogViewer.d.ts +2 -3
  277. package/dist/react/core/widgets/LogViewer.js +711 -607
  278. package/dist/react/core/widgets/LogViewer.js.map +1 -1
  279. package/dist/react/core/widgets/MessageStream.d.ts +3 -4
  280. package/dist/react/core/widgets/MessageStream.js +514 -444
  281. package/dist/react/core/widgets/MessageStream.js.map +1 -1
  282. package/dist/react/core/widgets/MissionCalendar.d.ts +3 -4
  283. package/dist/react/core/widgets/MissionCalendar.js +1488 -1020
  284. package/dist/react/core/widgets/MissionCalendar.js.map +1 -1
  285. package/dist/react/core/widgets/PacketViewer.d.ts +2 -3
  286. package/dist/react/core/widgets/PacketViewer.js +550 -421
  287. package/dist/react/core/widgets/PacketViewer.js.map +1 -1
  288. package/dist/react/core/widgets/capture-placeholder.png.js +6 -5
  289. package/dist/react/core/widgets/capture-placeholder.png.js.map +1 -1
  290. package/dist/react/hooks/index.d.ts +5 -5
  291. package/dist/react/hooks/useAccessWindows.d.ts +0 -1
  292. package/dist/react/hooks/useCompactMode.d.ts +1 -1
  293. package/dist/react/hooks/useCompactMode.js +104 -58
  294. package/dist/react/hooks/useCompactMode.js.map +1 -1
  295. package/dist/react/hooks/useGroundTrackHistory.d.ts +0 -1
  296. package/dist/react/hooks/useSimulationScene.d.ts +1 -2
  297. package/dist/react/hooks/useSimulationScene.js +481 -374
  298. package/dist/react/hooks/useSimulationScene.js.map +1 -1
  299. package/dist/react/index.d.ts +3 -3
  300. package/dist/react/panels/LayerControlPanel.d.ts +1 -2
  301. package/dist/react/panels/LayerControlPanel.js +223 -180
  302. package/dist/react/panels/LayerControlPanel.js.map +1 -1
  303. package/dist/react/panels/ObjectInventoryPanel.d.ts +2 -3
  304. package/dist/react/panels/ObjectInventoryPanel.js +315 -250
  305. package/dist/react/panels/ObjectInventoryPanel.js.map +1 -1
  306. package/dist/react/panels/index.d.ts +1 -1
  307. package/dist/react/panels/index.js +1 -6
  308. package/dist/react/shared/ErrorBoundary.d.ts +1 -2
  309. package/dist/react/shared/ErrorBoundary.js +170 -139
  310. package/dist/react/shared/ErrorBoundary.js.map +1 -1
  311. package/dist/react/shared/Skeleton.d.ts +1 -2
  312. package/dist/react/shared/Skeleton.js +300 -301
  313. package/dist/react/shared/Skeleton.js.map +1 -1
  314. package/dist/react/theme/ThemeProvider.d.ts +2 -321
  315. package/dist/react/theme/ThemeProvider.js +313 -939
  316. package/dist/react/theme/ThemeProvider.js.map +1 -1
  317. package/dist/react/theme/cardAccent.d.ts +1 -2
  318. package/dist/react/theme/cardAccent.js +196 -126
  319. package/dist/react/theme/cardAccent.js.map +1 -1
  320. package/dist/react/theme/config.d.ts +0 -1
  321. package/dist/react/theme/config.js +47 -25
  322. package/dist/react/theme/config.js.map +1 -1
  323. package/dist/react/theme/index.d.ts +2 -2
  324. package/dist/react/theme/index.js +1 -16
  325. package/dist/react/theme/types.d.ts +332 -0
  326. package/dist/react/theme/types.js +39 -0
  327. package/dist/react/theme/types.js.map +1 -0
  328. package/dist/react/types.d.ts +8 -8
  329. package/dist/react/types.js +147 -47
  330. package/dist/react/types.js.map +1 -1
  331. package/dist/react/utils/categoryPalette.d.ts +0 -1
  332. package/dist/react/utils/categoryPalette.js +91 -102
  333. package/dist/react/utils/categoryPalette.js.map +1 -1
  334. package/dist/react/utils/index.d.ts +5 -5
  335. package/dist/react/utils/index.js +457 -371
  336. package/dist/react/utils/index.js.map +1 -1
  337. package/dist/react/visualizations/EclipseTimerCard.d.ts +0 -1
  338. package/dist/react/visualizations/LinkBudgetCard.d.ts +0 -1
  339. package/dist/react/visualizations/NavBallCard.d.ts +0 -1
  340. package/dist/react/visualizations/PropulsionCard.d.ts +1 -2
  341. package/dist/react/visualizations/SensorFootprintCard.d.ts +1 -2
  342. package/dist/react/visualizations/ThermalHeatmapCard.d.ts +0 -1
  343. package/dist/react/visualizations/index.d.ts +3 -3
  344. package/dist/react.d.ts +2 -1
  345. package/dist/react.js +17 -221
  346. package/dist/shaders/atmosphere.frag.js +6 -5
  347. package/dist/shaders/atmosphere.frag.js.map +1 -1
  348. package/dist/shaders/atmosphere.vert.js +6 -5
  349. package/dist/shaders/atmosphere.vert.js.map +1 -1
  350. package/dist/shaders/stars.frag.js +6 -5
  351. package/dist/shaders/stars.frag.js.map +1 -1
  352. package/dist/shaders/stars.vert.js +6 -5
  353. package/dist/shaders/stars.vert.js.map +1 -1
  354. package/dist/style.css +26 -3
  355. package/dist/tokens/core.d.ts +159 -0
  356. package/dist/tokens/core.js +144 -0
  357. package/dist/tokens/core.js.map +1 -0
  358. package/dist/tokens/css-vars.d.ts +213 -2
  359. package/dist/tokens/css-vars.js +590 -227
  360. package/dist/tokens/css-vars.js.map +1 -1
  361. package/dist/tokens/index.d.ts +31 -228
  362. package/dist/tokens/index.js +281 -366
  363. package/dist/tokens/index.js.map +1 -1
  364. package/dist/tokens/palettes/astro.d.ts +129 -0
  365. package/dist/tokens/palettes/astro.js +112 -0
  366. package/dist/tokens/palettes/astro.js.map +1 -0
  367. package/dist/tokens/palettes/index.d.ts +288 -0
  368. package/dist/tokens/palettes/index.js +35 -0
  369. package/dist/tokens/palettes/index.js.map +1 -0
  370. package/dist/tokens/palettes/tailwind.d.ts +324 -0
  371. package/dist/tokens/palettes/tailwind.js +201 -0
  372. package/dist/tokens/palettes/tailwind.js.map +1 -0
  373. package/dist/tokens/palettes/zendir.d.ts +126 -0
  374. package/dist/tokens/palettes/zendir.js +92 -0
  375. package/dist/tokens/palettes/zendir.js.map +1 -0
  376. package/dist/tokens/themes.d.ts +30 -0
  377. package/dist/tokens/themes.js +704 -0
  378. package/dist/tokens/themes.js.map +1 -0
  379. package/dist/tokens/tokens.css +533 -155
  380. package/dist/types/index.d.ts +2 -2
  381. package/package.json +310 -313
  382. package/dist/index.js.map +0 -1
  383. package/dist/react/3d/index.js.map +0 -1
  384. package/dist/react/astro/index.js.map +0 -1
  385. package/dist/react/cards/index.js.map +0 -1
  386. package/dist/react/charts/index.js.map +0 -1
  387. package/dist/react/core/index.js.map +0 -1
  388. package/dist/react/panels/index.js.map +0 -1
  389. package/dist/react/theme/astro-tokens.d.ts +0 -153
  390. package/dist/react/theme/index.js.map +0 -1
  391. package/dist/react.js.map +0 -1
@@ -1,804 +1,990 @@
1
- import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
- import { memo, forwardRef, useState, useRef, useEffect, useCallback, useImperativeHandle } from "react";
3
1
  import { useTheme } from "../../theme/ThemeProvider.js";
4
2
  import { useCopyToClipboard } from "../display/CopyButton.js";
5
- import placeholderImageUrl from "./capture-placeholder.png.js";
3
+ import capture_placeholder_default from "./capture-placeholder.png.js";
4
+ import { forwardRef, memo, useCallback, useEffect, useImperativeHandle, useRef, useState } from "react";
5
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
+ //#region src/react/core/widgets/Capture.tsx
7
+ /**
8
+ * @zendir/ui - Capture Component
9
+ *
10
+ * Transport-agnostic image capture server component. Generates a unique server
11
+ * ID, accepts capture requests imperatively, and returns image bytes. The host
12
+ * app (e.g. space-range-operator) handles all MQTT/transport wiring and uses
13
+ * the server ID to construct topic paths externally.
14
+ *
15
+ * Features:
16
+ * - Unique server ID with copy-to-clipboard
17
+ * - Pause/resume capture processing
18
+ * - Collapsible gallery of captured images
19
+ * - Connection status display
20
+ * - forwardRef with imperative CaptureHandle
21
+ *
22
+ * @example
23
+ * ```tsx
24
+ * const captureRef = useRef<CaptureHandle>(null);
25
+ *
26
+ * <Capture
27
+ * ref={captureRef}
28
+ * connected={isConnected}
29
+ * onReady={(serverId) => {
30
+ * mqttClient.subscribe('Zendir/SpaceImage/Request');
31
+ * }}
32
+ * onCapture={(req) => console.log('Captured', req.req_id)}
33
+ * />
34
+ *
35
+ * // When a request arrives from external transport:
36
+ * // Request: { req_id, args: { game_id, team_id, ...captureArgs } }
37
+ * const imageBytes = await captureRef.current.capture(request);
38
+ * // Publish response to: Zendir/SpaceImage/{game_id}/{team_id}/Response/{req_id}
39
+ * ```
40
+ */
41
+ /**
42
+ * Generates a visible 256x256 fallback PNG via canvas when the bundled
43
+ * astronaut image cannot be fetched (Storybook asset path, SSR, etc.).
44
+ * Shows a branded "ZENDIR" placeholder so thumbnails are never invisible.
45
+ */
6
46
  async function generateCanvasFallback() {
7
- try {
8
- const size = 256;
9
- const canvas = document.createElement("canvas");
10
- canvas.width = size;
11
- canvas.height = size;
12
- const ctx = canvas.getContext("2d");
13
- const bg = ctx.createRadialGradient(size / 2, size / 2, 20, size / 2, size / 2, size);
14
- bg.addColorStop(0, "#1e2a4a");
15
- bg.addColorStop(1, "#0a0e1a");
16
- ctx.fillStyle = bg;
17
- ctx.fillRect(0, 0, size, size);
18
- ctx.fillStyle = "rgba(255, 255, 255, 0.5)";
19
- for (let i = 0; i < 40; i++) {
20
- const sx = (i * 97 + 31) % size;
21
- const sy = (i * 61 + 17) % size;
22
- const sr = (i % 3 + 1) * 0.6;
23
- ctx.beginPath();
24
- ctx.arc(sx, sy, sr, 0, Math.PI * 2);
25
- ctx.fill();
26
- }
27
- ctx.strokeStyle = "rgba(74, 143, 255, 0.35)";
28
- ctx.lineWidth = 1;
29
- ctx.beginPath();
30
- ctx.moveTo(size / 2, 40);
31
- ctx.lineTo(size / 2, size - 40);
32
- ctx.moveTo(40, size / 2);
33
- ctx.lineTo(size - 40, size / 2);
34
- ctx.stroke();
35
- ctx.beginPath();
36
- ctx.arc(size / 2, size / 2, 50, 0, Math.PI * 2);
37
- ctx.stroke();
38
- ctx.fillStyle = "#4a8fff";
39
- ctx.font = "bold 28px monospace";
40
- ctx.textAlign = "center";
41
- ctx.textBaseline = "middle";
42
- ctx.fillText("ZENDIR", size / 2, size / 2 - 16);
43
- ctx.fillStyle = "rgba(255, 255, 255, 0.4)";
44
- ctx.font = "13px monospace";
45
- ctx.fillText("PLACEHOLDER", size / 2, size / 2 + 14);
46
- ctx.fillStyle = "rgba(255, 255, 255, 0.25)";
47
- ctx.font = "10px monospace";
48
- ctx.fillText(`${size}x${size}`, size / 2, size - 20);
49
- const blob = await new Promise(
50
- (resolve) => canvas.toBlob((b) => resolve(b), "image/png")
51
- );
52
- return new Uint8Array(await blob.arrayBuffer());
53
- } catch {
54
- return new Uint8Array([
55
- 137,
56
- 80,
57
- 78,
58
- 71,
59
- 13,
60
- 10,
61
- 26,
62
- 10,
63
- 0,
64
- 0,
65
- 0,
66
- 13,
67
- 73,
68
- 72,
69
- 68,
70
- 82,
71
- 0,
72
- 0,
73
- 0,
74
- 1,
75
- 0,
76
- 0,
77
- 0,
78
- 1,
79
- 8,
80
- 2,
81
- 0,
82
- 0,
83
- 0,
84
- 144,
85
- 119,
86
- 83,
87
- 222,
88
- 0,
89
- 0,
90
- 0,
91
- 12,
92
- 73,
93
- 68,
94
- 65,
95
- 84,
96
- 120,
97
- 156,
98
- 98,
99
- 104,
100
- 96,
101
- 96,
102
- 0,
103
- 0,
104
- 0,
105
- 4,
106
- 0,
107
- 1,
108
- 39,
109
- 52,
110
- 39,
111
- 10,
112
- 0,
113
- 0,
114
- 0,
115
- 0,
116
- 73,
117
- 69,
118
- 78,
119
- 68,
120
- 174,
121
- 66,
122
- 96,
123
- 130
124
- ]);
125
- }
47
+ try {
48
+ const size = 256;
49
+ const canvas = document.createElement("canvas");
50
+ canvas.width = size;
51
+ canvas.height = size;
52
+ const ctx = canvas.getContext("2d");
53
+ const bg = ctx.createRadialGradient(size / 2, size / 2, 20, size / 2, size / 2, size);
54
+ bg.addColorStop(0, "#1e2a4a");
55
+ bg.addColorStop(1, "#0a0e1a");
56
+ ctx.fillStyle = bg;
57
+ ctx.fillRect(0, 0, size, size);
58
+ ctx.fillStyle = "rgba(255, 255, 255, 0.5)";
59
+ for (let i = 0; i < 40; i++) {
60
+ const sx = (i * 97 + 31) % size;
61
+ const sy = (i * 61 + 17) % size;
62
+ const sr = (i % 3 + 1) * .6;
63
+ ctx.beginPath();
64
+ ctx.arc(sx, sy, sr, 0, Math.PI * 2);
65
+ ctx.fill();
66
+ }
67
+ ctx.strokeStyle = "rgba(74, 143, 255, 0.35)";
68
+ ctx.lineWidth = 1;
69
+ ctx.beginPath();
70
+ ctx.moveTo(size / 2, 40);
71
+ ctx.lineTo(size / 2, size - 40);
72
+ ctx.moveTo(40, size / 2);
73
+ ctx.lineTo(size - 40, size / 2);
74
+ ctx.stroke();
75
+ ctx.beginPath();
76
+ ctx.arc(size / 2, size / 2, 50, 0, Math.PI * 2);
77
+ ctx.stroke();
78
+ ctx.fillStyle = "#4a8fff";
79
+ ctx.font = "bold 28px monospace";
80
+ ctx.textAlign = "center";
81
+ ctx.textBaseline = "middle";
82
+ ctx.fillText("ZENDIR", size / 2, size / 2 - 16);
83
+ ctx.fillStyle = "rgba(255, 255, 255, 0.4)";
84
+ ctx.font = "13px monospace";
85
+ ctx.fillText("PLACEHOLDER", size / 2, 142);
86
+ ctx.fillStyle = "rgba(255, 255, 255, 0.25)";
87
+ ctx.font = "10px monospace";
88
+ ctx.fillText(`${size}x${size}`, size / 2, size - 20);
89
+ const blob = await new Promise((resolve) => canvas.toBlob((b) => resolve(b), "image/png"));
90
+ return new Uint8Array(await blob.arrayBuffer());
91
+ } catch {
92
+ return new Uint8Array([
93
+ 137,
94
+ 80,
95
+ 78,
96
+ 71,
97
+ 13,
98
+ 10,
99
+ 26,
100
+ 10,
101
+ 0,
102
+ 0,
103
+ 0,
104
+ 13,
105
+ 73,
106
+ 72,
107
+ 68,
108
+ 82,
109
+ 0,
110
+ 0,
111
+ 0,
112
+ 1,
113
+ 0,
114
+ 0,
115
+ 0,
116
+ 1,
117
+ 8,
118
+ 2,
119
+ 0,
120
+ 0,
121
+ 0,
122
+ 144,
123
+ 119,
124
+ 83,
125
+ 222,
126
+ 0,
127
+ 0,
128
+ 0,
129
+ 12,
130
+ 73,
131
+ 68,
132
+ 65,
133
+ 84,
134
+ 120,
135
+ 156,
136
+ 98,
137
+ 104,
138
+ 96,
139
+ 96,
140
+ 0,
141
+ 0,
142
+ 0,
143
+ 4,
144
+ 0,
145
+ 1,
146
+ 39,
147
+ 52,
148
+ 39,
149
+ 10,
150
+ 0,
151
+ 0,
152
+ 0,
153
+ 0,
154
+ 73,
155
+ 69,
156
+ 78,
157
+ 68,
158
+ 174,
159
+ 66,
160
+ 96,
161
+ 130
162
+ ]);
163
+ }
126
164
  }
127
- let placeholderCache = null;
165
+ /** Cached placeholder image bytes (lazy-loaded from bundled asset) */
166
+ var placeholderCache = null;
167
+ /**
168
+ * Loads the bundled placeholder image (astronaut). Falls back to a
169
+ * canvas-generated branded placeholder if the fetch fails (e.g. SSR,
170
+ * Storybook asset paths, missing file).
171
+ */
128
172
  async function loadPlaceholderImage() {
129
- if (placeholderCache) return placeholderCache;
130
- try {
131
- const response = await fetch(placeholderImageUrl);
132
- if (!response.ok) throw new Error(`HTTP ${response.status}`);
133
- const buf = await response.arrayBuffer();
134
- if (buf.byteLength < 100) throw new Error("Suspiciously small image");
135
- placeholderCache = new Uint8Array(buf);
136
- } catch {
137
- placeholderCache = await generateCanvasFallback();
138
- }
139
- return placeholderCache;
173
+ if (placeholderCache) return placeholderCache;
174
+ try {
175
+ const response = await fetch(capture_placeholder_default);
176
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
177
+ const buf = await response.arrayBuffer();
178
+ if (buf.byteLength < 100) throw new Error("Suspiciously small image");
179
+ placeholderCache = new Uint8Array(buf);
180
+ } catch {
181
+ placeholderCache = await generateCanvasFallback();
182
+ }
183
+ return placeholderCache;
140
184
  }
185
+ /**
186
+ * Bundled astronaut placeholder PNG (or 1x1 fallback). Exported for
187
+ * `CesiumCaptureSource` when globe capture fails (e.g. missing Cesium assets, WebGL).
188
+ */
141
189
  async function loadCapturePlaceholderImage() {
142
- return loadPlaceholderImage();
190
+ return loadPlaceholderImage();
143
191
  }
192
+ /**
193
+ * Hook for capture server logic. Generates a stable UUID, manages
194
+ * pause state, and processes capture requests into image bytes.
195
+ *
196
+ * @example
197
+ * ```tsx
198
+ * const { serverId, capture, paused, setPaused } = useCapture();
199
+ * // call capture(request) when external message arrives
200
+ * ```
201
+ */
144
202
  function useCapture(options) {
145
- const serverIdRef = useRef((options == null ? void 0 : options.serverId) || crypto.randomUUID());
146
- const [paused, setPaused] = useState(false);
147
- const [requestCount, setRequestCount] = useState(0);
148
- const [lastRequest, setLastRequest] = useState(null);
149
- const [capturedImages, setCapturedImages] = useState([]);
150
- const imageSourceRef = useRef(options == null ? void 0 : options.imageSource);
151
- imageSourceRef.current = options == null ? void 0 : options.imageSource;
152
- const pausedRef = useRef(paused);
153
- pausedRef.current = paused;
154
- const imageCacheRef = useRef(/* @__PURE__ */ new Map());
155
- const cesiumSourceRef = useRef(null);
156
- const cesiumFailedRef = useRef(false);
157
- const cesiumLoadingRef = useRef(null);
158
- const initCesium = useCallback(async () => {
159
- if (cesiumSourceRef.current || cesiumFailedRef.current) return;
160
- try {
161
- const { createCesiumCaptureSource } = await import("../../3d/CesiumCaptureSource.js");
162
- cesiumSourceRef.current = await createCesiumCaptureSource();
163
- } catch {
164
- cesiumFailedRef.current = true;
165
- }
166
- }, []);
167
- const preload = (options == null ? void 0 : options.preload) !== false;
168
- useEffect(() => {
169
- if (imageSourceRef.current || !preload) return;
170
- cesiumLoadingRef.current = initCesium();
171
- }, [preload, initCesium]);
172
- useEffect(() => {
173
- return () => {
174
- var _a;
175
- (_a = cesiumSourceRef.current) == null ? void 0 : _a.destroy();
176
- cesiumSourceRef.current = null;
177
- };
178
- }, []);
179
- const capture = useCallback(async (request) => {
180
- if (pausedRef.current) return new Uint8Array(0);
181
- if (!request || request.type !== "capture" || typeof request.req_id !== "number") {
182
- return new Uint8Array(0);
183
- }
184
- const cached = imageCacheRef.current.get(request.req_id);
185
- if (cached) return cached;
186
- let bytes;
187
- const src = imageSourceRef.current;
188
- if (src instanceof Uint8Array) {
189
- bytes = src;
190
- } else if (typeof src === "function") {
191
- bytes = await src(request.args);
192
- } else {
193
- if (!cesiumSourceRef.current && !cesiumFailedRef.current) {
194
- if (cesiumLoadingRef.current) {
195
- await cesiumLoadingRef.current;
196
- } else {
197
- await initCesium();
198
- }
199
- }
200
- if (cesiumSourceRef.current) {
201
- bytes = await cesiumSourceRef.current.capture(request.args);
202
- } else {
203
- bytes = await loadPlaceholderImage();
204
- }
205
- }
206
- imageCacheRef.current.set(request.req_id, bytes);
207
- setRequestCount((c) => c + 1);
208
- setLastRequest(request);
209
- const mimeType = request.args.format === "jpeg" ? "image/jpeg" : "image/png";
210
- const blob = new Blob([bytes], { type: mimeType });
211
- const dataUrl = URL.createObjectURL(blob);
212
- setCapturedImages((prev) => [
213
- { reqId: request.req_id, dataUrl, timestamp: Date.now(), bytes: bytes.length, args: request.args },
214
- ...prev
215
- ]);
216
- return bytes;
217
- }, []);
218
- return {
219
- serverId: serverIdRef.current,
220
- capture,
221
- paused,
222
- setPaused,
223
- requestCount,
224
- lastRequest,
225
- capturedImages
226
- };
203
+ const serverIdRef = useRef(options?.serverId || crypto.randomUUID());
204
+ const [paused, setPaused] = useState(false);
205
+ const [requestCount, setRequestCount] = useState(0);
206
+ const [lastRequest, setLastRequest] = useState(null);
207
+ const [capturedImages, setCapturedImages] = useState([]);
208
+ const imageSourceRef = useRef(options?.imageSource);
209
+ imageSourceRef.current = options?.imageSource;
210
+ const pausedRef = useRef(paused);
211
+ pausedRef.current = paused;
212
+ const imageCacheRef = useRef(/* @__PURE__ */ new Map());
213
+ const cesiumSourceRef = useRef(null);
214
+ const cesiumFailedRef = useRef(false);
215
+ const cesiumLoadingRef = useRef(null);
216
+ const initCesium = useCallback(async () => {
217
+ if (cesiumSourceRef.current || cesiumFailedRef.current) return;
218
+ try {
219
+ const { createCesiumCaptureSource } = await import("../../3d/CesiumCaptureSource.js");
220
+ cesiumSourceRef.current = await createCesiumCaptureSource();
221
+ } catch {
222
+ cesiumFailedRef.current = true;
223
+ }
224
+ }, []);
225
+ const preload = options?.preload !== false;
226
+ useEffect(() => {
227
+ if (imageSourceRef.current || !preload) return;
228
+ cesiumLoadingRef.current = initCesium();
229
+ }, [preload, initCesium]);
230
+ useEffect(() => {
231
+ return () => {
232
+ cesiumSourceRef.current?.destroy();
233
+ cesiumSourceRef.current = null;
234
+ };
235
+ }, []);
236
+ const capture = useCallback(async (request) => {
237
+ if (pausedRef.current) return /* @__PURE__ */ new Uint8Array(0);
238
+ if (!request || request.type !== "capture" || typeof request.req_id !== "number") return /* @__PURE__ */ new Uint8Array(0);
239
+ const cached = imageCacheRef.current.get(request.req_id);
240
+ if (cached) return cached;
241
+ let bytes;
242
+ const src = imageSourceRef.current;
243
+ if (src instanceof Uint8Array) bytes = src;
244
+ else if (typeof src === "function") bytes = await src(request.args);
245
+ else {
246
+ if (!cesiumSourceRef.current && !cesiumFailedRef.current) if (cesiumLoadingRef.current) await cesiumLoadingRef.current;
247
+ else await initCesium();
248
+ if (cesiumSourceRef.current) bytes = await cesiumSourceRef.current.capture(request.args);
249
+ else bytes = await loadPlaceholderImage();
250
+ }
251
+ imageCacheRef.current.set(request.req_id, bytes);
252
+ setRequestCount((c) => c + 1);
253
+ setLastRequest(request);
254
+ const mimeType = request.args.format === "jpeg" ? "image/jpeg" : "image/png";
255
+ const blob = new Blob([bytes], { type: mimeType });
256
+ const dataUrl = URL.createObjectURL(blob);
257
+ setCapturedImages((prev) => [{
258
+ reqId: request.req_id,
259
+ dataUrl,
260
+ timestamp: Date.now(),
261
+ bytes: bytes.length,
262
+ args: request.args
263
+ }, ...prev]);
264
+ return bytes;
265
+ }, []);
266
+ return {
267
+ serverId: serverIdRef.current,
268
+ capture,
269
+ paused,
270
+ setPaused,
271
+ requestCount,
272
+ lastRequest,
273
+ capturedImages
274
+ };
227
275
  }
228
- const PauseIcon = ({ size = 14 }) => /* @__PURE__ */ jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "currentColor", children: [
229
- /* @__PURE__ */ jsx("rect", { x: "6", y: "4", width: "4", height: "16", rx: "1" }),
230
- /* @__PURE__ */ jsx("rect", { x: "14", y: "4", width: "4", height: "16", rx: "1" })
231
- ] });
232
- const PlayIcon = ({ size = 14 }) => /* @__PURE__ */ jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx("polygon", { points: "6,4 20,12 6,20" }) });
233
- const ChevronIcon = ({ size = 14, expanded }) => /* @__PURE__ */ jsx(
234
- "svg",
235
- {
236
- width: size,
237
- height: size,
238
- viewBox: "0 0 24 24",
239
- fill: "none",
240
- stroke: "currentColor",
241
- strokeWidth: "2",
242
- strokeLinecap: "round",
243
- strokeLinejoin: "round",
244
- style: {
245
- transition: "transform 0.2s ease",
246
- transform: expanded ? "rotate(90deg)" : "rotate(0deg)"
247
- },
248
- children: /* @__PURE__ */ jsx("polyline", { points: "9 18 15 12 9 6" })
249
- }
250
- );
251
- const CopyIcon = ({ size = 14 }) => /* @__PURE__ */ jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
252
- /* @__PURE__ */ jsx("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }),
253
- /* @__PURE__ */ jsx("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })
254
- ] });
255
- const CheckIcon = ({ size = 14, color }) => /* @__PURE__ */ jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: color, strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("polyline", { points: "20 6 9 17 4 12" }) });
256
- const Capture = memo(forwardRef(function Capture2({
257
- connected = false,
258
- imageSource,
259
- preload,
260
- serverId: serverIdProp,
261
- onCapture,
262
- onReady,
263
- defaultGalleryExpanded = false,
264
- style
265
- }, ref) {
266
- const { tokens } = useTheme();
267
- const {
268
- serverId,
269
- capture,
270
- paused,
271
- setPaused,
272
- requestCount,
273
- capturedImages
274
- } = useCapture({ imageSource, preload, serverId: serverIdProp });
275
- const { copy, copied } = useCopyToClipboard();
276
- const [galleryExpanded, setGalleryExpanded] = useState(defaultGalleryExpanded);
277
- const [hoveredBtn, setHoveredBtn] = useState(null);
278
- const readyFiredRef = useRef(false);
279
- useEffect(() => {
280
- if (onReady && !readyFiredRef.current) {
281
- readyFiredRef.current = true;
282
- onReady(serverId);
283
- }
284
- }, [onReady, serverId]);
285
- const wrappedCapture = useCallback(async (request) => {
286
- const bytes = await capture(request);
287
- if (bytes.length > 0) {
288
- onCapture == null ? void 0 : onCapture(request);
289
- }
290
- return bytes;
291
- }, [capture, onCapture]);
292
- useImperativeHandle(ref, () => ({
293
- capture: wrappedCapture,
294
- serverId
295
- }), [wrappedCapture, serverId]);
296
- const statusColor = connected ? tokens.colors.status.normal : tokens.colors.status.off;
297
- const statusLabel = connected ? "Connected" : "Disconnected";
298
- const btnBase = {
299
- display: "inline-flex",
300
- alignItems: "center",
301
- justifyContent: "center",
302
- gap: "6px",
303
- height: 32,
304
- padding: "0 12px",
305
- fontSize: tokens.typography.fontSize.sm,
306
- fontWeight: 500,
307
- fontFamily: tokens.typography.fontFamily.primary,
308
- color: tokens.colors.text.secondary,
309
- backgroundColor: "transparent",
310
- border: `1px solid ${tokens.colors.border.muted}`,
311
- borderRadius: tokens.borderRadius.md,
312
- cursor: "pointer",
313
- transition: tokens.animation.fast,
314
- outline: "none",
315
- whiteSpace: "nowrap"
316
- };
317
- return /* @__PURE__ */ jsxs(
318
- "div",
319
- {
320
- style: {
321
- display: "flex",
322
- flexDirection: "column",
323
- borderRadius: tokens.borderRadius.lg,
324
- border: `1px solid ${tokens.colors.border.default}`,
325
- backgroundColor: tokens.colors.background.surface,
326
- overflow: "hidden",
327
- /* When gallery is expanded, allow container to stretch to fill parent */
328
- ...galleryExpanded ? { flex: 1, minHeight: 0 } : {},
329
- ...style
330
- },
331
- children: [
332
- /* @__PURE__ */ jsxs(
333
- "div",
334
- {
335
- style: {
336
- display: "flex",
337
- alignItems: "center",
338
- justifyContent: "space-between",
339
- padding: "8px 12px",
340
- gap: "12px",
341
- minHeight: 48,
342
- flexShrink: 0
343
- },
344
- children: [
345
- /* @__PURE__ */ jsxs(
346
- "button",
347
- {
348
- type: "button",
349
- onClick: () => setPaused(!paused),
350
- onMouseEnter: () => setHoveredBtn("pause"),
351
- onMouseLeave: () => setHoveredBtn(null),
352
- "aria-label": paused ? "Resume capture server" : "Pause capture server",
353
- style: {
354
- ...btnBase,
355
- color: paused ? tokens.colors.status.caution : tokens.colors.text.secondary,
356
- borderColor: paused ? `${tokens.colors.status.caution}40` : tokens.colors.border.muted,
357
- backgroundColor: hoveredBtn === "pause" ? `${tokens.colors.accent.primary}12` : "transparent"
358
- },
359
- children: [
360
- paused ? /* @__PURE__ */ jsx(PlayIcon, {}) : /* @__PURE__ */ jsx(PauseIcon, {}),
361
- /* @__PURE__ */ jsx("span", { children: paused ? "Resume" : "Pause" })
362
- ]
363
- }
364
- ),
365
- /* @__PURE__ */ jsxs(
366
- "div",
367
- {
368
- style: {
369
- display: "flex",
370
- alignItems: "center",
371
- gap: "8px",
372
- fontSize: tokens.typography.fontSize.sm,
373
- color: tokens.colors.text.secondary
374
- },
375
- children: [
376
- /* @__PURE__ */ jsx(
377
- "span",
378
- {
379
- style: {
380
- width: 8,
381
- height: 8,
382
- borderRadius: "50%",
383
- backgroundColor: statusColor,
384
- flexShrink: 0
385
- }
386
- }
387
- ),
388
- /* @__PURE__ */ jsx("span", { children: statusLabel }),
389
- requestCount > 0 && /* @__PURE__ */ jsxs(
390
- "span",
391
- {
392
- style: {
393
- fontSize: tokens.typography.fontSize.xs,
394
- color: tokens.colors.text.tertiary,
395
- fontFamily: tokens.typography.fontFamily.mono
396
- },
397
- children: [
398
- "(",
399
- requestCount,
400
- ")"
401
- ]
402
- }
403
- )
404
- ]
405
- }
406
- ),
407
- /* @__PURE__ */ jsxs(
408
- "button",
409
- {
410
- type: "button",
411
- onClick: () => copy(serverId),
412
- onMouseEnter: () => setHoveredBtn("copy"),
413
- onMouseLeave: () => setHoveredBtn(null),
414
- title: copied ? "Copied!" : `Copy Server ID: ${serverId}`,
415
- "aria-label": copied ? "Server ID copied" : "Copy server ID",
416
- style: {
417
- ...btnBase,
418
- color: copied ? tokens.colors.status.normal : tokens.colors.text.secondary,
419
- borderColor: copied ? `${tokens.colors.status.normal}40` : tokens.colors.border.muted,
420
- backgroundColor: hoveredBtn === "copy" ? `${tokens.colors.accent.primary}12` : "transparent"
421
- },
422
- children: [
423
- copied ? /* @__PURE__ */ jsx(CheckIcon, { color: tokens.colors.status.normal }) : /* @__PURE__ */ jsx(CopyIcon, {}),
424
- /* @__PURE__ */ jsx(
425
- "span",
426
- {
427
- style: {
428
- fontFamily: tokens.typography.fontFamily.mono,
429
- fontSize: tokens.typography.fontSize.xs
430
- },
431
- children: copied ? "Copied!" : serverId.slice(0, 8)
432
- }
433
- )
434
- ]
435
- }
436
- )
437
- ]
438
- }
439
- ),
440
- /* @__PURE__ */ jsx(
441
- CaptureGallery,
442
- {
443
- images: capturedImages,
444
- expanded: galleryExpanded,
445
- onToggle: () => setGalleryExpanded(!galleryExpanded),
446
- tokens,
447
- serverId
448
- }
449
- )
450
- ]
451
- }
452
- );
276
+ var PauseIcon = ({ size = 14 }) => /* @__PURE__ */ jsxs("svg", {
277
+ width: size,
278
+ height: size,
279
+ viewBox: "0 0 24 24",
280
+ fill: "currentColor",
281
+ children: [/* @__PURE__ */ jsx("rect", {
282
+ x: "6",
283
+ y: "4",
284
+ width: "4",
285
+ height: "16",
286
+ rx: "1"
287
+ }), /* @__PURE__ */ jsx("rect", {
288
+ x: "14",
289
+ y: "4",
290
+ width: "4",
291
+ height: "16",
292
+ rx: "1"
293
+ })]
294
+ });
295
+ var PlayIcon = ({ size = 14 }) => /* @__PURE__ */ jsx("svg", {
296
+ width: size,
297
+ height: size,
298
+ viewBox: "0 0 24 24",
299
+ fill: "currentColor",
300
+ children: /* @__PURE__ */ jsx("polygon", { points: "6,4 20,12 6,20" })
301
+ });
302
+ var ChevronIcon = ({ size = 14, expanded }) => /* @__PURE__ */ jsx("svg", {
303
+ width: size,
304
+ height: size,
305
+ viewBox: "0 0 24 24",
306
+ fill: "none",
307
+ stroke: "currentColor",
308
+ strokeWidth: "2",
309
+ strokeLinecap: "round",
310
+ strokeLinejoin: "round",
311
+ style: {
312
+ transition: "transform 0.2s ease",
313
+ transform: expanded ? "rotate(90deg)" : "rotate(0deg)"
314
+ },
315
+ children: /* @__PURE__ */ jsx("polyline", { points: "9 18 15 12 9 6" })
316
+ });
317
+ var CopyIcon = ({ size = 14 }) => /* @__PURE__ */ jsxs("svg", {
318
+ width: size,
319
+ height: size,
320
+ viewBox: "0 0 24 24",
321
+ fill: "none",
322
+ stroke: "currentColor",
323
+ strokeWidth: "2",
324
+ strokeLinecap: "round",
325
+ strokeLinejoin: "round",
326
+ children: [/* @__PURE__ */ jsx("rect", {
327
+ x: "9",
328
+ y: "9",
329
+ width: "13",
330
+ height: "13",
331
+ rx: "2",
332
+ ry: "2"
333
+ }), /* @__PURE__ */ jsx("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })]
334
+ });
335
+ var CheckIcon = ({ size = 14, color }) => /* @__PURE__ */ jsx("svg", {
336
+ width: size,
337
+ height: size,
338
+ viewBox: "0 0 24 24",
339
+ fill: "none",
340
+ stroke: color,
341
+ strokeWidth: "2.5",
342
+ strokeLinecap: "round",
343
+ strokeLinejoin: "round",
344
+ children: /* @__PURE__ */ jsx("polyline", { points: "20 6 9 17 4 12" })
345
+ });
346
+ /**
347
+ * Capture server UI component. Displays server status, pause/resume control,
348
+ * copy-ID button, and a collapsible gallery of captured images.
349
+ *
350
+ * Access `capture()` and `serverId` imperatively via the forwarded ref.
351
+ */
352
+ var Capture = memo(forwardRef(function Capture({ connected = false, imageSource, preload, serverId: serverIdProp, onCapture, onReady, defaultGalleryExpanded = false, style }, ref) {
353
+ const { tokens } = useTheme();
354
+ const { serverId, capture, paused, setPaused, requestCount, capturedImages } = useCapture({
355
+ imageSource,
356
+ preload,
357
+ serverId: serverIdProp
358
+ });
359
+ const { copy, copied } = useCopyToClipboard();
360
+ const [galleryExpanded, setGalleryExpanded] = useState(defaultGalleryExpanded);
361
+ const [hoveredBtn, setHoveredBtn] = useState(null);
362
+ const readyFiredRef = useRef(false);
363
+ useEffect(() => {
364
+ if (onReady && !readyFiredRef.current) {
365
+ readyFiredRef.current = true;
366
+ onReady(serverId);
367
+ }
368
+ }, [onReady, serverId]);
369
+ const wrappedCapture = useCallback(async (request) => {
370
+ const bytes = await capture(request);
371
+ if (bytes.length > 0) onCapture?.(request);
372
+ return bytes;
373
+ }, [capture, onCapture]);
374
+ useImperativeHandle(ref, () => ({
375
+ capture: wrappedCapture,
376
+ serverId
377
+ }), [wrappedCapture, serverId]);
378
+ const statusColor = connected ? tokens.colors.status.normal : tokens.colors.status.off;
379
+ const statusLabel = connected ? "Connected" : "Disconnected";
380
+ const btnBase = {
381
+ display: "inline-flex",
382
+ alignItems: "center",
383
+ justifyContent: "center",
384
+ gap: "6px",
385
+ height: 32,
386
+ padding: "0 12px",
387
+ fontSize: tokens.typography.fontSize.sm,
388
+ fontWeight: 500,
389
+ fontFamily: tokens.typography.fontFamily.primary,
390
+ color: tokens.colors.text.secondary,
391
+ backgroundColor: "transparent",
392
+ border: `1px solid ${tokens.colors.border.muted}`,
393
+ borderRadius: tokens.borderRadius.md,
394
+ cursor: "pointer",
395
+ transition: tokens.animation.fast,
396
+ outline: "none",
397
+ whiteSpace: "nowrap"
398
+ };
399
+ return /* @__PURE__ */ jsxs("div", {
400
+ style: {
401
+ display: "flex",
402
+ flexDirection: "column",
403
+ borderRadius: tokens.borderRadius.lg,
404
+ border: `1px solid ${tokens.colors.border.default}`,
405
+ backgroundColor: tokens.colors.background.surface,
406
+ overflow: "hidden",
407
+ ...galleryExpanded ? {
408
+ flex: 1,
409
+ minHeight: 0
410
+ } : {},
411
+ ...style
412
+ },
413
+ children: [/* @__PURE__ */ jsxs("div", {
414
+ style: {
415
+ display: "flex",
416
+ alignItems: "center",
417
+ justifyContent: "space-between",
418
+ padding: "8px 12px",
419
+ gap: "12px",
420
+ minHeight: 48,
421
+ flexShrink: 0
422
+ },
423
+ children: [
424
+ /* @__PURE__ */ jsxs("button", {
425
+ type: "button",
426
+ onClick: () => setPaused(!paused),
427
+ onMouseEnter: () => setHoveredBtn("pause"),
428
+ onMouseLeave: () => setHoveredBtn(null),
429
+ "aria-label": paused ? "Resume capture server" : "Pause capture server",
430
+ style: {
431
+ ...btnBase,
432
+ color: paused ? tokens.colors.status.caution : tokens.colors.text.secondary,
433
+ borderColor: paused ? `${tokens.colors.status.caution}40` : tokens.colors.border.muted,
434
+ backgroundColor: hoveredBtn === "pause" ? `${tokens.colors.accent.primary}12` : "transparent"
435
+ },
436
+ children: [paused ? /* @__PURE__ */ jsx(PlayIcon, {}) : /* @__PURE__ */ jsx(PauseIcon, {}), /* @__PURE__ */ jsx("span", { children: paused ? "Resume" : "Pause" })]
437
+ }),
438
+ /* @__PURE__ */ jsxs("div", {
439
+ style: {
440
+ display: "flex",
441
+ alignItems: "center",
442
+ gap: "8px",
443
+ fontSize: tokens.typography.fontSize.sm,
444
+ color: tokens.colors.text.secondary
445
+ },
446
+ children: [
447
+ /* @__PURE__ */ jsx("span", { style: {
448
+ width: 8,
449
+ height: 8,
450
+ borderRadius: "50%",
451
+ backgroundColor: statusColor,
452
+ flexShrink: 0
453
+ } }),
454
+ /* @__PURE__ */ jsx("span", { children: statusLabel }),
455
+ requestCount > 0 && /* @__PURE__ */ jsxs("span", {
456
+ style: {
457
+ fontSize: tokens.typography.fontSize.xs,
458
+ color: tokens.colors.text.tertiary,
459
+ fontFamily: tokens.typography.fontFamily.mono
460
+ },
461
+ children: [
462
+ "(",
463
+ requestCount,
464
+ ")"
465
+ ]
466
+ })
467
+ ]
468
+ }),
469
+ /* @__PURE__ */ jsxs("button", {
470
+ type: "button",
471
+ onClick: () => copy(serverId),
472
+ onMouseEnter: () => setHoveredBtn("copy"),
473
+ onMouseLeave: () => setHoveredBtn(null),
474
+ title: copied ? "Copied!" : `Copy Server ID: ${serverId}`,
475
+ "aria-label": copied ? "Server ID copied" : "Copy server ID",
476
+ style: {
477
+ ...btnBase,
478
+ color: copied ? tokens.colors.status.normal : tokens.colors.text.secondary,
479
+ borderColor: copied ? `${tokens.colors.status.normal}40` : tokens.colors.border.muted,
480
+ backgroundColor: hoveredBtn === "copy" ? `${tokens.colors.accent.primary}12` : "transparent"
481
+ },
482
+ children: [copied ? /* @__PURE__ */ jsx(CheckIcon, { color: tokens.colors.status.normal }) : /* @__PURE__ */ jsx(CopyIcon, {}), /* @__PURE__ */ jsx("span", {
483
+ style: {
484
+ fontFamily: tokens.typography.fontFamily.mono,
485
+ fontSize: tokens.typography.fontSize.xs
486
+ },
487
+ children: copied ? "Copied!" : serverId.slice(0, 8)
488
+ })]
489
+ })
490
+ ]
491
+ }), /* @__PURE__ */ jsx(CaptureGallery, {
492
+ images: capturedImages,
493
+ expanded: galleryExpanded,
494
+ onToggle: () => setGalleryExpanded(!galleryExpanded),
495
+ tokens,
496
+ serverId
497
+ })]
498
+ });
453
499
  }));
500
+ /** Format byte count for display */
454
501
  function fmtBytes(b) {
455
- if (b > 1048576) return `${(b / 1048576).toFixed(1)} MB`;
456
- if (b > 1024) return `${(b / 1024).toFixed(1)} KB`;
457
- return `${b} B`;
502
+ if (b > 1048576) return `${(b / 1048576).toFixed(1)} MB`;
503
+ if (b > 1024) return `${(b / 1024).toFixed(1)} KB`;
504
+ return `${b} B`;
458
505
  }
506
+ /** Download a single captured image with the correct file extension */
459
507
  function downloadCapturedImage(img, serverId) {
460
- var _a;
461
- const ext = ((_a = img.args) == null ? void 0 : _a.format) === "jpeg" ? "jpg" : "png";
462
- const a = document.createElement("a");
463
- a.href = img.dataUrl;
464
- a.download = `capture_${serverId}_${img.reqId}.${ext}`;
465
- a.click();
508
+ const ext = img.args?.format === "jpeg" ? "jpg" : "png";
509
+ const a = document.createElement("a");
510
+ a.href = img.dataUrl;
511
+ a.download = `capture_${serverId}_${img.reqId}.${ext}`;
512
+ a.click();
466
513
  }
514
+ /** Download all captured images sequentially */
467
515
  async function downloadAllImages(images, serverId) {
468
- for (const img of images) {
469
- downloadCapturedImage(img, serverId);
470
- await new Promise((r) => setTimeout(r, 200));
471
- }
516
+ for (const img of images) {
517
+ downloadCapturedImage(img, serverId);
518
+ await new Promise((r) => setTimeout(r, 200));
519
+ }
472
520
  }
473
- const DownloadIcon = ({ size = 12 }) => /* @__PURE__ */ jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
474
- /* @__PURE__ */ jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
475
- /* @__PURE__ */ jsx("polyline", { points: "7 10 12 15 17 10" }),
476
- /* @__PURE__ */ jsx("line", { x1: "12", y1: "15", x2: "12", y2: "3" })
477
- ] });
478
- const CloseIcon = ({ size = 12 }) => /* @__PURE__ */ jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", children: [
479
- /* @__PURE__ */ jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
480
- /* @__PURE__ */ jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
481
- ] });
521
+ /** Download icon (inline SVG) */
522
+ var DownloadIcon = ({ size = 12 }) => /* @__PURE__ */ jsxs("svg", {
523
+ width: size,
524
+ height: size,
525
+ viewBox: "0 0 24 24",
526
+ fill: "none",
527
+ stroke: "currentColor",
528
+ strokeWidth: "2.5",
529
+ strokeLinecap: "round",
530
+ strokeLinejoin: "round",
531
+ children: [
532
+ /* @__PURE__ */ jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
533
+ /* @__PURE__ */ jsx("polyline", { points: "7 10 12 15 17 10" }),
534
+ /* @__PURE__ */ jsx("line", {
535
+ x1: "12",
536
+ y1: "15",
537
+ x2: "12",
538
+ y2: "3"
539
+ })
540
+ ]
541
+ });
542
+ /** Close icon (inline SVG) */
543
+ var CloseIcon = ({ size = 12 }) => /* @__PURE__ */ jsxs("svg", {
544
+ width: size,
545
+ height: size,
546
+ viewBox: "0 0 24 24",
547
+ fill: "none",
548
+ stroke: "currentColor",
549
+ strokeWidth: "2.5",
550
+ strokeLinecap: "round",
551
+ children: [/* @__PURE__ */ jsx("line", {
552
+ x1: "18",
553
+ y1: "6",
554
+ x2: "6",
555
+ y2: "18"
556
+ }), /* @__PURE__ */ jsx("line", {
557
+ x1: "6",
558
+ y1: "6",
559
+ x2: "18",
560
+ y2: "18"
561
+ })]
562
+ });
563
+ /** Metadata key-value row */
482
564
  function MetaRow({ label, value, tokens, mono }) {
483
- return /* @__PURE__ */ jsxs("div", { style: {
484
- display: "flex",
485
- justifyContent: "space-between",
486
- alignItems: "center",
487
- padding: "2px 0",
488
- fontSize: tokens.typography.fontSize.xs
489
- }, children: [
490
- /* @__PURE__ */ jsx("span", { style: { color: tokens.colors.text.tertiary }, children: label }),
491
- /* @__PURE__ */ jsx("span", { style: {
492
- color: tokens.colors.text.primary,
493
- fontFamily: mono ? tokens.typography.fontFamily.mono : "inherit",
494
- fontSize: mono ? "0.68rem" : tokens.typography.fontSize.xs
495
- }, children: value })
496
- ] });
565
+ return /* @__PURE__ */ jsxs("div", {
566
+ style: {
567
+ display: "flex",
568
+ justifyContent: "space-between",
569
+ alignItems: "center",
570
+ padding: "2px 0",
571
+ fontSize: tokens.typography.fontSize.xs
572
+ },
573
+ children: [/* @__PURE__ */ jsx("span", {
574
+ style: { color: tokens.colors.text.tertiary },
575
+ children: label
576
+ }), /* @__PURE__ */ jsx("span", {
577
+ style: {
578
+ color: tokens.colors.text.primary,
579
+ fontFamily: mono ? tokens.typography.fontFamily.mono : "inherit",
580
+ fontSize: mono ? "0.68rem" : tokens.typography.fontSize.xs
581
+ },
582
+ children: value
583
+ })]
584
+ });
497
585
  }
586
+ /** Metadata group heading */
498
587
  function MetaGroup({ label, tokens, children }) {
499
- return /* @__PURE__ */ jsxs("div", { style: { marginTop: 6 }, children: [
500
- /* @__PURE__ */ jsx("div", { style: {
501
- fontSize: "0.6rem",
502
- fontWeight: 600,
503
- color: tokens.colors.text.tertiary,
504
- textTransform: "uppercase",
505
- letterSpacing: "0.06em",
506
- padding: "4px 0 2px",
507
- borderTop: `1px solid ${tokens.colors.border.muted}`
508
- }, children: label }),
509
- children
510
- ] });
588
+ return /* @__PURE__ */ jsxs("div", {
589
+ style: { marginTop: 6 },
590
+ children: [/* @__PURE__ */ jsx("div", {
591
+ style: {
592
+ fontSize: "0.6rem",
593
+ fontWeight: 600,
594
+ color: tokens.colors.text.tertiary,
595
+ textTransform: "uppercase",
596
+ letterSpacing: "0.06em",
597
+ padding: "4px 0 2px",
598
+ borderTop: `1px solid ${tokens.colors.border.muted}`
599
+ },
600
+ children: label
601
+ }), children]
602
+ });
511
603
  }
604
+ /**
605
+ * Image detail panel showing preview, metadata, camera settings, and position.
606
+ * Displayed inline when a gallery thumbnail is clicked.
607
+ */
512
608
  function CaptureImageDetail({ img, tokens, serverId, onClose }) {
513
- var _a, _b;
514
- const a = img.args;
515
- const time = new Date(img.timestamp).toLocaleString("en-US", {
516
- hour12: false,
517
- year: "numeric",
518
- month: "short",
519
- day: "numeric",
520
- hour: "2-digit",
521
- minute: "2-digit",
522
- second: "2-digit"
523
- });
524
- return /* @__PURE__ */ jsxs("div", { style: {
525
- backgroundColor: tokens.colors.background.elevated,
526
- display: "flex",
527
- gap: "8px",
528
- padding: "8px",
529
- minHeight: 0
530
- }, children: [
531
- /* @__PURE__ */ jsxs("div", { style: {
532
- flex: "1 1 50%",
533
- display: "flex",
534
- flexDirection: "column",
535
- gap: "6px",
536
- minWidth: 0
537
- }, children: [
538
- /* @__PURE__ */ jsx(
539
- "img",
540
- {
541
- src: img.dataUrl,
542
- alt: `capture_${serverId}_${img.reqId}`,
543
- style: {
544
- width: "100%",
545
- borderRadius: tokens.borderRadius.sm,
546
- border: `1px solid ${tokens.colors.border.muted}`,
547
- objectFit: "contain"
548
- }
549
- }
550
- ),
551
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "4px" }, children: [
552
- /* @__PURE__ */ jsxs(
553
- "button",
554
- {
555
- type: "button",
556
- onClick: () => downloadCapturedImage(img, serverId),
557
- style: {
558
- flex: 1,
559
- display: "flex",
560
- alignItems: "center",
561
- justifyContent: "center",
562
- gap: "4px",
563
- padding: "5px 0",
564
- borderRadius: tokens.borderRadius.sm,
565
- fontSize: tokens.typography.fontSize.xs,
566
- fontWeight: 500,
567
- fontFamily: tokens.typography.fontFamily.mono,
568
- color: tokens.colors.accent.primary,
569
- backgroundColor: `${tokens.colors.accent.primary}12`,
570
- border: `1px solid ${tokens.colors.accent.primary}30`,
571
- cursor: "pointer"
572
- },
573
- children: [
574
- /* @__PURE__ */ jsx(DownloadIcon, {}),
575
- " Download PNG"
576
- ]
577
- }
578
- ),
579
- /* @__PURE__ */ jsx(
580
- "button",
581
- {
582
- type: "button",
583
- onClick: onClose,
584
- style: {
585
- padding: "5px 8px",
586
- borderRadius: tokens.borderRadius.sm,
587
- fontSize: tokens.typography.fontSize.xs,
588
- color: tokens.colors.text.tertiary,
589
- backgroundColor: "transparent",
590
- border: `1px solid ${tokens.colors.border.muted}`,
591
- cursor: "pointer"
592
- },
593
- children: /* @__PURE__ */ jsx(CloseIcon, {})
594
- }
595
- )
596
- ] })
597
- ] }),
598
- /* @__PURE__ */ jsxs("div", { style: {
599
- flex: "1 1 50%",
600
- overflowY: "auto",
601
- minWidth: 0
602
- }, children: [
603
- /* @__PURE__ */ jsxs(MetaGroup, { label: "File Info", tokens, children: [
604
- /* @__PURE__ */ jsx(MetaRow, { label: "Filename", value: `capture_${serverId}_${img.reqId}.${((_a = img.args) == null ? void 0 : _a.format) === "jpeg" ? "jpg" : "png"}`, tokens, mono: true }),
605
- /* @__PURE__ */ jsx(MetaRow, { label: "Req ID", value: `#${img.reqId}`, tokens, mono: true }),
606
- /* @__PURE__ */ jsx(MetaRow, { label: "Size", value: fmtBytes(img.bytes), tokens }),
607
- /* @__PURE__ */ jsx(MetaRow, { label: "Format", value: ((_b = img.args) == null ? void 0 : _b.format) === "jpeg" ? `JPEG (q=${Math.round((img.args.jpeg_quality ?? 0.92) * 100)}%)` : "PNG", tokens }),
608
- /* @__PURE__ */ jsx(MetaRow, { label: "Captured", value: time, tokens })
609
- ] }),
610
- a && /* @__PURE__ */ jsxs(Fragment, { children: [
611
- /* @__PURE__ */ jsxs(MetaGroup, { label: "Camera", tokens, children: [
612
- /* @__PURE__ */ jsx(MetaRow, { label: "Resolution", value: `${a.resolution}px`, tokens }),
613
- /* @__PURE__ */ jsx(MetaRow, { label: "FOV", value: `${a.fov}°`, tokens }),
614
- /* @__PURE__ */ jsx(MetaRow, { label: "Focal Length", value: `${a.focal_length}mm`, tokens }),
615
- /* @__PURE__ */ jsx(MetaRow, { label: "Aperture", value: `f/${a.aperture}`, tokens }),
616
- /* @__PURE__ */ jsx(MetaRow, { label: "CoC", value: `${a.coc}mm`, tokens }),
617
- /* @__PURE__ */ jsx(MetaRow, { label: "Pixel Pitch", value: `${a.pixel_pitch}mm`, tokens }),
618
- /* @__PURE__ */ jsx(MetaRow, { label: "Focus Dist", value: `${a.focusing_distance}m`, tokens }),
619
- /* @__PURE__ */ jsx(MetaRow, { label: "Monochrome", value: a.monochromatic ? "Yes" : "No", tokens }),
620
- /* @__PURE__ */ jsx(MetaRow, { label: "Sample", value: a.sample ? "Yes (½ res)" : "No", tokens })
621
- ] }),
622
- /* @__PURE__ */ jsxs(MetaGroup, { label: "Position & Orientation", tokens, children: [
623
- /* @__PURE__ */ jsx(MetaRow, { label: "X", value: `${a.position[0].toFixed(2)} km`, tokens, mono: true }),
624
- /* @__PURE__ */ jsx(MetaRow, { label: "Y", value: `${a.position[1].toFixed(2)} km`, tokens, mono: true }),
625
- /* @__PURE__ */ jsx(MetaRow, { label: "Z", value: `${a.position[2].toFixed(2)} km`, tokens, mono: true }),
626
- /* @__PURE__ */ jsx(MetaRow, { label: "Heading", value: `${a.rotation[0].toFixed(1)}°`, tokens, mono: true }),
627
- /* @__PURE__ */ jsx(MetaRow, { label: "Pitch", value: `${a.rotation[1].toFixed(1)}°`, tokens, mono: true }),
628
- /* @__PURE__ */ jsx(MetaRow, { label: "Roll", value: `${a.rotation[2].toFixed(1)}°`, tokens, mono: true })
629
- ] })
630
- ] })
631
- ] })
632
- ] });
609
+ const a = img.args;
610
+ const time = new Date(img.timestamp).toLocaleString("en-US", {
611
+ hour12: false,
612
+ year: "numeric",
613
+ month: "short",
614
+ day: "numeric",
615
+ hour: "2-digit",
616
+ minute: "2-digit",
617
+ second: "2-digit"
618
+ });
619
+ return /* @__PURE__ */ jsxs("div", {
620
+ style: {
621
+ backgroundColor: tokens.colors.background.elevated,
622
+ display: "flex",
623
+ gap: "8px",
624
+ padding: "8px",
625
+ minHeight: 0
626
+ },
627
+ children: [/* @__PURE__ */ jsxs("div", {
628
+ style: {
629
+ flex: "1 1 50%",
630
+ display: "flex",
631
+ flexDirection: "column",
632
+ gap: "6px",
633
+ minWidth: 0
634
+ },
635
+ children: [/* @__PURE__ */ jsx("img", {
636
+ src: img.dataUrl,
637
+ alt: `capture_${serverId}_${img.reqId}`,
638
+ style: {
639
+ width: "100%",
640
+ borderRadius: tokens.borderRadius.sm,
641
+ border: `1px solid ${tokens.colors.border.muted}`,
642
+ objectFit: "contain"
643
+ }
644
+ }), /* @__PURE__ */ jsxs("div", {
645
+ style: {
646
+ display: "flex",
647
+ gap: "4px"
648
+ },
649
+ children: [/* @__PURE__ */ jsxs("button", {
650
+ type: "button",
651
+ onClick: () => downloadCapturedImage(img, serverId),
652
+ style: {
653
+ flex: 1,
654
+ display: "flex",
655
+ alignItems: "center",
656
+ justifyContent: "center",
657
+ gap: "4px",
658
+ padding: "5px 0",
659
+ borderRadius: tokens.borderRadius.sm,
660
+ fontSize: tokens.typography.fontSize.xs,
661
+ fontWeight: 500,
662
+ fontFamily: tokens.typography.fontFamily.mono,
663
+ color: tokens.colors.accent.primary,
664
+ backgroundColor: `${tokens.colors.accent.primary}12`,
665
+ border: `1px solid ${tokens.colors.accent.primary}30`,
666
+ cursor: "pointer"
667
+ },
668
+ children: [/* @__PURE__ */ jsx(DownloadIcon, {}), " Download PNG"]
669
+ }), /* @__PURE__ */ jsx("button", {
670
+ type: "button",
671
+ onClick: onClose,
672
+ style: {
673
+ padding: "5px 8px",
674
+ borderRadius: tokens.borderRadius.sm,
675
+ fontSize: tokens.typography.fontSize.xs,
676
+ color: tokens.colors.text.tertiary,
677
+ backgroundColor: "transparent",
678
+ border: `1px solid ${tokens.colors.border.muted}`,
679
+ cursor: "pointer"
680
+ },
681
+ children: /* @__PURE__ */ jsx(CloseIcon, {})
682
+ })]
683
+ })]
684
+ }), /* @__PURE__ */ jsxs("div", {
685
+ style: {
686
+ flex: "1 1 50%",
687
+ overflowY: "auto",
688
+ minWidth: 0
689
+ },
690
+ children: [/* @__PURE__ */ jsxs(MetaGroup, {
691
+ label: "File Info",
692
+ tokens,
693
+ children: [
694
+ /* @__PURE__ */ jsx(MetaRow, {
695
+ label: "Filename",
696
+ value: `capture_${serverId}_${img.reqId}.${img.args?.format === "jpeg" ? "jpg" : "png"}`,
697
+ tokens,
698
+ mono: true
699
+ }),
700
+ /* @__PURE__ */ jsx(MetaRow, {
701
+ label: "Req ID",
702
+ value: `#${img.reqId}`,
703
+ tokens,
704
+ mono: true
705
+ }),
706
+ /* @__PURE__ */ jsx(MetaRow, {
707
+ label: "Size",
708
+ value: fmtBytes(img.bytes),
709
+ tokens
710
+ }),
711
+ /* @__PURE__ */ jsx(MetaRow, {
712
+ label: "Format",
713
+ value: img.args?.format === "jpeg" ? `JPEG (q=${Math.round((img.args.jpeg_quality ?? .92) * 100)}%)` : "PNG",
714
+ tokens
715
+ }),
716
+ /* @__PURE__ */ jsx(MetaRow, {
717
+ label: "Captured",
718
+ value: time,
719
+ tokens
720
+ })
721
+ ]
722
+ }), a && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs(MetaGroup, {
723
+ label: "Camera",
724
+ tokens,
725
+ children: [
726
+ /* @__PURE__ */ jsx(MetaRow, {
727
+ label: "Resolution",
728
+ value: `${a.resolution}px`,
729
+ tokens
730
+ }),
731
+ /* @__PURE__ */ jsx(MetaRow, {
732
+ label: "FOV",
733
+ value: `${a.fov}°`,
734
+ tokens
735
+ }),
736
+ /* @__PURE__ */ jsx(MetaRow, {
737
+ label: "Focal Length",
738
+ value: `${a.focal_length}mm`,
739
+ tokens
740
+ }),
741
+ /* @__PURE__ */ jsx(MetaRow, {
742
+ label: "Aperture",
743
+ value: `f/${a.aperture}`,
744
+ tokens
745
+ }),
746
+ /* @__PURE__ */ jsx(MetaRow, {
747
+ label: "CoC",
748
+ value: `${a.coc}mm`,
749
+ tokens
750
+ }),
751
+ /* @__PURE__ */ jsx(MetaRow, {
752
+ label: "Pixel Pitch",
753
+ value: `${a.pixel_pitch}mm`,
754
+ tokens
755
+ }),
756
+ /* @__PURE__ */ jsx(MetaRow, {
757
+ label: "Focus Dist",
758
+ value: `${a.focusing_distance}m`,
759
+ tokens
760
+ }),
761
+ /* @__PURE__ */ jsx(MetaRow, {
762
+ label: "Monochrome",
763
+ value: a.monochromatic ? "Yes" : "No",
764
+ tokens
765
+ }),
766
+ /* @__PURE__ */ jsx(MetaRow, {
767
+ label: "Sample",
768
+ value: a.sample ? "Yes (½ res)" : "No",
769
+ tokens
770
+ })
771
+ ]
772
+ }), /* @__PURE__ */ jsxs(MetaGroup, {
773
+ label: "Position & Orientation",
774
+ tokens,
775
+ children: [
776
+ /* @__PURE__ */ jsx(MetaRow, {
777
+ label: "X",
778
+ value: `${a.position[0].toFixed(2)} km`,
779
+ tokens,
780
+ mono: true
781
+ }),
782
+ /* @__PURE__ */ jsx(MetaRow, {
783
+ label: "Y",
784
+ value: `${a.position[1].toFixed(2)} km`,
785
+ tokens,
786
+ mono: true
787
+ }),
788
+ /* @__PURE__ */ jsx(MetaRow, {
789
+ label: "Z",
790
+ value: `${a.position[2].toFixed(2)} km`,
791
+ tokens,
792
+ mono: true
793
+ }),
794
+ /* @__PURE__ */ jsx(MetaRow, {
795
+ label: "Heading",
796
+ value: `${a.rotation[0].toFixed(1)}°`,
797
+ tokens,
798
+ mono: true
799
+ }),
800
+ /* @__PURE__ */ jsx(MetaRow, {
801
+ label: "Pitch",
802
+ value: `${a.rotation[1].toFixed(1)}°`,
803
+ tokens,
804
+ mono: true
805
+ }),
806
+ /* @__PURE__ */ jsx(MetaRow, {
807
+ label: "Roll",
808
+ value: `${a.rotation[2].toFixed(1)}°`,
809
+ tokens,
810
+ mono: true
811
+ })
812
+ ]
813
+ })] })]
814
+ })]
815
+ });
633
816
  }
817
+ /**
818
+ * Collapsible image gallery with thumbnails, detail panel, download per-image, and download-all.
819
+ * Part of the Capture component's built-in UI.
820
+ */
634
821
  function CaptureGallery({ images, expanded, onToggle, tokens, serverId }) {
635
- const [selectedIdx, setSelectedIdx] = useState(null);
636
- const selectedImg = selectedIdx !== null ? images[selectedIdx] : null;
637
- return /* @__PURE__ */ jsxs("div", { style: {
638
- borderTop: `1px solid ${tokens.colors.border.muted}`,
639
- /* Fill remaining space when expanded */
640
- ...expanded ? { flex: 1, display: "flex", flexDirection: "column", minHeight: 0, overflow: "hidden" } : {}
641
- }, children: [
642
- /* @__PURE__ */ jsxs("div", { style: {
643
- display: "flex",
644
- alignItems: "center",
645
- gap: "4px",
646
- flexShrink: 0
647
- }, children: [
648
- /* @__PURE__ */ jsxs(
649
- "button",
650
- {
651
- type: "button",
652
- onClick: onToggle,
653
- "aria-expanded": expanded,
654
- "aria-label": `Captured images, ${images.length} total`,
655
- style: {
656
- flex: 1,
657
- display: "flex",
658
- alignItems: "center",
659
- gap: "8px",
660
- padding: "8px 12px",
661
- fontSize: tokens.typography.fontSize.sm,
662
- fontWeight: 500,
663
- fontFamily: tokens.typography.fontFamily.primary,
664
- color: tokens.colors.text.secondary,
665
- backgroundColor: "transparent",
666
- border: "none",
667
- cursor: "pointer",
668
- outline: "none",
669
- textAlign: "left"
670
- },
671
- children: [
672
- /* @__PURE__ */ jsx(ChevronIcon, { expanded }),
673
- /* @__PURE__ */ jsx("span", { children: "Captured Images" }),
674
- images.length > 0 && /* @__PURE__ */ jsxs("span", { style: {
675
- fontSize: tokens.typography.fontSize.xs,
676
- color: tokens.colors.text.tertiary,
677
- fontFamily: tokens.typography.fontFamily.mono
678
- }, children: [
679
- "(",
680
- images.length,
681
- ")"
682
- ] })
683
- ]
684
- }
685
- ),
686
- expanded && images.length > 0 && /* @__PURE__ */ jsxs(
687
- "button",
688
- {
689
- type: "button",
690
- onClick: () => downloadAllImages(images, serverId),
691
- title: `Download all ${images.length} images`,
692
- style: {
693
- display: "flex",
694
- alignItems: "center",
695
- gap: "4px",
696
- padding: "4px 8px",
697
- marginRight: 8,
698
- fontSize: "0.62rem",
699
- fontWeight: 500,
700
- fontFamily: tokens.typography.fontFamily.mono,
701
- color: tokens.colors.text.tertiary,
702
- backgroundColor: "transparent",
703
- border: `1px solid ${tokens.colors.border.muted}`,
704
- borderRadius: tokens.borderRadius.sm,
705
- cursor: "pointer",
706
- whiteSpace: "nowrap"
707
- },
708
- children: [
709
- /* @__PURE__ */ jsx(DownloadIcon, { size: 10 }),
710
- " All"
711
- ]
712
- }
713
- )
714
- ] }),
715
- expanded && /* @__PURE__ */ jsx("div", { style: {
716
- flex: 1,
717
- minHeight: 0,
718
- display: "flex",
719
- overflow: "hidden"
720
- }, children: images.length === 0 ? /* @__PURE__ */ jsx("div", { style: {
721
- fontSize: tokens.typography.fontSize.sm,
722
- color: tokens.colors.text.tertiary,
723
- padding: "12px",
724
- textAlign: "center",
725
- width: "100%"
726
- }, children: "No images captured yet" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
727
- /* @__PURE__ */ jsx("div", { style: {
728
- width: selectedImg ? 200 : "100%",
729
- flexShrink: 0,
730
- overflowY: "auto",
731
- padding: "6px",
732
- borderRight: selectedImg ? `1px solid ${tokens.colors.border.muted}` : "none",
733
- display: "grid",
734
- gridTemplateColumns: selectedImg ? "repeat(2, 1fr)" : "repeat(auto-fill, minmax(min(120px, 100%), 1fr))",
735
- gridAutoRows: "min-content",
736
- gap: "4px",
737
- alignContent: "start"
738
- }, children: images.map((img, idx) => {
739
- const isSelected = selectedIdx === idx;
740
- return /* @__PURE__ */ jsxs(
741
- "div",
742
- {
743
- onClick: () => setSelectedIdx(isSelected ? null : idx),
744
- style: {
745
- position: "relative",
746
- aspectRatio: "1",
747
- borderRadius: tokens.borderRadius.sm,
748
- overflow: "hidden",
749
- border: `2px solid ${isSelected ? tokens.colors.accent.primary : tokens.colors.border.muted}`,
750
- backgroundColor: tokens.colors.background.elevated,
751
- cursor: "pointer",
752
- transition: "border-color 0.15s"
753
- },
754
- children: [
755
- /* @__PURE__ */ jsx(
756
- "img",
757
- {
758
- src: img.dataUrl,
759
- alt: `capture_${serverId}_${img.reqId}`,
760
- style: { width: "100%", height: "100%", objectFit: "cover", display: "block" }
761
- }
762
- ),
763
- /* @__PURE__ */ jsxs("div", { style: {
764
- position: "absolute",
765
- bottom: 0,
766
- left: 0,
767
- right: 0,
768
- display: "flex",
769
- justifyContent: "space-between",
770
- padding: "2px 4px",
771
- fontSize: "9px",
772
- fontFamily: tokens.typography.fontFamily.mono,
773
- color: "#fff",
774
- backgroundColor: "rgba(0, 0, 0, 0.65)"
775
- }, children: [
776
- /* @__PURE__ */ jsxs("span", { children: [
777
- "#",
778
- img.reqId
779
- ] }),
780
- /* @__PURE__ */ jsx("span", { children: fmtBytes(img.bytes) })
781
- ] })
782
- ]
783
- },
784
- `${img.reqId}-${img.timestamp}`
785
- );
786
- }) }),
787
- selectedImg && /* @__PURE__ */ jsx("div", { style: { flex: 1, minWidth: 0, overflowY: "auto" }, children: /* @__PURE__ */ jsx(
788
- CaptureImageDetail,
789
- {
790
- img: selectedImg,
791
- tokens,
792
- serverId,
793
- onClose: () => setSelectedIdx(null)
794
- }
795
- ) })
796
- ] }) })
797
- ] });
822
+ const [selectedIdx, setSelectedIdx] = useState(null);
823
+ const selectedImg = selectedIdx !== null ? images[selectedIdx] : null;
824
+ return /* @__PURE__ */ jsxs("div", {
825
+ style: {
826
+ borderTop: `1px solid ${tokens.colors.border.muted}`,
827
+ ...expanded ? {
828
+ flex: 1,
829
+ display: "flex",
830
+ flexDirection: "column",
831
+ minHeight: 0,
832
+ overflow: "hidden"
833
+ } : {}
834
+ },
835
+ children: [/* @__PURE__ */ jsxs("div", {
836
+ style: {
837
+ display: "flex",
838
+ alignItems: "center",
839
+ gap: "4px",
840
+ flexShrink: 0
841
+ },
842
+ children: [/* @__PURE__ */ jsxs("button", {
843
+ type: "button",
844
+ onClick: onToggle,
845
+ "aria-expanded": expanded,
846
+ "aria-label": `Captured images, ${images.length} total`,
847
+ style: {
848
+ flex: 1,
849
+ display: "flex",
850
+ alignItems: "center",
851
+ gap: "8px",
852
+ padding: "8px 12px",
853
+ fontSize: tokens.typography.fontSize.sm,
854
+ fontWeight: 500,
855
+ fontFamily: tokens.typography.fontFamily.primary,
856
+ color: tokens.colors.text.secondary,
857
+ backgroundColor: "transparent",
858
+ border: "none",
859
+ cursor: "pointer",
860
+ outline: "none",
861
+ textAlign: "left"
862
+ },
863
+ children: [
864
+ /* @__PURE__ */ jsx(ChevronIcon, { expanded }),
865
+ /* @__PURE__ */ jsx("span", { children: "Captured Images" }),
866
+ images.length > 0 && /* @__PURE__ */ jsxs("span", {
867
+ style: {
868
+ fontSize: tokens.typography.fontSize.xs,
869
+ color: tokens.colors.text.tertiary,
870
+ fontFamily: tokens.typography.fontFamily.mono
871
+ },
872
+ children: [
873
+ "(",
874
+ images.length,
875
+ ")"
876
+ ]
877
+ })
878
+ ]
879
+ }), expanded && images.length > 0 && /* @__PURE__ */ jsxs("button", {
880
+ type: "button",
881
+ onClick: () => downloadAllImages(images, serverId),
882
+ title: `Download all ${images.length} images`,
883
+ style: {
884
+ display: "flex",
885
+ alignItems: "center",
886
+ gap: "4px",
887
+ padding: "4px 8px",
888
+ marginRight: 8,
889
+ fontSize: "0.62rem",
890
+ fontWeight: 500,
891
+ fontFamily: tokens.typography.fontFamily.mono,
892
+ color: tokens.colors.text.tertiary,
893
+ backgroundColor: "transparent",
894
+ border: `1px solid ${tokens.colors.border.muted}`,
895
+ borderRadius: tokens.borderRadius.sm,
896
+ cursor: "pointer",
897
+ whiteSpace: "nowrap"
898
+ },
899
+ children: [/* @__PURE__ */ jsx(DownloadIcon, { size: 10 }), " All"]
900
+ })]
901
+ }), expanded && /* @__PURE__ */ jsx("div", {
902
+ style: {
903
+ flex: 1,
904
+ minHeight: 0,
905
+ display: "flex",
906
+ overflow: "hidden"
907
+ },
908
+ children: images.length === 0 ? /* @__PURE__ */ jsx("div", {
909
+ style: {
910
+ fontSize: tokens.typography.fontSize.sm,
911
+ color: tokens.colors.text.tertiary,
912
+ padding: "12px",
913
+ textAlign: "center",
914
+ width: "100%"
915
+ },
916
+ children: "No images captured yet"
917
+ }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
918
+ style: {
919
+ width: selectedImg ? 200 : "100%",
920
+ flexShrink: 0,
921
+ overflowY: "auto",
922
+ padding: "6px",
923
+ borderRight: selectedImg ? `1px solid ${tokens.colors.border.muted}` : "none",
924
+ display: "grid",
925
+ gridTemplateColumns: selectedImg ? "repeat(2, 1fr)" : "repeat(auto-fill, minmax(min(120px, 100%), 1fr))",
926
+ gridAutoRows: "min-content",
927
+ gap: "4px",
928
+ alignContent: "start"
929
+ },
930
+ children: images.map((img, idx) => {
931
+ const isSelected = selectedIdx === idx;
932
+ return /* @__PURE__ */ jsxs("div", {
933
+ onClick: () => setSelectedIdx(isSelected ? null : idx),
934
+ style: {
935
+ position: "relative",
936
+ aspectRatio: "1",
937
+ borderRadius: tokens.borderRadius.sm,
938
+ overflow: "hidden",
939
+ border: `2px solid ${isSelected ? tokens.colors.accent.primary : tokens.colors.border.muted}`,
940
+ backgroundColor: tokens.colors.background.elevated,
941
+ cursor: "pointer",
942
+ transition: "border-color 0.15s"
943
+ },
944
+ children: [/* @__PURE__ */ jsx("img", {
945
+ src: img.dataUrl,
946
+ alt: `capture_${serverId}_${img.reqId}`,
947
+ style: {
948
+ width: "100%",
949
+ height: "100%",
950
+ objectFit: "cover",
951
+ display: "block"
952
+ }
953
+ }), /* @__PURE__ */ jsxs("div", {
954
+ style: {
955
+ position: "absolute",
956
+ bottom: 0,
957
+ left: 0,
958
+ right: 0,
959
+ display: "flex",
960
+ justifyContent: "space-between",
961
+ padding: "2px 4px",
962
+ fontSize: "9px",
963
+ fontFamily: tokens.typography.fontFamily.mono,
964
+ color: "#fff",
965
+ backgroundColor: "rgba(0, 0, 0, 0.65)"
966
+ },
967
+ children: [/* @__PURE__ */ jsxs("span", { children: ["#", img.reqId] }), /* @__PURE__ */ jsx("span", { children: fmtBytes(img.bytes) })]
968
+ })]
969
+ }, `${img.reqId}-${img.timestamp}`);
970
+ })
971
+ }), selectedImg && /* @__PURE__ */ jsx("div", {
972
+ style: {
973
+ flex: 1,
974
+ minWidth: 0,
975
+ overflowY: "auto"
976
+ },
977
+ children: /* @__PURE__ */ jsx(CaptureImageDetail, {
978
+ img: selectedImg,
979
+ tokens,
980
+ serverId,
981
+ onClose: () => setSelectedIdx(null)
982
+ })
983
+ })] })
984
+ })]
985
+ });
798
986
  }
799
- export {
800
- Capture,
801
- loadCapturePlaceholderImage,
802
- useCapture
803
- };
804
- //# sourceMappingURL=Capture.js.map
987
+ //#endregion
988
+ export { Capture, Capture as default, loadCapturePlaceholderImage, useCapture };
989
+
990
+ //# sourceMappingURL=Capture.js.map