@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.
- package/CHANGELOG.md +419 -386
- package/README.md +640 -631
- package/dist/_virtual/_rolldown/runtime.js +13 -0
- package/dist/index.js +17 -221
- package/dist/react/3d/CesiumCaptureSource.d.ts +0 -1
- package/dist/react/3d/CesiumCaptureSource.js +364 -283
- package/dist/react/3d/CesiumCaptureSource.js.map +1 -1
- package/dist/react/3d/ZenSpace3D.d.ts +2 -3
- package/dist/react/3d/ZenSpace3D.js +1681 -1237
- package/dist/react/3d/ZenSpace3D.js.map +1 -1
- package/dist/react/3d/ZenSpace3DCesium.d.ts +0 -1
- package/dist/react/3d/ZenSpace3DCesium.js +513 -560
- package/dist/react/3d/ZenSpace3DCesium.js.map +1 -1
- package/dist/react/3d/ZenSpace3DShaders.js +61 -66
- package/dist/react/3d/ZenSpace3DShaders.js.map +1 -1
- package/dist/react/3d/ZenSpace3DTypes.d.ts +19 -20
- package/dist/react/3d/ZenSpace3DUtils.d.ts +0 -1
- package/dist/react/3d/ZenSpace3DUtils.js +37 -24
- package/dist/react/3d/ZenSpace3DUtils.js.map +1 -1
- package/dist/react/3d/capturePngAnalysis.js +42 -36
- package/dist/react/3d/capturePngAnalysis.js.map +1 -1
- package/dist/react/3d/index.d.ts +3 -3
- package/dist/react/3d/index.js +5 -53
- package/dist/react/3d/threeLoader.d.ts +1 -1
- package/dist/react/3d/threeLoader.js +23 -18
- package/dist/react/3d/threeLoader.js.map +1 -1
- package/dist/react/astro/ClassificationBanner.d.ts +2 -3
- package/dist/react/astro/ClassificationBanner.js +95 -79
- package/dist/react/astro/ClassificationBanner.js.map +1 -1
- package/dist/react/astro/GlobalStatusBar.d.ts +0 -1
- package/dist/react/astro/GlobalStatusBar.js +157 -163
- package/dist/react/astro/GlobalStatusBar.js.map +1 -1
- package/dist/react/astro/MissionClock.d.ts +7 -8
- package/dist/react/astro/MissionClock.js +377 -400
- package/dist/react/astro/MissionClock.js.map +1 -1
- package/dist/react/astro/MonitoringIcon.d.ts +1 -2
- package/dist/react/astro/MonitoringIcon.js +422 -358
- package/dist/react/astro/MonitoringIcon.js.map +1 -1
- package/dist/react/astro/Notification.d.ts +1 -2
- package/dist/react/astro/Notification.js +229 -126
- package/dist/react/astro/Notification.js.map +1 -1
- package/dist/react/astro/Progress.d.ts +0 -1
- package/dist/react/astro/Progress.js +199 -141
- package/dist/react/astro/Progress.js.map +1 -1
- package/dist/react/astro/SimulationControls.d.ts +3 -4
- package/dist/react/astro/SimulationControls.js +596 -650
- package/dist/react/astro/SimulationControls.js.map +1 -1
- package/dist/react/astro/StatusIndicator.d.ts +0 -1
- package/dist/react/astro/StatusIndicator.js +236 -170
- package/dist/react/astro/StatusIndicator.js.map +1 -1
- package/dist/react/astro/UnifiedTimeline.d.ts +9 -10
- package/dist/react/astro/UnifiedTimeline.js +2345 -2392
- package/dist/react/astro/UnifiedTimeline.js.map +1 -1
- package/dist/react/astro/index.d.ts +5 -5
- package/dist/react/astro/index.js +1 -25
- package/dist/react/cards/AccessCard.d.ts +0 -1
- package/dist/react/cards/AccessCard.js +471 -405
- package/dist/react/cards/AccessCard.js.map +1 -1
- package/dist/react/cards/OrbitCard.d.ts +0 -1
- package/dist/react/cards/OrbitCard.js +487 -364
- package/dist/react/cards/OrbitCard.js.map +1 -1
- package/dist/react/cards/SpacecraftCard.d.ts +0 -1
- package/dist/react/cards/SpacecraftCard.js +813 -925
- package/dist/react/cards/SpacecraftCard.js.map +1 -1
- package/dist/react/cards/TelemetryCard.d.ts +0 -1
- package/dist/react/cards/TelemetryCard.js +1055 -732
- package/dist/react/cards/TelemetryCard.js.map +1 -1
- package/dist/react/cards/TelemetryStreamCard.d.ts +0 -1
- package/dist/react/cards/TelemetryStreamCard.js +323 -297
- package/dist/react/cards/TelemetryStreamCard.js.map +1 -1
- package/dist/react/cards/index.d.ts +1 -1
- package/dist/react/cards/index.js +1 -8
- package/dist/react/charts/GroundTrackMap.d.ts +6 -7
- package/dist/react/charts/GroundTrackMap.js +1377 -1256
- package/dist/react/charts/GroundTrackMap.js.map +1 -1
- package/dist/react/charts/GroundTrackMapLeaflet.d.ts +1 -2
- package/dist/react/charts/GroundTrackMapLeaflet.js +1065 -987
- package/dist/react/charts/GroundTrackMapLeaflet.js.map +1 -1
- package/dist/react/charts/groundTrackMapLeafletTiles.js +34 -25
- package/dist/react/charts/groundTrackMapLeafletTiles.js.map +1 -1
- package/dist/react/charts/groundTrackMapLeafletUtils.js +70 -61
- package/dist/react/charts/groundTrackMapLeafletUtils.js.map +1 -1
- package/dist/react/charts/index.d.ts +1 -1
- package/dist/react/charts/index.js +7 -100
- package/dist/react/charts/unified/AstroChart.d.ts +1 -2
- package/dist/react/charts/unified/AstroChart.js +1331 -1411
- package/dist/react/charts/unified/AstroChart.js.map +1 -1
- package/dist/react/charts/unified/PowerOverviewChart.d.ts +3 -3
- package/dist/react/charts/unified/PowerOverviewChart.js +495 -478
- package/dist/react/charts/unified/PowerOverviewChart.js.map +1 -1
- package/dist/react/charts/unified/domain.d.ts +12 -13
- package/dist/react/charts/unified/domain.js +3999 -3145
- package/dist/react/charts/unified/domain.js.map +1 -1
- package/dist/react/charts/unified/generators.d.ts +3 -4
- package/dist/react/charts/unified/generators.js +561 -498
- package/dist/react/charts/unified/generators.js.map +1 -1
- package/dist/react/charts/unified/index.d.ts +1 -1
- package/dist/react/charts/unified/presets.d.ts +14 -15
- package/dist/react/charts/unified/presets.js +857 -994
- package/dist/react/charts/unified/presets.js.map +1 -1
- package/dist/react/charts/unified/sync.d.ts +2 -3
- package/dist/react/charts/unified/sync.js +229 -195
- package/dist/react/charts/unified/sync.js.map +1 -1
- package/dist/react/charts/unified/theme.d.ts +11 -12
- package/dist/react/charts/unified/theme.js +606 -642
- package/dist/react/charts/unified/theme.js.map +1 -1
- package/dist/react/charts/unified/types.d.ts +32 -33
- package/dist/react/charts/unified/useChartStream.d.ts +1 -2
- package/dist/react/charts/unified/useChartStream.js +203 -216
- package/dist/react/charts/unified/useChartStream.js.map +1 -1
- package/dist/react/chatgpt/AppCard.d.ts +1 -2
- package/dist/react/chatgpt/index.d.ts +7 -7
- package/dist/react/chatgpt/index.js +160 -139
- package/dist/react/chatgpt/index.js.map +1 -1
- package/dist/react/context/CategoryContext.d.ts +0 -1
- package/dist/react/context/CategoryContext.js +78 -29
- package/dist/react/context/CategoryContext.js.map +1 -1
- package/dist/react/context/DisplaySettingsContext.d.ts +1 -2
- package/dist/react/context/DisplaySettingsContext.js +205 -156
- package/dist/react/context/DisplaySettingsContext.js.map +1 -1
- package/dist/react/context/SpatialSelectionContext.d.ts +0 -1
- package/dist/react/context/SpatialSelectionContext.js +36 -7
- package/dist/react/context/SpatialSelectionContext.js.map +1 -1
- package/dist/react/core/data/DataTable.d.ts +3 -4
- package/dist/react/core/data/DataTable.js +473 -440
- package/dist/react/core/data/DataTable.js.map +1 -1
- package/dist/react/core/data/DataValue.d.ts +4 -5
- package/dist/react/core/data/DataValue.js +605 -571
- package/dist/react/core/data/DataValue.js.map +1 -1
- package/dist/react/core/data/propertyConfig.d.ts +1 -2
- package/dist/react/core/data/propertyConfig.js +446 -382
- package/dist/react/core/data/propertyConfig.js.map +1 -1
- package/dist/react/core/display/AstroIcon.d.ts +4 -5
- package/dist/react/core/display/AstroIcon.js +339 -429
- package/dist/react/core/display/AstroIcon.js.map +1 -1
- package/dist/react/core/display/Badge.d.ts +2 -3
- package/dist/react/core/display/Badge.js +131 -131
- package/dist/react/core/display/Badge.js.map +1 -1
- package/dist/react/core/display/CardHeader.d.ts +0 -1
- package/dist/react/core/display/CardHeader.js +398 -311
- package/dist/react/core/display/CardHeader.js.map +1 -1
- package/dist/react/core/display/Container.d.ts +2 -3
- package/dist/react/core/display/Container.js +640 -668
- package/dist/react/core/display/Container.js.map +1 -1
- package/dist/react/core/display/CopyButton.d.ts +1 -2
- package/dist/react/core/display/CopyButton.js +158 -101
- package/dist/react/core/display/CopyButton.js.map +1 -1
- package/dist/react/core/display/GlassCard.d.ts +3 -4
- package/dist/react/core/display/GlassCard.js +416 -369
- package/dist/react/core/display/GlassCard.js.map +1 -1
- package/dist/react/core/display/HeaderIconWithStatus.d.ts +0 -1
- package/dist/react/core/display/HeaderIconWithStatus.js +152 -152
- package/dist/react/core/display/HeaderIconWithStatus.js.map +1 -1
- package/dist/react/core/display/Icon.d.ts +2 -3
- package/dist/react/core/display/Icon.js +172 -143
- package/dist/react/core/display/Icon.js.map +1 -1
- package/dist/react/core/display/Typography.d.ts +24 -25
- package/dist/react/core/display/Typography.js +360 -178
- package/dist/react/core/display/Typography.js.map +1 -1
- package/dist/react/core/feedback/ConfirmDialog.d.ts +1 -2
- package/dist/react/core/feedback/ConfirmDialog.js +343 -304
- package/dist/react/core/feedback/ConfirmDialog.js.map +1 -1
- package/dist/react/core/feedback/Dialog.d.ts +2 -3
- package/dist/react/core/feedback/Dialog.js +189 -197
- package/dist/react/core/feedback/Dialog.js.map +1 -1
- package/dist/react/core/feedback/Toast.d.ts +2 -3
- package/dist/react/core/feedback/Toast.js +346 -207
- package/dist/react/core/feedback/Toast.js.map +1 -1
- package/dist/react/core/index.d.ts +26 -26
- package/dist/react/core/index.js +7 -119
- package/dist/react/core/inputs/Button.d.ts +2 -3
- package/dist/react/core/inputs/Button.js +281 -304
- package/dist/react/core/inputs/Button.js.map +1 -1
- package/dist/react/core/inputs/Checkbox.d.ts +2 -3
- package/dist/react/core/inputs/Checkbox.js +139 -127
- package/dist/react/core/inputs/Checkbox.js.map +1 -1
- package/dist/react/core/inputs/Input.d.ts +3 -4
- package/dist/react/core/inputs/Input.js +277 -285
- package/dist/react/core/inputs/Input.js.map +1 -1
- package/dist/react/core/inputs/LimitsBar.d.ts +1 -2
- package/dist/react/core/inputs/LimitsBar.js +271 -196
- package/dist/react/core/inputs/LimitsBar.js.map +1 -1
- package/dist/react/core/inputs/NumberInput.d.ts +2 -3
- package/dist/react/core/inputs/NumberInput.js +531 -503
- package/dist/react/core/inputs/NumberInput.js.map +1 -1
- package/dist/react/core/inputs/PinInput.d.ts +2 -3
- package/dist/react/core/inputs/PinInput.js +224 -207
- package/dist/react/core/inputs/PinInput.js.map +1 -1
- package/dist/react/core/inputs/Select.d.ts +1 -2
- package/dist/react/core/inputs/Select.js +298 -299
- package/dist/react/core/inputs/Select.js.map +1 -1
- package/dist/react/core/inputs/Toggle.d.ts +2 -3
- package/dist/react/core/inputs/Toggle.js +142 -148
- package/dist/react/core/inputs/Toggle.js.map +1 -1
- package/dist/react/core/layout/Box.d.ts +3 -4
- package/dist/react/core/layout/Box.js +118 -122
- package/dist/react/core/layout/Box.js.map +1 -1
- package/dist/react/core/layout/Center.d.ts +0 -1
- package/dist/react/core/layout/Center.js +45 -32
- package/dist/react/core/layout/Center.js.map +1 -1
- package/dist/react/core/layout/Divider.d.ts +1 -2
- package/dist/react/core/layout/Divider.js +112 -106
- package/dist/react/core/layout/Divider.js.map +1 -1
- package/dist/react/core/layout/Flex.d.ts +4 -5
- package/dist/react/core/layout/Flex.js +120 -124
- package/dist/react/core/layout/Flex.js.map +1 -1
- package/dist/react/core/layout/Grid.d.ts +5 -6
- package/dist/react/core/layout/Grid.js +131 -138
- package/dist/react/core/layout/Grid.js.map +1 -1
- package/dist/react/core/layout/Spacer.d.ts +0 -1
- package/dist/react/core/layout/Spacer.js +36 -29
- package/dist/react/core/layout/Spacer.js.map +1 -1
- package/dist/react/core/layout/Stack.d.ts +4 -5
- package/dist/react/core/layout/Stack.js +127 -70
- package/dist/react/core/layout/Stack.js.map +1 -1
- package/dist/react/core/layout/responsive.d.ts +1 -2
- package/dist/react/core/layout/responsive.js +25 -19
- package/dist/react/core/layout/responsive.js.map +1 -1
- package/dist/react/core/layout/useBreakpoint.d.ts +1 -1
- package/dist/react/core/layout/useBreakpoint.js +119 -66
- package/dist/react/core/layout/useBreakpoint.js.map +1 -1
- package/dist/react/core/navigation/AppBar.d.ts +1 -2
- package/dist/react/core/navigation/AppBar.js +827 -805
- package/dist/react/core/navigation/AppBar.js.map +1 -1
- package/dist/react/core/navigation/Pagination.d.ts +2 -3
- package/dist/react/core/navigation/Pagination.js +207 -187
- package/dist/react/core/navigation/Pagination.js.map +1 -1
- package/dist/react/core/navigation/SideNav.d.ts +5 -6
- package/dist/react/core/navigation/SideNav.js +855 -629
- package/dist/react/core/navigation/SideNav.js.map +1 -1
- package/dist/react/core/navigation/Tabs.d.ts +0 -1
- package/dist/react/core/navigation/Tabs.js +113 -119
- package/dist/react/core/navigation/Tabs.js.map +1 -1
- package/dist/react/core/overlays/Popover.d.ts +2 -3
- package/dist/react/core/overlays/Popover.js +303 -282
- package/dist/react/core/overlays/Popover.js.map +1 -1
- package/dist/react/core/overlays/SidePanel.d.ts +1 -2
- package/dist/react/core/overlays/SidePanel.js +217 -212
- package/dist/react/core/overlays/SidePanel.js.map +1 -1
- package/dist/react/core/overlays/Tooltip.d.ts +1 -2
- package/dist/react/core/overlays/Tooltip.js +170 -176
- package/dist/react/core/overlays/Tooltip.js.map +1 -1
- package/dist/react/core/widgets/ActivityPlanner.d.ts +0 -1
- package/dist/react/core/widgets/ActivityPlanner.js +855 -517
- package/dist/react/core/widgets/ActivityPlanner.js.map +1 -1
- package/dist/react/core/widgets/Capture.d.ts +2 -3
- package/dist/react/core/widgets/Capture.js +965 -779
- package/dist/react/core/widgets/Capture.js.map +1 -1
- package/dist/react/core/widgets/ChatPanel.d.ts +16 -17
- package/dist/react/core/widgets/ChatPanel.js +1734 -1034
- package/dist/react/core/widgets/ChatPanel.js.map +1 -1
- package/dist/react/core/widgets/ColorPickerPanel.d.ts +0 -1
- package/dist/react/core/widgets/ColorPickerPanel.js +298 -287
- package/dist/react/core/widgets/ColorPickerPanel.js.map +1 -1
- package/dist/react/core/widgets/CommandBuilder.d.ts +2 -3
- package/dist/react/core/widgets/CommandBuilder.js +624 -516
- package/dist/react/core/widgets/CommandBuilder.js.map +1 -1
- package/dist/react/core/widgets/ConnectionForm.d.ts +2 -3
- package/dist/react/core/widgets/ConnectionForm.js +528 -492
- package/dist/react/core/widgets/ConnectionForm.js.map +1 -1
- package/dist/react/core/widgets/FileExplorer.d.ts +3 -4
- package/dist/react/core/widgets/FileExplorer.js +617 -505
- package/dist/react/core/widgets/FileExplorer.js.map +1 -1
- package/dist/react/core/widgets/HexViewer.d.ts +2 -3
- package/dist/react/core/widgets/HexViewer.js +1353 -1091
- package/dist/react/core/widgets/HexViewer.js.map +1 -1
- package/dist/react/core/widgets/ImageFrame.d.ts +1 -2
- package/dist/react/core/widgets/ImageFrame.js +230 -193
- package/dist/react/core/widgets/ImageFrame.js.map +1 -1
- package/dist/react/core/widgets/ImageGallery.d.ts +2 -1
- package/dist/react/core/widgets/ImageGallery.js +380 -314
- package/dist/react/core/widgets/ImageGallery.js.map +1 -1
- package/dist/react/core/widgets/ImagePanel.d.ts +0 -1
- package/dist/react/core/widgets/ImagePanel.js +110 -101
- package/dist/react/core/widgets/ImagePanel.js.map +1 -1
- package/dist/react/core/widgets/LogViewer.d.ts +2 -3
- package/dist/react/core/widgets/LogViewer.js +711 -607
- package/dist/react/core/widgets/LogViewer.js.map +1 -1
- package/dist/react/core/widgets/MessageStream.d.ts +3 -4
- package/dist/react/core/widgets/MessageStream.js +514 -444
- package/dist/react/core/widgets/MessageStream.js.map +1 -1
- package/dist/react/core/widgets/MissionCalendar.d.ts +3 -4
- package/dist/react/core/widgets/MissionCalendar.js +1488 -1020
- package/dist/react/core/widgets/MissionCalendar.js.map +1 -1
- package/dist/react/core/widgets/PacketViewer.d.ts +2 -3
- package/dist/react/core/widgets/PacketViewer.js +550 -421
- package/dist/react/core/widgets/PacketViewer.js.map +1 -1
- package/dist/react/core/widgets/capture-placeholder.png.js +6 -5
- package/dist/react/core/widgets/capture-placeholder.png.js.map +1 -1
- package/dist/react/hooks/index.d.ts +5 -5
- package/dist/react/hooks/useAccessWindows.d.ts +0 -1
- package/dist/react/hooks/useCompactMode.d.ts +1 -1
- package/dist/react/hooks/useCompactMode.js +104 -58
- package/dist/react/hooks/useCompactMode.js.map +1 -1
- package/dist/react/hooks/useGroundTrackHistory.d.ts +0 -1
- package/dist/react/hooks/useSimulationScene.d.ts +1 -2
- package/dist/react/hooks/useSimulationScene.js +481 -374
- package/dist/react/hooks/useSimulationScene.js.map +1 -1
- package/dist/react/index.d.ts +3 -3
- package/dist/react/panels/LayerControlPanel.d.ts +1 -2
- package/dist/react/panels/LayerControlPanel.js +223 -180
- package/dist/react/panels/LayerControlPanel.js.map +1 -1
- package/dist/react/panels/ObjectInventoryPanel.d.ts +2 -3
- package/dist/react/panels/ObjectInventoryPanel.js +315 -250
- package/dist/react/panels/ObjectInventoryPanel.js.map +1 -1
- package/dist/react/panels/index.d.ts +1 -1
- package/dist/react/panels/index.js +1 -6
- package/dist/react/shared/ErrorBoundary.d.ts +1 -2
- package/dist/react/shared/ErrorBoundary.js +170 -139
- package/dist/react/shared/ErrorBoundary.js.map +1 -1
- package/dist/react/shared/Skeleton.d.ts +1 -2
- package/dist/react/shared/Skeleton.js +300 -301
- package/dist/react/shared/Skeleton.js.map +1 -1
- package/dist/react/theme/ThemeProvider.d.ts +2 -321
- package/dist/react/theme/ThemeProvider.js +313 -939
- package/dist/react/theme/ThemeProvider.js.map +1 -1
- package/dist/react/theme/cardAccent.d.ts +1 -2
- package/dist/react/theme/cardAccent.js +196 -126
- package/dist/react/theme/cardAccent.js.map +1 -1
- package/dist/react/theme/config.d.ts +0 -1
- package/dist/react/theme/config.js +47 -25
- package/dist/react/theme/config.js.map +1 -1
- package/dist/react/theme/index.d.ts +2 -2
- package/dist/react/theme/index.js +1 -16
- package/dist/react/theme/types.d.ts +332 -0
- package/dist/react/theme/types.js +39 -0
- package/dist/react/theme/types.js.map +1 -0
- package/dist/react/types.d.ts +8 -8
- package/dist/react/types.js +147 -47
- package/dist/react/types.js.map +1 -1
- package/dist/react/utils/categoryPalette.d.ts +0 -1
- package/dist/react/utils/categoryPalette.js +91 -102
- package/dist/react/utils/categoryPalette.js.map +1 -1
- package/dist/react/utils/index.d.ts +5 -5
- package/dist/react/utils/index.js +457 -371
- package/dist/react/utils/index.js.map +1 -1
- package/dist/react/visualizations/EclipseTimerCard.d.ts +0 -1
- package/dist/react/visualizations/LinkBudgetCard.d.ts +0 -1
- package/dist/react/visualizations/NavBallCard.d.ts +0 -1
- package/dist/react/visualizations/PropulsionCard.d.ts +1 -2
- package/dist/react/visualizations/SensorFootprintCard.d.ts +1 -2
- package/dist/react/visualizations/ThermalHeatmapCard.d.ts +0 -1
- package/dist/react/visualizations/index.d.ts +3 -3
- package/dist/react.d.ts +2 -1
- package/dist/react.js +17 -221
- package/dist/shaders/atmosphere.frag.js +6 -5
- package/dist/shaders/atmosphere.frag.js.map +1 -1
- package/dist/shaders/atmosphere.vert.js +6 -5
- package/dist/shaders/atmosphere.vert.js.map +1 -1
- package/dist/shaders/stars.frag.js +6 -5
- package/dist/shaders/stars.frag.js.map +1 -1
- package/dist/shaders/stars.vert.js +6 -5
- package/dist/shaders/stars.vert.js.map +1 -1
- package/dist/style.css +26 -3
- package/dist/tokens/core.d.ts +159 -0
- package/dist/tokens/core.js +144 -0
- package/dist/tokens/core.js.map +1 -0
- package/dist/tokens/css-vars.d.ts +213 -2
- package/dist/tokens/css-vars.js +590 -227
- package/dist/tokens/css-vars.js.map +1 -1
- package/dist/tokens/index.d.ts +31 -228
- package/dist/tokens/index.js +281 -366
- package/dist/tokens/index.js.map +1 -1
- package/dist/tokens/palettes/astro.d.ts +129 -0
- package/dist/tokens/palettes/astro.js +112 -0
- package/dist/tokens/palettes/astro.js.map +1 -0
- package/dist/tokens/palettes/index.d.ts +288 -0
- package/dist/tokens/palettes/index.js +35 -0
- package/dist/tokens/palettes/index.js.map +1 -0
- package/dist/tokens/palettes/tailwind.d.ts +324 -0
- package/dist/tokens/palettes/tailwind.js +201 -0
- package/dist/tokens/palettes/tailwind.js.map +1 -0
- package/dist/tokens/palettes/zendir.d.ts +126 -0
- package/dist/tokens/palettes/zendir.js +92 -0
- package/dist/tokens/palettes/zendir.js.map +1 -0
- package/dist/tokens/themes.d.ts +30 -0
- package/dist/tokens/themes.js +704 -0
- package/dist/tokens/themes.js.map +1 -0
- package/dist/tokens/tokens.css +533 -155
- package/dist/types/index.d.ts +2 -2
- package/package.json +310 -313
- package/dist/index.js.map +0 -1
- package/dist/react/3d/index.js.map +0 -1
- package/dist/react/astro/index.js.map +0 -1
- package/dist/react/cards/index.js.map +0 -1
- package/dist/react/charts/index.js.map +0 -1
- package/dist/react/core/index.js.map +0 -1
- package/dist/react/panels/index.js.map +0 -1
- package/dist/react/theme/astro-tokens.d.ts +0 -153
- package/dist/react/theme/index.js.map +0 -1
- package/dist/react.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LogViewer.js","sources":["../../../../src/react/core/widgets/LogViewer.tsx"],"sourcesContent":["/**\n * @zendir/ui - LogViewer Component\n * \n * Real-time log/event viewer for mission operations and system monitoring.\n * Supports severity filtering, search, auto-scroll, pause/resume,\n * and timestamped entries with color-coded severity levels.\n * \n * Features:\n * - Severity levels: debug, info, warn, error, critical\n * - Color-coded entries with Astro status colors\n * - Real-time auto-scroll with pause/resume\n * - Search/filter by text\n * - Severity filter checkboxes\n * - Timestamp display\n * - Source/component tags\n * - Export log to clipboard or file\n * - Maximum log retention (auto-prune old entries)\n * - Virtualized rendering for large logs\n * \n * @example\n * ```tsx\n * <LogViewer\n * entries={logEntries}\n * maxEntries={5000}\n * showTimestamp\n * showSource\n * />\n * ```\n */\n\nimport React, { useState, useRef, useCallback, useEffect, useMemo, memo } from 'react';\nimport { useTheme } from '../../theme';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type LogSeverity = 'debug' | 'info' | 'warn' | 'error' | 'critical';\n\nexport interface LogEntry {\n /** Unique ID */\n id: string | number;\n /** Wall-clock timestamp (ISO string or Date) for ordering/export. */\n timestamp: string | Date;\n /**\n * Simulation-elapsed time in seconds (T+ format) when the event occurred.\n * Optional. When provided AND `timestampFormat=\"sim-elapsed\"`, this is what\n * the timestamp column displays. Falls through to `timestamp` otherwise.\n */\n simTime?: number;\n /** Severity level */\n severity: LogSeverity;\n /** Log message */\n message: string;\n /** Source component/system */\n source?: string;\n /** Additional metadata */\n metadata?: Record<string, unknown>;\n}\n\nconst DEFAULT_ESTIMATED_ROW_HEIGHT = 28;\nconst DEFAULT_OVERSCAN_COUNT = 12;\n\nexport interface LogViewerProps {\n /** Array of log entries */\n entries: LogEntry[];\n /** Maximum entries to keep in memory (default: 5000). Older entries are pruned. */\n maxEntries?: number;\n /** Height of the viewer (default: 400) */\n height?: number | string;\n /** Title */\n title?: string;\n /** Show timestamps (default: true) */\n showTimestamp?: boolean;\n /** Show source column (default: true) */\n showSource?: boolean;\n /** Show severity filter toolbar (default: true) */\n showFilters?: boolean;\n /** Show search bar (default: true) */\n showSearch?: boolean;\n /** Initial severity filters (all enabled by default) */\n defaultSeverities?: LogSeverity[];\n /**\n * Timestamp format. `'sim-elapsed'` displays `entry.simTime` as `T+HHH:MM:SS.s`\n * (falls back to wall-clock formatting if simTime is absent on an entry).\n * Default: 'time'.\n */\n timestampFormat?: 'time' | 'datetime' | 'relative' | 'sim-elapsed';\n /** Called when an entry is clicked */\n onEntryClick?: (entry: LogEntry) => void;\n /** CSS class */\n className?: string;\n /** Show outer container border (default: true). Set false when embedded inside a Container/card. */\n bordered?: boolean;\n /** Custom style overrides for the outer container */\n style?: React.CSSProperties;\n /** Estimated row height in px for virtualization (default: 28). Enables smooth scroll through large logs. */\n estimatedRowHeight?: number;\n /** Number of rows to render above/below viewport for virtualization (default: 12). */\n overscanCount?: number;\n /**\n * Enable pagination: show entries in pages with Previous/Next.\n * Page 1 = newest entries. Use with large maxEntries so users can navigate to older pages.\n * When set, scrolling is replaced by page navigation (better for very large logs).\n */\n pagination?: boolean | { pageSize?: number };\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\nconst SEVERITY_ORDER: LogSeverity[] = ['debug', 'info', 'warn', 'error', 'critical'];\n\nfunction formatSimElapsed(seconds: number): string {\n const sign = seconds < 0 ? '-' : '+';\n const abs = Math.abs(seconds);\n const h = Math.floor(abs / 3600);\n const m = Math.floor((abs % 3600) / 60);\n const s = abs % 60;\n const whole = Math.floor(s);\n const tenths = Math.floor((s - whole) * 10);\n return `T${sign}${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}:${String(whole).padStart(2, '0')}.${tenths}`;\n}\n\nfunction formatTimestamp(\n entry: Pick<LogEntry, 'timestamp' | 'simTime'>,\n format: 'time' | 'datetime' | 'relative' | 'sim-elapsed',\n): string {\n if (format === 'sim-elapsed' && typeof entry.simTime === 'number' && Number.isFinite(entry.simTime)) {\n return formatSimElapsed(entry.simTime);\n }\n\n const ts = entry.timestamp;\n const d = typeof ts === 'string' ? new Date(ts) : ts;\n if (isNaN(d.getTime())) return String(ts);\n\n if (format === 'sim-elapsed') {\n // Fallback when simTime missing — show wall-clock time so the row isn't blank.\n return d.toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' });\n }\n if (format === 'time') {\n return d.toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' })\n + '.' + d.getMilliseconds().toString().padStart(3, '0');\n }\n if (format === 'datetime') {\n return d.toLocaleDateString('en-US', { month: 'short', day: '2-digit' }) + ' '\n + d.toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' });\n }\n // relative\n const diff = Date.now() - d.getTime();\n if (diff < 1000) return 'just now';\n if (diff < 60000) return `${Math.floor(diff / 1000)}s ago`;\n if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`;\n return `${Math.floor(diff / 3600000)}h ago`;\n}\n\n// =============================================================================\n// Component\n// =============================================================================\n\nexport const LogViewer = memo(function LogViewer({\n entries,\n maxEntries = 5000,\n height = 400,\n title,\n showTimestamp = true,\n showSource = true,\n showFilters = true,\n showSearch = true,\n defaultSeverities,\n timestampFormat = 'time',\n onEntryClick,\n className = '',\n bordered = true,\n style: styleProp,\n estimatedRowHeight = DEFAULT_ESTIMATED_ROW_HEIGHT,\n overscanCount = DEFAULT_OVERSCAN_COUNT,\n pagination,\n}: LogViewerProps): React.ReactElement {\n const { tokens, theme } = useTheme();\n const isTransparentTheme =\n theme === 'transparent' || theme === 'transparent-bold' || theme === 'transparent-minimal';\n const scrollRef = useRef<HTMLDivElement>(null);\n const [paused, setPaused] = useState(false);\n const [searchQuery, setSearchQuery] = useState('');\n const [severityFilter, setSeverityFilter] = useState<Set<LogSeverity>>(\n new Set(defaultSeverities ?? SEVERITY_ORDER)\n );\n const [autoScroll, setAutoScroll] = useState(true);\n const prevLengthRef = useRef(entries.length);\n /** Scroll position and container height for virtualization */\n const [scrollState, setScrollState] = useState({ scrollTop: 0, containerHeight: 300 });\n const rafRef = useRef<number | null>(null);\n\n const severityColors: Record<LogSeverity, string> = {\n debug: tokens.colors.text.muted ?? tokens.colors.text.secondary,\n info: tokens.colors.status.standby,\n warn: tokens.colors.status.caution,\n error: tokens.colors.status.serious,\n critical: tokens.colors.status.critical,\n };\n\n /**\n * Astro UX status shapes mapped to log severity levels:\n * debug → off (small dot ·), info → standby (ring ◯),\n * warn → caution (square ■), error → serious (diamond ◆),\n * critical → critical (triangle down ▼)\n */\n const severityShapes: Record<LogSeverity, string> = {\n debug: '·',\n info: '○',\n warn: '■',\n error: '◆',\n critical: '▼',\n };\n\n const severityLabels: Record<LogSeverity, string> = {\n debug: 'DBG',\n info: 'INF',\n warn: 'WRN',\n error: 'ERR',\n critical: 'CRT',\n };\n\n // Filter & limit entries: keep NEWEST maxEntries (entries[0] = newest)\n const filteredEntries = useMemo(() => {\n let filtered = entries.filter(e => severityFilter.has(e.severity));\n if (searchQuery.trim()) {\n const q = searchQuery.toLowerCase();\n filtered = filtered.filter(e =>\n e.message.toLowerCase().includes(q) ||\n (e.source && e.source.toLowerCase().includes(q))\n );\n }\n return filtered.slice(0, maxEntries);\n }, [entries, severityFilter, searchQuery, maxEntries]);\n\n const paginationConfig = pagination === true ? { pageSize: 200 } : (pagination || undefined);\n const pageSize = paginationConfig?.pageSize ?? 200;\n const totalPages = Math.max(1, Math.ceil(filteredEntries.length / pageSize));\n const [currentPage, setCurrentPage] = useState(1);\n\n // Clamp current page when totalPages shrinks (e.g. after filter change)\n useEffect(() => {\n setCurrentPage(p => Math.min(p, totalPages));\n }, [totalPages]);\n\n const paginationEntries = useMemo(() => {\n if (!paginationConfig) return null;\n const start = (currentPage - 1) * pageSize;\n return filteredEntries.slice(start, start + pageSize);\n }, [paginationConfig, currentPage, pageSize, filteredEntries]);\n\n const totalHeight = filteredEntries.length * estimatedRowHeight;\n const { scrollTop, containerHeight } = scrollState;\n const visibleRange = useMemo(() => {\n const start = Math.max(0, Math.floor(scrollTop / estimatedRowHeight) - overscanCount);\n const end = Math.min(\n filteredEntries.length,\n Math.ceil((scrollTop + containerHeight) / estimatedRowHeight) + overscanCount\n );\n return { start, end };\n }, [scrollTop, containerHeight, estimatedRowHeight, overscanCount, filteredEntries.length]);\n const visibleEntries = useMemo(\n () => filteredEntries.slice(visibleRange.start, visibleRange.end),\n [filteredEntries, visibleRange.start, visibleRange.end],\n );\n\n const usePagination = !!paginationConfig;\n\n const renderRow = useCallback((entry: LogEntry) => (\n <div\n key={entry.id}\n role={onEntryClick ? 'button' : 'listitem'}\n tabIndex={onEntryClick ? 0 : undefined}\n aria-label={onEntryClick ? `${severityLabels[entry.severity]}: ${entry.message}` : undefined}\n onClick={() => onEntryClick?.(entry)}\n onKeyDown={onEntryClick ? (e) => (e.key === 'Enter' || e.key === ' ') && (e.preventDefault(), onEntryClick(entry)) : undefined}\n style={{\n display: 'flex',\n alignItems: 'flex-start',\n minHeight: estimatedRowHeight,\n boxSizing: 'border-box',\n padding: '3px 12px',\n borderBottom: `1px solid ${tokens.colors.border.muted}11`,\n cursor: onEntryClick ? 'pointer' : 'default',\n transition: 'background 150ms ease',\n }}\n onMouseEnter={e => { (e.currentTarget as HTMLDivElement).style.background = `${tokens.colors.background.surface}`; }}\n onMouseLeave={e => { (e.currentTarget as HTMLDivElement).style.background = 'transparent'; }}\n >\n <span style={{\n color: severityColors[entry.severity],\n fontWeight: 700,\n fontSize: tokens.typography.fontSize.xxs,\n minWidth: 38,\n textAlign: 'center',\n background: `${severityColors[entry.severity]}18`,\n borderRadius: tokens.borderRadius.sm,\n padding: '1px 4px',\n marginRight: 8,\n marginTop: 1,\n letterSpacing: '0.3px',\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: 3,\n }}>\n <span style={{ fontSize: '8px', lineHeight: 1 }}>{severityShapes[entry.severity]}</span>\n {severityLabels[entry.severity]}\n </span>\n {showTimestamp && (\n <span style={{\n color: tokens.colors.text.muted,\n marginRight: 8,\n // 'sim-elapsed' renders T+HH:MM:SS.s which is wider than the wall-clock formats.\n minWidth: timestampFormat === 'sim-elapsed' ? 110 : 85,\n fontSize: 11,\n flexShrink: 0,\n fontFamily: tokens.typography.fontFamily.mono,\n }}>\n {formatTimestamp(entry, timestampFormat)}\n </span>\n )}\n {showSource && entry.source && (\n <span style={{\n color: tokens.colors.interactive.default,\n marginRight: 8,\n fontSize: 11,\n minWidth: 70,\n maxWidth: 100,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n flexShrink: 0,\n }}>\n [{entry.source}]\n </span>\n )}\n <span style={{\n color: entry.severity === 'error' || entry.severity === 'critical'\n ? severityColors[entry.severity]\n : tokens.colors.text.primary,\n flex: 1,\n wordBreak: 'break-word',\n lineHeight: 1.4,\n }}>\n {entry.message}\n </span>\n </div>\n ), [severityColors, severityShapes, severityLabels, estimatedRowHeight, tokens, showTimestamp, showSource, timestampFormat, onEntryClick]);\n\n // Measure scroll container height\n useEffect(() => {\n const el = scrollRef.current;\n if (!el) return;\n const ro = new ResizeObserver(() => {\n setScrollState(s => ({ ...s, containerHeight: el.clientHeight }));\n });\n ro.observe(el);\n setScrollState(s => ({ ...s, containerHeight: el.clientHeight }));\n return () => ro.disconnect();\n }, []);\n\n const handleScroll = useCallback(() => {\n const el = scrollRef.current;\n if (!el) return;\n const { scrollTop: st, scrollHeight, clientHeight } = el;\n const atBottom = scrollHeight - st - clientHeight < 30;\n setAutoScroll(atBottom);\n if (rafRef.current !== null) cancelAnimationFrame(rafRef.current);\n rafRef.current = requestAnimationFrame(() => {\n rafRef.current = null;\n setScrollState(prev => ({ ...prev, scrollTop: el.scrollTop }));\n });\n }, []);\n\n // Auto-scroll to bottom when new entries arrive and user is at bottom (scroll mode).\n // In pagination mode: page 1 always shows newest entries (entries are prepended) so\n // content updates automatically — no forced jump needed. The user decides when to\n // follow new entries by clicking \"First\" (which sets autoScroll=true).\n useEffect(() => {\n if (entries.length <= prevLengthRef.current) {\n prevLengthRef.current = entries.length;\n return;\n }\n if (!usePagination && !paused && autoScroll && scrollRef.current) {\n scrollRef.current.scrollTop = scrollRef.current.scrollHeight;\n }\n prevLengthRef.current = entries.length;\n }, [entries.length, paused, autoScroll, usePagination]);\n\n useEffect(() => () => {\n if (rafRef.current !== null) cancelAnimationFrame(rafRef.current);\n }, []);\n\n const toggleSeverity = useCallback((sev: LogSeverity) => {\n setSeverityFilter(prev => {\n const next = new Set(prev);\n if (next.has(sev)) next.delete(sev);\n else next.add(sev);\n return next;\n });\n }, []);\n\n const exportLog = useCallback(() => {\n const text = filteredEntries.map(e => {\n const wall = formatTimestamp(e, 'datetime');\n // When sim-elapsed is in use, include the sim T+ stamp alongside wall time so\n // exports remain useful when correlating with sim playback.\n const sim = timestampFormat === 'sim-elapsed' && typeof e.simTime === 'number'\n ? ` ${formatSimElapsed(e.simTime)}`\n : '';\n const src = e.source ? `[${e.source}]` : '';\n return `${wall}${sim} [${e.severity.toUpperCase()}] ${src} ${e.message}`;\n }).join('\\n');\n navigator.clipboard?.writeText(text);\n }, [filteredEntries, timestampFormat]);\n\n const clearFilters = useCallback(() => {\n setSeverityFilter(new Set(SEVERITY_ORDER));\n setSearchQuery('');\n }, []);\n\n // Counts by severity\n const counts = useMemo(() => {\n const c: Record<LogSeverity, number> = { debug: 0, info: 0, warn: 0, error: 0, critical: 0 };\n entries.forEach(e => { c[e.severity]++; });\n return c;\n }, [entries]);\n\n return (\n <div\n className={`log-viewer ${className}`}\n style={{\n height: typeof height === 'number' ? `${height}px` : height,\n minHeight: typeof height === 'number' ? `${height}px` : height,\n background: tokens.colors.background.base,\n ...(bordered ? (tokens.colors.border.cardStyle ?? { border: `1px solid ${tokens.colors.border.muted}` }) : { border: 'none' }),\n borderRadius: tokens.borderRadius.lg,\n overflow: 'hidden',\n display: 'flex',\n flexDirection: 'column',\n fontFamily: tokens.typography.fontFamily.mono,\n ...styleProp,\n fontSize: tokens.typography.fontSize.xs,\n color: tokens.colors.text.primary,\n backdropFilter: isTransparentTheme ? 'blur(10px)' : undefined,\n WebkitBackdropFilter: isTransparentTheme ? 'blur(10px)' : undefined,\n }}\n >\n {/* Header */}\n {title && (\n <div style={{\n padding: '8px 12px',\n borderBottom: `1px solid ${tokens.colors.border.muted}`,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n background: tokens.colors.background.surface,\n fontFamily: tokens.typography.fontFamily.primary,\n backdropFilter: 'blur(8px)',\n }}>\n <span style={{ fontWeight: 600, fontSize: 13 }}>{title}</span>\n <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>\n <span style={{ fontSize: 11, color: tokens.colors.text.muted }}>\n {filteredEntries.length.toLocaleString()} entries\n </span>\n </div>\n </div>\n )}\n\n {/* Toolbar */}\n {(showFilters || showSearch) && (\n <div style={{\n padding: '6px 12px',\n borderBottom: `1px solid ${tokens.colors.border.muted}`,\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n flexWrap: 'wrap',\n background: tokens.colors.background.surface,\n fontFamily: tokens.typography.fontFamily.primary,\n }}>\n {showFilters && SEVERITY_ORDER.map(sev => (\n <button\n key={sev}\n onClick={() => toggleSeverity(sev)}\n aria-pressed={severityFilter.has(sev)}\n aria-label={`Filter ${sev} logs (${counts[sev]})`}\n style={{\n background: severityFilter.has(sev) ? `${severityColors[sev]}22` : 'transparent',\n color: severityFilter.has(sev) ? severityColors[sev] : tokens.colors.text.muted,\n border: `1px solid ${severityFilter.has(sev) ? severityColors[sev] + '66' : tokens.colors.border.muted}`,\n borderRadius: tokens.borderRadius.md,\n padding: `${tokens.spacing.xxs} ${tokens.spacing.sm}`,\n fontSize: tokens.typography.fontSize.xxs,\n fontWeight: tokens.typography.fontWeight.medium,\n cursor: 'pointer',\n textTransform: 'uppercase',\n letterSpacing: '0.5px',\n transition: 'all 150ms ease',\n }}\n >\n <span aria-hidden=\"true\">{severityShapes[sev]}</span> {sev} ({counts[sev]})\n </button>\n ))}\n <div style={{ flex: 1 }} />\n {showSearch && (\n <input\n type=\"text\"\n value={searchQuery}\n onChange={e => setSearchQuery(e.target.value)}\n placeholder=\"Filter messages...\"\n aria-label=\"Filter log messages\"\n style={{\n background: tokens.colors.background.base,\n color: tokens.colors.text.primary,\n border: `1px solid ${tokens.colors.border.muted}`,\n borderRadius: tokens.borderRadius.md,\n padding: `3px ${tokens.spacing.sm}`,\n fontSize: tokens.typography.fontSize.sm,\n outline: 'none',\n minWidth: 150,\n }}\n />\n )}\n <button\n onClick={() => setPaused(p => !p)}\n aria-label={paused ? 'Resume log stream' : 'Pause log stream'}\n style={{\n background: paused ? `${tokens.colors.status.caution}22` : tokens.colors.background.base,\n color: paused ? tokens.colors.status.caution : tokens.colors.text.secondary,\n border: `1px solid ${tokens.colors.border.muted}`,\n borderRadius: tokens.borderRadius.md, padding: `${tokens.spacing.xxs} ${tokens.spacing.sm}`,\n fontSize: tokens.typography.fontSize.xxs, cursor: 'pointer',\n fontWeight: tokens.typography.fontWeight.medium,\n transition: 'all 150ms ease',\n }}\n >\n {paused ? '\\u25B6 Resume' : '\\u23F8 Pause'}\n </button>\n <button onClick={exportLog} aria-label=\"Copy log to clipboard\" style={{\n background: tokens.colors.background.base,\n color: tokens.colors.text.secondary,\n border: `1px solid ${tokens.colors.border.muted}`,\n borderRadius: tokens.borderRadius.md, padding: `${tokens.spacing.xxs} ${tokens.spacing.sm}`,\n fontSize: tokens.typography.fontSize.xxs, cursor: 'pointer',\n transition: 'all 150ms ease',\n }}>\n Copy\n </button>\n <button onClick={clearFilters} aria-label=\"Reset all filters\" style={{\n background: tokens.colors.background.base,\n color: tokens.colors.text.secondary,\n border: `1px solid ${tokens.colors.border.muted}`,\n borderRadius: tokens.borderRadius.md, padding: `${tokens.spacing.xxs} ${tokens.spacing.sm}`,\n fontSize: tokens.typography.fontSize.xxs, cursor: 'pointer',\n transition: 'all 150ms ease',\n }}>\n Reset\n </button>\n </div>\n )}\n\n {/* Log entries — minHeight: 0 so flex child can shrink and scroll */}\n <div\n ref={scrollRef}\n onScroll={handleScroll}\n className=\"zendir-log-scroll\"\n style={{\n flex: 1,\n minHeight: 0,\n overflowY: 'auto',\n overflowX: 'hidden',\n padding: '2px 0',\n overscrollBehavior: 'contain',\n WebkitOverflowScrolling: 'touch',\n }}\n >\n {filteredEntries.length === 0 ? (\n <div style={{\n display: 'flex', alignItems: 'center', justifyContent: 'center',\n height: '100%', color: tokens.colors.text.muted, fontSize: 13,\n fontFamily: tokens.typography.fontFamily.primary,\n }}>\n {entries.length === 0 ? 'No log entries' : 'No matching entries'}\n </div>\n ) : usePagination ? (\n <div style={{ padding: '2px 0' }} role=\"list\" aria-label=\"Log entries\">\n {(paginationEntries ?? []).map(entry => renderRow(entry))}\n </div>\n ) : (\n <div\n style={{\n height: totalHeight,\n position: 'relative',\n width: '100%',\n }}\n role=\"list\"\n aria-label=\"Log entries\"\n >\n {visibleRange.start > 0 && (\n <div\n style={{ height: visibleRange.start * estimatedRowHeight, width: '100%' }}\n aria-hidden\n />\n )}\n {visibleEntries.map(entry => renderRow(entry))}\n {visibleRange.end < filteredEntries.length && (\n <div\n style={{\n height: (filteredEntries.length - visibleRange.end) * estimatedRowHeight,\n width: '100%',\n }}\n aria-hidden\n />\n )}\n </div>\n )}\n </div>\n\n {/* Pagination footer */}\n {usePagination && filteredEntries.length > 0 && (\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n flexWrap: 'wrap',\n gap: tokens.spacing.sm,\n padding: `${tokens.spacing.xs} ${tokens.spacing.smd}`,\n borderTop: `1px solid ${tokens.colors.border.muted}`,\n background: tokens.colors.background.surface,\n fontFamily: tokens.typography.fontFamily.primary,\n fontSize: tokens.typography.fontSize.xs,\n color: tokens.colors.text.secondary,\n }}\n >\n <span aria-live=\"polite\">\n Page {currentPage} of {totalPages}\n {filteredEntries.length > 0 && (\n <span style={{ marginLeft: tokens.spacing.sm }}>\n ({((currentPage - 1) * pageSize + 1).toLocaleString()}–{Math.min(currentPage * pageSize, filteredEntries.length).toLocaleString()} of {filteredEntries.length.toLocaleString()})\n </span>\n )}\n </span>\n <div style={{ display: 'flex', alignItems: 'center', gap: tokens.spacing.xs }}>\n <button\n type=\"button\"\n onClick={() => { setCurrentPage(1); setAutoScroll(true); }}\n disabled={currentPage <= 1}\n aria-label=\"First page (newest)\"\n style={{\n padding: `${tokens.spacing.xxs} ${tokens.spacing.sm}`,\n border: `1px solid ${tokens.colors.border.muted}`,\n borderRadius: tokens.borderRadius.md,\n background: tokens.colors.background.base,\n color: tokens.colors.text.primary,\n fontSize: tokens.typography.fontSize.xxs,\n cursor: currentPage <= 1 ? 'not-allowed' : 'pointer',\n opacity: currentPage <= 1 ? 0.5 : 1,\n }}\n >\n First\n </button>\n <button\n type=\"button\"\n onClick={() => { setCurrentPage(p => Math.max(1, p - 1)); setAutoScroll(false); }}\n disabled={currentPage <= 1}\n aria-label=\"Previous page\"\n style={{\n padding: `${tokens.spacing.xxs} ${tokens.spacing.sm}`,\n border: `1px solid ${tokens.colors.border.muted}`,\n borderRadius: tokens.borderRadius.md,\n background: tokens.colors.background.base,\n color: tokens.colors.text.primary,\n fontSize: tokens.typography.fontSize.xxs,\n cursor: currentPage <= 1 ? 'not-allowed' : 'pointer',\n opacity: currentPage <= 1 ? 0.5 : 1,\n }}\n >\n Previous\n </button>\n <button\n type=\"button\"\n onClick={() => { setCurrentPage(p => Math.min(totalPages, p + 1)); setAutoScroll(false); }}\n disabled={currentPage >= totalPages}\n aria-label=\"Next page\"\n style={{\n padding: `${tokens.spacing.xxs} ${tokens.spacing.sm}`,\n border: `1px solid ${tokens.colors.border.muted}`,\n borderRadius: tokens.borderRadius.md,\n background: tokens.colors.background.base,\n color: tokens.colors.text.primary,\n fontSize: tokens.typography.fontSize.xxs,\n cursor: currentPage >= totalPages ? 'not-allowed' : 'pointer',\n opacity: currentPage >= totalPages ? 0.5 : 1,\n }}\n >\n Next\n </button>\n <button\n type=\"button\"\n onClick={() => { setCurrentPage(totalPages); setAutoScroll(false); }}\n disabled={currentPage >= totalPages}\n aria-label=\"Last page (oldest)\"\n style={{\n padding: `${tokens.spacing.xxs} ${tokens.spacing.sm}`,\n border: `1px solid ${tokens.colors.border.muted}`,\n borderRadius: tokens.borderRadius.md,\n background: tokens.colors.background.base,\n color: tokens.colors.text.primary,\n fontSize: tokens.typography.fontSize.xxs,\n cursor: currentPage >= totalPages ? 'not-allowed' : 'pointer',\n opacity: currentPage >= totalPages ? 0.5 : 1,\n }}\n >\n Last\n </button>\n </div>\n </div>\n )}\n\n {/* Status bar */}\n <style>{`\n .zendir-log-scroll::-webkit-scrollbar { width: 6px; }\n .zendir-log-scroll::-webkit-scrollbar-track { background: transparent; }\n .zendir-log-scroll::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.15); border-radius: 3px; }\n .zendir-log-scroll::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.25); }\n .zendir-log-scroll { scrollbar-width: thin; scrollbar-color: rgba(255,255,255,0.15) transparent; }\n `}</style>\n\n {paused && (\n <div role=\"status\" style={{\n padding: `${tokens.spacing.xs} ${tokens.spacing.smd}`,\n borderTop: `1px solid ${tokens.colors.border.muted}`,\n background: `${tokens.colors.status.caution}15`,\n color: tokens.colors.status.caution,\n fontSize: tokens.typography.fontSize.sm,\n textAlign: 'center',\n fontFamily: tokens.typography.fontFamily.primary,\n fontWeight: tokens.typography.fontWeight.medium,\n }}>\n Log paused — new entries are buffered\n </div>\n )}\n </div>\n );\n});\n\nexport default LogViewer;\n"],"names":["LogViewer"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"LogViewer.js","names":[],"sources":["../../../../src/react/core/widgets/LogViewer.tsx"],"sourcesContent":["/**\r\n * @zendir/ui - LogViewer Component\r\n *\r\n * Real-time log/event viewer for mission operations and system monitoring.\r\n * Supports severity filtering, search, auto-scroll, pause/resume,\r\n * and timestamped entries with color-coded severity levels.\r\n *\r\n * Features:\r\n * - Severity levels: debug, info, warn, error, critical\r\n * - Color-coded entries with Astro status colors\r\n * - Real-time auto-scroll with pause/resume\r\n * - Search/filter by text\r\n * - Severity filter checkboxes\r\n * - Timestamp display\r\n * - Source/component tags\r\n * - Export log to clipboard or file\r\n * - Maximum log retention (auto-prune old entries)\r\n * - Virtualized rendering for large logs\r\n *\r\n * @example\r\n * ```tsx\r\n * <LogViewer\r\n * entries={logEntries}\r\n * maxEntries={5000}\r\n * showTimestamp\r\n * showSource\r\n * />\r\n * ```\r\n */\r\n\r\nimport React, {\r\n useState,\r\n useRef,\r\n useCallback,\r\n useEffect,\r\n useMemo,\r\n memo,\r\n} from \"react\";\r\nimport { useTheme } from \"../../theme\";\r\n\r\n// =============================================================================\r\n// Types\r\n// =============================================================================\r\n\r\nexport type LogSeverity = \"debug\" | \"info\" | \"warn\" | \"error\" | \"critical\";\r\n\r\nexport interface LogEntry {\r\n /** Unique ID */\r\n id: string | number;\r\n /** Wall-clock timestamp (ISO string or Date) for ordering/export. */\r\n timestamp: string | Date;\r\n /**\r\n * Simulation-elapsed time in seconds (T+ format) when the event occurred.\r\n * Optional. When provided AND `timestampFormat=\"sim-elapsed\"`, this is what\r\n * the timestamp column displays. Falls through to `timestamp` otherwise.\r\n */\r\n simTime?: number;\r\n /** Severity level */\r\n severity: LogSeverity;\r\n /** Log message */\r\n message: string;\r\n /** Source component/system */\r\n source?: string;\r\n /** Additional metadata */\r\n metadata?: Record<string, unknown>;\r\n}\r\n\r\nconst DEFAULT_ESTIMATED_ROW_HEIGHT = 28;\r\nconst DEFAULT_OVERSCAN_COUNT = 12;\r\n\r\nexport interface LogViewerProps {\r\n /** Array of log entries */\r\n entries: LogEntry[];\r\n /** Maximum entries to keep in memory (default: 5000). Older entries are pruned. */\r\n maxEntries?: number;\r\n /** Height of the viewer (default: 400) */\r\n height?: number | string;\r\n /** Title */\r\n title?: string;\r\n /** Show timestamps (default: true) */\r\n showTimestamp?: boolean;\r\n /** Show source column (default: true) */\r\n showSource?: boolean;\r\n /** Show severity filter toolbar (default: true) */\r\n showFilters?: boolean;\r\n /** Show search bar (default: true) */\r\n showSearch?: boolean;\r\n /** Initial severity filters (all enabled by default) */\r\n defaultSeverities?: LogSeverity[];\r\n /**\r\n * Timestamp format. `'sim-elapsed'` displays `entry.simTime` as `T+HHH:MM:SS.s`\r\n * (falls back to wall-clock formatting if simTime is absent on an entry).\r\n * Default: 'time'.\r\n */\r\n timestampFormat?: \"time\" | \"datetime\" | \"relative\" | \"sim-elapsed\";\r\n /** Called when an entry is clicked */\r\n onEntryClick?: (entry: LogEntry) => void;\r\n /** CSS class */\r\n className?: string;\r\n /** Show outer container border (default: true). Set false when embedded inside a Container/card. */\r\n bordered?: boolean;\r\n /** Custom style overrides for the outer container */\r\n style?: React.CSSProperties;\r\n /** Estimated row height in px for virtualization (default: 28). Enables smooth scroll through large logs. */\r\n estimatedRowHeight?: number;\r\n /** Number of rows to render above/below viewport for virtualization (default: 12). */\r\n overscanCount?: number;\r\n /**\r\n * Enable pagination: show entries in pages with Previous/Next.\r\n * Page 1 = newest entries. Use with large maxEntries so users can navigate to older pages.\r\n * When set, scrolling is replaced by page navigation (better for very large logs).\r\n */\r\n pagination?: boolean | { pageSize?: number };\r\n}\r\n\r\n// =============================================================================\r\n// Helpers\r\n// =============================================================================\r\n\r\nconst SEVERITY_ORDER: LogSeverity[] = [\r\n \"debug\",\r\n \"info\",\r\n \"warn\",\r\n \"error\",\r\n \"critical\",\r\n];\r\n\r\nfunction formatSimElapsed(seconds: number): string {\r\n const sign = seconds < 0 ? \"-\" : \"+\";\r\n const abs = Math.abs(seconds);\r\n const h = Math.floor(abs / 3600);\r\n const m = Math.floor((abs % 3600) / 60);\r\n const s = abs % 60;\r\n const whole = Math.floor(s);\r\n const tenths = Math.floor((s - whole) * 10);\r\n return `T${sign}${String(h).padStart(2, \"0\")}:${String(m).padStart(2, \"0\")}:${String(whole).padStart(2, \"0\")}.${tenths}`;\r\n}\r\n\r\nfunction formatTimestamp(\r\n entry: Pick<LogEntry, \"timestamp\" | \"simTime\">,\r\n format: \"time\" | \"datetime\" | \"relative\" | \"sim-elapsed\",\r\n): string {\r\n if (\r\n format === \"sim-elapsed\" &&\r\n typeof entry.simTime === \"number\" &&\r\n Number.isFinite(entry.simTime)\r\n ) {\r\n return formatSimElapsed(entry.simTime);\r\n }\r\n\r\n const ts = entry.timestamp;\r\n const d = typeof ts === \"string\" ? new Date(ts) : ts;\r\n if (isNaN(d.getTime())) return String(ts);\r\n\r\n if (format === \"sim-elapsed\") {\r\n // Fallback when simTime missing — show wall-clock time so the row isn't blank.\r\n return d.toLocaleTimeString(\"en-US\", {\r\n hour12: false,\r\n hour: \"2-digit\",\r\n minute: \"2-digit\",\r\n second: \"2-digit\",\r\n });\r\n }\r\n if (format === \"time\") {\r\n return (\r\n d.toLocaleTimeString(\"en-US\", {\r\n hour12: false,\r\n hour: \"2-digit\",\r\n minute: \"2-digit\",\r\n second: \"2-digit\",\r\n }) +\r\n \".\" +\r\n d.getMilliseconds().toString().padStart(3, \"0\")\r\n );\r\n }\r\n if (format === \"datetime\") {\r\n return (\r\n d.toLocaleDateString(\"en-US\", { month: \"short\", day: \"2-digit\" }) +\r\n \" \" +\r\n d.toLocaleTimeString(\"en-US\", {\r\n hour12: false,\r\n hour: \"2-digit\",\r\n minute: \"2-digit\",\r\n second: \"2-digit\",\r\n })\r\n );\r\n }\r\n // relative\r\n const diff = Date.now() - d.getTime();\r\n if (diff < 1000) return \"just now\";\r\n if (diff < 60000) return `${Math.floor(diff / 1000)}s ago`;\r\n if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`;\r\n return `${Math.floor(diff / 3600000)}h ago`;\r\n}\r\n\r\n// =============================================================================\r\n// Component\r\n// =============================================================================\r\n\r\nexport const LogViewer = memo(function LogViewer({\r\n entries,\r\n maxEntries = 5000,\r\n height = 400,\r\n title,\r\n showTimestamp = true,\r\n showSource = true,\r\n showFilters = true,\r\n showSearch = true,\r\n defaultSeverities,\r\n timestampFormat = \"time\",\r\n onEntryClick,\r\n className = \"\",\r\n bordered = true,\r\n style: styleProp,\r\n estimatedRowHeight = DEFAULT_ESTIMATED_ROW_HEIGHT,\r\n overscanCount = DEFAULT_OVERSCAN_COUNT,\r\n pagination,\r\n}: LogViewerProps): React.ReactElement {\r\n const { tokens, theme } = useTheme();\r\n const isTransparentTheme =\r\n theme === \"transparent\" ||\r\n theme === \"transparent-bold\" ||\r\n theme === \"transparent-minimal\";\r\n const scrollRef = useRef<HTMLDivElement>(null);\r\n const [paused, setPaused] = useState(false);\r\n const [searchQuery, setSearchQuery] = useState(\"\");\r\n const [severityFilter, setSeverityFilter] = useState<Set<LogSeverity>>(\r\n new Set(defaultSeverities ?? SEVERITY_ORDER),\r\n );\r\n const [autoScroll, setAutoScroll] = useState(true);\r\n const prevLengthRef = useRef(entries.length);\r\n /** Scroll position and container height for virtualization */\r\n const [scrollState, setScrollState] = useState({\r\n scrollTop: 0,\r\n containerHeight: 300,\r\n });\r\n const rafRef = useRef<number | null>(null);\r\n\r\n const severityColors: Record<LogSeverity, string> = {\r\n debug: tokens.colors.text.muted ?? tokens.colors.text.secondary,\r\n info: tokens.colors.status.standby,\r\n warn: tokens.colors.status.caution,\r\n error: tokens.colors.status.serious,\r\n critical: tokens.colors.status.critical,\r\n };\r\n\r\n /**\r\n * Astro UX status shapes mapped to log severity levels:\r\n * debug → off (small dot ·), info → standby (ring ◯),\r\n * warn → caution (square ■), error → serious (diamond ◆),\r\n * critical → critical (triangle down ▼)\r\n */\r\n const severityShapes: Record<LogSeverity, string> = {\r\n debug: \"·\",\r\n info: \"○\",\r\n warn: \"■\",\r\n error: \"◆\",\r\n critical: \"▼\",\r\n };\r\n\r\n const severityLabels: Record<LogSeverity, string> = {\r\n debug: \"DBG\",\r\n info: \"INF\",\r\n warn: \"WRN\",\r\n error: \"ERR\",\r\n critical: \"CRT\",\r\n };\r\n\r\n // Filter & limit entries: keep NEWEST maxEntries (entries[0] = newest)\r\n const filteredEntries = useMemo(() => {\r\n let filtered = entries.filter((e) => severityFilter.has(e.severity));\r\n if (searchQuery.trim()) {\r\n const q = searchQuery.toLowerCase();\r\n filtered = filtered.filter(\r\n (e) =>\r\n e.message.toLowerCase().includes(q) ||\r\n (e.source && e.source.toLowerCase().includes(q)),\r\n );\r\n }\r\n return filtered.slice(0, maxEntries);\r\n }, [entries, severityFilter, searchQuery, maxEntries]);\r\n\r\n const paginationConfig =\r\n pagination === true ? { pageSize: 200 } : pagination || undefined;\r\n const pageSize = paginationConfig?.pageSize ?? 200;\r\n const totalPages = Math.max(1, Math.ceil(filteredEntries.length / pageSize));\r\n const [currentPage, setCurrentPage] = useState(1);\r\n\r\n // Clamp current page when totalPages shrinks (e.g. after filter change)\r\n useEffect(() => {\r\n setCurrentPage((p) => Math.min(p, totalPages));\r\n }, [totalPages]);\r\n\r\n const paginationEntries = useMemo(() => {\r\n if (!paginationConfig) return null;\r\n const start = (currentPage - 1) * pageSize;\r\n return filteredEntries.slice(start, start + pageSize);\r\n }, [paginationConfig, currentPage, pageSize, filteredEntries]);\r\n\r\n const totalHeight = filteredEntries.length * estimatedRowHeight;\r\n const { scrollTop, containerHeight } = scrollState;\r\n const visibleRange = useMemo(() => {\r\n const start = Math.max(\r\n 0,\r\n Math.floor(scrollTop / estimatedRowHeight) - overscanCount,\r\n );\r\n const end = Math.min(\r\n filteredEntries.length,\r\n Math.ceil((scrollTop + containerHeight) / estimatedRowHeight) +\r\n overscanCount,\r\n );\r\n return { start, end };\r\n }, [\r\n scrollTop,\r\n containerHeight,\r\n estimatedRowHeight,\r\n overscanCount,\r\n filteredEntries.length,\r\n ]);\r\n const visibleEntries = useMemo(\r\n () => filteredEntries.slice(visibleRange.start, visibleRange.end),\r\n [filteredEntries, visibleRange.start, visibleRange.end],\r\n );\r\n\r\n const usePagination = !!paginationConfig;\r\n\r\n const renderRow = useCallback(\r\n (entry: LogEntry) => (\r\n <div\r\n key={entry.id}\r\n role={onEntryClick ? \"button\" : \"listitem\"}\r\n tabIndex={onEntryClick ? 0 : undefined}\r\n aria-label={\r\n onEntryClick\r\n ? `${severityLabels[entry.severity]}: ${entry.message}`\r\n : undefined\r\n }\r\n onClick={() => onEntryClick?.(entry)}\r\n onKeyDown={\r\n onEntryClick\r\n ? (e) =>\r\n (e.key === \"Enter\" || e.key === \" \") &&\r\n (e.preventDefault(), onEntryClick(entry))\r\n : undefined\r\n }\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"flex-start\",\r\n minHeight: estimatedRowHeight,\r\n boxSizing: \"border-box\",\r\n padding: \"3px 12px\",\r\n borderBottom: `1px solid ${tokens.colors.border.muted}11`,\r\n cursor: onEntryClick ? \"pointer\" : \"default\",\r\n transition: \"background 150ms ease\",\r\n }}\r\n onMouseEnter={(e) => {\r\n (e.currentTarget as HTMLDivElement).style.background =\r\n `${tokens.colors.background.surface}`;\r\n }}\r\n onMouseLeave={(e) => {\r\n (e.currentTarget as HTMLDivElement).style.background = \"transparent\";\r\n }}\r\n >\r\n <span\r\n style={{\r\n color: severityColors[entry.severity],\r\n fontWeight: 700,\r\n fontSize: tokens.typography.fontSize.xxs,\r\n minWidth: 38,\r\n textAlign: \"center\",\r\n background: `${severityColors[entry.severity]}18`,\r\n borderRadius: tokens.borderRadius.sm,\r\n padding: \"1px 4px\",\r\n marginRight: 8,\r\n marginTop: 1,\r\n letterSpacing: \"0.3px\",\r\n display: \"inline-flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n gap: 3,\r\n }}\r\n >\r\n <span style={{ fontSize: \"8px\", lineHeight: 1 }}>\r\n {severityShapes[entry.severity]}\r\n </span>\r\n {severityLabels[entry.severity]}\r\n </span>\r\n {showTimestamp && (\r\n <span\r\n style={{\r\n color: tokens.colors.text.muted,\r\n marginRight: 8,\r\n // 'sim-elapsed' renders T+HH:MM:SS.s which is wider than the wall-clock formats.\r\n minWidth: timestampFormat === \"sim-elapsed\" ? 110 : 85,\r\n fontSize: 11,\r\n flexShrink: 0,\r\n fontFamily: tokens.typography.fontFamily.mono,\r\n }}\r\n >\r\n {formatTimestamp(entry, timestampFormat)}\r\n </span>\r\n )}\r\n {showSource && entry.source && (\r\n <span\r\n style={{\r\n color: tokens.colors.interactive.default,\r\n marginRight: 8,\r\n fontSize: 11,\r\n minWidth: 70,\r\n maxWidth: 100,\r\n overflow: \"hidden\",\r\n textOverflow: \"ellipsis\",\r\n whiteSpace: \"nowrap\",\r\n flexShrink: 0,\r\n }}\r\n >\r\n [{entry.source}]\r\n </span>\r\n )}\r\n <span\r\n style={{\r\n color:\r\n entry.severity === \"error\" || entry.severity === \"critical\"\r\n ? severityColors[entry.severity]\r\n : tokens.colors.text.primary,\r\n flex: 1,\r\n wordBreak: \"break-word\",\r\n lineHeight: 1.4,\r\n }}\r\n >\r\n {entry.message}\r\n </span>\r\n </div>\r\n ),\r\n [\r\n severityColors,\r\n severityShapes,\r\n severityLabels,\r\n estimatedRowHeight,\r\n tokens,\r\n showTimestamp,\r\n showSource,\r\n timestampFormat,\r\n onEntryClick,\r\n ],\r\n );\r\n\r\n // Measure scroll container height\r\n useEffect(() => {\r\n const el = scrollRef.current;\r\n if (!el) return;\r\n const ro = new ResizeObserver(() => {\r\n setScrollState((s) => ({ ...s, containerHeight: el.clientHeight }));\r\n });\r\n ro.observe(el);\r\n setScrollState((s) => ({ ...s, containerHeight: el.clientHeight }));\r\n return () => ro.disconnect();\r\n }, []);\r\n\r\n const handleScroll = useCallback(() => {\r\n const el = scrollRef.current;\r\n if (!el) return;\r\n const { scrollTop: st, scrollHeight, clientHeight } = el;\r\n const atBottom = scrollHeight - st - clientHeight < 30;\r\n setAutoScroll(atBottom);\r\n if (rafRef.current !== null) cancelAnimationFrame(rafRef.current);\r\n rafRef.current = requestAnimationFrame(() => {\r\n rafRef.current = null;\r\n setScrollState((prev) => ({ ...prev, scrollTop: el.scrollTop }));\r\n });\r\n }, []);\r\n\r\n // Auto-scroll to bottom when new entries arrive and user is at bottom (scroll mode).\r\n // In pagination mode: page 1 always shows newest entries (entries are prepended) so\r\n // content updates automatically — no forced jump needed. The user decides when to\r\n // follow new entries by clicking \"First\" (which sets autoScroll=true).\r\n useEffect(() => {\r\n if (entries.length <= prevLengthRef.current) {\r\n prevLengthRef.current = entries.length;\r\n return;\r\n }\r\n if (!usePagination && !paused && autoScroll && scrollRef.current) {\r\n scrollRef.current.scrollTop = scrollRef.current.scrollHeight;\r\n }\r\n prevLengthRef.current = entries.length;\r\n }, [entries.length, paused, autoScroll, usePagination]);\r\n\r\n useEffect(\r\n () => () => {\r\n if (rafRef.current !== null) cancelAnimationFrame(rafRef.current);\r\n },\r\n [],\r\n );\r\n\r\n const toggleSeverity = useCallback((sev: LogSeverity) => {\r\n setSeverityFilter((prev) => {\r\n const next = new Set(prev);\r\n if (next.has(sev)) next.delete(sev);\r\n else next.add(sev);\r\n return next;\r\n });\r\n }, []);\r\n\r\n const exportLog = useCallback(() => {\r\n const text = filteredEntries\r\n .map((e) => {\r\n const wall = formatTimestamp(e, \"datetime\");\r\n // When sim-elapsed is in use, include the sim T+ stamp alongside wall time so\r\n // exports remain useful when correlating with sim playback.\r\n const sim =\r\n timestampFormat === \"sim-elapsed\" && typeof e.simTime === \"number\"\r\n ? ` ${formatSimElapsed(e.simTime)}`\r\n : \"\";\r\n const src = e.source ? `[${e.source}]` : \"\";\r\n return `${wall}${sim} [${e.severity.toUpperCase()}] ${src} ${e.message}`;\r\n })\r\n .join(\"\\n\");\r\n navigator.clipboard?.writeText(text);\r\n }, [filteredEntries, timestampFormat]);\r\n\r\n const clearFilters = useCallback(() => {\r\n setSeverityFilter(new Set(SEVERITY_ORDER));\r\n setSearchQuery(\"\");\r\n }, []);\r\n\r\n // Counts by severity\r\n const counts = useMemo(() => {\r\n const c: Record<LogSeverity, number> = {\r\n debug: 0,\r\n info: 0,\r\n warn: 0,\r\n error: 0,\r\n critical: 0,\r\n };\r\n entries.forEach((e) => {\r\n c[e.severity]++;\r\n });\r\n return c;\r\n }, [entries]);\r\n\r\n return (\r\n <div\r\n className={`log-viewer ${className}`}\r\n style={{\r\n height: typeof height === \"number\" ? `${height}px` : height,\r\n minHeight: typeof height === \"number\" ? `${height}px` : height,\r\n background: tokens.colors.background.base,\r\n ...(bordered\r\n ? (tokens.colors.border.cardStyle ?? {\r\n border: `1px solid ${tokens.colors.border.muted}`,\r\n })\r\n : { border: \"none\" }),\r\n borderRadius: tokens.borderRadius.lg,\r\n overflow: \"hidden\",\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n fontFamily: tokens.typography.fontFamily.mono,\r\n ...styleProp,\r\n fontSize: tokens.typography.fontSize.xs,\r\n color: tokens.colors.text.primary,\r\n backdropFilter: isTransparentTheme ? \"blur(10px)\" : undefined,\r\n WebkitBackdropFilter: isTransparentTheme ? \"blur(10px)\" : undefined,\r\n }}\r\n >\r\n {/* Header */}\r\n {title && (\r\n <div\r\n style={{\r\n padding: \"8px 12px\",\r\n borderBottom: `1px solid ${tokens.colors.border.muted}`,\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"space-between\",\r\n background: tokens.colors.background.surface,\r\n fontFamily: tokens.typography.fontFamily.primary,\r\n backdropFilter: \"blur(8px)\",\r\n }}\r\n >\r\n <span style={{ fontWeight: 600, fontSize: 13 }}>{title}</span>\r\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 8 }}>\r\n <span style={{ fontSize: 11, color: tokens.colors.text.muted }}>\r\n {filteredEntries.length.toLocaleString()} entries\r\n </span>\r\n </div>\r\n </div>\r\n )}\r\n\r\n {/* Toolbar */}\r\n {(showFilters || showSearch) && (\r\n <div\r\n style={{\r\n padding: \"6px 12px\",\r\n borderBottom: `1px solid ${tokens.colors.border.muted}`,\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: 8,\r\n flexWrap: \"wrap\",\r\n background: tokens.colors.background.surface,\r\n fontFamily: tokens.typography.fontFamily.primary,\r\n }}\r\n >\r\n {showFilters &&\r\n SEVERITY_ORDER.map((sev) => (\r\n <button\r\n key={sev}\r\n onClick={() => toggleSeverity(sev)}\r\n aria-pressed={severityFilter.has(sev)}\r\n aria-label={`Filter ${sev} logs (${counts[sev]})`}\r\n style={{\r\n background: severityFilter.has(sev)\r\n ? `${severityColors[sev]}22`\r\n : \"transparent\",\r\n color: severityFilter.has(sev)\r\n ? severityColors[sev]\r\n : tokens.colors.text.muted,\r\n border: `1px solid ${severityFilter.has(sev) ? severityColors[sev] + \"66\" : tokens.colors.border.muted}`,\r\n borderRadius: tokens.borderRadius.md,\r\n padding: `${tokens.spacing.xxs} ${tokens.spacing.sm}`,\r\n fontSize: tokens.typography.fontSize.xxs,\r\n fontWeight: tokens.typography.fontWeight.medium,\r\n cursor: \"pointer\",\r\n textTransform: \"uppercase\",\r\n letterSpacing: \"0.5px\",\r\n transition: \"all 150ms ease\",\r\n }}\r\n >\r\n <span aria-hidden=\"true\">{severityShapes[sev]}</span> {sev} (\r\n {counts[sev]})\r\n </button>\r\n ))}\r\n <div style={{ flex: 1 }} />\r\n {showSearch && (\r\n <input\r\n type=\"text\"\r\n value={searchQuery}\r\n onChange={(e) => setSearchQuery(e.target.value)}\r\n placeholder=\"Filter messages...\"\r\n aria-label=\"Filter log messages\"\r\n style={{\r\n background: tokens.colors.background.base,\r\n color: tokens.colors.text.primary,\r\n border: `1px solid ${tokens.colors.border.muted}`,\r\n borderRadius: tokens.borderRadius.md,\r\n padding: `3px ${tokens.spacing.sm}`,\r\n fontSize: tokens.typography.fontSize.sm,\r\n outline: \"none\",\r\n minWidth: 150,\r\n }}\r\n />\r\n )}\r\n <button\r\n onClick={() => setPaused((p) => !p)}\r\n aria-label={paused ? \"Resume log stream\" : \"Pause log stream\"}\r\n style={{\r\n background: paused\r\n ? `${tokens.colors.status.caution}22`\r\n : tokens.colors.background.base,\r\n color: paused\r\n ? tokens.colors.status.caution\r\n : tokens.colors.text.secondary,\r\n border: `1px solid ${tokens.colors.border.muted}`,\r\n borderRadius: tokens.borderRadius.md,\r\n padding: `${tokens.spacing.xxs} ${tokens.spacing.sm}`,\r\n fontSize: tokens.typography.fontSize.xxs,\r\n cursor: \"pointer\",\r\n fontWeight: tokens.typography.fontWeight.medium,\r\n transition: \"all 150ms ease\",\r\n }}\r\n >\r\n {paused ? \"\\u25B6 Resume\" : \"\\u23F8 Pause\"}\r\n </button>\r\n <button\r\n onClick={exportLog}\r\n aria-label=\"Copy log to clipboard\"\r\n style={{\r\n background: tokens.colors.background.base,\r\n color: tokens.colors.text.secondary,\r\n border: `1px solid ${tokens.colors.border.muted}`,\r\n borderRadius: tokens.borderRadius.md,\r\n padding: `${tokens.spacing.xxs} ${tokens.spacing.sm}`,\r\n fontSize: tokens.typography.fontSize.xxs,\r\n cursor: \"pointer\",\r\n transition: \"all 150ms ease\",\r\n }}\r\n >\r\n Copy\r\n </button>\r\n <button\r\n onClick={clearFilters}\r\n aria-label=\"Reset all filters\"\r\n style={{\r\n background: tokens.colors.background.base,\r\n color: tokens.colors.text.secondary,\r\n border: `1px solid ${tokens.colors.border.muted}`,\r\n borderRadius: tokens.borderRadius.md,\r\n padding: `${tokens.spacing.xxs} ${tokens.spacing.sm}`,\r\n fontSize: tokens.typography.fontSize.xxs,\r\n cursor: \"pointer\",\r\n transition: \"all 150ms ease\",\r\n }}\r\n >\r\n Reset\r\n </button>\r\n </div>\r\n )}\r\n\r\n {/* Log entries — minHeight: 0 so flex child can shrink and scroll */}\r\n <div\r\n ref={scrollRef}\r\n onScroll={handleScroll}\r\n className=\"zendir-log-scroll\"\r\n style={{\r\n flex: 1,\r\n minHeight: 0,\r\n overflowY: \"auto\",\r\n overflowX: \"hidden\",\r\n padding: \"2px 0\",\r\n overscrollBehavior: \"contain\",\r\n WebkitOverflowScrolling: \"touch\",\r\n }}\r\n >\r\n {filteredEntries.length === 0 ? (\r\n <div\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n height: \"100%\",\r\n color: tokens.colors.text.muted,\r\n fontSize: 13,\r\n fontFamily: tokens.typography.fontFamily.primary,\r\n }}\r\n >\r\n {entries.length === 0 ? \"No log entries\" : \"No matching entries\"}\r\n </div>\r\n ) : usePagination ? (\r\n <div\r\n style={{ padding: \"2px 0\" }}\r\n role=\"list\"\r\n aria-label=\"Log entries\"\r\n >\r\n {(paginationEntries ?? []).map((entry) => renderRow(entry))}\r\n </div>\r\n ) : (\r\n <div\r\n style={{\r\n height: totalHeight,\r\n position: \"relative\",\r\n width: \"100%\",\r\n }}\r\n role=\"list\"\r\n aria-label=\"Log entries\"\r\n >\r\n {visibleRange.start > 0 && (\r\n <div\r\n style={{\r\n height: visibleRange.start * estimatedRowHeight,\r\n width: \"100%\",\r\n }}\r\n aria-hidden\r\n />\r\n )}\r\n {visibleEntries.map((entry) => renderRow(entry))}\r\n {visibleRange.end < filteredEntries.length && (\r\n <div\r\n style={{\r\n height:\r\n (filteredEntries.length - visibleRange.end) *\r\n estimatedRowHeight,\r\n width: \"100%\",\r\n }}\r\n aria-hidden\r\n />\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n\r\n {/* Pagination footer */}\r\n {usePagination && filteredEntries.length > 0 && (\r\n <div\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"space-between\",\r\n flexWrap: \"wrap\",\r\n gap: tokens.spacing.sm,\r\n padding: `${tokens.spacing.xs} ${tokens.spacing.smd}`,\r\n borderTop: `1px solid ${tokens.colors.border.muted}`,\r\n background: tokens.colors.background.surface,\r\n fontFamily: tokens.typography.fontFamily.primary,\r\n fontSize: tokens.typography.fontSize.xs,\r\n color: tokens.colors.text.secondary,\r\n }}\r\n >\r\n <span aria-live=\"polite\">\r\n Page {currentPage} of {totalPages}\r\n {filteredEntries.length > 0 && (\r\n <span style={{ marginLeft: tokens.spacing.sm }}>\r\n ({((currentPage - 1) * pageSize + 1).toLocaleString()}–\r\n {Math.min(\r\n currentPage * pageSize,\r\n filteredEntries.length,\r\n ).toLocaleString()}{\" \"}\r\n of {filteredEntries.length.toLocaleString()})\r\n </span>\r\n )}\r\n </span>\r\n <div\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: tokens.spacing.xs,\r\n }}\r\n >\r\n <button\r\n type=\"button\"\r\n onClick={() => {\r\n setCurrentPage(1);\r\n setAutoScroll(true);\r\n }}\r\n disabled={currentPage <= 1}\r\n aria-label=\"First page (newest)\"\r\n style={{\r\n padding: `${tokens.spacing.xxs} ${tokens.spacing.sm}`,\r\n border: `1px solid ${tokens.colors.border.muted}`,\r\n borderRadius: tokens.borderRadius.md,\r\n background: tokens.colors.background.base,\r\n color: tokens.colors.text.primary,\r\n fontSize: tokens.typography.fontSize.xxs,\r\n cursor: currentPage <= 1 ? \"not-allowed\" : \"pointer\",\r\n opacity: currentPage <= 1 ? 0.5 : 1,\r\n }}\r\n >\r\n First\r\n </button>\r\n <button\r\n type=\"button\"\r\n onClick={() => {\r\n setCurrentPage((p) => Math.max(1, p - 1));\r\n setAutoScroll(false);\r\n }}\r\n disabled={currentPage <= 1}\r\n aria-label=\"Previous page\"\r\n style={{\r\n padding: `${tokens.spacing.xxs} ${tokens.spacing.sm}`,\r\n border: `1px solid ${tokens.colors.border.muted}`,\r\n borderRadius: tokens.borderRadius.md,\r\n background: tokens.colors.background.base,\r\n color: tokens.colors.text.primary,\r\n fontSize: tokens.typography.fontSize.xxs,\r\n cursor: currentPage <= 1 ? \"not-allowed\" : \"pointer\",\r\n opacity: currentPage <= 1 ? 0.5 : 1,\r\n }}\r\n >\r\n Previous\r\n </button>\r\n <button\r\n type=\"button\"\r\n onClick={() => {\r\n setCurrentPage((p) => Math.min(totalPages, p + 1));\r\n setAutoScroll(false);\r\n }}\r\n disabled={currentPage >= totalPages}\r\n aria-label=\"Next page\"\r\n style={{\r\n padding: `${tokens.spacing.xxs} ${tokens.spacing.sm}`,\r\n border: `1px solid ${tokens.colors.border.muted}`,\r\n borderRadius: tokens.borderRadius.md,\r\n background: tokens.colors.background.base,\r\n color: tokens.colors.text.primary,\r\n fontSize: tokens.typography.fontSize.xxs,\r\n cursor: currentPage >= totalPages ? \"not-allowed\" : \"pointer\",\r\n opacity: currentPage >= totalPages ? 0.5 : 1,\r\n }}\r\n >\r\n Next\r\n </button>\r\n <button\r\n type=\"button\"\r\n onClick={() => {\r\n setCurrentPage(totalPages);\r\n setAutoScroll(false);\r\n }}\r\n disabled={currentPage >= totalPages}\r\n aria-label=\"Last page (oldest)\"\r\n style={{\r\n padding: `${tokens.spacing.xxs} ${tokens.spacing.sm}`,\r\n border: `1px solid ${tokens.colors.border.muted}`,\r\n borderRadius: tokens.borderRadius.md,\r\n background: tokens.colors.background.base,\r\n color: tokens.colors.text.primary,\r\n fontSize: tokens.typography.fontSize.xxs,\r\n cursor: currentPage >= totalPages ? \"not-allowed\" : \"pointer\",\r\n opacity: currentPage >= totalPages ? 0.5 : 1,\r\n }}\r\n >\r\n Last\r\n </button>\r\n </div>\r\n </div>\r\n )}\r\n\r\n {/* Status bar */}\r\n <style>{`\r\n .zendir-log-scroll::-webkit-scrollbar { width: 6px; }\r\n .zendir-log-scroll::-webkit-scrollbar-track { background: transparent; }\r\n .zendir-log-scroll::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.15); border-radius: 3px; }\r\n .zendir-log-scroll::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.25); }\r\n .zendir-log-scroll { scrollbar-width: thin; scrollbar-color: rgba(255,255,255,0.15) transparent; }\r\n `}</style>\r\n\r\n {paused && (\r\n <div\r\n role=\"status\"\r\n style={{\r\n padding: `${tokens.spacing.xs} ${tokens.spacing.smd}`,\r\n borderTop: `1px solid ${tokens.colors.border.muted}`,\r\n background: `${tokens.colors.status.caution}15`,\r\n color: tokens.colors.status.caution,\r\n fontSize: tokens.typography.fontSize.sm,\r\n textAlign: \"center\",\r\n fontFamily: tokens.typography.fontFamily.primary,\r\n fontWeight: tokens.typography.fontWeight.medium,\r\n }}\r\n >\r\n Log paused — new entries are buffered\r\n </div>\r\n )}\r\n </div>\r\n );\r\n});\r\n\r\nexport default LogViewer;\r\n"],"mappings":"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"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
2
|
import { StatusLevel } from '../../utils';
|
|
3
3
|
import { AstroIconName } from '../display/AstroIcon';
|
|
4
|
-
|
|
5
4
|
export interface StreamMessage {
|
|
6
5
|
/** Unique identifier */
|
|
7
6
|
id: string;
|
|
@@ -32,13 +31,13 @@ export interface MessageStreamProps {
|
|
|
32
31
|
/** Show timestamps */
|
|
33
32
|
showTimestamps?: boolean;
|
|
34
33
|
/** Time format for timestamps */
|
|
35
|
-
timeFormat?:
|
|
34
|
+
timeFormat?: "relative" | "absolute" | "time-only";
|
|
36
35
|
/** Variant: 'list' for vertical, 'tree' for hierarchical */
|
|
37
|
-
variant?:
|
|
36
|
+
variant?: "list" | "tree" | "compact";
|
|
38
37
|
/** Custom className */
|
|
39
38
|
className?: string;
|
|
40
39
|
/** Card size */
|
|
41
|
-
size?:
|
|
40
|
+
size?: "sm" | "md" | "lg";
|
|
42
41
|
/** On message click */
|
|
43
42
|
onMessageClick?: (message: StreamMessage) => void;
|
|
44
43
|
/** Loading state */
|