@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,619 +1,723 @@
1
- import { jsxs, jsx } from "react/jsx-runtime";
2
- import { memo, useRef, useState, useMemo, useEffect, useCallback } from "react";
3
1
  import { useTheme } from "../../theme/ThemeProvider.js";
4
- const DEFAULT_ESTIMATED_ROW_HEIGHT = 28;
5
- const DEFAULT_OVERSCAN_COUNT = 12;
6
- const SEVERITY_ORDER = ["debug", "info", "warn", "error", "critical"];
2
+ import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ //#region src/react/core/widgets/LogViewer.tsx
5
+ /**
6
+ * @zendir/ui - LogViewer Component
7
+ *
8
+ * Real-time log/event viewer for mission operations and system monitoring.
9
+ * Supports severity filtering, search, auto-scroll, pause/resume,
10
+ * and timestamped entries with color-coded severity levels.
11
+ *
12
+ * Features:
13
+ * - Severity levels: debug, info, warn, error, critical
14
+ * - Color-coded entries with Astro status colors
15
+ * - Real-time auto-scroll with pause/resume
16
+ * - Search/filter by text
17
+ * - Severity filter checkboxes
18
+ * - Timestamp display
19
+ * - Source/component tags
20
+ * - Export log to clipboard or file
21
+ * - Maximum log retention (auto-prune old entries)
22
+ * - Virtualized rendering for large logs
23
+ *
24
+ * @example
25
+ * ```tsx
26
+ * <LogViewer
27
+ * entries={logEntries}
28
+ * maxEntries={5000}
29
+ * showTimestamp
30
+ * showSource
31
+ * />
32
+ * ```
33
+ */
34
+ var DEFAULT_ESTIMATED_ROW_HEIGHT = 28;
35
+ var DEFAULT_OVERSCAN_COUNT = 12;
36
+ var SEVERITY_ORDER = [
37
+ "debug",
38
+ "info",
39
+ "warn",
40
+ "error",
41
+ "critical"
42
+ ];
7
43
  function formatSimElapsed(seconds) {
8
- const sign = seconds < 0 ? "-" : "+";
9
- const abs = Math.abs(seconds);
10
- const h = Math.floor(abs / 3600);
11
- const m = Math.floor(abs % 3600 / 60);
12
- const s = abs % 60;
13
- const whole = Math.floor(s);
14
- const tenths = Math.floor((s - whole) * 10);
15
- return `T${sign}${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}:${String(whole).padStart(2, "0")}.${tenths}`;
44
+ const sign = seconds < 0 ? "-" : "+";
45
+ const abs = Math.abs(seconds);
46
+ const h = Math.floor(abs / 3600);
47
+ const m = Math.floor(abs % 3600 / 60);
48
+ const s = abs % 60;
49
+ const whole = Math.floor(s);
50
+ const tenths = Math.floor((s - whole) * 10);
51
+ return `T${sign}${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}:${String(whole).padStart(2, "0")}.${tenths}`;
16
52
  }
17
53
  function formatTimestamp(entry, format) {
18
- if (format === "sim-elapsed" && typeof entry.simTime === "number" && Number.isFinite(entry.simTime)) {
19
- return formatSimElapsed(entry.simTime);
20
- }
21
- const ts = entry.timestamp;
22
- const d = typeof ts === "string" ? new Date(ts) : ts;
23
- if (isNaN(d.getTime())) return String(ts);
24
- if (format === "sim-elapsed") {
25
- return d.toLocaleTimeString("en-US", { hour12: false, hour: "2-digit", minute: "2-digit", second: "2-digit" });
26
- }
27
- if (format === "time") {
28
- return d.toLocaleTimeString("en-US", { hour12: false, hour: "2-digit", minute: "2-digit", second: "2-digit" }) + "." + d.getMilliseconds().toString().padStart(3, "0");
29
- }
30
- if (format === "datetime") {
31
- return d.toLocaleDateString("en-US", { month: "short", day: "2-digit" }) + " " + d.toLocaleTimeString("en-US", { hour12: false, hour: "2-digit", minute: "2-digit", second: "2-digit" });
32
- }
33
- const diff = Date.now() - d.getTime();
34
- if (diff < 1e3) return "just now";
35
- if (diff < 6e4) return `${Math.floor(diff / 1e3)}s ago`;
36
- if (diff < 36e5) return `${Math.floor(diff / 6e4)}m ago`;
37
- return `${Math.floor(diff / 36e5)}h ago`;
54
+ if (format === "sim-elapsed" && typeof entry.simTime === "number" && Number.isFinite(entry.simTime)) return formatSimElapsed(entry.simTime);
55
+ const ts = entry.timestamp;
56
+ const d = typeof ts === "string" ? new Date(ts) : ts;
57
+ if (isNaN(d.getTime())) return String(ts);
58
+ if (format === "sim-elapsed") return d.toLocaleTimeString("en-US", {
59
+ hour12: false,
60
+ hour: "2-digit",
61
+ minute: "2-digit",
62
+ second: "2-digit"
63
+ });
64
+ if (format === "time") return d.toLocaleTimeString("en-US", {
65
+ hour12: false,
66
+ hour: "2-digit",
67
+ minute: "2-digit",
68
+ second: "2-digit"
69
+ }) + "." + d.getMilliseconds().toString().padStart(3, "0");
70
+ if (format === "datetime") return d.toLocaleDateString("en-US", {
71
+ month: "short",
72
+ day: "2-digit"
73
+ }) + " " + d.toLocaleTimeString("en-US", {
74
+ hour12: false,
75
+ hour: "2-digit",
76
+ minute: "2-digit",
77
+ second: "2-digit"
78
+ });
79
+ const diff = Date.now() - d.getTime();
80
+ if (diff < 1e3) return "just now";
81
+ if (diff < 6e4) return `${Math.floor(diff / 1e3)}s ago`;
82
+ if (diff < 36e5) return `${Math.floor(diff / 6e4)}m ago`;
83
+ return `${Math.floor(diff / 36e5)}h ago`;
38
84
  }
39
- const LogViewer = memo(function LogViewer2({
40
- entries,
41
- maxEntries = 5e3,
42
- height = 400,
43
- title,
44
- showTimestamp = true,
45
- showSource = true,
46
- showFilters = true,
47
- showSearch = true,
48
- defaultSeverities,
49
- timestampFormat = "time",
50
- onEntryClick,
51
- className = "",
52
- bordered = true,
53
- style: styleProp,
54
- estimatedRowHeight = DEFAULT_ESTIMATED_ROW_HEIGHT,
55
- overscanCount = DEFAULT_OVERSCAN_COUNT,
56
- pagination
57
- }) {
58
- const { tokens, theme } = useTheme();
59
- const isTransparentTheme = theme === "transparent" || theme === "transparent-bold" || theme === "transparent-minimal";
60
- const scrollRef = useRef(null);
61
- const [paused, setPaused] = useState(false);
62
- const [searchQuery, setSearchQuery] = useState("");
63
- const [severityFilter, setSeverityFilter] = useState(
64
- new Set(defaultSeverities ?? SEVERITY_ORDER)
65
- );
66
- const [autoScroll, setAutoScroll] = useState(true);
67
- const prevLengthRef = useRef(entries.length);
68
- const [scrollState, setScrollState] = useState({ scrollTop: 0, containerHeight: 300 });
69
- const rafRef = useRef(null);
70
- const severityColors = {
71
- debug: tokens.colors.text.muted ?? tokens.colors.text.secondary,
72
- info: tokens.colors.status.standby,
73
- warn: tokens.colors.status.caution,
74
- error: tokens.colors.status.serious,
75
- critical: tokens.colors.status.critical
76
- };
77
- const severityShapes = {
78
- debug: "·",
79
- info: "",
80
- warn: "■",
81
- error: "◆",
82
- critical: "▼"
83
- };
84
- const severityLabels = {
85
- debug: "DBG",
86
- info: "INF",
87
- warn: "WRN",
88
- error: "ERR",
89
- critical: "CRT"
90
- };
91
- const filteredEntries = useMemo(() => {
92
- let filtered = entries.filter((e) => severityFilter.has(e.severity));
93
- if (searchQuery.trim()) {
94
- const q = searchQuery.toLowerCase();
95
- filtered = filtered.filter(
96
- (e) => e.message.toLowerCase().includes(q) || e.source && e.source.toLowerCase().includes(q)
97
- );
98
- }
99
- return filtered.slice(0, maxEntries);
100
- }, [entries, severityFilter, searchQuery, maxEntries]);
101
- const paginationConfig = pagination === true ? { pageSize: 200 } : pagination || void 0;
102
- const pageSize = (paginationConfig == null ? void 0 : paginationConfig.pageSize) ?? 200;
103
- const totalPages = Math.max(1, Math.ceil(filteredEntries.length / pageSize));
104
- const [currentPage, setCurrentPage] = useState(1);
105
- useEffect(() => {
106
- setCurrentPage((p) => Math.min(p, totalPages));
107
- }, [totalPages]);
108
- const paginationEntries = useMemo(() => {
109
- if (!paginationConfig) return null;
110
- const start = (currentPage - 1) * pageSize;
111
- return filteredEntries.slice(start, start + pageSize);
112
- }, [paginationConfig, currentPage, pageSize, filteredEntries]);
113
- const totalHeight = filteredEntries.length * estimatedRowHeight;
114
- const { scrollTop, containerHeight } = scrollState;
115
- const visibleRange = useMemo(() => {
116
- const start = Math.max(0, Math.floor(scrollTop / estimatedRowHeight) - overscanCount);
117
- const end = Math.min(
118
- filteredEntries.length,
119
- Math.ceil((scrollTop + containerHeight) / estimatedRowHeight) + overscanCount
120
- );
121
- return { start, end };
122
- }, [scrollTop, containerHeight, estimatedRowHeight, overscanCount, filteredEntries.length]);
123
- const visibleEntries = useMemo(
124
- () => filteredEntries.slice(visibleRange.start, visibleRange.end),
125
- [filteredEntries, visibleRange.start, visibleRange.end]
126
- );
127
- const usePagination = !!paginationConfig;
128
- const renderRow = useCallback((entry) => /* @__PURE__ */ jsxs(
129
- "div",
130
- {
131
- role: onEntryClick ? "button" : "listitem",
132
- tabIndex: onEntryClick ? 0 : void 0,
133
- "aria-label": onEntryClick ? `${severityLabels[entry.severity]}: ${entry.message}` : void 0,
134
- onClick: () => onEntryClick == null ? void 0 : onEntryClick(entry),
135
- onKeyDown: onEntryClick ? (e) => (e.key === "Enter" || e.key === " ") && (e.preventDefault(), onEntryClick(entry)) : void 0,
136
- style: {
137
- display: "flex",
138
- alignItems: "flex-start",
139
- minHeight: estimatedRowHeight,
140
- boxSizing: "border-box",
141
- padding: "3px 12px",
142
- borderBottom: `1px solid ${tokens.colors.border.muted}11`,
143
- cursor: onEntryClick ? "pointer" : "default",
144
- transition: "background 150ms ease"
145
- },
146
- onMouseEnter: (e) => {
147
- e.currentTarget.style.background = `${tokens.colors.background.surface}`;
148
- },
149
- onMouseLeave: (e) => {
150
- e.currentTarget.style.background = "transparent";
151
- },
152
- children: [
153
- /* @__PURE__ */ jsxs("span", { style: {
154
- color: severityColors[entry.severity],
155
- fontWeight: 700,
156
- fontSize: tokens.typography.fontSize.xxs,
157
- minWidth: 38,
158
- textAlign: "center",
159
- background: `${severityColors[entry.severity]}18`,
160
- borderRadius: tokens.borderRadius.sm,
161
- padding: "1px 4px",
162
- marginRight: 8,
163
- marginTop: 1,
164
- letterSpacing: "0.3px",
165
- display: "inline-flex",
166
- alignItems: "center",
167
- justifyContent: "center",
168
- gap: 3
169
- }, children: [
170
- /* @__PURE__ */ jsx("span", { style: { fontSize: "8px", lineHeight: 1 }, children: severityShapes[entry.severity] }),
171
- severityLabels[entry.severity]
172
- ] }),
173
- showTimestamp && /* @__PURE__ */ jsx("span", { style: {
174
- color: tokens.colors.text.muted,
175
- marginRight: 8,
176
- // 'sim-elapsed' renders T+HH:MM:SS.s which is wider than the wall-clock formats.
177
- minWidth: timestampFormat === "sim-elapsed" ? 110 : 85,
178
- fontSize: 11,
179
- flexShrink: 0,
180
- fontFamily: tokens.typography.fontFamily.mono
181
- }, children: formatTimestamp(entry, timestampFormat) }),
182
- showSource && entry.source && /* @__PURE__ */ jsxs("span", { style: {
183
- color: tokens.colors.interactive.default,
184
- marginRight: 8,
185
- fontSize: 11,
186
- minWidth: 70,
187
- maxWidth: 100,
188
- overflow: "hidden",
189
- textOverflow: "ellipsis",
190
- whiteSpace: "nowrap",
191
- flexShrink: 0
192
- }, children: [
193
- "[",
194
- entry.source,
195
- "]"
196
- ] }),
197
- /* @__PURE__ */ jsx("span", { style: {
198
- color: entry.severity === "error" || entry.severity === "critical" ? severityColors[entry.severity] : tokens.colors.text.primary,
199
- flex: 1,
200
- wordBreak: "break-word",
201
- lineHeight: 1.4
202
- }, children: entry.message })
203
- ]
204
- },
205
- entry.id
206
- ), [severityColors, severityShapes, severityLabels, estimatedRowHeight, tokens, showTimestamp, showSource, timestampFormat, onEntryClick]);
207
- useEffect(() => {
208
- const el = scrollRef.current;
209
- if (!el) return;
210
- const ro = new ResizeObserver(() => {
211
- setScrollState((s) => ({ ...s, containerHeight: el.clientHeight }));
212
- });
213
- ro.observe(el);
214
- setScrollState((s) => ({ ...s, containerHeight: el.clientHeight }));
215
- return () => ro.disconnect();
216
- }, []);
217
- const handleScroll = useCallback(() => {
218
- const el = scrollRef.current;
219
- if (!el) return;
220
- const { scrollTop: st, scrollHeight, clientHeight } = el;
221
- const atBottom = scrollHeight - st - clientHeight < 30;
222
- setAutoScroll(atBottom);
223
- if (rafRef.current !== null) cancelAnimationFrame(rafRef.current);
224
- rafRef.current = requestAnimationFrame(() => {
225
- rafRef.current = null;
226
- setScrollState((prev) => ({ ...prev, scrollTop: el.scrollTop }));
227
- });
228
- }, []);
229
- useEffect(() => {
230
- if (entries.length <= prevLengthRef.current) {
231
- prevLengthRef.current = entries.length;
232
- return;
233
- }
234
- if (!usePagination && !paused && autoScroll && scrollRef.current) {
235
- scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
236
- }
237
- prevLengthRef.current = entries.length;
238
- }, [entries.length, paused, autoScroll, usePagination]);
239
- useEffect(() => () => {
240
- if (rafRef.current !== null) cancelAnimationFrame(rafRef.current);
241
- }, []);
242
- const toggleSeverity = useCallback((sev) => {
243
- setSeverityFilter((prev) => {
244
- const next = new Set(prev);
245
- if (next.has(sev)) next.delete(sev);
246
- else next.add(sev);
247
- return next;
248
- });
249
- }, []);
250
- const exportLog = useCallback(() => {
251
- var _a;
252
- const text = filteredEntries.map((e) => {
253
- const wall = formatTimestamp(e, "datetime");
254
- const sim = timestampFormat === "sim-elapsed" && typeof e.simTime === "number" ? ` ${formatSimElapsed(e.simTime)}` : "";
255
- const src = e.source ? `[${e.source}]` : "";
256
- return `${wall}${sim} [${e.severity.toUpperCase()}] ${src} ${e.message}`;
257
- }).join("\n");
258
- (_a = navigator.clipboard) == null ? void 0 : _a.writeText(text);
259
- }, [filteredEntries, timestampFormat]);
260
- const clearFilters = useCallback(() => {
261
- setSeverityFilter(new Set(SEVERITY_ORDER));
262
- setSearchQuery("");
263
- }, []);
264
- const counts = useMemo(() => {
265
- const c = { debug: 0, info: 0, warn: 0, error: 0, critical: 0 };
266
- entries.forEach((e) => {
267
- c[e.severity]++;
268
- });
269
- return c;
270
- }, [entries]);
271
- return /* @__PURE__ */ jsxs(
272
- "div",
273
- {
274
- className: `log-viewer ${className}`,
275
- style: {
276
- height: typeof height === "number" ? `${height}px` : height,
277
- minHeight: typeof height === "number" ? `${height}px` : height,
278
- background: tokens.colors.background.base,
279
- ...bordered ? tokens.colors.border.cardStyle ?? { border: `1px solid ${tokens.colors.border.muted}` } : { border: "none" },
280
- borderRadius: tokens.borderRadius.lg,
281
- overflow: "hidden",
282
- display: "flex",
283
- flexDirection: "column",
284
- fontFamily: tokens.typography.fontFamily.mono,
285
- ...styleProp,
286
- fontSize: tokens.typography.fontSize.xs,
287
- color: tokens.colors.text.primary,
288
- backdropFilter: isTransparentTheme ? "blur(10px)" : void 0,
289
- WebkitBackdropFilter: isTransparentTheme ? "blur(10px)" : void 0
290
- },
291
- children: [
292
- title && /* @__PURE__ */ jsxs("div", { style: {
293
- padding: "8px 12px",
294
- borderBottom: `1px solid ${tokens.colors.border.muted}`,
295
- display: "flex",
296
- alignItems: "center",
297
- justifyContent: "space-between",
298
- background: tokens.colors.background.surface,
299
- fontFamily: tokens.typography.fontFamily.primary,
300
- backdropFilter: "blur(8px)"
301
- }, children: [
302
- /* @__PURE__ */ jsx("span", { style: { fontWeight: 600, fontSize: 13 }, children: title }),
303
- /* @__PURE__ */ jsx("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: /* @__PURE__ */ jsxs("span", { style: { fontSize: 11, color: tokens.colors.text.muted }, children: [
304
- filteredEntries.length.toLocaleString(),
305
- " entries"
306
- ] }) })
307
- ] }),
308
- (showFilters || showSearch) && /* @__PURE__ */ jsxs("div", { style: {
309
- padding: "6px 12px",
310
- borderBottom: `1px solid ${tokens.colors.border.muted}`,
311
- display: "flex",
312
- alignItems: "center",
313
- gap: 8,
314
- flexWrap: "wrap",
315
- background: tokens.colors.background.surface,
316
- fontFamily: tokens.typography.fontFamily.primary
317
- }, children: [
318
- showFilters && SEVERITY_ORDER.map((sev) => /* @__PURE__ */ jsxs(
319
- "button",
320
- {
321
- onClick: () => toggleSeverity(sev),
322
- "aria-pressed": severityFilter.has(sev),
323
- "aria-label": `Filter ${sev} logs (${counts[sev]})`,
324
- style: {
325
- background: severityFilter.has(sev) ? `${severityColors[sev]}22` : "transparent",
326
- color: severityFilter.has(sev) ? severityColors[sev] : tokens.colors.text.muted,
327
- border: `1px solid ${severityFilter.has(sev) ? severityColors[sev] + "66" : tokens.colors.border.muted}`,
328
- borderRadius: tokens.borderRadius.md,
329
- padding: `${tokens.spacing.xxs} ${tokens.spacing.sm}`,
330
- fontSize: tokens.typography.fontSize.xxs,
331
- fontWeight: tokens.typography.fontWeight.medium,
332
- cursor: "pointer",
333
- textTransform: "uppercase",
334
- letterSpacing: "0.5px",
335
- transition: "all 150ms ease"
336
- },
337
- children: [
338
- /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: severityShapes[sev] }),
339
- " ",
340
- sev,
341
- " (",
342
- counts[sev],
343
- ")"
344
- ]
345
- },
346
- sev
347
- )),
348
- /* @__PURE__ */ jsx("div", { style: { flex: 1 } }),
349
- showSearch && /* @__PURE__ */ jsx(
350
- "input",
351
- {
352
- type: "text",
353
- value: searchQuery,
354
- onChange: (e) => setSearchQuery(e.target.value),
355
- placeholder: "Filter messages...",
356
- "aria-label": "Filter log messages",
357
- style: {
358
- background: tokens.colors.background.base,
359
- color: tokens.colors.text.primary,
360
- border: `1px solid ${tokens.colors.border.muted}`,
361
- borderRadius: tokens.borderRadius.md,
362
- padding: `3px ${tokens.spacing.sm}`,
363
- fontSize: tokens.typography.fontSize.sm,
364
- outline: "none",
365
- minWidth: 150
366
- }
367
- }
368
- ),
369
- /* @__PURE__ */ jsx(
370
- "button",
371
- {
372
- onClick: () => setPaused((p) => !p),
373
- "aria-label": paused ? "Resume log stream" : "Pause log stream",
374
- style: {
375
- background: paused ? `${tokens.colors.status.caution}22` : tokens.colors.background.base,
376
- color: paused ? tokens.colors.status.caution : tokens.colors.text.secondary,
377
- border: `1px solid ${tokens.colors.border.muted}`,
378
- borderRadius: tokens.borderRadius.md,
379
- padding: `${tokens.spacing.xxs} ${tokens.spacing.sm}`,
380
- fontSize: tokens.typography.fontSize.xxs,
381
- cursor: "pointer",
382
- fontWeight: tokens.typography.fontWeight.medium,
383
- transition: "all 150ms ease"
384
- },
385
- children: paused ? "▶ Resume" : "⏸ Pause"
386
- }
387
- ),
388
- /* @__PURE__ */ jsx("button", { onClick: exportLog, "aria-label": "Copy log to clipboard", style: {
389
- background: tokens.colors.background.base,
390
- color: tokens.colors.text.secondary,
391
- border: `1px solid ${tokens.colors.border.muted}`,
392
- borderRadius: tokens.borderRadius.md,
393
- padding: `${tokens.spacing.xxs} ${tokens.spacing.sm}`,
394
- fontSize: tokens.typography.fontSize.xxs,
395
- cursor: "pointer",
396
- transition: "all 150ms ease"
397
- }, children: "Copy" }),
398
- /* @__PURE__ */ jsx("button", { onClick: clearFilters, "aria-label": "Reset all filters", style: {
399
- background: tokens.colors.background.base,
400
- color: tokens.colors.text.secondary,
401
- border: `1px solid ${tokens.colors.border.muted}`,
402
- borderRadius: tokens.borderRadius.md,
403
- padding: `${tokens.spacing.xxs} ${tokens.spacing.sm}`,
404
- fontSize: tokens.typography.fontSize.xxs,
405
- cursor: "pointer",
406
- transition: "all 150ms ease"
407
- }, children: "Reset" })
408
- ] }),
409
- /* @__PURE__ */ jsx(
410
- "div",
411
- {
412
- ref: scrollRef,
413
- onScroll: handleScroll,
414
- className: "zendir-log-scroll",
415
- style: {
416
- flex: 1,
417
- minHeight: 0,
418
- overflowY: "auto",
419
- overflowX: "hidden",
420
- padding: "2px 0",
421
- overscrollBehavior: "contain",
422
- WebkitOverflowScrolling: "touch"
423
- },
424
- children: filteredEntries.length === 0 ? /* @__PURE__ */ jsx("div", { style: {
425
- display: "flex",
426
- alignItems: "center",
427
- justifyContent: "center",
428
- height: "100%",
429
- color: tokens.colors.text.muted,
430
- fontSize: 13,
431
- fontFamily: tokens.typography.fontFamily.primary
432
- }, children: entries.length === 0 ? "No log entries" : "No matching entries" }) : usePagination ? /* @__PURE__ */ jsx("div", { style: { padding: "2px 0" }, role: "list", "aria-label": "Log entries", children: (paginationEntries ?? []).map((entry) => renderRow(entry)) }) : /* @__PURE__ */ jsxs(
433
- "div",
434
- {
435
- style: {
436
- height: totalHeight,
437
- position: "relative",
438
- width: "100%"
439
- },
440
- role: "list",
441
- "aria-label": "Log entries",
442
- children: [
443
- visibleRange.start > 0 && /* @__PURE__ */ jsx(
444
- "div",
445
- {
446
- style: { height: visibleRange.start * estimatedRowHeight, width: "100%" },
447
- "aria-hidden": true
448
- }
449
- ),
450
- visibleEntries.map((entry) => renderRow(entry)),
451
- visibleRange.end < filteredEntries.length && /* @__PURE__ */ jsx(
452
- "div",
453
- {
454
- style: {
455
- height: (filteredEntries.length - visibleRange.end) * estimatedRowHeight,
456
- width: "100%"
457
- },
458
- "aria-hidden": true
459
- }
460
- )
461
- ]
462
- }
463
- )
464
- }
465
- ),
466
- usePagination && filteredEntries.length > 0 && /* @__PURE__ */ jsxs(
467
- "div",
468
- {
469
- style: {
470
- display: "flex",
471
- alignItems: "center",
472
- justifyContent: "space-between",
473
- flexWrap: "wrap",
474
- gap: tokens.spacing.sm,
475
- padding: `${tokens.spacing.xs} ${tokens.spacing.smd}`,
476
- borderTop: `1px solid ${tokens.colors.border.muted}`,
477
- background: tokens.colors.background.surface,
478
- fontFamily: tokens.typography.fontFamily.primary,
479
- fontSize: tokens.typography.fontSize.xs,
480
- color: tokens.colors.text.secondary
481
- },
482
- children: [
483
- /* @__PURE__ */ jsxs("span", { "aria-live": "polite", children: [
484
- "Page ",
485
- currentPage,
486
- " of ",
487
- totalPages,
488
- filteredEntries.length > 0 && /* @__PURE__ */ jsxs("span", { style: { marginLeft: tokens.spacing.sm }, children: [
489
- "(",
490
- ((currentPage - 1) * pageSize + 1).toLocaleString(),
491
- "–",
492
- Math.min(currentPage * pageSize, filteredEntries.length).toLocaleString(),
493
- " of ",
494
- filteredEntries.length.toLocaleString(),
495
- ")"
496
- ] })
497
- ] }),
498
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: tokens.spacing.xs }, children: [
499
- /* @__PURE__ */ jsx(
500
- "button",
501
- {
502
- type: "button",
503
- onClick: () => {
504
- setCurrentPage(1);
505
- setAutoScroll(true);
506
- },
507
- disabled: currentPage <= 1,
508
- "aria-label": "First page (newest)",
509
- style: {
510
- padding: `${tokens.spacing.xxs} ${tokens.spacing.sm}`,
511
- border: `1px solid ${tokens.colors.border.muted}`,
512
- borderRadius: tokens.borderRadius.md,
513
- background: tokens.colors.background.base,
514
- color: tokens.colors.text.primary,
515
- fontSize: tokens.typography.fontSize.xxs,
516
- cursor: currentPage <= 1 ? "not-allowed" : "pointer",
517
- opacity: currentPage <= 1 ? 0.5 : 1
518
- },
519
- children: "First"
520
- }
521
- ),
522
- /* @__PURE__ */ jsx(
523
- "button",
524
- {
525
- type: "button",
526
- onClick: () => {
527
- setCurrentPage((p) => Math.max(1, p - 1));
528
- setAutoScroll(false);
529
- },
530
- disabled: currentPage <= 1,
531
- "aria-label": "Previous page",
532
- style: {
533
- padding: `${tokens.spacing.xxs} ${tokens.spacing.sm}`,
534
- border: `1px solid ${tokens.colors.border.muted}`,
535
- borderRadius: tokens.borderRadius.md,
536
- background: tokens.colors.background.base,
537
- color: tokens.colors.text.primary,
538
- fontSize: tokens.typography.fontSize.xxs,
539
- cursor: currentPage <= 1 ? "not-allowed" : "pointer",
540
- opacity: currentPage <= 1 ? 0.5 : 1
541
- },
542
- children: "Previous"
543
- }
544
- ),
545
- /* @__PURE__ */ jsx(
546
- "button",
547
- {
548
- type: "button",
549
- onClick: () => {
550
- setCurrentPage((p) => Math.min(totalPages, p + 1));
551
- setAutoScroll(false);
552
- },
553
- disabled: currentPage >= totalPages,
554
- "aria-label": "Next page",
555
- style: {
556
- padding: `${tokens.spacing.xxs} ${tokens.spacing.sm}`,
557
- border: `1px solid ${tokens.colors.border.muted}`,
558
- borderRadius: tokens.borderRadius.md,
559
- background: tokens.colors.background.base,
560
- color: tokens.colors.text.primary,
561
- fontSize: tokens.typography.fontSize.xxs,
562
- cursor: currentPage >= totalPages ? "not-allowed" : "pointer",
563
- opacity: currentPage >= totalPages ? 0.5 : 1
564
- },
565
- children: "Next"
566
- }
567
- ),
568
- /* @__PURE__ */ jsx(
569
- "button",
570
- {
571
- type: "button",
572
- onClick: () => {
573
- setCurrentPage(totalPages);
574
- setAutoScroll(false);
575
- },
576
- disabled: currentPage >= totalPages,
577
- "aria-label": "Last page (oldest)",
578
- style: {
579
- padding: `${tokens.spacing.xxs} ${tokens.spacing.sm}`,
580
- border: `1px solid ${tokens.colors.border.muted}`,
581
- borderRadius: tokens.borderRadius.md,
582
- background: tokens.colors.background.base,
583
- color: tokens.colors.text.primary,
584
- fontSize: tokens.typography.fontSize.xxs,
585
- cursor: currentPage >= totalPages ? "not-allowed" : "pointer",
586
- opacity: currentPage >= totalPages ? 0.5 : 1
587
- },
588
- children: "Last"
589
- }
590
- )
591
- ] })
592
- ]
593
- }
594
- ),
595
- /* @__PURE__ */ jsx("style", { children: `
85
+ var LogViewer = memo(function LogViewer({ entries, maxEntries = 5e3, height = 400, title, showTimestamp = true, showSource = true, showFilters = true, showSearch = true, defaultSeverities, timestampFormat = "time", onEntryClick, className = "", bordered = true, style: styleProp, estimatedRowHeight = DEFAULT_ESTIMATED_ROW_HEIGHT, overscanCount = DEFAULT_OVERSCAN_COUNT, pagination }) {
86
+ const { tokens, theme } = useTheme();
87
+ const isTransparentTheme = theme === "transparent" || theme === "transparent-bold" || theme === "transparent-minimal";
88
+ const scrollRef = useRef(null);
89
+ const [paused, setPaused] = useState(false);
90
+ const [searchQuery, setSearchQuery] = useState("");
91
+ const [severityFilter, setSeverityFilter] = useState(new Set(defaultSeverities ?? SEVERITY_ORDER));
92
+ const [autoScroll, setAutoScroll] = useState(true);
93
+ const prevLengthRef = useRef(entries.length);
94
+ /** Scroll position and container height for virtualization */
95
+ const [scrollState, setScrollState] = useState({
96
+ scrollTop: 0,
97
+ containerHeight: 300
98
+ });
99
+ const rafRef = useRef(null);
100
+ const severityColors = {
101
+ debug: tokens.colors.text.muted ?? tokens.colors.text.secondary,
102
+ info: tokens.colors.status.standby,
103
+ warn: tokens.colors.status.caution,
104
+ error: tokens.colors.status.serious,
105
+ critical: tokens.colors.status.critical
106
+ };
107
+ /**
108
+ * Astro UX status shapes mapped to log severity levels:
109
+ * debug → off (small dot ·), info standby (ring ◯),
110
+ * warn → caution (square ■), error → serious (diamond ◆),
111
+ * critical → critical (triangle down ▼)
112
+ */
113
+ const severityShapes = {
114
+ debug: "·",
115
+ info: "○",
116
+ warn: "■",
117
+ error: "◆",
118
+ critical: "▼"
119
+ };
120
+ const severityLabels = {
121
+ debug: "DBG",
122
+ info: "INF",
123
+ warn: "WRN",
124
+ error: "ERR",
125
+ critical: "CRT"
126
+ };
127
+ const filteredEntries = useMemo(() => {
128
+ let filtered = entries.filter((e) => severityFilter.has(e.severity));
129
+ if (searchQuery.trim()) {
130
+ const q = searchQuery.toLowerCase();
131
+ filtered = filtered.filter((e) => e.message.toLowerCase().includes(q) || e.source && e.source.toLowerCase().includes(q));
132
+ }
133
+ return filtered.slice(0, maxEntries);
134
+ }, [
135
+ entries,
136
+ severityFilter,
137
+ searchQuery,
138
+ maxEntries
139
+ ]);
140
+ const paginationConfig = pagination === true ? { pageSize: 200 } : pagination || void 0;
141
+ const pageSize = paginationConfig?.pageSize ?? 200;
142
+ const totalPages = Math.max(1, Math.ceil(filteredEntries.length / pageSize));
143
+ const [currentPage, setCurrentPage] = useState(1);
144
+ useEffect(() => {
145
+ setCurrentPage((p) => Math.min(p, totalPages));
146
+ }, [totalPages]);
147
+ const paginationEntries = useMemo(() => {
148
+ if (!paginationConfig) return null;
149
+ const start = (currentPage - 1) * pageSize;
150
+ return filteredEntries.slice(start, start + pageSize);
151
+ }, [
152
+ paginationConfig,
153
+ currentPage,
154
+ pageSize,
155
+ filteredEntries
156
+ ]);
157
+ const totalHeight = filteredEntries.length * estimatedRowHeight;
158
+ const { scrollTop, containerHeight } = scrollState;
159
+ const visibleRange = useMemo(() => {
160
+ return {
161
+ start: Math.max(0, Math.floor(scrollTop / estimatedRowHeight) - overscanCount),
162
+ end: Math.min(filteredEntries.length, Math.ceil((scrollTop + containerHeight) / estimatedRowHeight) + overscanCount)
163
+ };
164
+ }, [
165
+ scrollTop,
166
+ containerHeight,
167
+ estimatedRowHeight,
168
+ overscanCount,
169
+ filteredEntries.length
170
+ ]);
171
+ const visibleEntries = useMemo(() => filteredEntries.slice(visibleRange.start, visibleRange.end), [
172
+ filteredEntries,
173
+ visibleRange.start,
174
+ visibleRange.end
175
+ ]);
176
+ const usePagination = !!paginationConfig;
177
+ const renderRow = useCallback((entry) => /* @__PURE__ */ jsxs("div", {
178
+ role: onEntryClick ? "button" : "listitem",
179
+ tabIndex: onEntryClick ? 0 : void 0,
180
+ "aria-label": onEntryClick ? `${severityLabels[entry.severity]}: ${entry.message}` : void 0,
181
+ onClick: () => onEntryClick?.(entry),
182
+ onKeyDown: onEntryClick ? (e) => (e.key === "Enter" || e.key === " ") && (e.preventDefault(), onEntryClick(entry)) : void 0,
183
+ style: {
184
+ display: "flex",
185
+ alignItems: "flex-start",
186
+ minHeight: estimatedRowHeight,
187
+ boxSizing: "border-box",
188
+ padding: "3px 12px",
189
+ borderBottom: `1px solid ${tokens.colors.border.muted}11`,
190
+ cursor: onEntryClick ? "pointer" : "default",
191
+ transition: "background 150ms ease"
192
+ },
193
+ onMouseEnter: (e) => {
194
+ e.currentTarget.style.background = `${tokens.colors.background.surface}`;
195
+ },
196
+ onMouseLeave: (e) => {
197
+ e.currentTarget.style.background = "transparent";
198
+ },
199
+ children: [
200
+ /* @__PURE__ */ jsxs("span", {
201
+ style: {
202
+ color: severityColors[entry.severity],
203
+ fontWeight: 700,
204
+ fontSize: tokens.typography.fontSize.xxs,
205
+ minWidth: 38,
206
+ textAlign: "center",
207
+ background: `${severityColors[entry.severity]}18`,
208
+ borderRadius: tokens.borderRadius.sm,
209
+ padding: "1px 4px",
210
+ marginRight: 8,
211
+ marginTop: 1,
212
+ letterSpacing: "0.3px",
213
+ display: "inline-flex",
214
+ alignItems: "center",
215
+ justifyContent: "center",
216
+ gap: 3
217
+ },
218
+ children: [/* @__PURE__ */ jsx("span", {
219
+ style: {
220
+ fontSize: "8px",
221
+ lineHeight: 1
222
+ },
223
+ children: severityShapes[entry.severity]
224
+ }), severityLabels[entry.severity]]
225
+ }),
226
+ showTimestamp && /* @__PURE__ */ jsx("span", {
227
+ style: {
228
+ color: tokens.colors.text.muted,
229
+ marginRight: 8,
230
+ minWidth: timestampFormat === "sim-elapsed" ? 110 : 85,
231
+ fontSize: 11,
232
+ flexShrink: 0,
233
+ fontFamily: tokens.typography.fontFamily.mono
234
+ },
235
+ children: formatTimestamp(entry, timestampFormat)
236
+ }),
237
+ showSource && entry.source && /* @__PURE__ */ jsxs("span", {
238
+ style: {
239
+ color: tokens.colors.interactive.default,
240
+ marginRight: 8,
241
+ fontSize: 11,
242
+ minWidth: 70,
243
+ maxWidth: 100,
244
+ overflow: "hidden",
245
+ textOverflow: "ellipsis",
246
+ whiteSpace: "nowrap",
247
+ flexShrink: 0
248
+ },
249
+ children: [
250
+ "[",
251
+ entry.source,
252
+ "]"
253
+ ]
254
+ }),
255
+ /* @__PURE__ */ jsx("span", {
256
+ style: {
257
+ color: entry.severity === "error" || entry.severity === "critical" ? severityColors[entry.severity] : tokens.colors.text.primary,
258
+ flex: 1,
259
+ wordBreak: "break-word",
260
+ lineHeight: 1.4
261
+ },
262
+ children: entry.message
263
+ })
264
+ ]
265
+ }, entry.id), [
266
+ severityColors,
267
+ severityShapes,
268
+ severityLabels,
269
+ estimatedRowHeight,
270
+ tokens,
271
+ showTimestamp,
272
+ showSource,
273
+ timestampFormat,
274
+ onEntryClick
275
+ ]);
276
+ useEffect(() => {
277
+ const el = scrollRef.current;
278
+ if (!el) return;
279
+ const ro = new ResizeObserver(() => {
280
+ setScrollState((s) => ({
281
+ ...s,
282
+ containerHeight: el.clientHeight
283
+ }));
284
+ });
285
+ ro.observe(el);
286
+ setScrollState((s) => ({
287
+ ...s,
288
+ containerHeight: el.clientHeight
289
+ }));
290
+ return () => ro.disconnect();
291
+ }, []);
292
+ const handleScroll = useCallback(() => {
293
+ const el = scrollRef.current;
294
+ if (!el) return;
295
+ const { scrollTop: st, scrollHeight, clientHeight } = el;
296
+ const atBottom = scrollHeight - st - clientHeight < 30;
297
+ setAutoScroll(atBottom);
298
+ if (rafRef.current !== null) cancelAnimationFrame(rafRef.current);
299
+ rafRef.current = requestAnimationFrame(() => {
300
+ rafRef.current = null;
301
+ setScrollState((prev) => ({
302
+ ...prev,
303
+ scrollTop: el.scrollTop
304
+ }));
305
+ });
306
+ }, []);
307
+ useEffect(() => {
308
+ if (entries.length <= prevLengthRef.current) {
309
+ prevLengthRef.current = entries.length;
310
+ return;
311
+ }
312
+ if (!usePagination && !paused && autoScroll && scrollRef.current) scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
313
+ prevLengthRef.current = entries.length;
314
+ }, [
315
+ entries.length,
316
+ paused,
317
+ autoScroll,
318
+ usePagination
319
+ ]);
320
+ useEffect(() => () => {
321
+ if (rafRef.current !== null) cancelAnimationFrame(rafRef.current);
322
+ }, []);
323
+ const toggleSeverity = useCallback((sev) => {
324
+ setSeverityFilter((prev) => {
325
+ const next = new Set(prev);
326
+ if (next.has(sev)) next.delete(sev);
327
+ else next.add(sev);
328
+ return next;
329
+ });
330
+ }, []);
331
+ const exportLog = useCallback(() => {
332
+ const text = filteredEntries.map((e) => {
333
+ const wall = formatTimestamp(e, "datetime");
334
+ const sim = timestampFormat === "sim-elapsed" && typeof e.simTime === "number" ? ` ${formatSimElapsed(e.simTime)}` : "";
335
+ const src = e.source ? `[${e.source}]` : "";
336
+ return `${wall}${sim} [${e.severity.toUpperCase()}] ${src} ${e.message}`;
337
+ }).join("\n");
338
+ navigator.clipboard?.writeText(text);
339
+ }, [filteredEntries, timestampFormat]);
340
+ const clearFilters = useCallback(() => {
341
+ setSeverityFilter(new Set(SEVERITY_ORDER));
342
+ setSearchQuery("");
343
+ }, []);
344
+ const counts = useMemo(() => {
345
+ const c = {
346
+ debug: 0,
347
+ info: 0,
348
+ warn: 0,
349
+ error: 0,
350
+ critical: 0
351
+ };
352
+ entries.forEach((e) => {
353
+ c[e.severity]++;
354
+ });
355
+ return c;
356
+ }, [entries]);
357
+ return /* @__PURE__ */ jsxs("div", {
358
+ className: `log-viewer ${className}`,
359
+ style: {
360
+ height: typeof height === "number" ? `${height}px` : height,
361
+ minHeight: typeof height === "number" ? `${height}px` : height,
362
+ background: tokens.colors.background.base,
363
+ ...bordered ? tokens.colors.border.cardStyle ?? { border: `1px solid ${tokens.colors.border.muted}` } : { border: "none" },
364
+ borderRadius: tokens.borderRadius.lg,
365
+ overflow: "hidden",
366
+ display: "flex",
367
+ flexDirection: "column",
368
+ fontFamily: tokens.typography.fontFamily.mono,
369
+ ...styleProp,
370
+ fontSize: tokens.typography.fontSize.xs,
371
+ color: tokens.colors.text.primary,
372
+ backdropFilter: isTransparentTheme ? "blur(10px)" : void 0,
373
+ WebkitBackdropFilter: isTransparentTheme ? "blur(10px)" : void 0
374
+ },
375
+ children: [
376
+ title && /* @__PURE__ */ jsxs("div", {
377
+ style: {
378
+ padding: "8px 12px",
379
+ borderBottom: `1px solid ${tokens.colors.border.muted}`,
380
+ display: "flex",
381
+ alignItems: "center",
382
+ justifyContent: "space-between",
383
+ background: tokens.colors.background.surface,
384
+ fontFamily: tokens.typography.fontFamily.primary,
385
+ backdropFilter: "blur(8px)"
386
+ },
387
+ children: [/* @__PURE__ */ jsx("span", {
388
+ style: {
389
+ fontWeight: 600,
390
+ fontSize: 13
391
+ },
392
+ children: title
393
+ }), /* @__PURE__ */ jsx("div", {
394
+ style: {
395
+ display: "flex",
396
+ alignItems: "center",
397
+ gap: 8
398
+ },
399
+ children: /* @__PURE__ */ jsxs("span", {
400
+ style: {
401
+ fontSize: 11,
402
+ color: tokens.colors.text.muted
403
+ },
404
+ children: [filteredEntries.length.toLocaleString(), " entries"]
405
+ })
406
+ })]
407
+ }),
408
+ (showFilters || showSearch) && /* @__PURE__ */ jsxs("div", {
409
+ style: {
410
+ padding: "6px 12px",
411
+ borderBottom: `1px solid ${tokens.colors.border.muted}`,
412
+ display: "flex",
413
+ alignItems: "center",
414
+ gap: 8,
415
+ flexWrap: "wrap",
416
+ background: tokens.colors.background.surface,
417
+ fontFamily: tokens.typography.fontFamily.primary
418
+ },
419
+ children: [
420
+ showFilters && SEVERITY_ORDER.map((sev) => /* @__PURE__ */ jsxs("button", {
421
+ onClick: () => toggleSeverity(sev),
422
+ "aria-pressed": severityFilter.has(sev),
423
+ "aria-label": `Filter ${sev} logs (${counts[sev]})`,
424
+ style: {
425
+ background: severityFilter.has(sev) ? `${severityColors[sev]}22` : "transparent",
426
+ color: severityFilter.has(sev) ? severityColors[sev] : tokens.colors.text.muted,
427
+ border: `1px solid ${severityFilter.has(sev) ? severityColors[sev] + "66" : tokens.colors.border.muted}`,
428
+ borderRadius: tokens.borderRadius.md,
429
+ padding: `${tokens.spacing.xxs} ${tokens.spacing.sm}`,
430
+ fontSize: tokens.typography.fontSize.xxs,
431
+ fontWeight: tokens.typography.fontWeight.medium,
432
+ cursor: "pointer",
433
+ textTransform: "uppercase",
434
+ letterSpacing: "0.5px",
435
+ transition: "all 150ms ease"
436
+ },
437
+ children: [
438
+ /* @__PURE__ */ jsx("span", {
439
+ "aria-hidden": "true",
440
+ children: severityShapes[sev]
441
+ }),
442
+ " ",
443
+ sev,
444
+ " (",
445
+ counts[sev],
446
+ ")"
447
+ ]
448
+ }, sev)),
449
+ /* @__PURE__ */ jsx("div", { style: { flex: 1 } }),
450
+ showSearch && /* @__PURE__ */ jsx("input", {
451
+ type: "text",
452
+ value: searchQuery,
453
+ onChange: (e) => setSearchQuery(e.target.value),
454
+ placeholder: "Filter messages...",
455
+ "aria-label": "Filter log messages",
456
+ style: {
457
+ background: tokens.colors.background.base,
458
+ color: tokens.colors.text.primary,
459
+ border: `1px solid ${tokens.colors.border.muted}`,
460
+ borderRadius: tokens.borderRadius.md,
461
+ padding: `3px ${tokens.spacing.sm}`,
462
+ fontSize: tokens.typography.fontSize.sm,
463
+ outline: "none",
464
+ minWidth: 150
465
+ }
466
+ }),
467
+ /* @__PURE__ */ jsx("button", {
468
+ onClick: () => setPaused((p) => !p),
469
+ "aria-label": paused ? "Resume log stream" : "Pause log stream",
470
+ style: {
471
+ background: paused ? `${tokens.colors.status.caution}22` : tokens.colors.background.base,
472
+ color: paused ? tokens.colors.status.caution : tokens.colors.text.secondary,
473
+ border: `1px solid ${tokens.colors.border.muted}`,
474
+ borderRadius: tokens.borderRadius.md,
475
+ padding: `${tokens.spacing.xxs} ${tokens.spacing.sm}`,
476
+ fontSize: tokens.typography.fontSize.xxs,
477
+ cursor: "pointer",
478
+ fontWeight: tokens.typography.fontWeight.medium,
479
+ transition: "all 150ms ease"
480
+ },
481
+ children: paused ? "▶ Resume" : "⏸ Pause"
482
+ }),
483
+ /* @__PURE__ */ jsx("button", {
484
+ onClick: exportLog,
485
+ "aria-label": "Copy log to clipboard",
486
+ style: {
487
+ background: tokens.colors.background.base,
488
+ color: tokens.colors.text.secondary,
489
+ border: `1px solid ${tokens.colors.border.muted}`,
490
+ borderRadius: tokens.borderRadius.md,
491
+ padding: `${tokens.spacing.xxs} ${tokens.spacing.sm}`,
492
+ fontSize: tokens.typography.fontSize.xxs,
493
+ cursor: "pointer",
494
+ transition: "all 150ms ease"
495
+ },
496
+ children: "Copy"
497
+ }),
498
+ /* @__PURE__ */ jsx("button", {
499
+ onClick: clearFilters,
500
+ "aria-label": "Reset all filters",
501
+ style: {
502
+ background: tokens.colors.background.base,
503
+ color: tokens.colors.text.secondary,
504
+ border: `1px solid ${tokens.colors.border.muted}`,
505
+ borderRadius: tokens.borderRadius.md,
506
+ padding: `${tokens.spacing.xxs} ${tokens.spacing.sm}`,
507
+ fontSize: tokens.typography.fontSize.xxs,
508
+ cursor: "pointer",
509
+ transition: "all 150ms ease"
510
+ },
511
+ children: "Reset"
512
+ })
513
+ ]
514
+ }),
515
+ /* @__PURE__ */ jsx("div", {
516
+ ref: scrollRef,
517
+ onScroll: handleScroll,
518
+ className: "zendir-log-scroll",
519
+ style: {
520
+ flex: 1,
521
+ minHeight: 0,
522
+ overflowY: "auto",
523
+ overflowX: "hidden",
524
+ padding: "2px 0",
525
+ overscrollBehavior: "contain",
526
+ WebkitOverflowScrolling: "touch"
527
+ },
528
+ children: filteredEntries.length === 0 ? /* @__PURE__ */ jsx("div", {
529
+ style: {
530
+ display: "flex",
531
+ alignItems: "center",
532
+ justifyContent: "center",
533
+ height: "100%",
534
+ color: tokens.colors.text.muted,
535
+ fontSize: 13,
536
+ fontFamily: tokens.typography.fontFamily.primary
537
+ },
538
+ children: entries.length === 0 ? "No log entries" : "No matching entries"
539
+ }) : usePagination ? /* @__PURE__ */ jsx("div", {
540
+ style: { padding: "2px 0" },
541
+ role: "list",
542
+ "aria-label": "Log entries",
543
+ children: (paginationEntries ?? []).map((entry) => renderRow(entry))
544
+ }) : /* @__PURE__ */ jsxs("div", {
545
+ style: {
546
+ height: totalHeight,
547
+ position: "relative",
548
+ width: "100%"
549
+ },
550
+ role: "list",
551
+ "aria-label": "Log entries",
552
+ children: [
553
+ visibleRange.start > 0 && /* @__PURE__ */ jsx("div", {
554
+ style: {
555
+ height: visibleRange.start * estimatedRowHeight,
556
+ width: "100%"
557
+ },
558
+ "aria-hidden": true
559
+ }),
560
+ visibleEntries.map((entry) => renderRow(entry)),
561
+ visibleRange.end < filteredEntries.length && /* @__PURE__ */ jsx("div", {
562
+ style: {
563
+ height: (filteredEntries.length - visibleRange.end) * estimatedRowHeight,
564
+ width: "100%"
565
+ },
566
+ "aria-hidden": true
567
+ })
568
+ ]
569
+ })
570
+ }),
571
+ usePagination && filteredEntries.length > 0 && /* @__PURE__ */ jsxs("div", {
572
+ style: {
573
+ display: "flex",
574
+ alignItems: "center",
575
+ justifyContent: "space-between",
576
+ flexWrap: "wrap",
577
+ gap: tokens.spacing.sm,
578
+ padding: `${tokens.spacing.xs} ${tokens.spacing.smd}`,
579
+ borderTop: `1px solid ${tokens.colors.border.muted}`,
580
+ background: tokens.colors.background.surface,
581
+ fontFamily: tokens.typography.fontFamily.primary,
582
+ fontSize: tokens.typography.fontSize.xs,
583
+ color: tokens.colors.text.secondary
584
+ },
585
+ children: [/* @__PURE__ */ jsxs("span", {
586
+ "aria-live": "polite",
587
+ children: [
588
+ "Page ",
589
+ currentPage,
590
+ " of ",
591
+ totalPages,
592
+ filteredEntries.length > 0 && /* @__PURE__ */ jsxs("span", {
593
+ style: { marginLeft: tokens.spacing.sm },
594
+ children: [
595
+ "(",
596
+ ((currentPage - 1) * pageSize + 1).toLocaleString(),
597
+ "–",
598
+ Math.min(currentPage * pageSize, filteredEntries.length).toLocaleString(),
599
+ " ",
600
+ "of ",
601
+ filteredEntries.length.toLocaleString(),
602
+ ")"
603
+ ]
604
+ })
605
+ ]
606
+ }), /* @__PURE__ */ jsxs("div", {
607
+ style: {
608
+ display: "flex",
609
+ alignItems: "center",
610
+ gap: tokens.spacing.xs
611
+ },
612
+ children: [
613
+ /* @__PURE__ */ jsx("button", {
614
+ type: "button",
615
+ onClick: () => {
616
+ setCurrentPage(1);
617
+ setAutoScroll(true);
618
+ },
619
+ disabled: currentPage <= 1,
620
+ "aria-label": "First page (newest)",
621
+ style: {
622
+ padding: `${tokens.spacing.xxs} ${tokens.spacing.sm}`,
623
+ border: `1px solid ${tokens.colors.border.muted}`,
624
+ borderRadius: tokens.borderRadius.md,
625
+ background: tokens.colors.background.base,
626
+ color: tokens.colors.text.primary,
627
+ fontSize: tokens.typography.fontSize.xxs,
628
+ cursor: currentPage <= 1 ? "not-allowed" : "pointer",
629
+ opacity: currentPage <= 1 ? .5 : 1
630
+ },
631
+ children: "First"
632
+ }),
633
+ /* @__PURE__ */ jsx("button", {
634
+ type: "button",
635
+ onClick: () => {
636
+ setCurrentPage((p) => Math.max(1, p - 1));
637
+ setAutoScroll(false);
638
+ },
639
+ disabled: currentPage <= 1,
640
+ "aria-label": "Previous page",
641
+ style: {
642
+ padding: `${tokens.spacing.xxs} ${tokens.spacing.sm}`,
643
+ border: `1px solid ${tokens.colors.border.muted}`,
644
+ borderRadius: tokens.borderRadius.md,
645
+ background: tokens.colors.background.base,
646
+ color: tokens.colors.text.primary,
647
+ fontSize: tokens.typography.fontSize.xxs,
648
+ cursor: currentPage <= 1 ? "not-allowed" : "pointer",
649
+ opacity: currentPage <= 1 ? .5 : 1
650
+ },
651
+ children: "Previous"
652
+ }),
653
+ /* @__PURE__ */ jsx("button", {
654
+ type: "button",
655
+ onClick: () => {
656
+ setCurrentPage((p) => Math.min(totalPages, p + 1));
657
+ setAutoScroll(false);
658
+ },
659
+ disabled: currentPage >= totalPages,
660
+ "aria-label": "Next page",
661
+ style: {
662
+ padding: `${tokens.spacing.xxs} ${tokens.spacing.sm}`,
663
+ border: `1px solid ${tokens.colors.border.muted}`,
664
+ borderRadius: tokens.borderRadius.md,
665
+ background: tokens.colors.background.base,
666
+ color: tokens.colors.text.primary,
667
+ fontSize: tokens.typography.fontSize.xxs,
668
+ cursor: currentPage >= totalPages ? "not-allowed" : "pointer",
669
+ opacity: currentPage >= totalPages ? .5 : 1
670
+ },
671
+ children: "Next"
672
+ }),
673
+ /* @__PURE__ */ jsx("button", {
674
+ type: "button",
675
+ onClick: () => {
676
+ setCurrentPage(totalPages);
677
+ setAutoScroll(false);
678
+ },
679
+ disabled: currentPage >= totalPages,
680
+ "aria-label": "Last page (oldest)",
681
+ style: {
682
+ padding: `${tokens.spacing.xxs} ${tokens.spacing.sm}`,
683
+ border: `1px solid ${tokens.colors.border.muted}`,
684
+ borderRadius: tokens.borderRadius.md,
685
+ background: tokens.colors.background.base,
686
+ color: tokens.colors.text.primary,
687
+ fontSize: tokens.typography.fontSize.xxs,
688
+ cursor: currentPage >= totalPages ? "not-allowed" : "pointer",
689
+ opacity: currentPage >= totalPages ? .5 : 1
690
+ },
691
+ children: "Last"
692
+ })
693
+ ]
694
+ })]
695
+ }),
696
+ /* @__PURE__ */ jsx("style", { children: `
596
697
  .zendir-log-scroll::-webkit-scrollbar { width: 6px; }
597
698
  .zendir-log-scroll::-webkit-scrollbar-track { background: transparent; }
598
699
  .zendir-log-scroll::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.15); border-radius: 3px; }
599
700
  .zendir-log-scroll::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.25); }
600
701
  .zendir-log-scroll { scrollbar-width: thin; scrollbar-color: rgba(255,255,255,0.15) transparent; }
601
702
  ` }),
602
- paused && /* @__PURE__ */ jsx("div", { role: "status", style: {
603
- padding: `${tokens.spacing.xs} ${tokens.spacing.smd}`,
604
- borderTop: `1px solid ${tokens.colors.border.muted}`,
605
- background: `${tokens.colors.status.caution}15`,
606
- color: tokens.colors.status.caution,
607
- fontSize: tokens.typography.fontSize.sm,
608
- textAlign: "center",
609
- fontFamily: tokens.typography.fontFamily.primary,
610
- fontWeight: tokens.typography.fontWeight.medium
611
- }, children: "Log paused — new entries are buffered" })
612
- ]
613
- }
614
- );
703
+ paused && /* @__PURE__ */ jsx("div", {
704
+ role: "status",
705
+ style: {
706
+ padding: `${tokens.spacing.xs} ${tokens.spacing.smd}`,
707
+ borderTop: `1px solid ${tokens.colors.border.muted}`,
708
+ background: `${tokens.colors.status.caution}15`,
709
+ color: tokens.colors.status.caution,
710
+ fontSize: tokens.typography.fontSize.sm,
711
+ textAlign: "center",
712
+ fontFamily: tokens.typography.fontFamily.primary,
713
+ fontWeight: tokens.typography.fontWeight.medium
714
+ },
715
+ children: "Log paused — new entries are buffered"
716
+ })
717
+ ]
718
+ });
615
719
  });
616
- export {
617
- LogViewer
618
- };
619
- //# sourceMappingURL=LogViewer.js.map
720
+ //#endregion
721
+ export { LogViewer, LogViewer as default };
722
+
723
+ //# sourceMappingURL=LogViewer.js.map