@zendir/ui 0.2.19 → 0.2.21
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 +17 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/react/3d/CesiumCaptureSource.d.ts +119 -0
- package/dist/react/3d/CesiumCaptureSource.js +307 -0
- package/dist/react/3d/CesiumCaptureSource.js.map +1 -0
- package/dist/react/3d/ZenSpace3DUtils.js +9 -0
- package/dist/react/3d/ZenSpace3DUtils.js.map +1 -0
- package/dist/react/3d/capturePngAnalysis.d.ts +16 -0
- package/dist/react/3d/index.d.ts +4 -0
- package/dist/react/core/Capture.d.ts +140 -0
- package/dist/react/core/Capture.js +804 -0
- package/dist/react/core/Capture.js.map +1 -0
- package/dist/react/core/ChatPanel.js +3 -2
- package/dist/react/core/ChatPanel.js.map +1 -1
- package/dist/react/core/SideNav.js +5 -6
- package/dist/react/core/SideNav.js.map +1 -1
- package/dist/react/core/SidePanel.js +6 -6
- package/dist/react/core/SidePanel.js.map +1 -1
- package/dist/react/core/capture-placeholder.png.js +5 -0
- package/dist/react/core/capture-placeholder.png.js.map +1 -0
- package/dist/react/core/index.d.ts +2 -0
- package/dist/react/index.d.ts +3 -2
- package/dist/react.js +7 -0
- package/dist/react.js.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1 @@
|
|
|
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":";;;;;AAwIA,eAAe,yBAA8C;AAC3D,MAAI;AACF,UAAM,OAAO;AACb,UAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,WAAO,QAAQ;AACf,WAAO,SAAS;AAChB,UAAM,MAAM,OAAO,WAAW,IAAI;AAGlC,UAAM,KAAK,IAAI,qBAAqB,OAAO,GAAG,OAAO,GAAG,IAAI,OAAO,GAAG,OAAO,GAAG,IAAI;AACpF,OAAG,aAAa,GAAG,SAAS;AAC5B,OAAG,aAAa,GAAG,SAAS;AAC5B,QAAI,YAAY;AAChB,QAAI,SAAS,GAAG,GAAG,MAAM,IAAI;AAG7B,QAAI,YAAY;AAChB,aAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AAC3B,YAAM,MAAQ,IAAI,KAAM,MAAM;AAC9B,YAAM,MAAQ,IAAI,KAAM,MAAM;AAC9B,YAAM,MAAO,IAAI,IAAK,KAAK;AAC3B,UAAI,UAAA;AACJ,UAAI,IAAI,IAAI,IAAI,IAAI,GAAG,KAAK,KAAK,CAAC;AAClC,UAAI,KAAA;AAAA,IACN;AAGA,QAAI,cAAc;AAClB,QAAI,YAAY;AAChB,QAAI,UAAA;AACJ,QAAI,OAAO,OAAO,GAAG,EAAE;AACvB,QAAI,OAAO,OAAO,GAAG,OAAO,EAAE;AAC9B,QAAI,OAAO,IAAI,OAAO,CAAC;AACvB,QAAI,OAAO,OAAO,IAAI,OAAO,CAAC;AAC9B,QAAI,OAAA;AACJ,QAAI,UAAA;AACJ,QAAI,IAAI,OAAO,GAAG,OAAO,GAAG,IAAI,GAAG,KAAK,KAAK,CAAC;AAC9C,QAAI,OAAA;AAGJ,QAAI,YAAY;AAChB,QAAI,OAAO;AACX,QAAI,YAAY;AAChB,QAAI,eAAe;AACnB,QAAI,SAAS,UAAU,OAAO,GAAG,OAAO,IAAI,EAAE;AAG9C,QAAI,YAAY;AAChB,QAAI,OAAO;AACX,QAAI,SAAS,eAAe,OAAO,GAAG,OAAO,IAAI,EAAE;AAGnD,QAAI,YAAY;AAChB,QAAI,OAAO;AACX,QAAI,SAAS,GAAG,IAAI,IAAI,IAAI,IAAI,OAAO,GAAG,OAAO,EAAE;AAEnD,UAAM,OAAO,MAAM,IAAI;AAAA,MAAc,CAAC,YACpC,OAAO,OAAO,CAAC,MAAM,QAAQ,CAAE,GAAG,WAAW;AAAA,IAAA;AAE/C,WAAO,IAAI,WAAW,MAAM,KAAK,aAAa;AAAA,EAChD,QAAQ;AAEN,WAAO,IAAI,WAAW;AAAA,MACpB;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAC1C;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAC1C;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAC1C;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAC1C;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAC1C;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAC1C;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAC1C;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAC1C;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,IAAA,CACzB;AAAA,EACH;AACF;AAGA,IAAI,mBAAsC;AAO1C,eAAe,uBAA4C;AACzD,MAAI,iBAAkB,QAAO;AAC7B,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,mBAAmB;AAChD,QAAI,CAAC,SAAS,GAAI,OAAM,IAAI,MAAM,QAAQ,SAAS,MAAM,EAAE;AAC3D,UAAM,MAAM,MAAM,SAAS,YAAA;AAC3B,QAAI,IAAI,aAAa,IAAK,OAAM,IAAI,MAAM,0BAA0B;AACpE,uBAAmB,IAAI,WAAW,GAAG;AAAA,EACvC,QAAQ;AACN,uBAAmB,MAAM,uBAAA;AAAA,EAC3B;AACA,SAAO;AACT;AAMA,eAAsB,8BAAmD;AACvE,SAAO,qBAAA;AACT;AAgDO,SAAS,WAAW,SAA+C;AACxE,QAAM,cAAc,QAAO,mCAAS,aAAY,OAAO,YAAY;AACnE,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAS,KAAK;AAC1C,QAAM,CAAC,cAAc,eAAe,IAAI,SAAS,CAAC;AAClD,QAAM,CAAC,aAAa,cAAc,IAAI,SAAgC,IAAI;AAC1E,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,SAA0B,CAAA,CAAE;AAExE,QAAM,iBAAiB,OAAO,mCAAS,WAAW;AAClD,iBAAe,UAAU,mCAAS;AAElC,QAAM,YAAY,OAAO,MAAM;AAC/B,YAAU,UAAU;AAGpB,QAAM,gBAAgB,OAAgC,oBAAI,KAAK;AAI/D,QAAM,kBAAkB,OAA4F,IAAI;AACxH,QAAM,kBAAkB,OAAO,KAAK;AACpC,QAAM,mBAAmB,OAA6B,IAAI;AAE1D,QAAM,aAAa,YAAY,YAAY;AACzC,QAAI,gBAAgB,WAAW,gBAAgB,QAAS;AACxD,QAAI;AACF,YAAM,EAAE,0BAAA,IAA8B,MAAM,OAAO,8BAA2B;AAC9E,sBAAgB,UAAU,MAAM,0BAAA;AAAA,IAClC,QAAQ;AACN,sBAAgB,UAAU;AAAA,IAC5B;AAAA,EACF,GAAG,CAAA,CAAE;AAGL,QAAM,WAAU,mCAAS,aAAY;AACrC,YAAU,MAAM;AACd,QAAI,eAAe,WAAW,CAAC,QAAS;AACxC,qBAAiB,UAAU,WAAA;AAAA,EAC7B,GAAG,CAAC,SAAS,UAAU,CAAC;AAGxB,YAAU,MAAM;AACd,WAAO,MAAM;;AACX,4BAAgB,YAAhB,mBAAyB;AACzB,sBAAgB,UAAU;AAAA,IAC5B;AAAA,EACF,GAAG,CAAA,CAAE;AAEL,QAAM,UAAU,YAAY,OAAO,YAAiD;AAClF,QAAI,UAAU,QAAS,QAAO,IAAI,WAAW,CAAC;AAE9C,QAAI,CAAC,WAAW,QAAQ,SAAS,aAAa,OAAO,QAAQ,WAAW,UAAU;AAChF,aAAO,IAAI,WAAW,CAAC;AAAA,IACzB;AAGA,UAAM,SAAS,cAAc,QAAQ,IAAI,QAAQ,MAAM;AACvD,QAAI,OAAQ,QAAO;AAEnB,QAAI;AACJ,UAAM,MAAM,eAAe;AAE3B,QAAI,eAAe,YAAY;AAC7B,cAAQ;AAAA,IACV,WAAW,OAAO,QAAQ,YAAY;AACpC,cAAQ,MAAM,IAAI,QAAQ,IAAI;AAAA,IAChC,OAAO;AAEL,UAAI,CAAC,gBAAgB,WAAW,CAAC,gBAAgB,SAAS;AACxD,YAAI,iBAAiB,SAAS;AAC5B,gBAAM,iBAAiB;AAAA,QACzB,OAAO;AACL,gBAAM,WAAA;AAAA,QACR;AAAA,MACF;AACA,UAAI,gBAAgB,SAAS;AAC3B,gBAAQ,MAAM,gBAAgB,QAAQ,QAAQ,QAAQ,IAAI;AAAA,MAC5D,OAAO;AACL,gBAAQ,MAAM,qBAAA;AAAA,MAChB;AAAA,IACF;AAGA,kBAAc,QAAQ,IAAI,QAAQ,QAAQ,KAAK;AAE/C,oBAAgB,CAAA,MAAK,IAAI,CAAC;AAC1B,mBAAe,OAAO;AAEtB,UAAM,WAAW,QAAQ,KAAK,WAAW,SAAS,eAAe;AACjE,UAAM,OAAO,IAAI,KAAK,CAAC,KAA4B,GAAG,EAAE,MAAM,UAAU;AACxE,UAAM,UAAU,IAAI,gBAAgB,IAAI;AACxC,sBAAkB,CAAA,SAAQ;AAAA,MACxB,EAAE,OAAO,QAAQ,QAAQ,SAAS,WAAW,KAAK,IAAA,GAAO,OAAO,MAAM,QAAQ,MAAM,QAAQ,KAAA;AAAA,MAC5F,GAAG;AAAA,IAAA,CACJ;AAED,WAAO;AAAA,EACT,GAAG,CAAA,CAAE;AAEL,SAAO;AAAA,IACL,UAAU,YAAY;AAAA,IACtB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAEJ;AAIA,MAAM,YAAY,CAAC,EAAE,OAAO,SAC1B,qBAAC,OAAA,EAAI,OAAO,MAAM,QAAQ,MAAM,SAAQ,aAAY,MAAK,gBACvD,UAAA;AAAA,EAAA,oBAAC,QAAA,EAAK,GAAE,KAAI,GAAE,KAAI,OAAM,KAAI,QAAO,MAAK,IAAG,IAAA,CAAI;AAAA,EAC/C,oBAAC,QAAA,EAAK,GAAE,MAAK,GAAE,KAAI,OAAM,KAAI,QAAO,MAAK,IAAG,IAAA,CAAI;AAAA,GAClD;AAGF,MAAM,WAAW,CAAC,EAAE,OAAO,SACzB,oBAAC,SAAI,OAAO,MAAM,QAAQ,MAAM,SAAQ,aAAY,MAAK,gBACvD,8BAAC,WAAA,EAAQ,QAAO,kBAAiB,GACnC;AAGF,MAAM,cAAc,CAAC,EAAE,OAAO,IAAI,eAChC;AAAA,EAAC;AAAA,EAAA;AAAA,IACC,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,SAAQ;AAAA,IACR,MAAK;AAAA,IACL,QAAO;AAAA,IACP,aAAY;AAAA,IACZ,eAAc;AAAA,IACd,gBAAe;AAAA,IACf,OAAO;AAAA,MACL,YAAY;AAAA,MACZ,WAAW,WAAW,kBAAkB;AAAA,IAAA;AAAA,IAG1C,UAAA,oBAAC,YAAA,EAAS,QAAO,iBAAA,CAAiB;AAAA,EAAA;AACpC;AAGF,MAAM,WAAW,CAAC,EAAE,OAAO,SACzB,qBAAC,OAAA,EAAI,OAAO,MAAM,QAAQ,MAAM,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SACzI,UAAA;AAAA,EAAA,oBAAC,QAAA,EAAK,GAAE,KAAI,GAAE,KAAI,OAAM,MAAK,QAAO,MAAK,IAAG,KAAI,IAAG,KAAI;AAAA,EACvD,oBAAC,QAAA,EAAK,GAAE,0DAAA,CAA0D;AAAA,GACpE;AAGF,MAAM,YAAY,CAAC,EAAE,OAAO,IAAI,MAAA,MAC9B,oBAAC,OAAA,EAAI,OAAO,MAAM,QAAQ,MAAM,SAAQ,aAAY,MAAK,QAAO,QAAQ,OAAO,aAAY,OAAM,eAAc,SAAQ,gBAAe,SACpI,UAAA,oBAAC,YAAA,EAAS,QAAO,kBAAiB,GACpC;AAWK,MAAM,UAAU,KAAK,WAAwC,SAASA,SAC3E;AAAA,EACE,YAAY;AAAA,EACZ;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV;AAAA,EACA;AAAA,EACA,yBAAyB;AAAA,EACzB;AACF,GACA,KACA;AACA,QAAM,EAAE,OAAA,IAAW,SAAA;AACnB,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,IACE,WAAW,EAAE,aAAa,SAAS,UAAU,cAAc;AAC/D,QAAM,EAAE,MAAM,OAAA,IAAW,mBAAA;AACzB,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,SAAS,sBAAsB;AAC7E,QAAM,CAAC,YAAY,aAAa,IAAI,SAAwB,IAAI;AAChE,QAAM,gBAAgB,OAAO,KAAK;AAElC,YAAU,MAAM;AACd,QAAI,WAAW,CAAC,cAAc,SAAS;AACrC,oBAAc,UAAU;AACxB,cAAQ,QAAQ;AAAA,IAClB;AAAA,EACF,GAAG,CAAC,SAAS,QAAQ,CAAC;AAEtB,QAAM,iBAAiB,YAAY,OAAO,YAA4B;AACpE,UAAM,QAAQ,MAAM,QAAQ,OAAO;AACnC,QAAI,MAAM,SAAS,GAAG;AACpB,6CAAY;AAAA,IACd;AACA,WAAO;AAAA,EACT,GAAG,CAAC,SAAS,SAAS,CAAC;AAEvB,sBAAoB,KAAK,OAAO;AAAA,IAC9B,SAAS;AAAA,IACT;AAAA,EAAA,IACE,CAAC,gBAAgB,QAAQ,CAAC;AAE9B,QAAM,cAAc,YAChB,OAAO,OAAO,OAAO,SACrB,OAAO,OAAO,OAAO;AACzB,QAAM,cAAc,YAAY,cAAc;AAE9C,QAAM,UAA+B;AAAA,IACnC,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,UAAU,OAAO,WAAW,SAAS;AAAA,IACrC,YAAY;AAAA,IACZ,YAAY,OAAO,WAAW,WAAW;AAAA,IACzC,OAAO,OAAO,OAAO,KAAK;AAAA,IAC1B,iBAAiB;AAAA,IACjB,QAAQ,aAAa,OAAO,OAAO,OAAO,KAAK;AAAA,IAC/C,cAAc,OAAO,aAAa;AAAA,IAClC,QAAQ;AAAA,IACR,YAAY,OAAO,UAAU;AAAA,IAC7B,SAAS;AAAA,IACT,YAAY;AAAA,EAAA;AAGd,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,OAAO;AAAA,QACL,SAAS;AAAA,QACT,eAAe;AAAA,QACf,cAAc,OAAO,aAAa;AAAA,QAClC,QAAQ,aAAa,OAAO,OAAO,OAAO,OAAO;AAAA,QACjD,iBAAiB,OAAO,OAAO,WAAW;AAAA,QAC1C,UAAU;AAAA;AAAA,QAEV,GAAI,kBAAkB,EAAE,MAAM,GAAG,WAAW,EAAA,IAAM,CAAA;AAAA,QAClD,GAAG;AAAA,MAAA;AAAA,MAIL,UAAA;AAAA,QAAA;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,OAAO;AAAA,cACL,SAAS;AAAA,cACT,YAAY;AAAA,cACZ,gBAAgB;AAAA,cAChB,SAAS;AAAA,cACT,KAAK;AAAA,cACL,WAAW;AAAA,cACX,YAAY;AAAA,YAAA;AAAA,YAId,UAAA;AAAA,cAAA;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBACC,MAAK;AAAA,kBACL,SAAS,MAAM,UAAU,CAAC,MAAM;AAAA,kBAChC,cAAc,MAAM,cAAc,OAAO;AAAA,kBACzC,cAAc,MAAM,cAAc,IAAI;AAAA,kBACtC,cAAY,SAAS,0BAA0B;AAAA,kBAC/C,OAAO;AAAA,oBACL,GAAG;AAAA,oBACH,OAAO,SAAS,OAAO,OAAO,OAAO,UAAU,OAAO,OAAO,KAAK;AAAA,oBAClE,aAAa,SACT,GAAG,OAAO,OAAO,OAAO,OAAO,OAC/B,OAAO,OAAO,OAAO;AAAA,oBACzB,iBAAiB,eAAe,UAC5B,GAAG,OAAO,OAAO,OAAO,OAAO,OAC/B;AAAA,kBAAA;AAAA,kBAGL,UAAA;AAAA,oBAAA,SAAS,oBAAC,UAAA,CAAA,CAAS,IAAK,oBAAC,WAAA,EAAU;AAAA,oBACpC,oBAAC,QAAA,EAAM,UAAA,SAAS,WAAW,QAAA,CAAQ;AAAA,kBAAA;AAAA,gBAAA;AAAA,cAAA;AAAA,cAIrC;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBACC,OAAO;AAAA,oBACL,SAAS;AAAA,oBACT,YAAY;AAAA,oBACZ,KAAK;AAAA,oBACL,UAAU,OAAO,WAAW,SAAS;AAAA,oBACrC,OAAO,OAAO,OAAO,KAAK;AAAA,kBAAA;AAAA,kBAG5B,UAAA;AAAA,oBAAA;AAAA,sBAAC;AAAA,sBAAA;AAAA,wBACC,OAAO;AAAA,0BACL,OAAO;AAAA,0BACP,QAAQ;AAAA,0BACR,cAAc;AAAA,0BACd,iBAAiB;AAAA,0BACjB,YAAY;AAAA,wBAAA;AAAA,sBACd;AAAA,oBAAA;AAAA,oBAEF,oBAAC,UAAM,UAAA,YAAA,CAAY;AAAA,oBAClB,eAAe,KACd;AAAA,sBAAC;AAAA,sBAAA;AAAA,wBACC,OAAO;AAAA,0BACL,UAAU,OAAO,WAAW,SAAS;AAAA,0BACrC,OAAO,OAAO,OAAO,KAAK;AAAA,0BAC1B,YAAY,OAAO,WAAW,WAAW;AAAA,wBAAA;AAAA,wBAE5C,UAAA;AAAA,0BAAA;AAAA,0BACG;AAAA,0BAAa;AAAA,wBAAA;AAAA,sBAAA;AAAA,oBAAA;AAAA,kBACjB;AAAA,gBAAA;AAAA,cAAA;AAAA,cAKJ;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBACC,MAAK;AAAA,kBACL,SAAS,MAAM,KAAK,QAAQ;AAAA,kBAC5B,cAAc,MAAM,cAAc,MAAM;AAAA,kBACxC,cAAc,MAAM,cAAc,IAAI;AAAA,kBACtC,OAAO,SAAS,YAAY,mBAAmB,QAAQ;AAAA,kBACvD,cAAY,SAAS,qBAAqB;AAAA,kBAC1C,OAAO;AAAA,oBACL,GAAG;AAAA,oBACH,OAAO,SACH,OAAO,OAAO,OAAO,SACrB,OAAO,OAAO,KAAK;AAAA,oBACvB,aAAa,SACT,GAAG,OAAO,OAAO,OAAO,MAAM,OAC9B,OAAO,OAAO,OAAO;AAAA,oBACzB,iBAAiB,eAAe,SAC5B,GAAG,OAAO,OAAO,OAAO,OAAO,OAC/B;AAAA,kBAAA;AAAA,kBAGL,UAAA;AAAA,oBAAA,SACG,oBAAC,aAAU,OAAO,OAAO,OAAO,OAAO,OAAA,CAAQ,IAC/C,oBAAC,UAAA,CAAA,CAAS;AAAA,oBACd;AAAA,sBAAC;AAAA,sBAAA;AAAA,wBACC,OAAO;AAAA,0BACL,YAAY,OAAO,WAAW,WAAW;AAAA,0BACzC,UAAU,OAAO,WAAW,SAAS;AAAA,wBAAA;AAAA,wBAGtC,UAAA,SAAS,YAAY,SAAS,MAAM,GAAG,CAAC;AAAA,sBAAA;AAAA,oBAAA;AAAA,kBAC3C;AAAA,gBAAA;AAAA,cAAA;AAAA,YACF;AAAA,UAAA;AAAA,QAAA;AAAA,QAIF;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,QAAQ;AAAA,YACR,UAAU;AAAA,YACV,UAAU,MAAM,mBAAmB,CAAC,eAAe;AAAA,YACnD;AAAA,YACA;AAAA,UAAA;AAAA,QAAA;AAAA,MACF;AAAA,IAAA;AAAA,EAAA;AAGN,CAAC,CAAC;AAKF,SAAS,SAAS,GAAmB;AACnC,MAAI,IAAI,QAAS,QAAO,IAAI,IAAI,SAAS,QAAQ,CAAC,CAAC;AACnD,MAAI,IAAI,KAAM,QAAO,IAAI,IAAI,MAAM,QAAQ,CAAC,CAAC;AAC7C,SAAO,GAAG,CAAC;AACb;AAGA,SAAS,sBAAsB,KAAoB,UAAwB;;AACzE,QAAM,QAAM,SAAI,SAAJ,mBAAU,YAAW,SAAS,QAAQ;AAClD,QAAM,IAAI,SAAS,cAAc,GAAG;AACpC,IAAE,OAAO,IAAI;AACb,IAAE,WAAW,WAAW,QAAQ,IAAI,IAAI,KAAK,IAAI,GAAG;AACpD,IAAE,MAAA;AACJ;AAGA,eAAe,kBAAkB,QAAyB,UAAiC;AACzF,aAAW,OAAO,QAAQ;AACxB,0BAAsB,KAAK,QAAQ;AACnC,UAAM,IAAI,QAAc,CAAA,MAAK,WAAW,GAAG,GAAG,CAAC;AAAA,EACjD;AACF;AAGA,MAAM,eAAe,CAAC,EAAE,OAAO,SAC7B,qBAAC,OAAA,EAAI,OAAO,MAAM,QAAQ,MAAM,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,OAAM,eAAc,SAAQ,gBAAe,SAC3I,UAAA;AAAA,EAAA,oBAAC,QAAA,EAAK,GAAE,4CAAA,CAA4C;AAAA,EACpD,oBAAC,YAAA,EAAS,QAAO,mBAAA,CAAmB;AAAA,EACpC,oBAAC,UAAK,IAAG,MAAK,IAAG,MAAK,IAAG,MAAK,IAAG,IAAA,CAAI;AAAA,GACvC;AAIF,MAAM,YAAY,CAAC,EAAE,OAAO,SAC1B,qBAAC,SAAI,OAAO,MAAM,QAAQ,MAAM,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,OAAM,eAAc,SACpH,UAAA;AAAA,EAAA,oBAAC,QAAA,EAAK,IAAG,MAAK,IAAG,KAAI,IAAG,KAAI,IAAG,KAAA,CAAK;AAAA,EACpC,oBAAC,UAAK,IAAG,KAAI,IAAG,KAAI,IAAG,MAAK,IAAG,KAAA,CAAK;AAAA,GACtC;AAIF,SAAS,QAAQ,EAAE,OAAO,OAAO,QAAQ,QAAuE;AAC9G,SACE,qBAAC,SAAI,OAAO;AAAA,IACV,SAAS;AAAA,IAAQ,gBAAgB;AAAA,IAAiB,YAAY;AAAA,IAC9D,SAAS;AAAA,IAAS,UAAU,OAAO,WAAW,SAAS;AAAA,EAAA,GAEvD,UAAA;AAAA,IAAA,oBAAC,QAAA,EAAK,OAAO,EAAE,OAAO,OAAO,OAAO,KAAK,SAAA,GAAa,UAAA,MAAA,CAAM;AAAA,IAC5D,oBAAC,UAAK,OAAO;AAAA,MACX,OAAO,OAAO,OAAO,KAAK;AAAA,MAC1B,YAAY,OAAO,OAAO,WAAW,WAAW,OAAO;AAAA,MACvD,UAAU,OAAO,YAAY,OAAO,WAAW,SAAS;AAAA,IAAA,GACtD,UAAA,MAAA,CAAM;AAAA,EAAA,GACZ;AAEJ;AAGA,SAAS,UAAU,EAAE,OAAO,QAAQ,YAAuE;AACzG,8BACG,OAAA,EAAI,OAAO,EAAE,WAAW,KACvB,UAAA;AAAA,IAAA,oBAAC,SAAI,OAAO;AAAA,MACV,UAAU;AAAA,MAAU,YAAY;AAAA,MAAK,OAAO,OAAO,OAAO,KAAK;AAAA,MAC/D,eAAe;AAAA,MAAsB,eAAe;AAAA,MACpD,SAAS;AAAA,MAAa,WAAW,aAAa,OAAO,OAAO,OAAO,KAAK;AAAA,IAAA,GACtE,UAAA,OAAM;AAAA,IACT;AAAA,EAAA,GACH;AAEJ;AAMA,SAAS,mBAAmB,EAAE,KAAK,QAAQ,UAAU,WAElD;;AACD,QAAM,IAAI,IAAI;AACd,QAAM,OAAO,IAAI,KAAK,IAAI,SAAS,EAAE,eAAe,SAAS;AAAA,IAC3D,QAAQ;AAAA,IAAO,MAAM;AAAA,IAAW,OAAO;AAAA,IAAS,KAAK;AAAA,IACrD,MAAM;AAAA,IAAW,QAAQ;AAAA,IAAW,QAAQ;AAAA,EAAA,CAC7C;AAED,SACE,qBAAC,SAAI,OAAO;AAAA,IACV,iBAAiB,OAAO,OAAO,WAAW;AAAA,IAC1C,SAAS;AAAA,IACT,KAAK;AAAA,IACL,SAAS;AAAA,IACT,WAAW;AAAA,EAAA,GAGX,UAAA;AAAA,IAAA,qBAAC,SAAI,OAAO;AAAA,MACV,MAAM;AAAA,MAAW,SAAS;AAAA,MAAQ,eAAe;AAAA,MAAmB,KAAK;AAAA,MACzE,UAAU;AAAA,IAAA,GAEV,UAAA;AAAA,MAAA;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,KAAK,IAAI;AAAA,UACT,KAAK,WAAW,QAAQ,IAAI,IAAI,KAAK;AAAA,UACrC,OAAO;AAAA,YACL,OAAO;AAAA,YAAQ,cAAc,OAAO,aAAa;AAAA,YACjD,QAAQ,aAAa,OAAO,OAAO,OAAO,KAAK;AAAA,YAC/C,WAAW;AAAA,UAAA;AAAA,QACb;AAAA,MAAA;AAAA,MAEF,qBAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,KAAK,SAClC,UAAA;AAAA,QAAA;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAS,MAAM,sBAAsB,KAAK,QAAQ;AAAA,YAClD,OAAO;AAAA,cACL,MAAM;AAAA,cAAG,SAAS;AAAA,cAAQ,YAAY;AAAA,cAAU,gBAAgB;AAAA,cAAU,KAAK;AAAA,cAC/E,SAAS;AAAA,cAAS,cAAc,OAAO,aAAa;AAAA,cACpD,UAAU,OAAO,WAAW,SAAS;AAAA,cAAI,YAAY;AAAA,cACrD,YAAY,OAAO,WAAW,WAAW;AAAA,cACzC,OAAO,OAAO,OAAO,OAAO;AAAA,cAC5B,iBAAiB,GAAG,OAAO,OAAO,OAAO,OAAO;AAAA,cAChD,QAAQ,aAAa,OAAO,OAAO,OAAO,OAAO;AAAA,cACjD,QAAQ;AAAA,YAAA;AAAA,YAGV,UAAA;AAAA,cAAA,oBAAC,cAAA,EAAa;AAAA,cAAE;AAAA,YAAA;AAAA,UAAA;AAAA,QAAA;AAAA,QAElB;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAS;AAAA,YACT,OAAO;AAAA,cACL,SAAS;AAAA,cAAW,cAAc,OAAO,aAAa;AAAA,cACtD,UAAU,OAAO,WAAW,SAAS;AAAA,cACrC,OAAO,OAAO,OAAO,KAAK;AAAA,cAC1B,iBAAiB;AAAA,cACjB,QAAQ,aAAa,OAAO,OAAO,OAAO,KAAK;AAAA,cAC/C,QAAQ;AAAA,YAAA;AAAA,YAGV,8BAAC,WAAA,CAAA,CAAU;AAAA,UAAA;AAAA,QAAA;AAAA,MACb,EAAA,CACF;AAAA,IAAA,GACF;AAAA,IAGA,qBAAC,SAAI,OAAO;AAAA,MACV,MAAM;AAAA,MAAW,WAAW;AAAA,MAAiB,UAAU;AAAA,IAAA,GAEvD,UAAA;AAAA,MAAA,qBAAC,WAAA,EAAU,OAAM,aAAY,QAC3B,UAAA;AAAA,QAAA,oBAAC,WAAQ,OAAM,YAAW,OAAO,WAAW,QAAQ,IAAI,IAAI,KAAK,MAAI,SAAI,SAAJ,mBAAU,YAAW,SAAS,QAAQ,KAAK,IAAI,QAAgB,MAAI,MAAC;AAAA,QACzI,oBAAC,SAAA,EAAQ,OAAM,UAAS,OAAO,IAAI,IAAI,KAAK,IAAI,QAAgB,MAAI,KAAA,CAAC;AAAA,QACrE,oBAAC,WAAQ,OAAM,QAAO,OAAO,SAAS,IAAI,KAAK,GAAG,QAAgB;AAAA,QAClE,oBAAC,WAAQ,OAAM,UAAS,SAAO,SAAI,SAAJ,mBAAU,YAAW,SAAS,WAAW,KAAK,OAAO,IAAI,KAAK,gBAAgB,QAAQ,GAAG,CAAC,OAAO,OAAO,OAAA,CAAgB;AAAA,4BACtJ,SAAA,EAAQ,OAAM,YAAW,OAAO,MAAM,OAAA,CAAgB;AAAA,MAAA,GACzD;AAAA,MAGC,KACC,qBAAA,UAAA,EACE,UAAA;AAAA,QAAA,qBAAC,WAAA,EAAU,OAAM,UAAS,QACxB,UAAA;AAAA,UAAA,oBAAC,SAAA,EAAQ,OAAM,cAAa,OAAO,GAAG,EAAE,UAAU,MAAM,OAAA,CAAgB;AAAA,UACxE,oBAAC,WAAQ,OAAM,OAAM,OAAO,GAAG,EAAE,GAAG,KAAK,OAAA,CAAgB;AAAA,UACzD,oBAAC,WAAQ,OAAM,gBAAe,OAAO,GAAG,EAAE,YAAY,MAAM,OAAA,CAAgB;AAAA,UAC5E,oBAAC,WAAQ,OAAM,YAAW,OAAO,KAAK,EAAE,QAAQ,IAAI,OAAA,CAAgB;AAAA,UACpE,oBAAC,WAAQ,OAAM,OAAM,OAAO,GAAG,EAAE,GAAG,MAAM,OAAA,CAAgB;AAAA,UAC1D,oBAAC,WAAQ,OAAM,eAAc,OAAO,GAAG,EAAE,WAAW,MAAM,OAAA,CAAgB;AAAA,UAC1E,oBAAC,WAAQ,OAAM,cAAa,OAAO,GAAG,EAAE,iBAAiB,KAAK,OAAA,CAAgB;AAAA,UAC9E,oBAAC,WAAQ,OAAM,cAAa,OAAO,EAAE,gBAAgB,QAAQ,MAAM,OAAA,CAAgB;AAAA,UACnF,oBAAC,WAAQ,OAAM,UAAS,OAAO,EAAE,SAAS,gBAAgB,MAAM,OAAA,CAAgB;AAAA,QAAA,GAClF;AAAA,QAEA,qBAAC,WAAA,EAAU,OAAM,0BAAyB,QACxC,UAAA;AAAA,UAAA,oBAAC,SAAA,EAAQ,OAAM,KAAI,OAAO,GAAG,EAAE,SAAS,CAAC,EAAE,QAAQ,CAAC,CAAC,OAAO,QAAgB,MAAI,MAAC;AAAA,8BAChF,SAAA,EAAQ,OAAM,KAAI,OAAO,GAAG,EAAE,SAAS,CAAC,EAAE,QAAQ,CAAC,CAAC,OAAO,QAAgB,MAAI,MAAC;AAAA,8BAChF,SAAA,EAAQ,OAAM,KAAI,OAAO,GAAG,EAAE,SAAS,CAAC,EAAE,QAAQ,CAAC,CAAC,OAAO,QAAgB,MAAI,MAAC;AAAA,8BAChF,SAAA,EAAQ,OAAM,WAAU,OAAO,GAAG,EAAE,SAAS,CAAC,EAAE,QAAQ,CAAC,CAAC,KAAK,QAAgB,MAAI,MAAC;AAAA,8BACpF,SAAA,EAAQ,OAAM,SAAQ,OAAO,GAAG,EAAE,SAAS,CAAC,EAAE,QAAQ,CAAC,CAAC,KAAK,QAAgB,MAAI,MAAC;AAAA,8BAClF,SAAA,EAAQ,OAAM,QAAO,OAAO,GAAG,EAAE,SAAS,CAAC,EAAE,QAAQ,CAAC,CAAC,KAAK,QAAgB,MAAI,KAAA,CAAC;AAAA,QAAA,EAAA,CACpF;AAAA,MAAA,EAAA,CACF;AAAA,IAAA,EAAA,CAEJ;AAAA,EAAA,GACF;AAEJ;AAMA,SAAS,eAAe,EAAE,QAAQ,UAAU,UAAU,QAAQ,YAE3D;AACD,QAAM,CAAC,aAAa,cAAc,IAAI,SAAwB,IAAI;AAClE,QAAM,cAAc,gBAAgB,OAAO,OAAO,WAAW,IAAI;AAEjE,SACE,qBAAC,SAAI,OAAO;AAAA,IACV,WAAW,aAAa,OAAO,OAAO,OAAO,KAAK;AAAA;AAAA,IAElD,GAAI,WAAW,EAAE,MAAM,GAAG,SAAS,QAAQ,eAAe,UAAmB,WAAW,GAAG,UAAU,SAAA,IAAa,CAAA;AAAA,EAAC,GAGnH,UAAA;AAAA,IAAA,qBAAC,SAAI,OAAO;AAAA,MACV,SAAS;AAAA,MAAQ,YAAY;AAAA,MAAU,KAAK;AAAA,MAAO,YAAY;AAAA,IAAA,GAE/D,UAAA;AAAA,MAAA;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS;AAAA,UACT,iBAAe;AAAA,UACf,cAAY,oBAAoB,OAAO,MAAM;AAAA,UAC7C,OAAO;AAAA,YACL,MAAM;AAAA,YAAG,SAAS;AAAA,YAAQ,YAAY;AAAA,YAAU,KAAK;AAAA,YACrD,SAAS;AAAA,YACT,UAAU,OAAO,WAAW,SAAS;AAAA,YACrC,YAAY;AAAA,YACZ,YAAY,OAAO,WAAW,WAAW;AAAA,YACzC,OAAO,OAAO,OAAO,KAAK;AAAA,YAC1B,iBAAiB;AAAA,YACjB,QAAQ;AAAA,YACR,QAAQ;AAAA,YACR,SAAS;AAAA,YACT,WAAW;AAAA,UAAA;AAAA,UAGb,UAAA;AAAA,YAAA,oBAAC,eAAY,UAAoB;AAAA,YACjC,oBAAC,UAAK,UAAA,kBAAA,CAAe;AAAA,YACpB,OAAO,SAAS,KACf,qBAAC,UAAK,OAAO;AAAA,cACX,UAAU,OAAO,WAAW,SAAS;AAAA,cACrC,OAAO,OAAO,OAAO,KAAK;AAAA,cAC1B,YAAY,OAAO,WAAW,WAAW;AAAA,YAAA,GACxC,UAAA;AAAA,cAAA;AAAA,cACC,OAAO;AAAA,cAAO;AAAA,YAAA,EAAA,CAClB;AAAA,UAAA;AAAA,QAAA;AAAA,MAAA;AAAA,MAKH,YAAY,OAAO,SAAS,KAC3B;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS,MAAM,kBAAkB,QAAQ,QAAQ;AAAA,UACjD,OAAO,gBAAgB,OAAO,MAAM;AAAA,UACpC,OAAO;AAAA,YACL,SAAS;AAAA,YAAQ,YAAY;AAAA,YAAU,KAAK;AAAA,YAC5C,SAAS;AAAA,YAAW,aAAa;AAAA,YACjC,UAAU;AAAA,YAAW,YAAY;AAAA,YACjC,YAAY,OAAO,WAAW,WAAW;AAAA,YACzC,OAAO,OAAO,OAAO,KAAK;AAAA,YAC1B,iBAAiB;AAAA,YACjB,QAAQ,aAAa,OAAO,OAAO,OAAO,KAAK;AAAA,YAC/C,cAAc,OAAO,aAAa;AAAA,YAClC,QAAQ;AAAA,YACR,YAAY;AAAA,UAAA;AAAA,UAGd,UAAA;AAAA,YAAA,oBAAC,cAAA,EAAa,MAAM,GAAA,CAAI;AAAA,YAAE;AAAA,UAAA;AAAA,QAAA;AAAA,MAAA;AAAA,IAC5B,GAEJ;AAAA,IAEC,YACC,oBAAC,OAAA,EAAI,OAAO;AAAA,MACV,MAAM;AAAA,MAAG,WAAW;AAAA,MAAG,SAAS;AAAA,MAChC,UAAU;AAAA,IAAA,GAET,UAAA,OAAO,WAAW,IACjB,oBAAC,SAAI,OAAO;AAAA,MACV,UAAU,OAAO,WAAW,SAAS;AAAA,MACrC,OAAO,OAAO,OAAO,KAAK;AAAA,MAC1B,SAAS;AAAA,MACT,WAAW;AAAA,MACX,OAAO;AAAA,IAAA,GACN,UAAA,yBAAA,CAEH,IAEA,qBAAA,UAAA,EAIE,UAAA;AAAA,MAAA,oBAAC,SAAI,OAAO;AAAA,QACV,OAAO,cAAc,MAAM;AAAA,QAC3B,YAAY;AAAA,QACZ,WAAW;AAAA,QACX,SAAS;AAAA,QACT,aAAa,cAAc,aAAa,OAAO,OAAO,OAAO,KAAK,KAAK;AAAA,QACvE,SAAS;AAAA,QACT,qBAAqB,cACjB,mBACA;AAAA,QACJ,cAAc;AAAA,QACd,KAAK;AAAA,QACL,cAAc;AAAA,MAAA,GAEb,UAAA,OAAO,IAAI,CAAC,KAAK,QAAQ;AACxB,cAAM,aAAa,gBAAgB;AACnC,eACE;AAAA,UAAC;AAAA,UAAA;AAAA,YAEC,SAAS,MAAM,eAAe,aAAa,OAAO,GAAG;AAAA,YACrD,OAAO;AAAA,cACL,UAAU;AAAA,cACV,aAAa;AAAA,cACb,cAAc,OAAO,aAAa;AAAA,cAClC,UAAU;AAAA,cACV,QAAQ,aAAa,aAAa,OAAO,OAAO,OAAO,UAAU,OAAO,OAAO,OAAO,KAAK;AAAA,cAC3F,iBAAiB,OAAO,OAAO,WAAW;AAAA,cAC1C,QAAQ;AAAA,cACR,YAAY;AAAA,YAAA;AAAA,YAGd,UAAA;AAAA,cAAA;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBACC,KAAK,IAAI;AAAA,kBACT,KAAK,WAAW,QAAQ,IAAI,IAAI,KAAK;AAAA,kBACrC,OAAO,EAAE,OAAO,QAAQ,QAAQ,QAAQ,WAAW,SAAkB,SAAS,QAAA;AAAA,gBAAQ;AAAA,cAAA;AAAA,cAGxF,qBAAC,SAAI,OAAO;AAAA,gBACV,UAAU;AAAA,gBACV,QAAQ;AAAA,gBAAG,MAAM;AAAA,gBAAG,OAAO;AAAA,gBAC3B,SAAS;AAAA,gBAAQ,gBAAgB;AAAA,gBACjC,SAAS;AAAA,gBACT,UAAU;AAAA,gBACV,YAAY,OAAO,WAAW,WAAW;AAAA,gBACzC,OAAO;AAAA,gBACP,iBAAiB;AAAA,cAAA,GAEjB,UAAA;AAAA,gBAAA,qBAAC,QAAA,EAAK,UAAA;AAAA,kBAAA;AAAA,kBAAE,IAAI;AAAA,gBAAA,GAAM;AAAA,gBAClB,oBAAC,QAAA,EAAM,UAAA,SAAS,IAAI,KAAK,EAAA,CAAE;AAAA,cAAA,EAAA,CAC7B;AAAA,YAAA;AAAA,UAAA;AAAA,UA/BK,GAAG,IAAI,KAAK,IAAI,IAAI,SAAS;AAAA,QAAA;AAAA,MAkCxC,CAAC,EAAA,CACH;AAAA,MAGC,eACC,oBAAC,OAAA,EAAI,OAAO,EAAE,MAAM,GAAG,UAAU,GAAG,WAAW,OAAA,GAC7C,UAAA;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,KAAK;AAAA,UACL;AAAA,UACA;AAAA,UACA,SAAS,MAAM,eAAe,IAAI;AAAA,QAAA;AAAA,MAAA,EACpC,CACF;AAAA,IAAA,EAAA,CAEJ,EAAA,CAEJ;AAAA,EAAA,GAEJ;AAEJ;"}
|
|
@@ -1107,9 +1107,10 @@ const ChatPanel = memo(function ChatPanel2({
|
|
|
1107
1107
|
color: tokens.colors.text.primary,
|
|
1108
1108
|
backgroundColor: tokens.colors.background.base,
|
|
1109
1109
|
outline: "none",
|
|
1110
|
-
transition:
|
|
1110
|
+
transition: "border-color 150ms, background-color 150ms",
|
|
1111
1111
|
maxHeight: 120,
|
|
1112
|
-
lineHeight: 1.5
|
|
1112
|
+
lineHeight: 1.5,
|
|
1113
|
+
overflowY: "auto"
|
|
1113
1114
|
}
|
|
1114
1115
|
}
|
|
1115
1116
|
),
|