@zendir/ui 0.2.21 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +183 -1
- package/README.md +70 -28
- package/dist/index.d.ts +1 -1
- package/dist/index.js +51 -42
- package/dist/index.js.map +1 -1
- package/dist/react/3d/CesiumCaptureSource.d.ts +1 -1
- package/dist/react/3d/CesiumCaptureSource.js +1 -1
- package/dist/react/3d/CesiumCaptureSource.js.map +1 -1
- package/dist/react/3d/ZenSpace3D.js +1253 -0
- package/dist/react/3d/ZenSpace3D.js.map +1 -0
- package/dist/react/3d/ZenSpace3DCesium.js +579 -0
- package/dist/react/3d/ZenSpace3DCesium.js.map +1 -0
- package/dist/react/3d/ZenSpace3DTypes.d.ts +28 -1
- package/dist/react/3d/ZenSpace3DUtils.d.ts +17 -173
- package/dist/react/3d/ZenSpace3DUtils.js +20 -1
- package/dist/react/3d/ZenSpace3DUtils.js.map +1 -1
- package/dist/react/3d/index.d.ts +6 -12
- package/dist/react/3d/threeLoader.js +18 -0
- package/dist/react/3d/threeLoader.js.map +1 -0
- package/dist/react/astro/MonitoringIcon.js +1 -1
- package/dist/react/astro/MonitoringIcon.js.map +1 -1
- package/dist/react/astro/SimulationControls.js +2 -2
- package/dist/react/astro/SimulationControls.js.map +1 -1
- package/dist/react/astro/UnifiedTimeline.js +4 -4
- package/dist/react/astro/UnifiedTimeline.js.map +1 -1
- package/dist/react/charts/GroundTrackMap.d.ts +2 -15
- package/dist/react/charts/GroundTrackMap.js +1 -1
- package/dist/react/charts/GroundTrackMap.js.map +1 -1
- package/dist/react/charts/unified/AstroChart.js +34 -13
- package/dist/react/charts/unified/AstroChart.js.map +1 -1
- package/dist/react/chatgpt/AppCard.d.ts +0 -4
- package/dist/react/chatgpt/index.d.ts +0 -19
- package/dist/react/context/SpatialSelectionContext.d.ts +40 -0
- package/dist/react/context/SpatialSelectionContext.js +10 -0
- package/dist/react/context/SpatialSelectionContext.js.map +1 -0
- package/dist/react/context/index.d.ts +2 -0
- package/dist/react/core/{DataTable.d.ts → data/DataTable.d.ts} +1 -1
- package/dist/react/core/{DataTable.js → data/DataTable.js} +4 -4
- package/dist/react/core/data/DataTable.js.map +1 -0
- package/dist/react/core/{DataValue.d.ts → data/DataValue.d.ts} +2 -2
- package/dist/react/core/{DataValue.js → data/DataValue.js} +2 -2
- package/dist/react/core/data/DataValue.js.map +1 -0
- package/dist/react/core/{propertyConfig.d.ts → data/propertyConfig.d.ts} +2 -2
- package/dist/react/core/data/propertyConfig.js.map +1 -0
- package/dist/react/core/{AstroIcon.js → display/AstroIcon.js} +1 -1
- package/dist/react/core/display/AstroIcon.js.map +1 -0
- package/dist/react/core/{Badge.d.ts → display/Badge.d.ts} +1 -1
- package/dist/react/core/{Badge.js → display/Badge.js} +2 -2
- package/dist/react/core/display/Badge.js.map +1 -0
- package/dist/react/core/{CardHeader.d.ts → display/CardHeader.d.ts} +1 -1
- package/dist/react/core/{CardHeader.js → display/CardHeader.js} +2 -2
- package/dist/react/core/display/CardHeader.js.map +1 -0
- package/dist/react/core/{Container.d.ts → display/Container.d.ts} +1 -1
- package/dist/react/core/{Container.js → display/Container.js} +3 -3
- package/dist/react/core/display/Container.js.map +1 -0
- package/dist/react/core/{CopyButton.js → display/CopyButton.js} +1 -1
- package/dist/react/core/display/CopyButton.js.map +1 -0
- package/dist/react/core/{GlassCard.d.ts → display/GlassCard.d.ts} +1 -1
- package/dist/react/core/{GlassCard.js → display/GlassCard.js} +2 -2
- package/dist/react/core/display/GlassCard.js.map +1 -0
- package/dist/react/core/{HeaderIconWithStatus.d.ts → display/HeaderIconWithStatus.d.ts} +1 -1
- package/dist/react/core/{HeaderIconWithStatus.js → display/HeaderIconWithStatus.js} +1 -1
- package/dist/react/core/display/HeaderIconWithStatus.js.map +1 -0
- package/dist/react/core/{Icon.d.ts → display/Icon.d.ts} +1 -1
- package/dist/react/core/{Icon.js → display/Icon.js} +1 -1
- package/dist/react/core/display/Icon.js.map +1 -0
- package/dist/react/core/{Typography.d.ts → display/Typography.d.ts} +13 -4
- package/dist/react/core/{Typography.js → display/Typography.js} +1 -1
- package/dist/react/core/display/Typography.js.map +1 -0
- package/dist/react/core/{ConfirmDialog.js → feedback/ConfirmDialog.js} +1 -1
- package/dist/react/core/feedback/ConfirmDialog.js.map +1 -0
- package/dist/react/core/{Dialog.js → feedback/Dialog.js} +2 -2
- package/dist/react/core/feedback/Dialog.js.map +1 -0
- package/dist/react/core/{Toast.js → feedback/Toast.js} +3 -3
- package/dist/react/core/feedback/Toast.js.map +1 -0
- package/dist/react/core/index.d.ts +85 -85
- package/dist/react/core/{Button.js → inputs/Button.js} +2 -2
- package/dist/react/core/inputs/Button.js.map +1 -0
- package/dist/react/core/{Checkbox.js → inputs/Checkbox.js} +2 -2
- package/dist/react/core/inputs/Checkbox.js.map +1 -0
- package/dist/react/core/{Input.d.ts → inputs/Input.d.ts} +1 -1
- package/dist/react/core/{Input.js → inputs/Input.js} +3 -3
- package/dist/react/core/inputs/Input.js.map +1 -0
- package/dist/react/core/{LimitsBar.js → inputs/LimitsBar.js} +1 -1
- package/dist/react/core/inputs/LimitsBar.js.map +1 -0
- package/dist/react/core/{NumberInput.d.ts → inputs/NumberInput.d.ts} +2 -2
- package/dist/react/core/{NumberInput.js → inputs/NumberInput.js} +3 -3
- package/dist/react/core/inputs/NumberInput.js.map +1 -0
- package/dist/react/core/{PinInput.js → inputs/PinInput.js} +2 -2
- package/dist/react/core/inputs/PinInput.js.map +1 -0
- package/dist/react/core/{Select.js → inputs/Select.js} +3 -3
- package/dist/react/core/inputs/Select.js.map +1 -0
- package/dist/react/core/{Toggle.js → inputs/Toggle.js} +2 -2
- package/dist/react/core/inputs/Toggle.js.map +1 -0
- package/dist/react/core/{AppBar.d.ts → navigation/AppBar.d.ts} +1 -1
- package/dist/react/core/{AppBar.js → navigation/AppBar.js} +7 -7
- package/dist/react/core/navigation/AppBar.js.map +1 -0
- package/dist/react/core/{Pagination.js → navigation/Pagination.js} +2 -2
- package/dist/react/core/navigation/Pagination.js.map +1 -0
- package/dist/react/core/{SideNav.d.ts → navigation/SideNav.d.ts} +1 -1
- package/dist/react/core/{SideNav.js → navigation/SideNav.js} +3 -3
- package/dist/react/core/navigation/SideNav.js.map +1 -0
- package/dist/react/core/{Tabs.js → navigation/Tabs.js} +2 -2
- package/dist/react/core/navigation/Tabs.js.map +1 -0
- package/dist/react/core/{Popover.js → overlays/Popover.js} +1 -1
- package/dist/react/core/overlays/Popover.js.map +1 -0
- package/dist/react/core/{SidePanel.js → overlays/SidePanel.js} +3 -3
- package/dist/react/core/overlays/SidePanel.js.map +1 -0
- package/dist/react/core/{Tooltip.js → overlays/Tooltip.js} +2 -2
- package/dist/react/core/overlays/Tooltip.js.map +1 -0
- package/dist/react/core/{ActivityPlanner.js → widgets/ActivityPlanner.js} +1 -1
- package/dist/react/core/widgets/ActivityPlanner.js.map +1 -0
- package/dist/react/core/{Capture.js → widgets/Capture.js} +3 -3
- package/dist/react/core/widgets/Capture.js.map +1 -0
- package/dist/react/core/{ChatPanel.d.ts → widgets/ChatPanel.d.ts} +1 -1
- package/dist/react/core/{ChatPanel.js → widgets/ChatPanel.js} +2 -2
- package/dist/react/core/widgets/ChatPanel.js.map +1 -0
- package/dist/react/core/{ColorPickerPanel.d.ts → widgets/ColorPickerPanel.d.ts} +1 -1
- package/dist/react/core/{ColorPickerPanel.js → widgets/ColorPickerPanel.js} +3 -3
- package/dist/react/core/widgets/ColorPickerPanel.js.map +1 -0
- package/dist/react/core/{CommandBuilder.js → widgets/CommandBuilder.js} +1 -1
- package/dist/react/core/widgets/CommandBuilder.js.map +1 -0
- package/dist/react/core/{ConnectionForm.d.ts → widgets/ConnectionForm.d.ts} +1 -1
- package/dist/react/core/{ConnectionForm.js → widgets/ConnectionForm.js} +2 -2
- package/dist/react/core/widgets/ConnectionForm.js.map +1 -0
- package/dist/react/core/{FileExplorer.js → widgets/FileExplorer.js} +2 -2
- package/dist/react/core/widgets/FileExplorer.js.map +1 -0
- package/dist/react/core/{HexViewer.js → widgets/HexViewer.js} +1 -1
- package/dist/react/core/widgets/HexViewer.js.map +1 -0
- package/dist/react/core/{ImageGallery.d.ts → widgets/ImageGallery.d.ts} +1 -1
- package/dist/react/core/{ImageGallery.js → widgets/ImageGallery.js} +3 -3
- package/dist/react/core/widgets/ImageGallery.js.map +1 -0
- package/dist/react/core/{LogViewer.d.ts → widgets/LogViewer.d.ts} +13 -3
- package/dist/react/core/{LogViewer.js → widgets/LogViewer.js} +28 -8
- package/dist/react/core/widgets/LogViewer.js.map +1 -0
- package/dist/react/core/{MessageStream.d.ts → widgets/MessageStream.d.ts} +2 -2
- package/dist/react/core/{MessageStream.js → widgets/MessageStream.js} +4 -4
- package/dist/react/core/widgets/MessageStream.js.map +1 -0
- package/dist/react/core/{MissionCalendar.js → widgets/MissionCalendar.js} +2 -2
- package/dist/react/core/widgets/MissionCalendar.js.map +1 -0
- package/dist/react/core/{PacketViewer.js → widgets/PacketViewer.js} +1 -1
- package/dist/react/core/widgets/PacketViewer.js.map +1 -0
- package/dist/react/core/widgets/capture-placeholder.png.js.map +1 -0
- package/dist/react/hooks/index.d.ts +9 -11
- package/dist/react/hooks/useAccessWindows.d.ts +15 -19
- package/dist/react/hooks/useGroundTrackHistory.d.ts +34 -0
- package/dist/react/hooks/useSimulationScene.d.ts +141 -0
- package/dist/react/hooks/useSimulationScene.js +401 -0
- package/dist/react/hooks/useSimulationScene.js.map +1 -0
- package/dist/react/hooks/useZendirSession.d.ts +44 -69
- package/dist/react/index.d.ts +7 -3
- package/dist/react/panels/LayerControlPanel.d.ts +54 -0
- package/dist/react/panels/LayerControlPanel.js +184 -0
- package/dist/react/panels/LayerControlPanel.js.map +1 -0
- package/dist/react/panels/ObjectInventoryPanel.d.ts +57 -0
- package/dist/react/panels/ObjectInventoryPanel.js +261 -0
- package/dist/react/panels/ObjectInventoryPanel.js.map +1 -0
- package/dist/react/panels/index.d.ts +15 -0
- package/dist/react/theme/ThemeProvider.d.ts +2 -0
- package/dist/react/theme/ThemeProvider.js +50 -72
- package/dist/react/theme/ThemeProvider.js.map +1 -1
- package/dist/react/types.d.ts +32 -3
- package/dist/react/types.js.map +1 -1
- package/dist/react.js +51 -42
- package/dist/react.js.map +1 -1
- package/dist/shaders/atmosphere.frag.js +5 -0
- package/dist/shaders/atmosphere.frag.js.map +1 -0
- package/dist/shaders/atmosphere.vert.js +5 -0
- package/dist/shaders/atmosphere.vert.js.map +1 -0
- package/dist/shaders/stars.frag.js +5 -0
- package/dist/shaders/stars.frag.js.map +1 -0
- package/dist/shaders/stars.vert.js +5 -0
- package/dist/shaders/stars.vert.js.map +1 -0
- package/dist/style.css +6 -4
- package/dist/tokens/css-vars.d.ts +91 -0
- package/dist/tokens/css-vars.js +228 -0
- package/dist/tokens/css-vars.js.map +1 -0
- package/dist/tokens/index.d.ts +71 -18
- package/dist/tokens/index.js +206 -97
- package/dist/tokens/index.js.map +1 -1
- package/dist/tokens/tokens.css +50 -50
- package/package.json +26 -22
- package/sdk-stub.js +10 -5
- package/dist/react/3d/EarthViewer.d.ts +0 -46
- package/dist/react/3d/SolarSystemViewer.d.ts +0 -43
- package/dist/react/chatgpt/ChatGPTCard.d.ts +0 -6
- package/dist/react/core/ActivityPlanner.js.map +0 -1
- package/dist/react/core/AppBar.js.map +0 -1
- package/dist/react/core/AstroIcon.js.map +0 -1
- package/dist/react/core/Badge.js.map +0 -1
- package/dist/react/core/Button.js.map +0 -1
- package/dist/react/core/Capture.js.map +0 -1
- package/dist/react/core/CardHeader.js.map +0 -1
- package/dist/react/core/ChatPanel.js.map +0 -1
- package/dist/react/core/Checkbox.js.map +0 -1
- package/dist/react/core/ColorPickerPanel.js.map +0 -1
- package/dist/react/core/CommandBuilder.js.map +0 -1
- package/dist/react/core/ConfirmDialog.js.map +0 -1
- package/dist/react/core/ConnectionForm.js.map +0 -1
- package/dist/react/core/Container.js.map +0 -1
- package/dist/react/core/CopyButton.js.map +0 -1
- package/dist/react/core/DataTable.js.map +0 -1
- package/dist/react/core/DataValue.js.map +0 -1
- package/dist/react/core/Dialog.js.map +0 -1
- package/dist/react/core/FileExplorer.js.map +0 -1
- package/dist/react/core/GlassCard.js.map +0 -1
- package/dist/react/core/HeaderIconWithStatus.js.map +0 -1
- package/dist/react/core/HexViewer.js.map +0 -1
- package/dist/react/core/Icon.js.map +0 -1
- package/dist/react/core/ImageGallery.js.map +0 -1
- package/dist/react/core/Input.js.map +0 -1
- package/dist/react/core/LimitsBar.js.map +0 -1
- package/dist/react/core/LogViewer.js.map +0 -1
- package/dist/react/core/MessageStream.js.map +0 -1
- package/dist/react/core/MissionCalendar.js.map +0 -1
- package/dist/react/core/NumberInput.js.map +0 -1
- package/dist/react/core/PacketViewer.js.map +0 -1
- package/dist/react/core/Pagination.js.map +0 -1
- package/dist/react/core/PinInput.js.map +0 -1
- package/dist/react/core/Popover.js.map +0 -1
- package/dist/react/core/Select.js.map +0 -1
- package/dist/react/core/SideNav.js.map +0 -1
- package/dist/react/core/SidePanel.js.map +0 -1
- package/dist/react/core/Tabs.js.map +0 -1
- package/dist/react/core/Toast.js.map +0 -1
- package/dist/react/core/Toggle.js.map +0 -1
- package/dist/react/core/Tooltip.js.map +0 -1
- package/dist/react/core/Typography.js.map +0 -1
- package/dist/react/core/capture-placeholder.png.js.map +0 -1
- package/dist/react/core/propertyConfig.js.map +0 -1
- package/dist/react/hooks/useSimulationTime.d.ts +0 -61
- package/dist/react/hooks/useSpacecraftPosition.d.ts +0 -50
- package/dist/react/hooks/useTelemetry.d.ts +0 -55
- package/dist/types.d.ts +0 -1
- package/dist/types.js +0 -2
- package/dist/types.js.map +0 -1
- /package/dist/react/core/{propertyConfig.js → data/propertyConfig.js} +0 -0
- /package/dist/react/core/{AstroIcon.d.ts → display/AstroIcon.d.ts} +0 -0
- /package/dist/react/core/{CopyButton.d.ts → display/CopyButton.d.ts} +0 -0
- /package/dist/react/core/{ConfirmDialog.d.ts → feedback/ConfirmDialog.d.ts} +0 -0
- /package/dist/react/core/{Dialog.d.ts → feedback/Dialog.d.ts} +0 -0
- /package/dist/react/core/{Toast.d.ts → feedback/Toast.d.ts} +0 -0
- /package/dist/react/core/{Button.d.ts → inputs/Button.d.ts} +0 -0
- /package/dist/react/core/{Checkbox.d.ts → inputs/Checkbox.d.ts} +0 -0
- /package/dist/react/core/{LimitsBar.d.ts → inputs/LimitsBar.d.ts} +0 -0
- /package/dist/react/core/{PinInput.d.ts → inputs/PinInput.d.ts} +0 -0
- /package/dist/react/core/{Select.d.ts → inputs/Select.d.ts} +0 -0
- /package/dist/react/core/{Toggle.d.ts → inputs/Toggle.d.ts} +0 -0
- /package/dist/react/core/{Pagination.d.ts → navigation/Pagination.d.ts} +0 -0
- /package/dist/react/core/{Tabs.d.ts → navigation/Tabs.d.ts} +0 -0
- /package/dist/react/core/{Popover.d.ts → overlays/Popover.d.ts} +0 -0
- /package/dist/react/core/{SidePanel.d.ts → overlays/SidePanel.d.ts} +0 -0
- /package/dist/react/core/{Tooltip.d.ts → overlays/Tooltip.d.ts} +0 -0
- /package/dist/react/core/{ActivityPlanner.d.ts → widgets/ActivityPlanner.d.ts} +0 -0
- /package/dist/react/core/{Capture.d.ts → widgets/Capture.d.ts} +0 -0
- /package/dist/react/core/{CommandBuilder.d.ts → widgets/CommandBuilder.d.ts} +0 -0
- /package/dist/react/core/{FileExplorer.d.ts → widgets/FileExplorer.d.ts} +0 -0
- /package/dist/react/core/{HexViewer.d.ts → widgets/HexViewer.d.ts} +0 -0
- /package/dist/react/core/{MissionCalendar.d.ts → widgets/MissionCalendar.d.ts} +0 -0
- /package/dist/react/core/{PacketViewer.d.ts → widgets/PacketViewer.d.ts} +0 -0
- /package/dist/react/core/{capture-placeholder.png.js → widgets/capture-placeholder.png.js} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AppBar.js","sources":["../../../src/react/core/AppBar.tsx"],"sourcesContent":["/**\n * @zendir/ui - AppBar Component\n * \n * Enterprise-grade application bar with integrated theme controls, \n * compact mode toggle, and display settings management.\n * \n * Features:\n * - Customizable branding (logo, name, domain, version)\n * - Dark/light mode toggle\n * - Theme variant selector\n * - Color picker (accent + glass tint)\n * - Compact mode toggle (affects all cards globally)\n * - Integrates with DisplaySettingsProvider\n * - Responsive design\n * - AstroUXDS compliant\n * \n * @example\n * ```tsx\n * import { AppBar, ThemeProvider, DisplaySettingsProvider } from '@zendir/ui';\n * \n * <ThemeProvider>\n * <DisplaySettingsProvider>\n * <AppBar\n * appName=\"Mission Control\"\n * appDomain=\"NASA\"\n * appVersion=\"1.0.0\"\n * showThemeControls\n * showCompactToggle\n * />\n * <App />\n * </DisplaySettingsProvider>\n * </ThemeProvider>\n * ```\n */\n\nimport React, { memo, useMemo, useState, useRef, useEffect, type ReactNode } from 'react';\nimport { useTheme, type ThemeVariant, type ThemeMode } from '../theme';\nimport { useDisplaySettingsOptional } from '../context/DisplaySettingsContext';\nimport { classNames, safeAccentText } from '../utils';\nimport { AstroIcon } from './AstroIcon';\nimport { ColorPickerPanel } from './ColorPickerPanel';\nimport { Tooltip } from './Tooltip';\nimport { useBreakpoint } from './layout/useBreakpoint';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface AppBarBranding {\n /** Logo image URL (takes precedence over text) */\n logoUrl?: string;\n /** Logo as React node (takes precedence over logoUrl) */\n logo?: ReactNode;\n /** Logo alt text for accessibility */\n logoAlt?: string;\n /** Logo height in pixels (default: 28) */\n logoHeight?: number;\n /** Link URL when clicking on branding */\n href?: string;\n}\n\nexport interface AppBarProps {\n /** Application name */\n appName?: string;\n /** Application domain/organization (shown above app name) */\n appDomain?: string;\n /** Application version (shown smaller) */\n appVersion?: string;\n /** Logo/branding configuration */\n branding?: AppBarBranding;\n \n // Control visibility\n /** Show theme controls (mode toggle, theme selector, color picker) */\n showThemeControls?: boolean;\n /** Show compact mode toggle */\n showCompactToggle?: boolean;\n /** Show color picker (only visible for transparent themes) */\n showColorPicker?: boolean;\n /** Show theme variant selector */\n showThemeSelector?: boolean;\n /** Show only mode toggle (hide other theme controls) */\n showModeOnly?: boolean;\n /** Use compact dropdown for theme controls instead of buttons */\n compactThemeControls?: boolean;\n \n // Default values (for when controls are hidden)\n /** Default theme to apply if not already set */\n defaultTheme?: ThemeVariant;\n /** Default mode to apply if not already set */\n defaultMode?: ThemeMode;\n /** Default compact mode */\n defaultCompactMode?: boolean;\n \n // Slots\n /** Left slot content (after branding, or only content when showBranding is false) */\n leftSlot?: ReactNode;\n /** Center slot content */\n centerSlot?: ReactNode;\n /** Right slot content (before theme controls) */\n rightSlot?: ReactNode;\n /** Optional title shown above right slot (e.g. \"Data\" for metrics/refresh section) */\n rightSectionTitle?: string;\n \n // Layout (operator / mission-control style)\n /** Show branding block (app name, logo). Set false for control-only bars. */\n showBranding?: boolean;\n /** Let center slot grow to fill space and stay centered (e.g. simulation controls). */\n centerGrow?: boolean;\n /** Bar variant: default (52px) or dense (44px min height, tighter padding/gap). */\n variant?: 'default' | 'dense';\n \n // Callbacks\n /** Callback when theme changes */\n onThemeChange?: (theme: ThemeVariant) => void;\n /** Callback when mode changes */\n onModeChange?: (mode: ThemeMode) => void;\n /** Callback when compact mode changes */\n onCompactModeChange?: (compact: boolean) => void;\n \n // Styling\n /** Custom className */\n className?: string;\n /** Custom height (default: 52) */\n height?: number;\n /** Sticky positioning */\n sticky?: boolean;\n}\n\n// =============================================================================\n// Theme Variants for Selector\n// =============================================================================\n\nconst THEME_VARIANTS: { key: ThemeVariant; label: string; tooltip: string }[] = [\n { key: 'hybrid', label: 'Zen (Hybrid)', tooltip: 'Default theme - Astro status colors with purple accents' },\n { key: 'purple-hue', label: 'Zen (Purple Hue)', tooltip: 'Purple accent theme - vibrant purple tones' },\n { key: 'astro', label: 'Astro', tooltip: 'Astro UX Design System theme - dark blue tones' },\n { key: 'transparent', label: 'Glass', tooltip: 'Glassmorphism theme - transparent with blur effects' },\n { key: 'transparent-bold', label: 'Bold', tooltip: 'Bold glass theme - stronger transparency effects' },\n { key: 'transparent-minimal', label: 'Minimal', tooltip: 'Minimal glass theme - ultra-subtle borders, clean surfaces' },\n];\n\n// =============================================================================\n// Mode Toggle Component\n// =============================================================================\n\ninterface ModeToggleProps {\n mode: ThemeMode;\n onToggle: () => void;\n tokens: any;\n}\n\nconst ModeToggle = memo(function ModeToggle({ mode, onToggle, tokens }: ModeToggleProps) {\n const isDark = mode === 'dark';\n const tooltipText = isDark ? 'Switch to Light Mode' : 'Switch to Dark Mode';\n \n return (\n <Tooltip content={tooltipText} placement=\"bottom\">\n <button\n onClick={onToggle}\n aria-label={tooltipText}\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: 32,\n height: 32,\n padding: 0,\n border: 'none',\n borderRadius: tokens.borderRadius.md,\n backgroundColor: 'rgba(255,255,255,0.08)',\n color: tokens.colors.text.primary,\n cursor: 'pointer',\n transition: 'all 0.15s ease',\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.backgroundColor = 'rgba(255,255,255,0.15)';\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = 'rgba(255,255,255,0.08)';\n }}\n >\n <AstroIcon \n name={isDark ? 'brightness-3' : 'brightness-high'} \n size={16} \n color={tokens.colors.text.primary}\n />\n </button>\n </Tooltip>\n );\n});\n\n// =============================================================================\n// Compact Mode Toggle Component\n// =============================================================================\n\ninterface CompactToggleProps {\n isCompact: boolean;\n onToggle: () => void;\n tokens: any;\n}\n\nconst CompactToggle = memo(function CompactToggle({ isCompact, onToggle, tokens }: CompactToggleProps) {\n const tooltipText = isCompact ? 'Switch to Expanded View' : 'Switch to Compact View';\n \n return (\n <Tooltip content={tooltipText} placement=\"bottom\">\n <button\n onClick={onToggle}\n aria-label={tooltipText}\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: 32,\n height: 32,\n padding: 0,\n border: 'none',\n borderRadius: tokens.borderRadius.md,\n backgroundColor: isCompact ? 'rgba(139, 92, 246, 0.25)' : 'rgba(255,255,255,0.08)',\n color: isCompact ? '#a78bfa' : tokens.colors.text.primary,\n cursor: 'pointer',\n transition: 'all 0.15s ease',\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.backgroundColor = isCompact \n ? 'rgba(139, 92, 246, 0.35)' \n : 'rgba(255,255,255,0.15)';\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = isCompact \n ? 'rgba(139, 92, 246, 0.25)' \n : 'rgba(255,255,255,0.08)';\n }}\n >\n <AstroIcon \n name={isCompact ? 'view-comfy' : 'view-module'} \n size={16} \n color={isCompact ? '#a78bfa' : tokens.colors.text.primary}\n />\n </button>\n </Tooltip>\n );\n});\n\n// =============================================================================\n// Theme Selector Component\n// =============================================================================\n\ninterface ThemeSelectorProps {\n currentTheme: ThemeVariant;\n onSelect: (theme: ThemeVariant) => void;\n tokens: any;\n}\n\nconst ThemeSelector = memo(function ThemeSelector({ currentTheme, onSelect, tokens }: ThemeSelectorProps) {\n // WCAG AA: compute a safe background for selected theme buttons (white text)\n const safeAccentBg = useMemo(() => {\n const hex = tokens.colors.accent.primary.replace('#', '');\n if (hex.length < 6) return tokens.colors.accent.primary;\n const toLinear = (c: number) => c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);\n const r = parseInt(hex.slice(0, 2), 16) / 255;\n const g = parseInt(hex.slice(2, 4), 16) / 255;\n const b = parseInt(hex.slice(4, 6), 16) / 255;\n const L = 0.2126 * toLinear(r) + 0.7152 * toLinear(g) + 0.0722 * toLinear(b);\n if ((1.05 / (L + 0.05)) >= 4.5) return tokens.colors.accent.primary;\n const factor = 0.88;\n const dr = Math.min(255, Math.round(r * 255 * factor));\n const dg = Math.min(255, Math.round(g * 255 * factor));\n const db = Math.min(255, Math.round(b * 255 * factor));\n return `#${dr.toString(16).padStart(2, '0')}${dg.toString(16).padStart(2, '0')}${db.toString(16).padStart(2, '0')}`;\n }, [tokens.colors.accent.primary]);\n\n // Group themes for better organization\n const coreThemes = THEME_VARIANTS.filter(t => !t.key.startsWith('transparent'));\n const glassThemes = THEME_VARIANTS.filter(t => t.key.startsWith('transparent'));\n \n return (\n <div \n style={{ \n display: 'flex', \n alignItems: 'center', \n gap: 4,\n padding: 2,\n backgroundColor: 'rgba(255,255,255,0.05)',\n borderRadius: tokens.borderRadius.md,\n }}\n role=\"group\"\n aria-label=\"Theme selector\"\n >\n {/* Core themes */}\n {coreThemes.map(({ key, label, tooltip }) => (\n <Tooltip key={key} content={tooltip} placement=\"bottom\">\n <button\n onClick={() => onSelect(key)}\n aria-label={`Select ${label} theme`}\n style={{\n padding: '4px 10px',\n border: 'none',\n borderRadius: tokens.borderRadius.sm,\n backgroundColor: currentTheme === key \n ? safeAccentBg \n : 'transparent',\n color: currentTheme === key \n ? '#ffffff' \n : tokens.colors.text.secondary,\n fontSize: '0.6875rem', // 11px in rem\n fontWeight: currentTheme === key ? 500 : 400, // AstroUXDS: 500 (medium) not 600\n cursor: 'pointer',\n transition: 'all 0.15s ease',\n whiteSpace: 'nowrap',\n }}\n onMouseEnter={(e) => {\n if (currentTheme !== key) {\n e.currentTarget.style.backgroundColor = 'rgba(255,255,255,0.1)';\n }\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = currentTheme === key \n ? safeAccentBg \n : 'transparent';\n }}\n >\n {label}\n </button>\n </Tooltip>\n ))}\n \n {/* Separator */}\n <div style={{ width: 1, height: 16, backgroundColor: 'rgba(255,255,255,0.15)', margin: '0 2px' }} />\n \n {/* Glass themes */}\n {glassThemes.map(({ key, label, tooltip }) => (\n <Tooltip key={key} content={tooltip} placement=\"bottom\">\n <button\n onClick={() => onSelect(key)}\n aria-label={`Select ${label} theme`}\n style={{\n padding: '4px 10px',\n border: 'none',\n borderRadius: tokens.borderRadius.sm,\n backgroundColor: currentTheme === key \n ? safeAccentBg \n : 'transparent',\n color: currentTheme === key \n ? '#ffffff' \n : tokens.colors.text.secondary,\n fontSize: '0.6875rem', // 11px in rem\n fontWeight: currentTheme === key ? 500 : 400, // AstroUXDS: 500 (medium) not 600\n cursor: 'pointer',\n transition: 'all 0.15s ease',\n whiteSpace: 'nowrap',\n }}\n onMouseEnter={(e) => {\n if (currentTheme !== key) {\n e.currentTarget.style.backgroundColor = 'rgba(255,255,255,0.1)';\n }\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = currentTheme === key \n ? safeAccentBg \n : 'transparent';\n }}\n >\n {label}\n </button>\n </Tooltip>\n ))}\n </div>\n );\n});\n\n// =============================================================================\n// Accent Borders Toggle Component\n// =============================================================================\n\ninterface AccentBordersToggleProps {\n enabled: boolean;\n onToggle: () => void;\n tokens: any;\n}\n\nconst AccentBordersToggle = memo(function AccentBordersToggle({ enabled, onToggle, tokens }: AccentBordersToggleProps) {\n return (\n <Tooltip content={enabled ? 'Accent borders enabled - Click to disable' : 'Accent borders disabled - Click to enable'} placement=\"bottom\">\n <button\n onClick={onToggle}\n aria-label={enabled ? 'Disable accent borders' : 'Enable accent borders'}\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: 4,\n padding: '4px 8px',\n border: 'none',\n borderRadius: tokens.borderRadius.sm,\n backgroundColor: enabled \n ? `${tokens.colors.accent.primary}30`\n : 'rgba(255,255,255,0.05)',\n color: enabled \n ? safeAccentText(tokens.colors.accent.primary) \n : tokens.colors.text.tertiary,\n fontSize: '0.625rem', // 10px in rem\n fontWeight: 500,\n cursor: 'pointer',\n transition: 'all 0.15s ease',\n whiteSpace: 'nowrap',\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.backgroundColor = enabled \n ? `${tokens.colors.accent.primary}40`\n : 'rgba(255,255,255,0.1)';\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = enabled \n ? `${tokens.colors.accent.primary}30`\n : 'rgba(255,255,255,0.05)';\n }}\n >\n <span style={{ \n width: 8, \n height: 8, \n borderRadius: 2,\n border: `2px solid ${enabled ? tokens.colors.accent.primary : tokens.colors.text.tertiary}`,\n backgroundColor: 'transparent',\n }} />\n <span>Borders</span>\n </button>\n </Tooltip>\n );\n});\n\n// =============================================================================\n// Theme Controls Dropdown (Compact)\n// =============================================================================\n\ninterface ThemeControlsDropdownProps {\n currentTheme: ThemeVariant;\n mode: ThemeMode;\n onThemeSelect: (theme: ThemeVariant) => void;\n onModeToggle: () => void;\n accentBordersEnabled?: boolean;\n onAccentBordersToggle?: () => void;\n showAccentBorders?: boolean;\n tokens: any;\n}\n\nconst ThemeControlsDropdown = memo(function ThemeControlsDropdown({\n currentTheme,\n mode,\n onThemeSelect,\n onModeToggle,\n accentBordersEnabled = true,\n onAccentBordersToggle,\n showAccentBorders = false,\n tokens,\n}: ThemeControlsDropdownProps) {\n const [isOpen, setIsOpen] = useState(false);\n const dropdownRef = useRef<HTMLDivElement>(null);\n \n // Close on click outside\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {\n setIsOpen(false);\n }\n };\n \n if (isOpen) {\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }\n }, [isOpen]);\n \n // Get current theme label\n const currentLabel = THEME_VARIANTS.find(t => t.key === currentTheme)?.label || 'Theme';\n \n // Group themes\n const coreThemes = THEME_VARIANTS.filter(t => !t.key.startsWith('transparent'));\n const glassThemes = THEME_VARIANTS.filter(t => t.key.startsWith('transparent'));\n \n return (\n <div ref={dropdownRef} style={{ position: 'relative' }}>\n {/* Trigger Button */}\n <Tooltip content=\"Theme settings\" placement=\"bottom\">\n <button\n onClick={() => setIsOpen(!isOpen)}\n aria-label=\"Theme settings\"\n aria-expanded={isOpen}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 6,\n padding: '6px 10px',\n border: `1px solid ${tokens.colors.border.muted}`,\n borderRadius: tokens.borderRadius.md,\n backgroundColor: isOpen ? tokens.colors.background.elevated : 'transparent',\n color: tokens.colors.text.secondary,\n fontSize: '0.6875rem', // 11px in rem\n fontWeight: 500,\n cursor: 'pointer',\n transition: 'all 0.15s ease',\n }}\n onMouseEnter={(e) => {\n if (!isOpen) {\n e.currentTarget.style.backgroundColor = tokens.colors.background.elevated;\n }\n }}\n onMouseLeave={(e) => {\n if (!isOpen) {\n e.currentTarget.style.backgroundColor = 'transparent';\n }\n }}\n >\n <AstroIcon name=\"settings\" size={14} color={tokens.colors.text.secondary} />\n <span>{currentLabel}</span>\n <AstroIcon \n name={isOpen ? 'arrow-up' : 'arrow-down'} \n size={10} \n color={tokens.colors.text.tertiary} \n />\n </button>\n </Tooltip>\n \n {/* Dropdown Panel */}\n {isOpen && (\n <div\n style={{\n position: 'absolute',\n top: '100%',\n right: 0,\n marginTop: 4,\n minWidth: 220,\n padding: tokens.spacing.sm,\n backgroundColor: tokens.colors.background.surface,\n border: `1px solid ${tokens.colors.border.muted}`,\n borderRadius: tokens.borderRadius.lg,\n boxShadow: '0 8px 32px rgba(0,0,0,0.3)',\n zIndex: 1000,\n }}\n >\n {/* Theme Section */}\n <div style={{ marginBottom: tokens.spacing.sm }}>\n <div style={{ \n fontSize: '0.625rem', // 10px in rem \n color: tokens.colors.text.muted, \n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n marginBottom: 8,\n paddingLeft: 4,\n }}>\n Theme\n </div>\n \n {/* Core Themes */}\n <div style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n {coreThemes.map(({ key, label }) => (\n <button\n key={key}\n onClick={() => {\n onThemeSelect(key);\n setIsOpen(false);\n }}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n padding: '8px 12px',\n border: 'none',\n borderRadius: tokens.borderRadius.sm,\n backgroundColor: currentTheme === key \n ? `${tokens.colors.accent.primary}20`\n : 'transparent',\n color: currentTheme === key \n ? tokens.colors.accent.primary\n : tokens.colors.text.primary,\n fontSize: '0.75rem', // 12px in rem\n fontWeight: currentTheme === key ? 600 : 400,\n cursor: 'pointer',\n textAlign: 'left',\n transition: 'all 0.1s ease',\n }}\n onMouseEnter={(e) => {\n if (currentTheme !== key) {\n e.currentTarget.style.backgroundColor = tokens.colors.background.elevated;\n }\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = currentTheme === key \n ? `${tokens.colors.accent.primary}20`\n : 'transparent';\n }}\n >\n {currentTheme === key && (\n <AstroIcon name=\"check\" size={12} color={tokens.colors.accent.primary} />\n )}\n <span style={{ marginLeft: currentTheme === key ? 0 : 20 }}>{label}</span>\n </button>\n ))}\n </div>\n \n {/* Separator */}\n <div style={{ \n height: 1, \n backgroundColor: tokens.colors.border.muted, \n margin: `${tokens.spacing.xs} 0`,\n }} />\n \n {/* Glass Themes */}\n <div style={{ \n fontSize: '0.625rem', // 10px in rem \n color: tokens.colors.text.muted, \n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n marginBottom: 6,\n paddingLeft: 4,\n }}>\n Glass\n </div>\n <div style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n {glassThemes.map(({ key, label }) => (\n <button\n key={key}\n onClick={() => {\n onThemeSelect(key);\n setIsOpen(false);\n }}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n padding: '8px 12px',\n border: 'none',\n borderRadius: tokens.borderRadius.sm,\n backgroundColor: currentTheme === key \n ? `${tokens.colors.accent.primary}20`\n : 'transparent',\n color: currentTheme === key \n ? tokens.colors.accent.primary\n : tokens.colors.text.primary,\n fontSize: '0.75rem', // 12px in rem\n fontWeight: currentTheme === key ? 600 : 400,\n cursor: 'pointer',\n textAlign: 'left',\n transition: 'all 0.1s ease',\n }}\n onMouseEnter={(e) => {\n if (currentTheme !== key) {\n e.currentTarget.style.backgroundColor = tokens.colors.background.elevated;\n }\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = currentTheme === key \n ? `${tokens.colors.accent.primary}20`\n : 'transparent';\n }}\n >\n {currentTheme === key && (\n <AstroIcon name=\"check\" size={12} color={tokens.colors.accent.primary} />\n )}\n <span style={{ marginLeft: currentTheme === key ? 0 : 20 }}>{label}</span>\n </button>\n ))}\n </div>\n </div>\n \n {/* Separator */}\n <div style={{ \n height: 1, \n backgroundColor: tokens.colors.border.muted, \n margin: `${tokens.spacing.sm} 0`,\n }} />\n \n {/* Options Section */}\n <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>\n {/* Mode Toggle */}\n <button\n onClick={() => {\n onModeToggle();\n }}\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '8px 12px',\n border: 'none',\n borderRadius: tokens.borderRadius.sm,\n backgroundColor: 'transparent',\n color: tokens.colors.text.primary,\n fontSize: '0.75rem', // 12px in rem\n cursor: 'pointer',\n transition: 'all 0.1s ease',\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.backgroundColor = tokens.colors.background.elevated;\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = 'transparent';\n }}\n >\n <span style={{ display: 'flex', alignItems: 'center', gap: 8 }}>\n <AstroIcon \n name={mode === 'dark' ? 'brightness-high' : 'brightness-low'} \n size={14} \n color={tokens.colors.text.secondary} \n />\n {mode === 'dark' ? 'Dark Mode' : 'Light Mode'}\n </span>\n <div style={{\n width: 32,\n height: 18,\n borderRadius: 9,\n backgroundColor: mode === 'dark' ? tokens.colors.accent.primary : tokens.colors.border.default,\n padding: 2,\n transition: 'all 0.15s ease',\n }}>\n <div style={{\n width: 14,\n height: 14,\n borderRadius: '50%',\n backgroundColor: '#fff',\n transition: 'all 0.15s ease',\n transform: mode === 'dark' ? 'translateX(14px)' : 'translateX(0)',\n }} />\n </div>\n </button>\n \n {/* Accent Borders (only for glass themes) */}\n {showAccentBorders && onAccentBordersToggle && (\n <button\n onClick={onAccentBordersToggle}\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '8px 12px',\n border: 'none',\n borderRadius: tokens.borderRadius.sm,\n backgroundColor: 'transparent',\n color: tokens.colors.text.primary,\n fontSize: '0.75rem', // 12px in rem\n cursor: 'pointer',\n transition: 'all 0.1s ease',\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.backgroundColor = tokens.colors.background.elevated;\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = 'transparent';\n }}\n >\n <span style={{ display: 'flex', alignItems: 'center', gap: 8 }}>\n <span style={{ \n width: 14, \n height: 14, \n borderRadius: 3,\n border: `2px solid ${tokens.colors.text.secondary}`,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }}>\n {accentBordersEnabled && (\n <AstroIcon name=\"check\" size={10} color={tokens.colors.text.secondary} />\n )}\n </span>\n Accent Borders\n </span>\n </button>\n )}\n </div>\n </div>\n )}\n </div>\n );\n});\n\n// =============================================================================\n// AppBar Component\n// =============================================================================\n\n// =============================================================================\n// Section header for right slot (operator-style \"Data\" header)\n// =============================================================================\n\ninterface SectionHeaderProps {\n title: string;\n tokens: { typography: any; spacing: any; colors: any; borders: any };\n}\n\nconst SectionHeader = memo(function SectionHeader({ title, tokens }: SectionHeaderProps) {\n return (\n <div style={{ textAlign: 'center', marginBottom: tokens.spacing.xs }}>\n <span style={{\n fontSize: tokens.typography.fontSize.xxs,\n fontFamily: tokens.typography.fontFamily.primary,\n color: tokens.colors.interactive?.default ?? tokens.colors.text.secondary,\n fontWeight: tokens.typography.fontWeight.bold,\n }}>\n {title}\n </span>\n <div style={{\n width: '100%',\n height: tokens.borders.width.thin,\n background: tokens.colors.interactive?.default ?? tokens.colors.text.secondary,\n opacity: 0.5,\n marginTop: tokens.spacing.xs,\n }} />\n </div>\n );\n});\n\n// =============================================================================\n// AppBar Component\n// =============================================================================\n\nexport const AppBar = memo(function AppBar({\n appName = 'Zendir',\n appDomain,\n appVersion,\n branding,\n showThemeControls = true,\n showCompactToggle = true,\n showColorPicker = true,\n showThemeSelector = true,\n showModeOnly = false,\n compactThemeControls = false,\n defaultTheme,\n defaultMode,\n defaultCompactMode,\n leftSlot,\n centerSlot,\n rightSlot,\n rightSectionTitle,\n showBranding = true,\n centerGrow = false,\n variant = 'default',\n onThemeChange,\n onModeChange,\n onCompactModeChange,\n className = '',\n height = 52,\n sticky = true,\n}: AppBarProps): React.ReactElement {\n const { tokens, theme, mode, setTheme, setMode, toggleMode } = useTheme();\n const { isMobile } = useBreakpoint();\n const displaySettings = useDisplaySettingsOptional();\n \n const isTransparentTheme = theme === 'transparent' || theme === 'transparent-bold' || theme === 'transparent-minimal';\n \n // Get compact mode from context or default\n const compactMode = displaySettings?.compactMode ?? defaultCompactMode ?? false;\n \n // Apply defaults on first render if specified\n React.useEffect(() => {\n if (defaultTheme && theme !== defaultTheme) {\n setTheme(defaultTheme);\n onThemeChange?.(defaultTheme);\n }\n if (defaultMode && mode !== defaultMode) {\n setMode(defaultMode);\n onModeChange?.(defaultMode);\n }\n // Only run on mount\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n \n // Handle mode toggle\n const handleModeToggle = () => {\n toggleMode();\n onModeChange?.(mode === 'dark' ? 'light' : 'dark');\n };\n \n // Handle theme change\n const handleThemeChange = (newTheme: ThemeVariant) => {\n setTheme(newTheme);\n onThemeChange?.(newTheme);\n };\n \n // Handle compact mode toggle\n const handleCompactToggle = () => {\n if (displaySettings) {\n displaySettings.toggleCompactMode();\n onCompactModeChange?.(!compactMode);\n } else {\n onCompactModeChange?.(!compactMode);\n }\n };\n\n // Variant-driven layout (operator / mission-control style)\n const isDense = variant === 'dense';\n const effectiveHeight = height ?? (isDense ? 44 : 52);\n const paddingH = isDense\n ? (isMobile ? tokens.spacing.xs : tokens.spacing.sm)\n : (isMobile ? tokens.spacing.md : tokens.spacing.lg);\n const paddingV = isDense ? tokens.spacing.xs : 0;\n const gap = isDense ? (isMobile ? tokens.spacing.sm : tokens.spacing.md) : tokens.spacing.sm;\n const minHeight = isDense && isMobile ? 44 : undefined;\n \n // Render branding section\n const renderBranding = () => {\n // Custom logo node\n if (branding?.logo) {\n const content = branding.logo;\n if (branding.href) {\n return (\n <a href={branding.href} style={{ display: 'flex', alignItems: 'center', textDecoration: 'none' }}>\n {content}\n </a>\n );\n }\n return <>{content}</>;\n }\n \n // Logo image URL\n if (branding?.logoUrl) {\n const img = (\n <img\n src={branding.logoUrl}\n alt={branding.logoAlt || appName}\n style={{\n height: branding.logoHeight || 28,\n width: 'auto',\n objectFit: 'contain',\n }}\n />\n );\n \n if (branding.href) {\n return (\n <a href={branding.href} style={{ display: 'flex', alignItems: 'center', textDecoration: 'none' }}>\n {img}\n </a>\n );\n }\n return img;\n }\n \n // Text branding\n return (\n <div style={{ display: 'flex', alignItems: 'baseline', gap: tokens.spacing.sm }}>\n {appDomain && (\n <span style={{\n fontSize: tokens.typography.fontSize.sm,\n fontWeight: 400,\n color: tokens.colors.text.secondary,\n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n }}>\n {appDomain}\n </span>\n )}\n <span style={{\n fontSize: tokens.typography.fontSize.lg,\n fontWeight: 500, // AstroUXDS medium for app title\n color: tokens.colors.text.primary,\n }}>\n {appName}\n </span>\n {appVersion && (\n <span style={{\n fontSize: tokens.typography.fontSize.xs,\n color: tokens.colors.text.tertiary,\n }}>\n v{appVersion.replace(/^v/, '')}\n </span>\n )}\n </div>\n );\n };\n \n return (\n <header\n className={classNames('zendir-app-bar', className)}\n role=\"banner\"\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n height: effectiveHeight,\n padding: paddingV ? `${paddingV} ${paddingH}` : `0 ${paddingH}`,\n backgroundColor: isTransparentTheme \n ? 'rgba(0,0,0,0.4)' \n : tokens.colors.background.surface,\n ...(isTransparentTheme && { \n backdropFilter: 'blur(16px)', \n WebkitBackdropFilter: 'blur(16px)' as const \n }),\n borderBottom: tokens.borders.divider,\n color: tokens.colors.text.primary,\n fontFamily: tokens.typography.fontFamily.primary,\n gap,\n ...(minHeight && { minHeight }),\n ...(sticky && {\n position: 'sticky',\n top: 0,\n zIndex: 100,\n }),\n }}\n >\n {/* Left section: Branding (optional) + leftSlot */}\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: isDense ? gap : (isMobile ? tokens.spacing.md : tokens.spacing.lg),\n flex: '1 1 0',\n minWidth: 0,\n }}\n >\n {showBranding && renderBranding()}\n {leftSlot}\n </div>\n \n {/* Center section (optionally grows to fill and center content) */}\n {centerSlot && (\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n flex: centerGrow ? '1 1 0' : '0 0 auto',\n minWidth: 0,\n }}\n >\n {centerSlot}\n </div>\n )}\n \n {/* Right section: optional section title + rightSlot + theme controls + compact toggle */}\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'flex-end',\n gap: tokens.spacing.sm,\n flex: '1 1 0',\n minWidth: 0,\n }}\n >\n {rightSectionTitle != null && rightSectionTitle !== '' ? (\n <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', flexShrink: 0 }}>\n <SectionHeader title={rightSectionTitle} tokens={tokens} />\n {rightSlot}\n </div>\n ) : (\n rightSlot\n )}\n \n {/* Theme Controls Group */}\n {showThemeControls && (\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: isMobile ? 6 : 8,\n marginLeft: isMobile ? tokens.spacing.xs : tokens.spacing.sm,\n minWidth: 0,\n }}\n >\n {/* Compact Dropdown Mode */}\n {compactThemeControls ? (\n <ThemeControlsDropdown\n currentTheme={theme}\n mode={mode}\n onThemeSelect={handleThemeChange}\n onModeToggle={handleModeToggle}\n accentBordersEnabled={displaySettings?.accentBorders ?? true}\n onAccentBordersToggle={displaySettings?.toggleAccentBorders}\n showAccentBorders={isTransparentTheme && !!displaySettings}\n tokens={tokens}\n />\n ) : (\n <>\n {/* Theme Selector */}\n {showThemeSelector && !showModeOnly && (\n <ThemeSelector\n currentTheme={theme}\n onSelect={handleThemeChange}\n tokens={tokens}\n />\n )}\n \n {/* Accent Borders Toggle (only for transparent themes) */}\n {!showModeOnly && isTransparentTheme && displaySettings && (\n <AccentBordersToggle\n enabled={displaySettings.accentBorders}\n onToggle={displaySettings.toggleAccentBorders}\n tokens={tokens}\n />\n )}\n \n {/* Color Picker (only for transparent themes) */}\n {showColorPicker && !showModeOnly && isTransparentTheme && displaySettings && (\n <ColorPickerPanel\n accentColor={displaySettings.accentColor}\n glassTint={displaySettings.glassTint}\n colorTarget={displaySettings.colorTarget}\n onAccentChange={displaySettings.setAccentColor}\n onGlassTintChange={displaySettings.setGlassTint}\n onTargetChange={displaySettings.setColorTarget}\n onColorSelect={displaySettings.setColor}\n compact\n />\n )}\n \n {/* Dark/Light Mode Toggle */}\n <ModeToggle \n mode={mode}\n onToggle={handleModeToggle}\n tokens={tokens}\n />\n </>\n )}\n </div>\n )}\n \n {/* Compact Mode Toggle */}\n {showCompactToggle && (\n <CompactToggle\n isCompact={compactMode}\n onToggle={handleCompactToggle}\n tokens={tokens}\n />\n )}\n </div>\n </header>\n );\n});\n\nexport default AppBar;\n"],"names":["ModeToggle","CompactToggle","ThemeSelector","AccentBordersToggle","ThemeControlsDropdown","SectionHeader","AppBar"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AstroIcon.js","sources":["../../../src/react/core/AstroIcon.tsx"],"sourcesContent":["/**\n * @zendir/ui - AstroIcon Component\n * \n * Wrapper around official @astrouxds/react RuxIcon component.\n * Provides all 400+ official Astro UXD icons with consistent API.\n * \n * SECURITY: Uses dynamic import with proper error handling.\n * FLEXIBILITY: Works with or without @astrouxds/react installed.\n * \n * @see https://www.astrouxds.com/components/icon-library/\n * \n * @example\n * ```tsx\n * import { AstroIcon } from '@zendir/ui/react';\n * \n * <AstroIcon name=\"satellite\" />\n * <AstroIcon name=\"antenna\" size=\"large\" />\n * <AstroIcon name=\"thermal\" color=\"#ff0000\" />\n * ```\n */\n\nimport React, { memo, useState, useEffect, useRef } from 'react';\nimport { useTheme } from '../theme';\nimport { Icon, isValidIconName } from './Icon';\n\n/**\n * All official Astro UXD icon names\n * @see https://www.astrouxds.com/components/icon-library/\n */\nexport type AstroIconName =\n // === Astro Space/Mission Icons (Custom by Rocket Communications) ===\n | 'altitude'\n | 'antenna'\n | 'antenna-off'\n | 'antenna-receive'\n | 'antenna-transmit'\n | 'equipment'\n | 'hardware'\n | 'mission'\n | 'netcom'\n | 'payload'\n | 'processor'\n | 'processor-alt'\n | 'propulsion-power'\n | 'satellite-off'\n | 'satellite-receive'\n | 'satellite-transmit'\n | 'solar'\n | 'thermal'\n // === Actions ===\n | 'settings'\n | 'search'\n | 'home'\n | 'info'\n | 'help'\n | 'delete'\n | 'edit'\n | 'lock'\n | 'lock-open'\n | 'visibility'\n | 'visibility-off'\n | 'refresh'\n | 'sync'\n | 'download'\n | 'upload'\n | 'fullscreen'\n | 'fullscreen-exit'\n | 'zoom-in'\n | 'zoom-out'\n | 'schedule'\n | 'timeline'\n | 'today'\n | 'event'\n | 'bookmark'\n | 'favorite'\n | 'star'\n | 'check-circle'\n | 'done'\n | 'done-all'\n | 'print'\n | 'open-in-new'\n | 'launch'\n | 'power-settings-new'\n | 'history'\n | 'cached'\n | 'build'\n | 'code'\n | 'dns'\n | 'language'\n | 'input'\n | 'label'\n | 'list'\n | 'track-changes'\n | 'verified-user'\n | 'work'\n // === Alerts ===\n | 'add-alert'\n | 'error'\n | 'error-outline'\n | 'notification-important'\n | 'warning'\n // === AV ===\n | 'play-arrow'\n | 'pause'\n | 'stop'\n | 'skip-next'\n | 'skip-previous'\n | 'fast-forward'\n | 'fast-rewind'\n | 'replay'\n | 'loop'\n | 'shuffle'\n | 'volume-up'\n | 'volume-down'\n | 'volume-off'\n | 'volume-mute'\n | 'mic'\n | 'mic-off'\n | 'videocam'\n | 'videocam-off'\n | 'movie'\n | 'music-note'\n | 'equalizer'\n | 'library-music'\n | 'queue-music'\n | 'subscriptions'\n | 'fiber-new'\n | 'fiber-manual-record'\n | 'fiber-smart-record'\n // === Communication ===\n | 'call'\n | 'call-end'\n | 'call-made'\n | 'call-received'\n | 'call-missed'\n | 'call-split'\n | 'chat'\n | 'chat-bubble'\n | 'comment'\n | 'contacts'\n | 'dialer-sip'\n | 'email'\n | 'forum'\n | 'import-export'\n | 'live-help'\n | 'location-off'\n | 'location-on'\n | 'message'\n | 'phone'\n | 'phonelink-erase'\n | 'phonelink-lock'\n | 'phonelink-ring'\n | 'portable-wifi-off'\n | 'present-to-all'\n | 'ring-volume'\n | 'screen-share'\n | 'speaker-phone'\n | 'stay-primary-landscape'\n | 'stay-primary-portrait'\n | 'stop-screen-share'\n | 'swap-calls'\n | 'textsms'\n | 'voicemail'\n | 'vpn-key'\n // === Content ===\n | 'add'\n | 'add-box'\n | 'add-circle'\n | 'add-circle-outline'\n | 'archive'\n | 'backspace'\n | 'block'\n | 'clear'\n | 'content-copy'\n | 'content-cut'\n | 'content-paste'\n | 'create'\n | 'drafts'\n | 'filter-list'\n | 'flag'\n | 'forward'\n | 'gesture'\n | 'inbox'\n | 'link'\n | 'link-off'\n | 'low-priority'\n | 'mail'\n | 'markunread'\n | 'move-to-inbox'\n | 'next-week'\n | 'redo'\n | 'remove'\n | 'remove-circle'\n | 'remove-circle-outline'\n | 'reply'\n | 'reply-all'\n | 'report'\n | 'save'\n | 'select-all'\n | 'send'\n | 'sort'\n | 'text-format'\n | 'unarchive'\n | 'undo'\n | 'weekend'\n // === Device ===\n | 'access-alarm'\n | 'access-alarms'\n | 'access-time'\n | 'add-alarm'\n | 'airplanemode-active'\n | 'airplanemode-inactive'\n | 'battery-20'\n | 'battery-30'\n | 'battery-50'\n | 'battery-60'\n | 'battery-80'\n | 'battery-90'\n | 'battery-alert'\n | 'battery-charging-20'\n | 'battery-charging-30'\n | 'battery-charging-50'\n | 'battery-charging-60'\n | 'battery-charging-80'\n | 'battery-charging-90'\n | 'battery-charging-full'\n | 'battery-full'\n | 'battery-std'\n | 'battery-unknown'\n | 'bluetooth'\n | 'bluetooth-connected'\n | 'bluetooth-disabled'\n | 'bluetooth-searching'\n | 'brightness-auto'\n | 'brightness-high'\n | 'brightness-low'\n | 'brightness-medium'\n | 'data-usage'\n | 'developer-mode'\n | 'devices'\n | 'dvr'\n | 'gps-fixed'\n | 'gps-not-fixed'\n | 'gps-off'\n | 'graphic-eq'\n | 'location-disabled'\n | 'location-searching'\n | 'network-cell'\n | 'network-wifi'\n | 'nfc'\n | 'screen-lock-landscape'\n | 'screen-lock-portrait'\n | 'screen-lock-rotation'\n | 'screen-rotation'\n | 'sd-storage'\n | 'settings-system-daydream'\n | 'signal-cellular-0-bar'\n | 'signal-cellular-1-bar'\n | 'signal-cellular-2-bar'\n | 'signal-cellular-3-bar'\n | 'signal-cellular-4-bar'\n | 'signal-cellular-connected-no-internet-0-bar'\n | 'signal-cellular-connected-no-internet-1-bar'\n | 'signal-cellular-connected-no-internet-2-bar'\n | 'signal-cellular-connected-no-internet-3-bar'\n | 'signal-cellular-connected-no-internet-4-bar'\n | 'signal-cellular-no-sim'\n | 'signal-cellular-null'\n | 'signal-cellular-off'\n | 'signal-wifi-0-bar'\n | 'signal-wifi-1-bar'\n | 'signal-wifi-1-bar-lock'\n | 'signal-wifi-2-bar'\n | 'signal-wifi-2-bar-lock'\n | 'signal-wifi-3-bar'\n | 'signal-wifi-3-bar-lock'\n | 'signal-wifi-4-bar'\n | 'signal-wifi-4-bar-lock'\n | 'signal-wifi-off'\n | 'storage'\n | 'usb'\n | 'wallpaper'\n | 'widgets'\n | 'wifi-lock'\n | 'wifi-tethering'\n // === Editor ===\n | 'attach-file'\n | 'attach-money'\n | 'border-all'\n | 'border-bottom'\n | 'border-clear'\n | 'border-color'\n | 'border-horizontal'\n | 'border-inner'\n | 'border-left'\n | 'border-outer'\n | 'border-right'\n | 'border-style'\n | 'border-top'\n | 'border-vertical'\n | 'bubble-chart'\n | 'drag-handle'\n | 'format-align-center'\n | 'format-align-justify'\n | 'format-align-left'\n | 'format-align-right'\n | 'format-bold'\n | 'format-clear'\n | 'format-color-fill'\n | 'format-color-reset'\n | 'format-color-text'\n | 'format-indent-decrease'\n | 'format-indent-increase'\n | 'format-italic'\n | 'format-line-spacing'\n | 'format-list-bulleted'\n | 'format-list-numbered'\n | 'format-paint'\n | 'format-quote'\n | 'format-shapes'\n | 'format-size'\n | 'format-strikethrough'\n | 'format-textdirection-l-to-r'\n | 'format-textdirection-r-to-l'\n | 'format-underlined'\n | 'functions'\n | 'highlight'\n | 'insert-chart'\n | 'insert-comment'\n | 'insert-drive-file'\n | 'insert-emoticon'\n | 'insert-invitation'\n | 'insert-link'\n | 'insert-photo'\n | 'linear-scale'\n | 'merge-type'\n | 'mode-comment'\n | 'mode-edit'\n | 'monetization-on'\n | 'money-off'\n | 'multiline-chart'\n | 'pie-chart'\n | 'pie-chart-outlined'\n | 'publish'\n | 'short-text'\n | 'show-chart'\n | 'space-bar'\n | 'strikethrough-s'\n | 'table-chart'\n | 'text-fields'\n | 'title'\n | 'vertical-align-bottom'\n | 'vertical-align-center'\n | 'vertical-align-top'\n | 'wrap-text'\n // === File ===\n | 'attachment'\n | 'cloud'\n | 'cloud-circle'\n | 'cloud-done'\n | 'cloud-download'\n | 'cloud-off'\n | 'cloud-queue'\n | 'cloud-upload'\n | 'create-new-folder'\n | 'file-download'\n | 'file-upload'\n | 'folder'\n | 'folder-open'\n | 'folder-shared'\n // === Hardware ===\n | 'cast'\n | 'cast-connected'\n | 'computer'\n | 'desktop-mac'\n | 'desktop-windows'\n | 'developer-board'\n | 'device-hub'\n | 'devices-other'\n | 'dock'\n | 'gamepad'\n | 'headset'\n | 'headset-mic'\n | 'keyboard'\n | 'keyboard-arrow-down'\n | 'keyboard-arrow-left'\n | 'keyboard-arrow-right'\n | 'keyboard-arrow-up'\n | 'keyboard-backspace'\n | 'keyboard-capslock'\n | 'keyboard-hide'\n | 'keyboard-return'\n | 'keyboard-tab'\n | 'keyboard-voice'\n | 'laptop'\n | 'laptop-chromebook'\n | 'laptop-mac'\n | 'laptop-windows'\n | 'memory'\n | 'mouse'\n | 'phone-android'\n | 'phone-iphone'\n | 'phonelink'\n | 'phonelink-off'\n | 'power-input'\n | 'router'\n | 'scanner'\n | 'security'\n | 'sim-card'\n | 'smartphone'\n | 'speaker'\n | 'speaker-group'\n | 'tablet'\n | 'tablet-android'\n | 'tablet-mac'\n | 'toys'\n | 'tv'\n | 'videogame-asset'\n | 'watch'\n // === Image ===\n | 'add-a-photo'\n | 'add-to-photos'\n | 'adjust'\n | 'assistant'\n | 'assistant-photo'\n | 'audiotrack'\n | 'blur-circular'\n | 'blur-linear'\n | 'blur-off'\n | 'blur-on'\n | 'brightness-1'\n | 'brightness-2'\n | 'brightness-3'\n | 'brightness-4'\n | 'brightness-5'\n | 'brightness-6'\n | 'brightness-7'\n | 'broken-image'\n | 'brush'\n | 'burst-mode'\n | 'camera'\n | 'camera-alt'\n | 'camera-front'\n | 'camera-rear'\n | 'camera-roll'\n | 'center-focus-strong'\n | 'center-focus-weak'\n | 'collections'\n | 'collections-bookmark'\n | 'color-lens'\n | 'colorize'\n | 'compare'\n | 'control-point'\n | 'control-point-duplicate'\n | 'crop'\n | 'crop-16-9'\n | 'crop-3-2'\n | 'crop-5-4'\n | 'crop-7-5'\n | 'crop-din'\n | 'crop-free'\n | 'crop-landscape'\n | 'crop-original'\n | 'crop-portrait'\n | 'crop-rotate'\n | 'crop-square'\n | 'dehaze'\n | 'details'\n | 'edit'\n | 'exposure'\n | 'exposure-neg-1'\n | 'exposure-neg-2'\n | 'exposure-plus-1'\n | 'exposure-plus-2'\n | 'exposure-zero'\n | 'filter'\n | 'filter-1'\n | 'filter-2'\n | 'filter-3'\n | 'filter-4'\n | 'filter-5'\n | 'filter-6'\n | 'filter-7'\n | 'filter-8'\n | 'filter-9'\n | 'filter-9-plus'\n | 'filter-b-and-w'\n | 'filter-center-focus'\n | 'filter-drama'\n | 'filter-frames'\n | 'filter-hdr'\n | 'filter-none'\n | 'filter-tilt-shift'\n | 'filter-vintage'\n | 'flare'\n | 'flash-auto'\n | 'flash-off'\n | 'flash-on'\n | 'flip'\n | 'gradient'\n | 'grain'\n | 'grid-off'\n | 'grid-on'\n | 'hdr-off'\n | 'hdr-on'\n | 'hdr-strong'\n | 'hdr-weak'\n | 'healing'\n | 'image'\n | 'image-aspect-ratio'\n | 'iso'\n | 'landscape'\n | 'leak-add'\n | 'leak-remove'\n | 'lens'\n | 'linked-camera'\n | 'looks'\n | 'looks-3'\n | 'looks-4'\n | 'looks-5'\n | 'looks-6'\n | 'looks-one'\n | 'looks-two'\n | 'loupe'\n | 'monochrome-photos'\n | 'movie-creation'\n | 'movie-filter'\n | 'music-note'\n | 'nature'\n | 'nature-people'\n | 'navigate-before'\n | 'navigate-next'\n | 'palette'\n | 'panorama'\n | 'panorama-fish-eye'\n | 'panorama-horizontal'\n | 'panorama-vertical'\n | 'panorama-wide-angle'\n | 'photo'\n | 'photo-album'\n | 'photo-camera'\n | 'photo-filter'\n | 'photo-library'\n | 'photo-size-select-actual'\n | 'photo-size-select-large'\n | 'photo-size-select-small'\n | 'picture-as-pdf'\n | 'portrait'\n | 'remove-red-eye'\n | 'rotate-90-degrees-ccw'\n | 'rotate-left'\n | 'rotate-right'\n | 'slideshow'\n | 'straighten'\n | 'style'\n | 'switch-camera'\n | 'switch-video'\n | 'tag-faces'\n | 'texture'\n | 'timelapse'\n | 'timer'\n | 'timer-10'\n | 'timer-3'\n | 'timer-off'\n | 'tonality'\n | 'transform'\n | 'tune'\n | 'view-comfy'\n | 'view-compact'\n | 'vignette'\n | 'wb-auto'\n | 'wb-cloudy'\n | 'wb-incandescent'\n | 'wb-iridescent'\n | 'wb-sunny'\n // === Maps ===\n | 'add-location'\n | 'beenhere'\n | 'directions'\n | 'directions-bike'\n | 'directions-boat'\n | 'directions-bus'\n | 'directions-car'\n | 'directions-railway'\n | 'directions-run'\n | 'directions-subway'\n | 'directions-transit'\n | 'directions-walk'\n | 'edit-location'\n | 'ev-station'\n | 'explore'\n | 'flight'\n | 'hotel'\n | 'layers'\n | 'layers-clear'\n | 'local-activity'\n | 'local-airport'\n | 'local-atm'\n | 'local-bar'\n | 'local-cafe'\n | 'local-car-wash'\n | 'local-convenience-store'\n | 'local-dining'\n | 'local-drink'\n | 'local-florist'\n | 'local-gas-station'\n | 'local-grocery-store'\n | 'local-hospital'\n | 'local-hotel'\n | 'local-laundry-service'\n | 'local-library'\n | 'local-mall'\n | 'local-movies'\n | 'local-offer'\n | 'local-parking'\n | 'local-pharmacy'\n | 'local-phone'\n | 'local-pizza'\n | 'local-play'\n | 'local-post-office'\n | 'local-printshop'\n | 'local-see'\n | 'local-shipping'\n | 'local-taxi'\n | 'map'\n | 'my-location'\n | 'navigation'\n | 'near-me'\n | 'person-pin'\n | 'person-pin-circle'\n | 'pin-drop'\n | 'place'\n | 'rate-review'\n | 'restaurant'\n | 'restaurant-menu'\n | 'satellite'\n | 'store-mall-directory'\n | 'streetview'\n | 'subway'\n | 'terrain'\n | 'traffic'\n | 'train'\n | 'tram'\n | 'transfer-within-a-station'\n | 'zoom-out-map'\n // === Navigation ===\n | 'apps'\n | 'arrow-back'\n | 'arrow-downward'\n | 'arrow-drop-down'\n | 'arrow-drop-down-circle'\n | 'arrow-drop-up'\n | 'arrow-forward'\n | 'arrow-upward'\n | 'cancel'\n | 'check'\n | 'chevron-left'\n | 'chevron-right'\n | 'close'\n | 'expand-less'\n | 'expand-more'\n | 'first-page'\n | 'fullscreen'\n | 'fullscreen-exit'\n | 'last-page'\n | 'menu'\n | 'more-horiz'\n | 'more-vert'\n | 'refresh'\n | 'subdirectory-arrow-left'\n | 'subdirectory-arrow-right'\n | 'unfold-less'\n | 'unfold-more'\n // === Notification ===\n | 'adb'\n | 'airline-seat-flat'\n | 'airline-seat-flat-angled'\n | 'airline-seat-individual-suite'\n | 'airline-seat-legroom-extra'\n | 'airline-seat-legroom-normal'\n | 'airline-seat-legroom-reduced'\n | 'airline-seat-recline-extra'\n | 'airline-seat-recline-normal'\n | 'bluetooth-audio'\n | 'confirmation-number'\n | 'disc-full'\n | 'do-not-disturb'\n | 'do-not-disturb-alt'\n | 'do-not-disturb-off'\n | 'do-not-disturb-on'\n | 'drive-eta'\n | 'enhanced-encryption'\n | 'event-available'\n | 'event-busy'\n | 'event-note'\n | 'folder-special'\n | 'live-tv'\n | 'mms'\n | 'more'\n | 'network-check'\n | 'network-locked'\n | 'no-encryption'\n | 'ondemand-video'\n | 'personal-video'\n | 'phone-bluetooth-speaker'\n | 'phone-forwarded'\n | 'phone-in-talk'\n | 'phone-locked'\n | 'phone-missed'\n | 'phone-paused'\n | 'power'\n | 'power-off'\n | 'priority-high'\n | 'sd-card'\n | 'sim-card-alert'\n | 'sms'\n | 'sms-failed'\n | 'sync'\n | 'sync-disabled'\n | 'sync-problem'\n | 'system-update'\n | 'tap-and-play'\n | 'time-to-leave'\n | 'vibration'\n | 'voice-chat'\n | 'vpn-lock'\n | 'wc'\n | 'wifi'\n | 'wifi-off'\n // === Places ===\n | 'ac-unit'\n | 'airport-shuttle'\n | 'all-inclusive'\n | 'beach-access'\n | 'business-center'\n | 'casino'\n | 'child-care'\n | 'child-friendly'\n | 'fitness-center'\n | 'free-breakfast'\n | 'golf-course'\n | 'hot-tub'\n | 'kitchen'\n | 'pool'\n | 'room-service'\n | 'rv-hookup'\n | 'smoke-free'\n | 'smoking-rooms'\n | 'spa'\n // === Social ===\n | 'cake'\n | 'domain'\n | 'group'\n | 'group-add'\n | 'location-city'\n | 'mood'\n | 'mood-bad'\n | 'notifications'\n | 'notifications-active'\n | 'notifications-none'\n | 'notifications-off'\n | 'notifications-paused'\n | 'pages'\n | 'party-mode'\n | 'people'\n | 'people-outline'\n | 'person'\n | 'person-add'\n | 'person-outline'\n | 'plus-one'\n | 'poll'\n | 'public'\n | 'school'\n | 'sentiment-dissatisfied'\n | 'sentiment-neutral'\n | 'sentiment-satisfied'\n | 'sentiment-very-dissatisfied'\n | 'sentiment-very-satisfied'\n | 'share'\n | 'whatshot'\n // === Toggle ===\n | 'check-box'\n | 'check-box-outline-blank'\n | 'indeterminate-check-box'\n | 'radio-button-checked'\n | 'radio-button-unchecked'\n | 'star'\n | 'star-border'\n | 'star-half'\n // === Space Operations Aliases (mapped to existing icons) ===\n // Orbit & Trajectory\n | 'orbit' | 'trajectory' | 'orbital'\n // Ground Operations\n | 'ground-station' | 'ground-control' | 'dish' | 'tracking-station'\n // Communication\n | 'uplink' | 'downlink' | 'command' | 'telemetry' | 'comms' | 'rf' | 'link' | 'signal' | 'data-link'\n // Satellite States\n | 'satellite' | 'spacecraft' | 'vehicle' | 'sat' | 'sc'\n // Power Systems\n | 'power' | 'battery' | 'energy' | 'solar-array' | 'solar-panel' | 'photovoltaic'\n // Propulsion\n | 'thruster' | 'engine' | 'burn' | 'maneuver' | 'delta-v' | 'rcs'\n // Thermal\n | 'temperature' | 'heat' | 'cooling' | 'heater'\n // Attitude & Control\n | 'attitude' | 'pointing' | 'orientation' | 'adcs' | 'gyro' | 'gyroscope'\n // Navigation\n | 'gps' | 'gnss' | 'position' | 'location' | 'ephemeris'\n // Status & Health\n | 'health' | 'status' | 'nominal' | 'anomaly' | 'fault' | 'safe-mode' | 'emergency' | 'critical' | 'caution'\n // Time & Scheduling\n | 'pass' | 'contact' | 'access' | 'window' | 'aos' | 'los' | 'eclipse' | 'sunlight' | 'shadow'\n // Subsystems\n | 'subsystem' | 'computer' | 'obc' | 'cdh' | 'flight-computer' | 'sensor' | 'instrument'\n // Launch & Deployment\n | 'launch' | 'rocket' | 'liftoff' | 'deployment' | 'deploy' | 'separation'\n // Operations\n | 'ops' | 'operations' | 'control' | 'automation' | 'sequence' | 'procedure'\n // Data & Storage\n | 'storage' | 'memory' | 'recorder' | 'playback' | 'record'\n // Monitoring\n | 'monitor' | 'dashboard' | 'console' | 'display'\n // Crew\n | 'astronaut' | 'crew' | 'eva'\n // Catch-all for any other icon (flexibility)\n | (string & {});\n\nexport type AstroIconSize = 'extra-small' | 'small' | 'normal' | 'large';\n\nexport interface AstroIconProps {\n /** Icon name from Astro UXD library */\n name: AstroIconName;\n /** Icon size preset or custom pixel value */\n size?: AstroIconSize | number;\n /** Custom color (hex, rgb, or CSS color) */\n color?: string;\n /** Accessibility label (required for screen readers) */\n label?: string;\n /** Additional CSS class */\n className?: string;\n /** Additional inline styles */\n style?: React.CSSProperties;\n /** Click handler */\n onClick?: () => void;\n /** Test ID for testing */\n 'data-testid'?: string;\n}\n\n// Size mapping for fallback mode\nconst SIZE_MAP: Record<AstroIconSize, number> = {\n 'extra-small': 16,\n 'small': 24,\n 'normal': 32,\n 'large': 48,\n};\n\n/**\n * Space Operations Icon Aliases\n * Maps common space terminology to existing Astro icons.\n * Provides better developer experience with intuitive naming.\n */\nconst ICON_ALIASES: Record<string, string> = {\n // === Orbit & Trajectory ===\n 'orbit': '360', // Orbital path\n 'trajectory': '360', // Flight path\n 'orbital': '360', // Orbital mechanics\n \n // === Ground Operations ===\n 'ground-station': 'antenna', // Ground station\n 'ground-control': 'antenna', // Mission control\n 'dish': 'antenna', // Antenna dish\n 'tracking-station': 'antenna', // Tracking facility\n \n // === Communication ===\n 'uplink': 'antenna-transmit', // Command upload\n 'downlink': 'antenna-receive', // Data download\n 'command': 'antenna-transmit', // Send command\n 'telemetry': 'antenna-receive', // Receive telemetry\n 'comms': 'netcom', // Communications\n 'rf': 'antenna', // Radio frequency\n 'link': 'netcom', // Communication link\n 'signal': 'antenna-transmit', // Signal transmission\n 'data-link': 'netcom', // Data link\n \n // === Satellite States ===\n 'satellite': 'satellite-transmit', // Default satellite (active)\n 'spacecraft': 'satellite-transmit', // Spacecraft\n 'vehicle': 'satellite-transmit', // Space vehicle\n 'sat': 'satellite-transmit', // Abbreviation\n 'sc': 'satellite-transmit', // Spacecraft abbreviation\n \n // === Power Systems ===\n 'power': 'propulsion-power', // Power system\n 'battery': 'battery-full', // Battery status\n 'energy': 'propulsion-power', // Energy management\n 'solar-array': 'solar', // Solar arrays\n 'solar-panel': 'solar', // Solar panels\n 'photovoltaic': 'solar', // PV system\n \n // === Propulsion ===\n 'thruster': 'propulsion-power', // Thruster\n 'engine': 'propulsion-power', // Engine\n 'burn': 'propulsion-power', // Burn/maneuver\n 'maneuver': 'propulsion-power', // Orbital maneuver\n 'delta-v': 'propulsion-power', // Delta-V\n 'rcs': 'propulsion-power', // Reaction control system\n \n // === Thermal ===\n 'temperature': 'thermal', // Temperature\n 'heat': 'thermal', // Heat management\n 'cooling': 'thermal', // Cooling system\n 'heater': 'thermal', // Heater\n \n // === Attitude & Control ===\n 'attitude': 'explore', // Attitude control\n 'pointing': 'explore', // Pointing direction\n 'orientation': 'explore', // Spacecraft orientation\n 'adcs': 'explore', // Attitude determination & control\n 'gyro': 'explore', // Gyroscope\n 'gyroscope': 'explore', // Gyroscope\n \n // === Navigation ===\n 'gps': 'gps-fixed', // GPS/GNSS\n 'gnss': 'gps-fixed', // Global navigation\n 'position': 'gps-fixed', // Position\n 'location': 'place', // Location\n 'ephemeris': 'timeline', // Ephemeris data\n \n // === Status & Health ===\n 'health': 'favorite', // System health\n 'status': 'info', // Status indicator\n 'nominal': 'check-circle', // Nominal/OK\n 'anomaly': 'error', // Anomaly detected\n 'fault': 'error', // Fault condition\n 'safe-mode': 'warning', // Safe mode\n 'emergency': 'warning', // Emergency\n 'critical': 'error', // Critical status\n 'caution': 'warning', // Caution\n \n // === Time & Scheduling ===\n 'pass': 'schedule', // Satellite pass\n 'contact': 'schedule', // Ground contact\n 'access': 'schedule', // Access window\n 'window': 'schedule', // Time window\n 'aos': 'schedule', // Acquisition of signal\n 'los': 'schedule', // Loss of signal\n 'eclipse': 'brightness-3', // Eclipse period\n 'sunlight': 'wb-sunny', // In sunlight\n 'shadow': 'brightness-3', // In shadow\n \n // === Subsystems ===\n 'subsystem': 'developer-board', // Generic subsystem\n 'computer': 'memory', // Onboard computer\n 'obc': 'memory', // Onboard computer\n 'cdh': 'memory', // Command & data handling\n 'flight-computer': 'memory', // Flight computer\n 'sensor': 'settings-input-component', // Sensor\n 'instrument': 'settings-input-component', // Instrument\n \n // === Launch & Deployment ===\n 'launch': 'flight-takeoff', // Launch\n 'rocket': 'flight-takeoff', // Rocket\n 'liftoff': 'flight-takeoff', // Liftoff\n 'deployment': 'open-in-new', // Deployment\n 'deploy': 'open-in-new', // Deploy\n 'separation': 'call-split', // Stage separation\n \n // === Operations ===\n 'ops': 'build', // Operations\n 'operations': 'build', // Operations\n 'control': 'tune', // Control\n 'automation': 'autorenew', // Automation\n 'sequence': 'playlist-play', // Command sequence\n 'procedure': 'list', // Procedure\n \n // === Data & Storage ===\n 'storage': 'sd-storage', // Data storage\n 'memory': 'memory', // Memory\n 'recorder': 'fiber-dvr', // Data recorder\n 'playback': 'play-arrow', // Playback\n 'record': 'fiber-manual-record', // Recording\n \n // === Monitoring ===\n 'monitor': 'desktop-mac', // Monitoring\n 'dashboard': 'dashboard', // Dashboard\n 'console': 'desktop-windows', // Console\n 'display': 'tv', // Display\n \n // === Crew (if applicable) ===\n 'astronaut': 'person', // Astronaut\n 'crew': 'group', // Crew\n 'eva': 'directions-walk', // Extra-vehicular activity\n};\n\n/**\n * Resolve icon alias to actual Astro icon name\n */\nfunction resolveIconAlias(name: string): string {\n return ICON_ALIASES[name] || name;\n}\n\n/**\n * Get all available icon aliases\n */\nexport function getIconAliases(): Record<string, string> {\n return { ...ICON_ALIASES };\n}\n\n/**\n * Check if an icon name is an alias\n */\nexport function isIconAlias(name: string): boolean {\n return name in ICON_ALIASES;\n}\n\n/**\n * All available Astro UXD icon names (official icons, not aliases)\n * This is the comprehensive list from the Astro UXD icon library\n */\nexport const ASTRO_ICON_NAMES: readonly string[] = [\n // === Astro Space/Mission Icons (Custom by Rocket Communications) ===\n 'altitude', 'antenna', 'antenna-off', 'antenna-receive', 'antenna-transmit',\n 'equipment', 'hardware', 'mission', 'netcom', 'payload',\n 'processor', 'processor-alt', 'propulsion-power', 'satellite-off',\n 'satellite-receive', 'satellite-transmit', 'solar', 'thermal',\n // === Actions ===\n 'settings', 'search', 'home', 'info', 'help', 'delete', 'edit',\n 'lock', 'lock-open', 'visibility', 'visibility-off', 'refresh', 'sync',\n 'download', 'upload', 'fullscreen', 'fullscreen-exit', 'zoom-in', 'zoom-out',\n 'schedule', 'timeline', 'today', 'event', 'bookmark', 'favorite', 'star',\n 'check-circle', 'done', 'done-all', 'print', 'open-in-new', 'launch',\n 'power-settings-new', 'history', 'cached', 'build', 'code', 'dns',\n 'language', 'input', 'label', 'list', 'track-changes', 'verified-user', 'work',\n // === Alerts ===\n 'add-alert', 'error', 'error-outline', 'notification-important', 'warning',\n // === AV ===\n 'play-arrow', 'pause', 'stop', 'skip-next', 'skip-previous',\n 'fast-forward', 'fast-rewind', 'replay', 'loop', 'shuffle',\n 'volume-up', 'volume-down', 'volume-off', 'volume-mute',\n 'mic', 'mic-off', 'videocam', 'videocam-off', 'movie',\n 'music-note', 'equalizer', 'library-music', 'queue-music',\n // === Communication ===\n 'call', 'call-end', 'chat', 'chat-bubble', 'chat-bubble-outline',\n 'comment', 'contacts', 'email', 'mail-outline', 'message',\n 'phone', 'voicemail', 'vpn-key', 'forum', 'live-help',\n 'location-on', 'location-off', 'business', 'import-contacts',\n // === Content ===\n 'add', 'remove', 'add-circle', 'add-circle-outline', 'remove-circle',\n 'remove-circle-outline', 'clear', 'content-copy', 'content-cut', 'content-paste',\n 'create', 'flag', 'forward', 'reply', 'reply-all',\n 'save', 'save-alt', 'select-all', 'send', 'undo', 'redo',\n 'filter-list', 'sort', 'drafts', 'link', 'link-off',\n // === Device ===\n 'battery-full', 'battery-charging-full', 'battery-alert', 'battery-unknown',\n 'bluetooth', 'bluetooth-disabled', 'bluetooth-connected', 'bluetooth-searching',\n 'brightness-1', 'brightness-2', 'brightness-3', 'brightness-4',\n 'brightness-5', 'brightness-6', 'brightness-7', 'brightness-auto',\n 'brightness-high', 'brightness-low', 'brightness-medium',\n 'gps-fixed', 'gps-not-fixed', 'gps-off', 'graphic-eq',\n 'network-cell', 'network-wifi', 'nfc', 'screen-rotation',\n 'sd-storage', 'settings-system-daydream', 'signal-cellular-4-bar',\n 'signal-cellular-connected-no-internet-4-bar', 'signal-cellular-no-sim',\n 'signal-cellular-null', 'signal-cellular-off', 'signal-wifi-4-bar',\n 'signal-wifi-4-bar-lock', 'signal-wifi-off', 'storage',\n 'usb', 'wifi', 'wifi-lock', 'wifi-off', 'wifi-tethering',\n // === Editor ===\n 'attach-file', 'attach-money', 'border-all', 'border-bottom',\n 'border-clear', 'border-color', 'border-horizontal', 'border-inner',\n 'border-left', 'border-outer', 'border-right', 'border-style',\n 'border-top', 'border-vertical', 'format-align-center', 'format-align-justify',\n 'format-align-left', 'format-align-right', 'format-bold', 'format-clear',\n 'format-color-fill', 'format-color-reset', 'format-color-text',\n 'format-indent-decrease', 'format-indent-increase', 'format-italic',\n 'format-line-spacing', 'format-list-bulleted', 'format-list-numbered',\n 'format-paint', 'format-quote', 'format-size', 'format-strikethrough',\n 'format-textdirection-l-to-r', 'format-textdirection-r-to-l',\n 'format-underlined', 'functions', 'insert-chart', 'insert-comment',\n 'insert-drive-file', 'insert-emoticon', 'insert-invitation', 'insert-link',\n 'insert-photo', 'merge-type', 'mode-comment', 'mode-edit',\n 'publish', 'short-text', 'space-bar', 'strikethrough-s',\n 'text-fields', 'title', 'vertical-align-bottom', 'vertical-align-center',\n 'vertical-align-top', 'wrap-text',\n // === File ===\n 'attachment', 'cloud', 'cloud-circle', 'cloud-done', 'cloud-download',\n 'cloud-off', 'cloud-queue', 'cloud-upload', 'create-new-folder',\n 'file-download', 'file-upload', 'folder', 'folder-open', 'folder-shared',\n // === Hardware ===\n 'cast', 'cast-connected', 'computer', 'desktop-mac', 'desktop-windows',\n 'developer-board', 'device-hub', 'devices-other', 'dock', 'gamepad',\n 'headset', 'headset-mic', 'keyboard', 'keyboard-arrow-down',\n 'keyboard-arrow-left', 'keyboard-arrow-right', 'keyboard-arrow-up',\n 'keyboard-backspace', 'keyboard-capslock', 'keyboard-hide',\n 'keyboard-return', 'keyboard-tab', 'keyboard-voice', 'laptop',\n 'laptop-chromebook', 'laptop-mac', 'laptop-windows', 'memory',\n 'mouse', 'phone-android', 'phone-iphone', 'phonelink', 'phonelink-off',\n 'power-input', 'router', 'scanner', 'security', 'sim-card',\n 'smartphone', 'speaker', 'speaker-group', 'tablet', 'tablet-android',\n 'tablet-mac', 'toys', 'tv', 'watch',\n // === Image ===\n 'add-a-photo', 'add-photo-alternate', 'add-to-photos', 'adjust',\n 'brightness-1', 'broken-image', 'brush', 'burst-mode', 'camera',\n 'camera-alt', 'camera-front', 'camera-rear', 'camera-roll',\n 'center-focus-strong', 'center-focus-weak', 'collections',\n 'collections-bookmark', 'color-lens', 'colorize', 'compare',\n 'control-point', 'control-point-duplicate', 'crop', 'crop-16-9',\n 'crop-3-2', 'crop-5-4', 'crop-7-5', 'crop-din', 'crop-free',\n 'crop-landscape', 'crop-original', 'crop-portrait', 'crop-rotate',\n 'crop-square', 'dehaze', 'details', 'edit', 'exposure',\n 'exposure-neg-1', 'exposure-neg-2', 'exposure-plus-1', 'exposure-plus-2',\n 'exposure-zero', 'filter', 'filter-1', 'filter-2', 'filter-3',\n 'filter-4', 'filter-5', 'filter-6', 'filter-7', 'filter-8', 'filter-9',\n 'filter-9-plus', 'filter-b-and-w', 'filter-center-focus', 'filter-drama',\n 'filter-frames', 'filter-hdr', 'filter-none', 'filter-tilt-shift',\n 'filter-vintage', 'flare', 'flash-auto', 'flash-off', 'flash-on',\n 'flip', 'gradient', 'grain', 'grid-off', 'grid-on', 'hdr-off',\n 'hdr-on', 'hdr-strong', 'hdr-weak', 'healing', 'image',\n 'image-aspect-ratio', 'image-search', 'iso', 'landscape', 'leak-add',\n 'leak-remove', 'lens', 'linked-camera', 'looks', 'looks-3',\n 'looks-4', 'looks-5', 'looks-6', 'looks-one', 'looks-two', 'loupe',\n 'monochrome-photos', 'movie-creation', 'movie-filter', 'music-off',\n 'music-on', 'navigate-before', 'navigate-next', 'nature', 'nature-people',\n 'palette', 'panorama', 'panorama-fish-eye', 'panorama-horizontal',\n 'panorama-vertical', 'panorama-wide-angle', 'photo', 'photo-album',\n 'photo-camera', 'photo-filter', 'photo-library', 'photo-size-select-actual',\n 'photo-size-select-large', 'photo-size-select-small', 'picture-as-pdf',\n 'portrait', 'remove-red-eye', 'rotate-90-degrees-ccw', 'rotate-left',\n 'rotate-right', 'slideshow', 'straighten', 'style', 'switch-camera',\n 'switch-video', 'tag-faces', 'texture', 'timelapse', 'timer',\n 'timer-10', 'timer-3', 'timer-off', 'tonality', 'transform', 'tune',\n 'view-comfy', 'view-compact', 'vignette', 'wb-auto', 'wb-cloudy',\n 'wb-incandescent', 'wb-iridescent', 'wb-sunny',\n // === Maps ===\n 'add-location', 'beenhere', 'directions', 'directions-bike',\n 'directions-boat', 'directions-bus', 'directions-car',\n 'directions-railway', 'directions-run', 'directions-subway',\n 'directions-transit', 'directions-walk', 'edit-location', 'ev-station',\n 'flight', 'hotel', 'layers', 'layers-clear', 'local-activity',\n 'local-airport', 'local-atm', 'local-bar', 'local-cafe', 'local-car-wash',\n 'local-convenience-store', 'local-dining', 'local-drink', 'local-florist',\n 'local-gas-station', 'local-grocery-store', 'local-hospital',\n 'local-hotel', 'local-laundry-service', 'local-library', 'local-mall',\n 'local-movies', 'local-offer', 'local-parking', 'local-pharmacy',\n 'local-phone', 'local-pizza', 'local-play', 'local-post-office',\n 'local-printshop', 'local-see', 'local-shipping', 'local-taxi', 'map',\n 'my-location', 'navigation', 'near-me', 'person-pin', 'person-pin-circle',\n 'pin-drop', 'place', 'rate-review', 'restaurant', 'restaurant-menu',\n 'satellite', 'store-mall-directory', 'streetview', 'subway', 'terrain',\n 'traffic', 'train', 'tram', 'transfer-within-a-station', 'zoom-out-map',\n // === Navigation ===\n 'apps', 'arrow-back', 'arrow-downward', 'arrow-drop-down',\n 'arrow-drop-down-circle', 'arrow-drop-up', 'arrow-forward', 'arrow-upward',\n 'cancel', 'check', 'chevron-left', 'chevron-right', 'close',\n 'expand-less', 'expand-more', 'first-page', 'last-page', 'menu',\n 'more-horiz', 'more-vert', 'subdirectory-arrow-left',\n 'subdirectory-arrow-right', 'unfold-less', 'unfold-more',\n // === Notification ===\n 'adb', 'airline-seat-flat', 'airline-seat-flat-angled',\n 'airline-seat-individual-suite', 'airline-seat-legroom-extra',\n 'airline-seat-legroom-normal', 'airline-seat-legroom-reduced',\n 'airline-seat-recline-extra', 'airline-seat-recline-normal',\n 'bluetooth-audio', 'confirmation-number', 'disc-full', 'do-not-disturb',\n 'do-not-disturb-alt', 'do-not-disturb-off', 'do-not-disturb-on',\n 'drive-eta', 'enhanced-encryption', 'event-available', 'event-busy',\n 'event-note', 'folder-special', 'live-tv', 'mms', 'more',\n 'network-check', 'network-locked', 'no-encryption', 'ondemand-video',\n 'personal-video', 'phone-bluetooth-speaker', 'phone-callback',\n 'phone-forwarded', 'phone-in-talk', 'phone-locked', 'phone-missed',\n 'phone-paused', 'power', 'power-off', 'priority-high', 'sd-card',\n 'sim-card-alert', 'sms', 'sms-failed', 'sync-disabled', 'sync-problem',\n 'system-update', 'tap-and-play', 'time-to-leave', 'vibration',\n 'voice-chat', 'vpn-lock', 'wc', 'wifi', 'notifications', 'notifications-active',\n 'notifications-none', 'notifications-off', 'notifications-paused',\n // === Places ===\n 'ac-unit', 'airport-shuttle', 'all-inclusive', 'beach-access',\n 'business-center', 'casino', 'child-care', 'child-friendly',\n 'fitness-center', 'free-breakfast', 'golf-course', 'hot-tub',\n 'kitchen', 'meeting-room', 'no-meeting-room', 'pool', 'room-service',\n 'rv-hookup', 'smoke-free', 'smoking-rooms', 'spa',\n // === Social ===\n 'cake', 'domain', 'group', 'group-add', 'location-city', 'mood',\n 'mood-bad', 'notifications', 'pages', 'party-mode', 'people',\n 'people-outline', 'person', 'person-add', 'person-outline',\n 'plus-one', 'poll', 'public', 'school', 'sentiment-dissatisfied',\n 'sentiment-neutral', 'sentiment-satisfied', 'sentiment-very-dissatisfied',\n 'sentiment-very-satisfied', 'share', 'thumb-down', 'thumb-up',\n 'whatshot',\n // === Toggle ===\n 'check-box', 'check-box-outline-blank', 'indeterminate-check-box',\n 'radio-button-checked', 'radio-button-unchecked', 'star', 'star-border', 'star-half',\n // === Additional common icons ===\n '360', 'autorenew', 'call-split', 'explore', 'extension', 'face',\n 'feedback', 'fiber-dvr', 'fiber-manual-record', 'fiber-new', 'fiber-pin',\n 'fiber-smart-record', 'get-app', 'grade', 'highlight-off', 'http',\n 'https', 'important-devices', 'lightbulb', 'lightbulb-outline',\n 'perm-camera-mic', 'perm-contact-calendar', 'perm-data-setting',\n 'perm-device-information', 'perm-identity', 'perm-media', 'perm-phone-msg',\n 'perm-scan-wifi', 'pets', 'playlist-add', 'playlist-add-check', 'playlist-play',\n 'pregnant-woman', 'query-builder', 'question-answer', 'receipt', 'record-voice-over',\n 'redeem', 'report-problem', 'restore', 'restore-page', 'room', 'rounded-corner',\n 'rowing', 'settings-applications', 'settings-backup-restore', 'settings-bluetooth',\n 'settings-brightness', 'settings-cell', 'settings-ethernet', 'settings-input-antenna',\n 'settings-input-component', 'settings-input-composite', 'settings-input-hdmi',\n 'settings-input-svideo', 'settings-overscan', 'settings-phone', 'settings-power',\n 'settings-remote', 'settings-voice', 'shop', 'shop-two', 'shopping-basket',\n 'shopping-cart', 'speaker-notes', 'speaker-notes-off', 'spellcheck', 'stars',\n 'subject', 'supervisor-account', 'swap-horiz', 'swap-vert', 'swap-vertical-circle',\n 'system-update-alt', 'tab', 'tab-unselected', 'theaters', 'thumb-down',\n 'thumb-up', 'thumbs-up-down', 'toc', 'toll', 'touch-app', 'translate',\n 'trending-down', 'trending-flat', 'trending-up', 'turned-in', 'turned-in-not',\n 'update', 'view-agenda', 'view-array', 'view-carousel', 'view-column',\n 'view-day', 'view-headline', 'view-list', 'view-module', 'view-quilt',\n 'view-stream', 'view-week', 'visibility', 'visibility-off', 'watch-later',\n 'youtube-searched-for', 'aspect-ratio', 'assignment', 'assignment-ind',\n 'assignment-late', 'assignment-return', 'assignment-returned', 'assignment-turned-in',\n 'backup', 'book', 'flight-takeoff', 'flight-land',\n] as const;\n\n/**\n * Get all available Astro icon names\n */\nexport function getAstroIconNames(): readonly string[] {\n return ASTRO_ICON_NAMES;\n}\n\n/**\n * Get all available icon names including aliases\n */\nexport function getAllIconNames(): { icons: readonly string[]; aliases: Record<string, string> } {\n return {\n icons: ASTRO_ICON_NAMES,\n aliases: ICON_ALIASES,\n };\n}\n\n// Global state for RuxIcon loading with subscription pattern\ntype LoadState = 'pending' | 'loading' | 'loaded' | 'failed';\nlet loadState: LoadState = 'pending';\nlet RuxIconComponent: React.ComponentType<any> | null = null;\nconst subscribers = new Set<() => void>();\n\nfunction subscribe(callback: () => void) {\n subscribers.add(callback);\n return () => subscribers.delete(callback);\n}\n\nfunction notifySubscribers() {\n subscribers.forEach(cb => cb());\n}\n\n/**\n * Safely load RuxIcon from @astrouxds/react\n * Returns null if not available\n */\nasync function loadRuxIcon(): Promise<React.ComponentType<any> | null> {\n if (loadState === 'loaded') {\n return RuxIconComponent;\n }\n \n if (loadState === 'failed') {\n return null;\n }\n \n if (loadState === 'loading') {\n // Wait for the existing load to complete\n return new Promise(resolve => {\n const unsub = subscribe(() => {\n unsub();\n resolve(RuxIconComponent);\n });\n });\n }\n \n loadState = 'loading';\n \n try {\n // Dynamic import for tree-shaking and optional dependency\n const astroModule = await import('@astrouxds/react');\n RuxIconComponent = astroModule.RuxIcon;\n loadState = 'loaded';\n notifySubscribers();\n return RuxIconComponent;\n } catch (err) {\n loadState = 'failed';\n notifySubscribers();\n return null;\n }\n}\n\n/**\n * Fallback icon component when @astrouxds/react is not installed.\n * Uses built-in Icon SVGs when the name matches, otherwise shows a generic placeholder.\n */\nfunction FallbackIcon({\n name,\n size,\n color,\n label,\n className,\n style,\n onClick,\n 'data-testid': testId,\n}: AstroIconProps) {\n const { tokens } = useTheme();\n const actualSize = typeof size === 'number' ? size : SIZE_MAP[size || 'normal'];\n\n if (isValidIconName(name)) {\n return (\n <Icon\n name={name}\n size={actualSize}\n color={color}\n className={className}\n aria-label={label || name}\n onClick={onClick}\n style={style}\n />\n );\n }\n\n return (\n <span\n role=\"img\"\n aria-label={label || name}\n className={className}\n data-testid={testId}\n onClick={onClick}\n tabIndex={onClick ? 0 : undefined}\n onKeyDown={onClick ? (e) => e.key === 'Enter' && onClick() : undefined}\n style={{\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: actualSize,\n height: actualSize,\n fontSize: actualSize * 0.4,\n fontFamily: tokens.typography.fontFamily.mono,\n fontWeight: 700,\n color: color || tokens.colors.status.off,\n backgroundColor: tokens.colors.background.surface,\n border: `1px solid ${tokens.colors.border.default}`,\n borderRadius: 4,\n cursor: onClick ? 'pointer' : 'default',\n userSelect: 'none',\n ...style,\n }}\n title={`Icon: ${name} (install @astrouxds/react for official icons)`}\n >\n <svg\n width={actualSize * 0.6}\n height={actualSize * 0.6}\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n >\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\" />\n <circle cx=\"12\" cy=\"12\" r=\"2\" fill=\"currentColor\" />\n </svg>\n </span>\n );\n}\n\n/**\n * AstroIcon - Official Astro UXD Icons\n * \n * Displays icons from the official Astro UX Design System.\n * Automatically uses @astrouxds/react if installed, otherwise shows fallback.\n * \n * @example\n * ```tsx\n * // Basic usage\n * <AstroIcon name=\"satellite\" />\n * \n * // With size\n * <AstroIcon name=\"antenna\" size=\"large\" />\n * <AstroIcon name=\"thermal\" size={48} />\n * \n * // With color\n * <AstroIcon name=\"warning\" color=\"var(--color-status-caution)\" />\n * \n * // With click handler\n * <AstroIcon name=\"settings\" onClick={() => openSettings()} />\n * ```\n */\nexport const AstroIcon = memo(function AstroIcon(props: AstroIconProps) {\n const {\n name,\n size = 'normal',\n color,\n label,\n className,\n style,\n onClick,\n 'data-testid': testId,\n } = props;\n \n const [, forceUpdate] = useState(0);\n const [currentState, setCurrentState] = useState(loadState);\n const mountedRef = useRef(true);\n \n // Subscribe to load state changes and trigger loading\n useEffect(() => {\n mountedRef.current = true;\n \n // Subscribe to state changes\n const unsubscribe = subscribe(() => {\n if (mountedRef.current) {\n setCurrentState(loadState);\n forceUpdate(n => n + 1);\n }\n });\n \n // Trigger load if not yet started\n if (loadState === 'pending') {\n loadRuxIcon();\n }\n \n return () => {\n mountedRef.current = false;\n unsubscribe();\n };\n }, []);\n \n // If still loading, show a subtle placeholder\n if (currentState === 'pending' || currentState === 'loading') {\n const actualSize = typeof size === 'number' ? size : SIZE_MAP[size];\n return (\n <span\n style={{\n display: 'inline-block',\n width: actualSize,\n height: actualSize,\n ...style,\n }}\n className={className}\n aria-hidden=\"true\"\n />\n );\n }\n \n // Resolve icon alias to actual Astro icon name\n const resolvedName = resolveIconAlias(name);\n \n // If @astrouxds/react is available, use official RuxIcon\n if (RuxIconComponent) {\n const RuxIcon = RuxIconComponent;\n const pixelSize = typeof size === 'number' ? size : SIZE_MAP[size];\n \n // Map numeric size to closest Astro size preset for RuxIcon\n // This helps RuxIcon render at approximately the right size before CSS kicks in\n const getClosestAstroSize = (px: number): AstroIconSize => {\n if (px <= 18) return 'extra-small'; // 16px\n if (px <= 28) return 'small'; // 24px\n if (px <= 40) return 'normal'; // 32px\n return 'large'; // 48px\n };\n const astroSize = typeof size === 'number' ? getClosestAstroSize(size) : size;\n \n // Default to off/grey color if no color specified\n const iconColor = color || 'var(--color-status-off, #a4abb6)';\n \n return (\n <span\n role=\"img\"\n aria-label={label || name}\n style={{\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: pixelSize,\n height: pixelSize,\n minWidth: pixelSize,\n minHeight: pixelSize,\n flexShrink: 0,\n // Don't use overflow:hidden - it clips the icon SVG\n ...style,\n }}\n className={className}\n >\n <RuxIcon\n icon={resolvedName}\n size={astroSize}\n label={label || name}\n data-testid={testId}\n onClick={onClick}\n style={{\n // Force the RuxIcon to respect our exact pixel size\n '--iconDefaultSize': `${pixelSize}px`,\n '--iconSize': `${pixelSize}px`,\n '--size': `${pixelSize}px`,\n color: iconColor,\n width: `${pixelSize}px`,\n height: `${pixelSize}px`,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n ...(onClick && { cursor: 'pointer' }),\n } as React.CSSProperties}\n />\n </span>\n );\n }\n \n // Fallback when @astrouxds/react is not installed\n return <FallbackIcon {...props} name={resolvedName} />;\n});\n\n/**\n * Check if @astrouxds/react is available\n * Useful for conditional rendering or feature detection\n */\nexport function isAstroIconsAvailable(): boolean {\n return loadState === 'loaded' && RuxIconComponent !== null;\n}\n\n/**\n * Preload the Astro icons module\n * Call this early in your app to ensure icons are ready\n */\nexport async function preloadAstroIcons(): Promise<boolean> {\n const component = await loadRuxIcon();\n return component !== null;\n}\n\nexport default AstroIcon;\n"],"names":["AstroIcon"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Badge.js","sources":["../../../src/react/core/Badge.tsx"],"sourcesContent":["/**\n * @zendir/ui - Badge/Tag Component\n * \n * Badge and tag following Astro UX Design System.\n * \n * @example\n * ```tsx\n * <Badge>Default</Badge>\n * <Badge status=\"normal\">Active</Badge>\n * <Badge variant=\"outline\" status=\"caution\">Warning</Badge>\n * <Badge icon={<Icon name=\"satellite\" size={12} />}>Satellite</Badge>\n * ```\n */\n\nimport React, { memo } from 'react';\nimport { useTheme } from '../theme';\nimport { classNames, type StatusLevel } from '../utils';\n\nexport type BadgeVariant = 'filled' | 'outline' | 'subtle';\nexport type BadgeSize = 'small' | 'medium';\n\nexport interface BadgeProps {\n /** Visual variant */\n variant?: BadgeVariant;\n /** Size */\n size?: BadgeSize;\n /** Status-based coloring */\n status?: StatusLevel;\n /** Custom color */\n color?: string;\n /** Icon before text */\n icon?: React.ReactNode;\n /** Removable (shows X button) */\n onRemove?: () => void;\n /** Children */\n children: React.ReactNode;\n /** Custom className */\n className?: string;\n /** Click handler */\n onClick?: () => void;\n}\n\nexport const Badge = memo(function Badge({\n variant = 'filled',\n size = 'small',\n status,\n color,\n icon,\n onRemove,\n children,\n className = '',\n onClick,\n}: BadgeProps): React.ReactElement {\n const { tokens } = useTheme();\n \n // Determine base color\n const baseColor = color ?? (status ? tokens.colors.status[status] : tokens.colors.accent.primary);\n\n // WCAG AA: choose black or white text based on background luminance\n // Uses relative luminance formula (WCAG 2.1 SC 1.4.3)\n const getContrastText = (bg: string): string => {\n const hex = bg.replace('#', '');\n if (hex.length < 6) return '#ffffff';\n const r = parseInt(hex.slice(0, 2), 16) / 255;\n const g = parseInt(hex.slice(2, 4), 16) / 255;\n const b = parseInt(hex.slice(4, 6), 16) / 255;\n const toLinear = (c: number) => c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);\n const L = 0.2126 * toLinear(r) + 0.7152 * toLinear(g) + 0.0722 * toLinear(b);\n // If luminance > 0.179, background is bright → use dark text\n return L > 0.179 ? '#000000' : '#ffffff';\n };\n \n const sizeConfig = {\n small: {\n padding: `2px ${tokens.spacing.xs}`,\n fontSize: tokens.typography.fontSize.xs,\n height: '20px',\n },\n medium: {\n padding: `${tokens.spacing.xs} ${tokens.spacing.sm}`,\n fontSize: tokens.typography.fontSize.sm,\n height: '26px',\n },\n };\n const config = sizeConfig[size];\n \n const variantStyles: Record<BadgeVariant, React.CSSProperties> = {\n filled: {\n backgroundColor: baseColor,\n color: getContrastText(baseColor), // WCAG AA: auto dark/light text based on bg luminance\n border: 'none',\n },\n outline: {\n backgroundColor: 'transparent',\n color: baseColor,\n border: `1px solid ${baseColor}`,\n },\n subtle: {\n backgroundColor: `${baseColor}20`,\n color: baseColor,\n border: 'none',\n },\n };\n \n const isInteractive = !!onClick;\n \n return (\n <span\n className={classNames('zendir-badge', `zendir-badge--${variant}`, className)}\n onClick={onClick}\n role={isInteractive ? 'button' : undefined}\n tabIndex={isInteractive ? 0 : undefined}\n onKeyDown={onClick ? (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onClick(); } } : undefined}\n style={{\n display: 'inline-flex',\n alignItems: 'center',\n gap: tokens.spacing.xs,\n padding: config.padding,\n height: config.height,\n fontSize: config.fontSize,\n fontWeight: tokens.typography.fontWeight.medium,\n fontFamily: tokens.typography.fontFamily.primary,\n borderRadius: tokens.borderRadius.full,\n cursor: isInteractive ? 'pointer' : 'default',\n transition: `all ${tokens.animation.fast}`,\n whiteSpace: 'nowrap',\n ...variantStyles[variant],\n }}\n >\n {icon}\n {children}\n {onRemove && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onRemove();\n }}\n aria-label=\"Remove\"\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n /* Visual size stays 14px; touch target expanded to 24px via padding (WCAG 2.5.8) */\n padding: '5px',\n marginLeft: tokens.spacing.xxs,\n width: '24px',\n height: '24px',\n backgroundColor: 'transparent',\n border: 'none',\n borderRadius: '50%',\n cursor: 'pointer',\n color: 'inherit',\n opacity: 0.7,\n transition: `opacity ${tokens.animation.fast}`,\n boxSizing: 'border-box',\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.opacity = '1';\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.opacity = '0.7';\n }}\n >\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\" />\n </svg>\n </button>\n )}\n </span>\n );\n});\n\nexport default Badge;\n"],"names":["Badge"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Button.js","sources":["../../../src/react/core/Button.tsx"],"sourcesContent":["/**\n * @zendir/ui - Button Component\n * \n * Enterprise-grade button following Astro UX Design System with Zendir purple accents.\n * \n * Astro UX Compliance:\n * - Three visual variants: primary, secondary, borderless\n * - Size variants: small, medium, large\n * - Sentence case capitalization\n * - Focus ring for accessibility (WCAG 2.1 AA)\n * - Right-align in button groups (primary on right)\n * \n * Zendir Enhancements:\n * - Purple accent colors\n * - Smooth micro-interactions\n * - Subtle glow effects\n * - Modern glassmorphism hover states\n * \n * @example\n * ```tsx\n * <Button>Primary Action</Button>\n * <Button variant=\"secondary\">Cancel</Button>\n * <Button variant=\"borderless\" icon={<Icon name=\"settings\" />}>Settings</Button>\n * <Button loading>Saving...</Button>\n * ```\n */\n\nimport React, { memo, forwardRef, useState, useMemo, useRef, useLayoutEffect, useCallback } from 'react';\nimport ReactDOM from 'react-dom';\nimport { useTheme } from '../theme';\nimport { classNames, safeAccentText } from '../utils';\n\nexport type ButtonVariant = 'primary' | 'secondary' | 'borderless';\nexport type ButtonSize = 'small' | 'medium' | 'large';\n\nexport interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n /** Visual variant */\n variant?: ButtonVariant;\n /** Size variant */\n size?: ButtonSize;\n /** Icon to display before text */\n icon?: React.ReactNode;\n /** Icon to display after text */\n iconEnd?: React.ReactNode;\n /** Show loading spinner */\n loading?: boolean;\n /** Full width button */\n fullWidth?: boolean;\n /** Tooltip text (shows on hover) */\n tooltip?: string;\n /** Custom className */\n className?: string;\n /** Children */\n children?: React.ReactNode;\n}\n\nexport const Button = memo(forwardRef<HTMLButtonElement, ButtonProps>(function Button(\n {\n variant = 'primary',\n size = 'medium',\n icon,\n iconEnd,\n loading = false,\n fullWidth = false,\n tooltip,\n disabled,\n className = '',\n children,\n style,\n onMouseEnter,\n onMouseLeave,\n onFocus,\n onBlur,\n ...rest\n },\n ref\n): React.ReactElement {\n const { tokens, theme } = useTheme();\n \n // Spin keyframe is used by loading spinner only (injected there)\n\n const isTransparentTheme = theme === 'transparent' || theme === 'transparent-bold' || theme === 'transparent-minimal';\n const [isHovered, setIsHovered] = useState(false);\n const [isActive, setIsActive] = useState(false);\n const [isFocused, setIsFocused] = useState(false);\n const [showTooltip, setShowTooltip] = useState(false);\n \n const isDisabled = disabled || loading;\n const transparentDefault = tokens.colors.interactive.transparentDefault;\n const transparentHover = tokens.colors.interactive.transparentHover;\n\n // WCAG AA: compute a button-safe background from accent.primary\n // If white text (#fff) on the accent bg doesn't meet 4.5:1, darken it proportionally.\n const safeAccentBg = useMemo(() => {\n const hex = tokens.colors.accent.primary.replace('#', '');\n if (hex.length < 6) return tokens.colors.accent.primary;\n const toLinear = (c: number) => c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);\n const r = parseInt(hex.slice(0, 2), 16) / 255;\n const g = parseInt(hex.slice(2, 4), 16) / 255;\n const b = parseInt(hex.slice(4, 6), 16) / 255;\n const L = 0.2126 * toLinear(r) + 0.7152 * toLinear(g) + 0.0722 * toLinear(b);\n if ((1.05 / (L + 0.05)) >= 4.5) return tokens.colors.accent.primary;\n // Darken just enough: target L for ~4.7:1 contrast with white\n const factor = 0.88;\n const dr = Math.min(255, Math.round(r * 255 * factor));\n const dg = Math.min(255, Math.round(g * 255 * factor));\n const db = Math.min(255, Math.round(b * 255 * factor));\n return `#${dr.toString(16).padStart(2, '0')}${dg.toString(16).padStart(2, '0')}${db.toString(16).padStart(2, '0')}`;\n }, [tokens.colors.accent.primary]);\n\n // WCAG AA safe accent for use as foreground text on dark backgrounds\n const safeAccentFg = useMemo(() => safeAccentText(tokens.colors.accent.primary), [tokens.colors.accent.primary]);\n \n const sizeConfig = {\n small: {\n padding: `${tokens.spacing.xxs} ${tokens.spacing.smd}`,\n fontSize: tokens.typography.fontSize.xs,\n height: '24px',\n gap: tokens.spacing.xs,\n iconSize: 13,\n borderRadius: tokens.borderRadius.sm,\n },\n medium: {\n padding: `${tokens.spacing.xxs} ${tokens.spacing.md}`,\n fontSize: tokens.typography.fontSize.sm,\n height: '28px',\n gap: tokens.spacing.xs,\n iconSize: 15,\n borderRadius: tokens.borderRadius.md,\n },\n large: {\n padding: `${tokens.spacing.xs} ${tokens.spacing.lg}`,\n fontSize: tokens.typography.fontSize.base,\n height: '34px',\n gap: tokens.spacing.sm,\n iconSize: 17,\n borderRadius: tokens.borderRadius.md,\n },\n };\n \n const config = sizeConfig[size];\n \n // Variant base styles (transparent theme: purple-hue transparent bg, smooth fade on hover)\n const getVariantStyles = (): React.CSSProperties => {\n const baseTransition = `${tokens.animation.normal}, opacity ${tokens.animation.duration.normal}ms ${tokens.animation.easing.default}`;\n \n if (isTransparentTheme && transparentDefault && transparentHover) {\n switch (variant) {\n case 'primary':\n return {\n backgroundColor: isHovered && !isDisabled ? transparentHover : transparentDefault,\n color: tokens.colors.text.primary,\n border: `1px solid ${isHovered && !isDisabled ? 'rgba(139, 92, 246, 0.5)' : 'rgba(139, 92, 246, 0.25)'}`,\n boxShadow: isHovered && !isDisabled ? '0 0 10px rgba(139, 92, 246, 0.15)' : 'none',\n transition: baseTransition,\n backdropFilter: 'blur(8px)',\n WebkitBackdropFilter: 'blur(8px)',\n };\n case 'secondary':\n return {\n backgroundColor: isHovered && !isDisabled \n ? `${tokens.colors.accent.primary}15` // Use accent color with opacity instead of transparentHover\n : transparentDefault,\n color: safeAccentFg,\n border: `1px solid ${isHovered && !isDisabled ? `${tokens.colors.accent.primary}70` : `${tokens.colors.accent.primary}30`}`,\n boxShadow: 'none',\n transition: baseTransition,\n backdropFilter: 'blur(8px)',\n WebkitBackdropFilter: 'blur(8px)',\n };\n case 'borderless':\n return {\n backgroundColor: isHovered && !isDisabled ? transparentHover : transparentDefault,\n color: safeAccentFg,\n border: 'none',\n boxShadow: 'none',\n transition: baseTransition,\n backdropFilter: 'blur(6px)',\n WebkitBackdropFilter: 'blur(6px)',\n };\n }\n }\n \n switch (variant) {\n case 'primary':\n return {\n backgroundColor: isHovered && !isDisabled\n ? tokens.colors.interactive.hover\n : safeAccentBg,\n color: '#ffffff',\n border: 'none',\n boxShadow: isHovered && !isDisabled\n ? `0 2px 8px -2px ${tokens.colors.accent.primary}35`\n : 'none',\n transition: baseTransition,\n };\n \n case 'secondary':\n return {\n backgroundColor: isHovered && !isDisabled\n ? `${tokens.colors.accent.primary}12`\n : 'transparent',\n color: isHovered && !isDisabled ? safeAccentFg : safeAccentFg,\n border: `${tokens.borders.width.medium} solid ${isHovered && !isDisabled ? tokens.colors.accent.primary : `${tokens.colors.accent.primary}80`}`,\n boxShadow: 'none',\n transition: baseTransition,\n };\n \n case 'borderless':\n return {\n backgroundColor: isHovered && !isDisabled\n ? `${tokens.colors.accent.primary}10`\n : 'transparent',\n color: safeAccentFg,\n border: `${tokens.borders.width.medium} solid transparent`,\n boxShadow: 'none',\n transition: baseTransition,\n };\n }\n };\n \n const focusStyles: React.CSSProperties = isFocused ? {\n outline: 'none',\n boxShadow: tokens.borders.focusRing.button,\n } : {};\n\n const activeStyles: React.CSSProperties = isActive && !isDisabled ? {\n filter: 'brightness(0.85)',\n transform: 'scale(0.98)',\n boxShadow: 'none',\n } : {};\n \n const baseStyle: React.CSSProperties = {\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: config.gap,\n padding: config.padding,\n height: config.height,\n fontSize: config.fontSize,\n fontWeight: 400,\n fontFamily: tokens.typography.fontFamily.primary,\n letterSpacing: '0.02em',\n borderRadius: config.borderRadius,\n cursor: isDisabled ? 'not-allowed' : 'pointer',\n opacity: isDisabled ? 0.5 : 1,\n outline: 'none',\n textDecoration: 'none',\n whiteSpace: 'nowrap',\n userSelect: 'none',\n width: fullWidth ? '100%' : 'auto',\n position: 'relative',\n ...getVariantStyles(),\n ...focusStyles,\n ...activeStyles,\n ...style,\n };\n \n // Loading spinner with smooth animation\n const spinner = (\n <svg\n width={config.iconSize}\n height={config.iconSize}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n style={{\n animation: 'zendir-spin 0.8s cubic-bezier(0.4, 0, 0.6, 1) infinite',\n }}\n >\n <circle\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeDasharray=\"31.4 31.4\"\n strokeDashoffset=\"10\"\n opacity=\"0.9\"\n />\n <style>\n {`@keyframes zendir-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }`}\n </style>\n </svg>\n );\n \n // Portal-based tooltip: position fixed relative to viewport, never clipped\n const internalRef = useRef<HTMLButtonElement>(null);\n const tooltipNodeRef = useRef<HTMLDivElement>(null);\n const [tooltipPos, setTooltipPos] = useState<{ top: number; left: number } | null>(null);\n\n const mergedRef = useCallback((node: HTMLButtonElement | null) => {\n (internalRef as React.MutableRefObject<HTMLButtonElement | null>).current = node;\n if (typeof ref === 'function') ref(node);\n else if (ref) (ref as React.MutableRefObject<HTMLButtonElement | null>).current = node;\n }, [ref]);\n\n useLayoutEffect(() => {\n if (!showTooltip || !tooltip || !internalRef.current) {\n setTooltipPos(null);\n return;\n }\n const btn = internalRef.current.getBoundingClientRect();\n const gap = 8;\n let top = btn.top - gap;\n let left = btn.left + btn.width / 2;\n // Adjust after tooltip renders so we can measure its width\n if (tooltipNodeRef.current) {\n const tt = tooltipNodeRef.current.getBoundingClientRect();\n top = btn.top - tt.height - gap;\n left = btn.left + (btn.width - tt.width) / 2;\n if (left < 8) left = 8;\n if (left + tt.width > window.innerWidth - 8) left = window.innerWidth - tt.width - 8;\n if (top < 8) top = btn.bottom + gap; // flip below if no room above\n }\n setTooltipPos({ top, left });\n }, [showTooltip, tooltip]);\n\n const tooltipPortal = tooltip && showTooltip && ReactDOM.createPortal(\n <div\n ref={tooltipNodeRef}\n role=\"tooltip\"\n style={{\n position: 'fixed',\n top: tooltipPos ? tooltipPos.top : -9999,\n left: tooltipPos ? tooltipPos.left : -9999,\n padding: `${tokens.spacing.xs} ${tokens.spacing.smd}`,\n fontSize: '0.75rem',\n fontWeight: 500,\n color: tokens.colors.text.primary,\n backgroundColor: tokens.colors.background.elevated,\n border: tokens.borders.divider,\n borderRadius: tokens.borderRadius.md,\n boxShadow: tokens.shadows.md,\n whiteSpace: 'nowrap',\n zIndex: 2147483100,\n pointerEvents: 'none',\n opacity: tooltipPos ? 1 : 0,\n animation: tooltipPos ? `zendir-btn-tip ${tokens.animation.duration.fast}ms ${tokens.animation.easing.default} both` : 'none',\n }}\n >\n {tooltip}\n <style>\n {`@keyframes zendir-btn-tip { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: translateY(0); } }`}\n </style>\n </div>,\n document.body\n );\n\n return (\n <>\n <button\n ref={mergedRef}\n type=\"button\"\n disabled={isDisabled}\n className={classNames('zendir-button', `zendir-button--${variant}`, `zendir-button--${size}`, className)}\n style={baseStyle}\n title={tooltip && !showTooltip ? tooltip : undefined}\n onMouseDown={() => setIsActive(true)}\n onMouseUp={() => setIsActive(false)}\n onMouseEnter={(e) => {\n setIsHovered(true);\n setShowTooltip(true);\n onMouseEnter?.(e);\n }}\n onMouseLeave={(e) => {\n setIsHovered(false);\n setIsActive(false);\n setShowTooltip(false);\n onMouseLeave?.(e);\n }}\n onFocus={(e) => {\n setIsFocused(true);\n onFocus?.(e);\n }}\n onBlur={(e) => {\n setIsFocused(false);\n onBlur?.(e);\n }}\n {...rest}\n >\n {loading ? spinner : icon}\n {children && <span>{children}</span>}\n {iconEnd && !loading && iconEnd}\n </button>\n {tooltipPortal}\n </>\n );\n}));\n\nexport default Button;\n"],"names":["Button"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Capture.js","sources":["../../../src/react/core/Capture.tsx"],"sourcesContent":["/**\n * @zendir/ui - Capture Component\n * \n * Transport-agnostic image capture server component. Generates a unique server\n * ID, accepts capture requests imperatively, and returns image bytes. The host\n * app (e.g. space-range-operator) handles all MQTT/transport wiring and uses\n * the server ID to construct topic paths externally.\n * \n * Features:\n * - Unique server ID with copy-to-clipboard\n * - Pause/resume capture processing\n * - Collapsible gallery of captured images\n * - Connection status display\n * - forwardRef with imperative CaptureHandle\n * \n * @example\n * ```tsx\n * const captureRef = useRef<CaptureHandle>(null);\n * \n * <Capture\n * ref={captureRef}\n * connected={isConnected}\n * onReady={(serverId) => {\n * mqttClient.subscribe('Zendir/SpaceImage/Request');\n * }}\n * onCapture={(req) => console.log('Captured', req.req_id)}\n * />\n * \n * // When a request arrives from external transport:\n * // Request: { req_id, args: { game_id, team_id, ...captureArgs } }\n * const imageBytes = await captureRef.current.capture(request);\n * // Publish response to: Zendir/SpaceImage/{game_id}/{team_id}/Response/{req_id}\n * ```\n */\n\nimport React, { memo, useState, useCallback, useRef, useEffect, useImperativeHandle, forwardRef } from 'react';\nimport { useTheme } from '../theme';\nimport { useCopyToClipboard } from './CopyButton';\nimport placeholderImageUrl from './capture-placeholder.png';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\n/** Camera capture arguments */\nexport interface CaptureArgs {\n monochromatic: boolean;\n resolution: number;\n coc: number;\n pixel_pitch: number;\n focusing_distance: number;\n aperture: number;\n focal_length: number;\n fov: number;\n sample: boolean;\n position: [number, number, number];\n rotation: [number, number, number];\n /**\n * Output image format. Defaults to 'png' when omitted.\n * - 'png' → lossless, larger file (good for scientific/archival use)\n * - 'jpeg' → lossy, smaller file (~3-5× smaller than PNG at q=0.92)\n */\n format?: 'png' | 'jpeg';\n /**\n * JPEG quality factor (0–1). Only used when format='jpeg'. Default: 0.92.\n * Ignored for PNG (which is always lossless).\n */\n jpeg_quality?: number;\n}\n\n/** Incoming capture request */\nexport interface CaptureRequest {\n type: 'capture';\n req_id: number;\n args: CaptureArgs;\n}\n\n/** Imperative handle exposed via ref */\nexport interface CaptureHandle {\n /** Process a capture request, returns image bytes */\n capture: (request: CaptureRequest) => Promise<Uint8Array>;\n /** The server's unique ID */\n serverId: string;\n}\n\n/** Captured image entry stored for gallery display */\nexport interface CapturedImage {\n reqId: number;\n dataUrl: string;\n timestamp: number;\n /** Raw image byte count */\n bytes: number;\n /** The CaptureArgs used for this capture (camera settings, position, etc.) */\n args?: CaptureArgs;\n}\n\nexport interface CaptureProps {\n /** Whether host transport is connected (drives status indicator) */\n connected?: boolean;\n /**\n * Image source for capture responses. Three tiers:\n * 1. `Uint8Array` -- static bytes returned for every request\n * 2. `(args) => Promise<Uint8Array>` -- async renderer (e.g. CesiumCaptureSource)\n * 3. `undefined` (default) -- auto-detects CesiumJS; falls back to placeholder image\n */\n imageSource?: Uint8Array | ((args: CaptureArgs) => Promise<Uint8Array>);\n /**\n * Pre-load the CesiumJS renderer on mount instead of waiting for the first\n * capture request. Only applies when `imageSource` is not provided (auto-detect mode).\n * @default true\n */\n preload?: boolean;\n /**\n * Explicit server ID. When provided the component uses this value instead\n * of generating a new UUID. Useful for persisting across reloads via localStorage.\n */\n serverId?: string;\n /** Fired after each capture is processed */\n onCapture?: (request: CaptureRequest) => void;\n /** Called once on mount with the server ID */\n onReady?: (serverId: string) => void;\n /**\n * Start with the image gallery expanded. When true, the component renders\n * in a flex-column layout designed to fill its container height.\n * @default false\n */\n defaultGalleryExpanded?: boolean;\n /** Custom style */\n style?: React.CSSProperties;\n}\n\n// ─── Placeholder Image ───────────────────────────────────────────────────────\n\n/**\n * Generates a visible 256x256 fallback PNG via canvas when the bundled\n * astronaut image cannot be fetched (Storybook asset path, SSR, etc.).\n * Shows a branded \"ZENDIR\" placeholder so thumbnails are never invisible.\n */\nasync function generateCanvasFallback(): Promise<Uint8Array> {\n try {\n const size = 256;\n const canvas = document.createElement('canvas');\n canvas.width = size;\n canvas.height = size;\n const ctx = canvas.getContext('2d')!;\n\n // Dark space-themed gradient background\n const bg = ctx.createRadialGradient(size / 2, size / 2, 20, size / 2, size / 2, size);\n bg.addColorStop(0, '#1e2a4a');\n bg.addColorStop(1, '#0a0e1a');\n ctx.fillStyle = bg;\n ctx.fillRect(0, 0, size, size);\n\n // Subtle star field\n ctx.fillStyle = 'rgba(255, 255, 255, 0.5)';\n for (let i = 0; i < 40; i++) {\n const sx = (((i * 97) + 31) % size);\n const sy = (((i * 61) + 17) % size);\n const sr = ((i % 3) + 1) * 0.6;\n ctx.beginPath();\n ctx.arc(sx, sy, sr, 0, Math.PI * 2);\n ctx.fill();\n }\n\n // Crosshair / reticle\n ctx.strokeStyle = 'rgba(74, 143, 255, 0.35)';\n ctx.lineWidth = 1;\n ctx.beginPath();\n ctx.moveTo(size / 2, 40);\n ctx.lineTo(size / 2, size - 40);\n ctx.moveTo(40, size / 2);\n ctx.lineTo(size - 40, size / 2);\n ctx.stroke();\n ctx.beginPath();\n ctx.arc(size / 2, size / 2, 50, 0, Math.PI * 2);\n ctx.stroke();\n\n // \"ZENDIR\" label\n ctx.fillStyle = '#4a8fff';\n ctx.font = 'bold 28px monospace';\n ctx.textAlign = 'center';\n ctx.textBaseline = 'middle';\n ctx.fillText('ZENDIR', size / 2, size / 2 - 16);\n\n // \"PLACEHOLDER\" subtitle\n ctx.fillStyle = 'rgba(255, 255, 255, 0.4)';\n ctx.font = '13px monospace';\n ctx.fillText('PLACEHOLDER', size / 2, size / 2 + 14);\n\n // Res badge at bottom\n ctx.fillStyle = 'rgba(255, 255, 255, 0.25)';\n ctx.font = '10px monospace';\n ctx.fillText(`${size}x${size}`, size / 2, size - 20);\n\n const blob = await new Promise<Blob>((resolve) =>\n canvas.toBlob((b) => resolve(b!), 'image/png'),\n );\n return new Uint8Array(await blob.arrayBuffer());\n } catch {\n // Absolute last resort: 1x1 opaque dark-blue PNG (never transparent/black)\n return new Uint8Array([\n 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,\n 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52,\n 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,\n 0x08, 0x02, 0x00, 0x00, 0x00, 0x90, 0x77, 0x53,\n 0xDE, 0x00, 0x00, 0x00, 0x0C, 0x49, 0x44, 0x41,\n 0x54, 0x78, 0x9C, 0x62, 0x68, 0x60, 0x60, 0x00,\n 0x00, 0x00, 0x04, 0x00, 0x01, 0x27, 0x34, 0x27,\n 0x0A, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E,\n 0x44, 0xAE, 0x42, 0x60, 0x82,\n ]);\n }\n}\n\n/** Cached placeholder image bytes (lazy-loaded from bundled asset) */\nlet placeholderCache: Uint8Array | null = null;\n\n/**\n * Loads the bundled placeholder image (astronaut). Falls back to a\n * canvas-generated branded placeholder if the fetch fails (e.g. SSR,\n * Storybook asset paths, missing file).\n */\nasync function loadPlaceholderImage(): Promise<Uint8Array> {\n if (placeholderCache) return placeholderCache;\n try {\n const response = await fetch(placeholderImageUrl);\n if (!response.ok) throw new Error(`HTTP ${response.status}`);\n const buf = await response.arrayBuffer();\n if (buf.byteLength < 100) throw new Error('Suspiciously small image');\n placeholderCache = new Uint8Array(buf);\n } catch {\n placeholderCache = await generateCanvasFallback();\n }\n return placeholderCache;\n}\n\n/**\n * Bundled astronaut placeholder PNG (or 1x1 fallback). Exported for\n * `CesiumCaptureSource` when globe capture fails (e.g. missing Cesium assets, WebGL).\n */\nexport async function loadCapturePlaceholderImage(): Promise<Uint8Array> {\n return loadPlaceholderImage();\n}\n\n// ─── Hook ─────────────────────────────────────────────────────────────────────\n\nexport interface UseCaptureOptions {\n /** Image to return: static bytes or async function receiving capture args */\n imageSource?: Uint8Array | ((args: CaptureArgs) => Promise<Uint8Array>);\n /**\n * Pre-load the CesiumJS renderer on mount (only when imageSource is undefined).\n * Set to false for lazy init on first capture request.\n * @default true\n */\n preload?: boolean;\n /**\n * Explicit server ID. When provided the hook uses this value instead of\n * generating a new `crypto.randomUUID()`. Useful for persisting the ID\n * across page reloads (e.g. from localStorage).\n */\n serverId?: string;\n}\n\nexport interface UseCaptureResult {\n /** Stable unique server ID */\n serverId: string;\n /** Process a capture request, returns image bytes (empty if paused) */\n capture: (request: CaptureRequest) => Promise<Uint8Array>;\n /** Whether capture processing is paused */\n paused: boolean;\n /** Toggle pause state */\n setPaused: (paused: boolean) => void;\n /** Number of successfully processed requests */\n requestCount: number;\n /** Most recent request */\n lastRequest: CaptureRequest | null;\n /** Gallery of captured images (newest first) */\n capturedImages: CapturedImage[];\n}\n\n/**\n * Hook for capture server logic. Generates a stable UUID, manages\n * pause state, and processes capture requests into image bytes.\n * \n * @example\n * ```tsx\n * const { serverId, capture, paused, setPaused } = useCapture();\n * // call capture(request) when external message arrives\n * ```\n */\nexport function useCapture(options?: UseCaptureOptions): UseCaptureResult {\n const serverIdRef = useRef(options?.serverId || crypto.randomUUID());\n const [paused, setPaused] = useState(false);\n const [requestCount, setRequestCount] = useState(0);\n const [lastRequest, setLastRequest] = useState<CaptureRequest | null>(null);\n const [capturedImages, setCapturedImages] = useState<CapturedImage[]>([]);\n\n const imageSourceRef = useRef(options?.imageSource);\n imageSourceRef.current = options?.imageSource;\n\n const pausedRef = useRef(paused);\n pausedRef.current = paused;\n\n // Cache generated images by req_id to avoid regenerating the same capture\n const imageCacheRef = useRef<Map<number, Uint8Array>>(new Map());\n\n // Auto-detect CesiumJS when no imageSource is provided.\n // Lazily creates a hidden Cesium.Viewer on first use (or on mount if preload=true).\n const cesiumSourceRef = useRef<{ capture: (args: CaptureArgs) => Promise<Uint8Array>; destroy: () => void } | null>(null);\n const cesiumFailedRef = useRef(false);\n const cesiumLoadingRef = useRef<Promise<void> | null>(null);\n\n const initCesium = useCallback(async () => {\n if (cesiumSourceRef.current || cesiumFailedRef.current) return;\n try {\n const { createCesiumCaptureSource } = await import('../3d/CesiumCaptureSource');\n cesiumSourceRef.current = await createCesiumCaptureSource();\n } catch {\n cesiumFailedRef.current = true;\n }\n }, []);\n\n // Pre-load Cesium on mount when preload is enabled and no explicit imageSource\n const preload = options?.preload !== false;\n useEffect(() => {\n if (imageSourceRef.current || !preload) return;\n cesiumLoadingRef.current = initCesium();\n }, [preload, initCesium]);\n\n // Cleanup Cesium viewer on unmount\n useEffect(() => {\n return () => {\n cesiumSourceRef.current?.destroy();\n cesiumSourceRef.current = null;\n };\n }, []);\n\n const capture = useCallback(async (request: CaptureRequest): Promise<Uint8Array> => {\n if (pausedRef.current) return new Uint8Array(0);\n\n if (!request || request.type !== 'capture' || typeof request.req_id !== 'number') {\n return new Uint8Array(0);\n }\n\n // Return cached image if this req_id was already processed\n const cached = imageCacheRef.current.get(request.req_id);\n if (cached) return cached;\n\n let bytes: Uint8Array;\n const src = imageSourceRef.current;\n\n if (src instanceof Uint8Array) {\n bytes = src;\n } else if (typeof src === 'function') {\n bytes = await src(request.args);\n } else {\n // Auto-detect: try CesiumJS, fall back to placeholder\n if (!cesiumSourceRef.current && !cesiumFailedRef.current) {\n if (cesiumLoadingRef.current) {\n await cesiumLoadingRef.current;\n } else {\n await initCesium();\n }\n }\n if (cesiumSourceRef.current) {\n bytes = await cesiumSourceRef.current.capture(request.args);\n } else {\n bytes = await loadPlaceholderImage();\n }\n }\n\n // Cache the result by req_id\n imageCacheRef.current.set(request.req_id, bytes);\n\n setRequestCount(c => c + 1);\n setLastRequest(request);\n\n const mimeType = request.args.format === 'jpeg' ? 'image/jpeg' : 'image/png';\n const blob = new Blob([bytes as unknown as BlobPart], { type: mimeType });\n const dataUrl = URL.createObjectURL(blob);\n setCapturedImages(prev => [\n { reqId: request.req_id, dataUrl, timestamp: Date.now(), bytes: bytes.length, args: request.args },\n ...prev,\n ]);\n\n return bytes;\n }, []);\n\n return {\n serverId: serverIdRef.current,\n capture,\n paused,\n setPaused,\n requestCount,\n lastRequest,\n capturedImages,\n };\n}\n\n// ─── Inline SVG Icons ─────────────────────────────────────────────────────────\n\nconst PauseIcon = ({ size = 14 }: { size?: number }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <rect x=\"6\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\" />\n <rect x=\"14\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\" />\n </svg>\n);\n\nconst PlayIcon = ({ size = 14 }: { size?: number }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <polygon points=\"6,4 20,12 6,20\" />\n </svg>\n);\n\nconst ChevronIcon = ({ size = 14, expanded }: { size?: number; expanded: boolean }) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n style={{\n transition: 'transform 0.2s ease',\n transform: expanded ? 'rotate(90deg)' : 'rotate(0deg)',\n }}\n >\n <polyline points=\"9 18 15 12 9 6\" />\n </svg>\n);\n\nconst CopyIcon = ({ size = 14 }: { size?: number }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\" />\n <path d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\" />\n </svg>\n);\n\nconst CheckIcon = ({ size = 14, color }: { size?: number; color: string }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke={color} strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <polyline points=\"20 6 9 17 4 12\" />\n </svg>\n);\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\n/**\n * Capture server UI component. Displays server status, pause/resume control,\n * copy-ID button, and a collapsible gallery of captured images.\n * \n * Access `capture()` and `serverId` imperatively via the forwarded ref.\n */\nexport const Capture = memo(forwardRef<CaptureHandle, CaptureProps>(function Capture(\n {\n connected = false,\n imageSource,\n preload,\n serverId: serverIdProp,\n onCapture,\n onReady,\n defaultGalleryExpanded = false,\n style,\n },\n ref,\n) {\n const { tokens } = useTheme();\n const {\n serverId,\n capture,\n paused,\n setPaused,\n requestCount,\n capturedImages,\n } = useCapture({ imageSource, preload, serverId: serverIdProp });\n const { copy, copied } = useCopyToClipboard();\n const [galleryExpanded, setGalleryExpanded] = useState(defaultGalleryExpanded);\n const [hoveredBtn, setHoveredBtn] = useState<string | null>(null);\n const readyFiredRef = useRef(false);\n\n useEffect(() => {\n if (onReady && !readyFiredRef.current) {\n readyFiredRef.current = true;\n onReady(serverId);\n }\n }, [onReady, serverId]);\n\n const wrappedCapture = useCallback(async (request: CaptureRequest) => {\n const bytes = await capture(request);\n if (bytes.length > 0) {\n onCapture?.(request);\n }\n return bytes;\n }, [capture, onCapture]);\n\n useImperativeHandle(ref, () => ({\n capture: wrappedCapture,\n serverId,\n }), [wrappedCapture, serverId]);\n\n const statusColor = connected\n ? tokens.colors.status.normal\n : tokens.colors.status.off;\n const statusLabel = connected ? 'Connected' : 'Disconnected';\n\n const btnBase: React.CSSProperties = {\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: '6px',\n height: 32,\n padding: '0 12px',\n fontSize: tokens.typography.fontSize.sm,\n fontWeight: 500,\n fontFamily: tokens.typography.fontFamily.primary,\n color: tokens.colors.text.secondary,\n backgroundColor: 'transparent',\n border: `1px solid ${tokens.colors.border.muted}`,\n borderRadius: tokens.borderRadius.md,\n cursor: 'pointer',\n transition: tokens.animation.fast,\n outline: 'none',\n whiteSpace: 'nowrap',\n };\n\n return (\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n borderRadius: tokens.borderRadius.lg,\n border: `1px solid ${tokens.colors.border.default}`,\n backgroundColor: tokens.colors.background.surface,\n overflow: 'hidden',\n /* When gallery is expanded, allow container to stretch to fill parent */\n ...(galleryExpanded ? { flex: 1, minHeight: 0 } : {}),\n ...style,\n }}\n >\n {/* Top bar: pause/resume | status | copy ID */}\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '8px 12px',\n gap: '12px',\n minHeight: 48,\n flexShrink: 0,\n }}\n >\n {/* Left: Pause/Resume */}\n <button\n type=\"button\"\n onClick={() => setPaused(!paused)}\n onMouseEnter={() => setHoveredBtn('pause')}\n onMouseLeave={() => setHoveredBtn(null)}\n aria-label={paused ? 'Resume capture server' : 'Pause capture server'}\n style={{\n ...btnBase,\n color: paused ? tokens.colors.status.caution : tokens.colors.text.secondary,\n borderColor: paused\n ? `${tokens.colors.status.caution}40`\n : tokens.colors.border.muted,\n backgroundColor: hoveredBtn === 'pause'\n ? `${tokens.colors.accent.primary}12`\n : 'transparent',\n }}\n >\n {paused ? <PlayIcon /> : <PauseIcon />}\n <span>{paused ? 'Resume' : 'Pause'}</span>\n </button>\n\n {/* Center: Status + request count */}\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n fontSize: tokens.typography.fontSize.sm,\n color: tokens.colors.text.secondary,\n }}\n >\n <span\n style={{\n width: 8,\n height: 8,\n borderRadius: '50%',\n backgroundColor: statusColor,\n flexShrink: 0,\n }}\n />\n <span>{statusLabel}</span>\n {requestCount > 0 && (\n <span\n style={{\n fontSize: tokens.typography.fontSize.xs,\n color: tokens.colors.text.tertiary,\n fontFamily: tokens.typography.fontFamily.mono,\n }}\n >\n ({requestCount})\n </span>\n )}\n </div>\n\n {/* Right: Copy ID */}\n <button\n type=\"button\"\n onClick={() => copy(serverId)}\n onMouseEnter={() => setHoveredBtn('copy')}\n onMouseLeave={() => setHoveredBtn(null)}\n title={copied ? 'Copied!' : `Copy Server ID: ${serverId}`}\n aria-label={copied ? 'Server ID copied' : 'Copy server ID'}\n style={{\n ...btnBase,\n color: copied\n ? tokens.colors.status.normal\n : tokens.colors.text.secondary,\n borderColor: copied\n ? `${tokens.colors.status.normal}40`\n : tokens.colors.border.muted,\n backgroundColor: hoveredBtn === 'copy'\n ? `${tokens.colors.accent.primary}12`\n : 'transparent',\n }}\n >\n {copied\n ? <CheckIcon color={tokens.colors.status.normal} />\n : <CopyIcon />}\n <span\n style={{\n fontFamily: tokens.typography.fontFamily.mono,\n fontSize: tokens.typography.fontSize.xs,\n }}\n >\n {copied ? 'Copied!' : serverId.slice(0, 8)}\n </span>\n </button>\n </div>\n\n {/* Captured Images gallery (respects defaultGalleryExpanded prop) */}\n <CaptureGallery\n images={capturedImages}\n expanded={galleryExpanded}\n onToggle={() => setGalleryExpanded(!galleryExpanded)}\n tokens={tokens}\n serverId={serverId}\n />\n </div>\n );\n}));\n\n// ─── Gallery Sub-Components ───────────────────────────────────────────────────\n\n/** Format byte count for display */\nfunction fmtBytes(b: number): string {\n if (b > 1048576) return `${(b / 1048576).toFixed(1)} MB`;\n if (b > 1024) return `${(b / 1024).toFixed(1)} KB`;\n return `${b} B`;\n}\n\n/** Download a single captured image with the correct file extension */\nfunction downloadCapturedImage(img: CapturedImage, serverId: string): void {\n const ext = img.args?.format === 'jpeg' ? 'jpg' : 'png';\n const a = document.createElement('a');\n a.href = img.dataUrl;\n a.download = `capture_${serverId}_${img.reqId}.${ext}`;\n a.click();\n}\n\n/** Download all captured images sequentially */\nasync function downloadAllImages(images: CapturedImage[], serverId: string): Promise<void> {\n for (const img of images) {\n downloadCapturedImage(img, serverId);\n await new Promise<void>(r => setTimeout(r, 200));\n }\n}\n\n/** Download icon (inline SVG) */\nconst DownloadIcon = ({ size = 12 }: { size?: number }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\" />\n <polyline points=\"7 10 12 15 17 10\" />\n <line x1=\"12\" y1=\"15\" x2=\"12\" y2=\"3\" />\n </svg>\n);\n\n/** Close icon (inline SVG) */\nconst CloseIcon = ({ size = 12 }: { size?: number }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n);\n\n/** Metadata key-value row */\nfunction MetaRow({ label, value, tokens, mono }: { label: string; value: string; tokens: any; mono?: boolean }) {\n return (\n <div style={{\n display: 'flex', justifyContent: 'space-between', alignItems: 'center',\n padding: '2px 0', fontSize: tokens.typography.fontSize.xs,\n }}>\n <span style={{ color: tokens.colors.text.tertiary }}>{label}</span>\n <span style={{\n color: tokens.colors.text.primary,\n fontFamily: mono ? tokens.typography.fontFamily.mono : 'inherit',\n fontSize: mono ? '0.68rem' : tokens.typography.fontSize.xs,\n }}>{value}</span>\n </div>\n );\n}\n\n/** Metadata group heading */\nfunction MetaGroup({ label, tokens, children }: { label: string; tokens: any; children: React.ReactNode }) {\n return (\n <div style={{ marginTop: 6 }}>\n <div style={{\n fontSize: '0.6rem', fontWeight: 600, color: tokens.colors.text.tertiary,\n textTransform: 'uppercase' as const, letterSpacing: '0.06em',\n padding: '4px 0 2px', borderTop: `1px solid ${tokens.colors.border.muted}`,\n }}>{label}</div>\n {children}\n </div>\n );\n}\n\n/**\n * Image detail panel showing preview, metadata, camera settings, and position.\n * Displayed inline when a gallery thumbnail is clicked.\n */\nfunction CaptureImageDetail({ img, tokens, serverId, onClose }: {\n img: CapturedImage; tokens: any; serverId: string; onClose: () => void;\n}) {\n const a = img.args;\n const time = new Date(img.timestamp).toLocaleString('en-US', {\n hour12: false, year: 'numeric', month: 'short', day: 'numeric',\n hour: '2-digit', minute: '2-digit', second: '2-digit',\n });\n\n return (\n <div style={{\n backgroundColor: tokens.colors.background.elevated,\n display: 'flex',\n gap: '8px',\n padding: '8px',\n minHeight: 0,\n }}>\n {/* Left: Image preview + actions */}\n <div style={{\n flex: '1 1 50%', display: 'flex', flexDirection: 'column' as const, gap: '6px',\n minWidth: 0,\n }}>\n <img\n src={img.dataUrl}\n alt={`capture_${serverId}_${img.reqId}`}\n style={{\n width: '100%', borderRadius: tokens.borderRadius.sm,\n border: `1px solid ${tokens.colors.border.muted}`,\n objectFit: 'contain' as const,\n }}\n />\n <div style={{ display: 'flex', gap: '4px' }}>\n <button\n type=\"button\"\n onClick={() => downloadCapturedImage(img, serverId)}\n style={{\n flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '4px',\n padding: '5px 0', borderRadius: tokens.borderRadius.sm,\n fontSize: tokens.typography.fontSize.xs, fontWeight: 500,\n fontFamily: tokens.typography.fontFamily.mono,\n color: tokens.colors.accent.primary,\n backgroundColor: `${tokens.colors.accent.primary}12`,\n border: `1px solid ${tokens.colors.accent.primary}30`,\n cursor: 'pointer',\n }}\n >\n <DownloadIcon /> Download PNG\n </button>\n <button\n type=\"button\"\n onClick={onClose}\n style={{\n padding: '5px 8px', borderRadius: tokens.borderRadius.sm,\n fontSize: tokens.typography.fontSize.xs,\n color: tokens.colors.text.tertiary,\n backgroundColor: 'transparent',\n border: `1px solid ${tokens.colors.border.muted}`,\n cursor: 'pointer',\n }}\n >\n <CloseIcon />\n </button>\n </div>\n </div>\n\n {/* Right: Metadata */}\n <div style={{\n flex: '1 1 50%', overflowY: 'auto' as const, minWidth: 0,\n }}>\n <MetaGroup label=\"File Info\" tokens={tokens}>\n <MetaRow label=\"Filename\" value={`capture_${serverId}_${img.reqId}.${img.args?.format === 'jpeg' ? 'jpg' : 'png'}`} tokens={tokens} mono />\n <MetaRow label=\"Req ID\" value={`#${img.reqId}`} tokens={tokens} mono />\n <MetaRow label=\"Size\" value={fmtBytes(img.bytes)} tokens={tokens} />\n <MetaRow label=\"Format\" value={img.args?.format === 'jpeg' ? `JPEG (q=${Math.round((img.args.jpeg_quality ?? 0.92) * 100)}%)` : 'PNG'} tokens={tokens} />\n <MetaRow label=\"Captured\" value={time} tokens={tokens} />\n </MetaGroup>\n\n {/* Camera settings (only if args are available) */}\n {a && (\n <>\n <MetaGroup label=\"Camera\" tokens={tokens}>\n <MetaRow label=\"Resolution\" value={`${a.resolution}px`} tokens={tokens} />\n <MetaRow label=\"FOV\" value={`${a.fov}°`} tokens={tokens} />\n <MetaRow label=\"Focal Length\" value={`${a.focal_length}mm`} tokens={tokens} />\n <MetaRow label=\"Aperture\" value={`f/${a.aperture}`} tokens={tokens} />\n <MetaRow label=\"CoC\" value={`${a.coc}mm`} tokens={tokens} />\n <MetaRow label=\"Pixel Pitch\" value={`${a.pixel_pitch}mm`} tokens={tokens} />\n <MetaRow label=\"Focus Dist\" value={`${a.focusing_distance}m`} tokens={tokens} />\n <MetaRow label=\"Monochrome\" value={a.monochromatic ? 'Yes' : 'No'} tokens={tokens} />\n <MetaRow label=\"Sample\" value={a.sample ? 'Yes (½ res)' : 'No'} tokens={tokens} />\n </MetaGroup>\n\n <MetaGroup label=\"Position & Orientation\" tokens={tokens}>\n <MetaRow label=\"X\" value={`${a.position[0].toFixed(2)} km`} tokens={tokens} mono />\n <MetaRow label=\"Y\" value={`${a.position[1].toFixed(2)} km`} tokens={tokens} mono />\n <MetaRow label=\"Z\" value={`${a.position[2].toFixed(2)} km`} tokens={tokens} mono />\n <MetaRow label=\"Heading\" value={`${a.rotation[0].toFixed(1)}°`} tokens={tokens} mono />\n <MetaRow label=\"Pitch\" value={`${a.rotation[1].toFixed(1)}°`} tokens={tokens} mono />\n <MetaRow label=\"Roll\" value={`${a.rotation[2].toFixed(1)}°`} tokens={tokens} mono />\n </MetaGroup>\n </>\n )}\n </div>\n </div>\n );\n}\n\n/**\n * Collapsible image gallery with thumbnails, detail panel, download per-image, and download-all.\n * Part of the Capture component's built-in UI.\n */\nfunction CaptureGallery({ images, expanded, onToggle, tokens, serverId }: {\n images: CapturedImage[]; expanded: boolean; onToggle: () => void; tokens: any; serverId: string;\n}) {\n const [selectedIdx, setSelectedIdx] = useState<number | null>(null);\n const selectedImg = selectedIdx !== null ? images[selectedIdx] : null;\n\n return (\n <div style={{\n borderTop: `1px solid ${tokens.colors.border.muted}`,\n /* Fill remaining space when expanded */\n ...(expanded ? { flex: 1, display: 'flex', flexDirection: 'column' as const, minHeight: 0, overflow: 'hidden' } : {}),\n }}>\n {/* Header row: toggle + count + download-all */}\n <div style={{\n display: 'flex', alignItems: 'center', gap: '4px', flexShrink: 0,\n }}>\n <button\n type=\"button\"\n onClick={onToggle}\n aria-expanded={expanded}\n aria-label={`Captured images, ${images.length} total`}\n style={{\n flex: 1, display: 'flex', alignItems: 'center', gap: '8px',\n padding: '8px 12px',\n fontSize: tokens.typography.fontSize.sm,\n fontWeight: 500,\n fontFamily: tokens.typography.fontFamily.primary,\n color: tokens.colors.text.secondary,\n backgroundColor: 'transparent',\n border: 'none',\n cursor: 'pointer',\n outline: 'none',\n textAlign: 'left' as const,\n }}\n >\n <ChevronIcon expanded={expanded} />\n <span>Captured Images</span>\n {images.length > 0 && (\n <span style={{\n fontSize: tokens.typography.fontSize.xs,\n color: tokens.colors.text.tertiary,\n fontFamily: tokens.typography.fontFamily.mono,\n }}>\n ({images.length})\n </span>\n )}\n </button>\n\n {/* Download All button (visible when expanded and has images) */}\n {expanded && images.length > 0 && (\n <button\n type=\"button\"\n onClick={() => downloadAllImages(images, serverId)}\n title={`Download all ${images.length} images`}\n style={{\n display: 'flex', alignItems: 'center', gap: '4px',\n padding: '4px 8px', marginRight: 8,\n fontSize: '0.62rem', fontWeight: 500,\n fontFamily: tokens.typography.fontFamily.mono,\n color: tokens.colors.text.tertiary,\n backgroundColor: 'transparent',\n border: `1px solid ${tokens.colors.border.muted}`,\n borderRadius: tokens.borderRadius.sm,\n cursor: 'pointer',\n whiteSpace: 'nowrap' as const,\n }}\n >\n <DownloadIcon size={10} /> All\n </button>\n )}\n </div>\n\n {expanded && (\n <div style={{\n flex: 1, minHeight: 0, display: 'flex',\n overflow: 'hidden',\n }}>\n {images.length === 0 ? (\n <div style={{\n fontSize: tokens.typography.fontSize.sm,\n color: tokens.colors.text.tertiary,\n padding: '12px',\n textAlign: 'center' as const,\n width: '100%',\n }}>\n No images captured yet\n </div>\n ) : (\n <>\n {/* Left: scrollable thumbnail grid\n - Detail panel open (200px strip): 2 columns fixed\n - Full width: 4 columns on desktop, collapses via auto-fill on narrow viewports */}\n <div style={{\n width: selectedImg ? 200 : '100%',\n flexShrink: 0,\n overflowY: 'auto' as const,\n padding: '6px',\n borderRight: selectedImg ? `1px solid ${tokens.colors.border.muted}` : 'none',\n display: 'grid',\n gridTemplateColumns: selectedImg\n ? 'repeat(2, 1fr)'\n : 'repeat(auto-fill, minmax(min(120px, 100%), 1fr))',\n gridAutoRows: 'min-content',\n gap: '4px',\n alignContent: 'start' as const,\n }}>\n {images.map((img, idx) => {\n const isSelected = selectedIdx === idx;\n return (\n <div\n key={`${img.reqId}-${img.timestamp}`}\n onClick={() => setSelectedIdx(isSelected ? null : idx)}\n style={{\n position: 'relative' as const,\n aspectRatio: '1',\n borderRadius: tokens.borderRadius.sm,\n overflow: 'hidden',\n border: `2px solid ${isSelected ? tokens.colors.accent.primary : tokens.colors.border.muted}`,\n backgroundColor: tokens.colors.background.elevated,\n cursor: 'pointer',\n transition: 'border-color 0.15s',\n }}\n >\n <img\n src={img.dataUrl}\n alt={`capture_${serverId}_${img.reqId}`}\n style={{ width: '100%', height: '100%', objectFit: 'cover' as const, display: 'block' }}\n />\n {/* Bottom label: #reqId + size */}\n <div style={{\n position: 'absolute' as const,\n bottom: 0, left: 0, right: 0,\n display: 'flex', justifyContent: 'space-between',\n padding: '2px 4px',\n fontSize: '9px',\n fontFamily: tokens.typography.fontFamily.mono,\n color: '#fff',\n backgroundColor: 'rgba(0, 0, 0, 0.65)',\n }}>\n <span>#{img.reqId}</span>\n <span>{fmtBytes(img.bytes)}</span>\n </div>\n </div>\n );\n })}\n </div>\n\n {/* Right: detail panel for selected image */}\n {selectedImg && (\n <div style={{ flex: 1, minWidth: 0, overflowY: 'auto' as const }}>\n <CaptureImageDetail\n img={selectedImg}\n tokens={tokens}\n serverId={serverId}\n onClose={() => setSelectedIdx(null)}\n />\n </div>\n )}\n </>\n )}\n </div>\n )}\n </div>\n );\n}\n\nexport default Capture;\n"],"names":["Capture"],"mappings":"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"}
|