@optifye/dashboard-core 6.12.50 → 6.12.52

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/types/efficiencyLegend.ts","../src/lib/internal/supabaseClientInstance.ts","../src/lib/services/hlsAuthService.ts","../src/lib/utils/r2Detection.ts","../src/lib/utils/browser.ts","../src/lib/hooks/useHlsStream.ts","../src/lib/hooks/useHlsStreamWithCropping.ts","../src/lib/utils/dashboardReload.ts","../src/lib/utils/rateLimit.ts","../src/lib/utils/sentryContext.ts","../src/lib/services/mixpanelService.ts","../src/lib/constants/idleReasonPresentation.ts","../src/lib/constants/videoGridMetricMode.ts","../src/components/dashboard/grid/videoGridMetricUtils.ts","../src/components/dashboard/grid/VideoCard.tsx","../src/components/automation/RecentFlowSnapshotGrid.tsx"],"names":["match","useRef","cropping","useEffect","mergedProps","useCallback","AlertTriangle","useState","jsxs","jsx"],"mappings":"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","file":"automation.mjs","sourcesContent":["/**\n * Type definitions for efficiency legend configuration\n */\n\nexport interface EfficiencyLegend {\n id: string;\n company_id: string;\n \n // Green threshold (default 80-100%)\n green_min: number;\n green_max: number;\n \n // Yellow threshold (default 70-79%)\n yellow_min: number;\n yellow_max: number;\n \n // Red threshold (default 0-69%)\n red_min: number;\n red_max: number;\n \n // Critical threshold for exclamation mark (default <50%)\n critical_threshold: number;\n \n created_at: string;\n updated_at: string;\n updated_by?: string;\n}\n\nexport interface EfficiencyLegendUpdate {\n green_min: number;\n green_max: number;\n yellow_min: number;\n yellow_max: number;\n red_min: number;\n red_max: number;\n critical_threshold: number;\n}\n\n/**\n * Default efficiency legend settings\n */\nexport const DEFAULT_EFFICIENCY_LEGEND: EfficiencyLegendUpdate = {\n green_min: 80,\n green_max: 100,\n yellow_min: 70,\n yellow_max: 79,\n red_min: 0,\n red_max: 69,\n critical_threshold: 50,\n};\n\n/**\n * Helper function to get color based on efficiency and legend\n */\nexport function getEfficiencyColor(\n efficiency: number,\n legend: EfficiencyLegendUpdate = DEFAULT_EFFICIENCY_LEGEND\n): 'green' | 'yellow' | 'red' {\n if (efficiency >= legend.green_min) {\n return 'green';\n } else if (efficiency >= legend.yellow_min) {\n return 'yellow';\n } else {\n return 'red';\n }\n}\n\n/**\n * Helper function to check if efficiency is critical (should show exclamation mark)\n */\nexport function isEfficiencyCritical(\n efficiency: number,\n legend: EfficiencyLegendUpdate = DEFAULT_EFFICIENCY_LEGEND\n): boolean {\n return efficiency < legend.critical_threshold;\n}\n\n/**\n * Get Tailwind color classes based on efficiency\n */\nexport function getEfficiencyColorClasses(\n efficiency: number,\n legend: EfficiencyLegendUpdate = DEFAULT_EFFICIENCY_LEGEND\n): string {\n const color = getEfficiencyColor(efficiency, legend);\n \n switch (color) {\n case 'green':\n return 'bg-[#00AB45]/90 hover:bg-[#00AB45]/95';\n case 'yellow':\n return 'bg-[#FFB020]/90 hover:bg-[#FFB020]/95';\n case 'red':\n return 'bg-[#E34329]/90 hover:bg-[#E34329]/95';\n default:\n return 'bg-gray-300/90';\n }\n}\n\n/**\n * Get hex color based on efficiency\n */\nexport function getEfficiencyHexColor(\n efficiency: number,\n legend: EfficiencyLegendUpdate = DEFAULT_EFFICIENCY_LEGEND\n): string {\n const color = getEfficiencyColor(efficiency, legend);\n \n switch (color) {\n case 'green':\n return '#00AB45';\n case 'yellow':\n return '#FFB020';\n case 'red':\n return '#E34329';\n default:\n return '#D1D5DB';\n }\n}\n\n/**\n * Get Tailwind text color classes based on efficiency\n */\nexport function getEfficiencyTextColorClasses(\n efficiency: number,\n legend: EfficiencyLegendUpdate = DEFAULT_EFFICIENCY_LEGEND\n): string {\n const color = getEfficiencyColor(efficiency, legend);\n \n switch (color) {\n case 'green':\n return 'text-[#00AB45]';\n case 'yellow':\n return 'text-[#FFB020]';\n case 'red':\n return 'text-[#E34329]';\n default:\n return 'text-gray-900';\n }\n}\n","import { SupabaseClient } from '@supabase/supabase-js';\n\nlet supabaseInstance: SupabaseClient | null = null;\n\nexport const _setSupabaseInstance = (client: SupabaseClient): void => {\n if (supabaseInstance && supabaseInstance !== client) {\n // console.warn(\n // 'Supabase client instance is being overridden in @optifye/dashboard-core. \\n' + \n // 'This may happen during HMR or if SupabaseProvider is re-rendered with a new client instance. \\n' + \n // 'Ensure this is intended if you see this message frequently.'\n // );\n }\n supabaseInstance = client;\n};\n\nexport const _getSupabaseInstance = (): SupabaseClient => {\n if (!supabaseInstance) {\n throw new Error(\n 'Supabase client has not been initialized in @optifye/dashboard-core. ' +\n 'Ensure your application is wrapped in the <SupabaseProvider> component from @optifye/dashboard-core ' +\n 'and that it is rendered before any hook or service using Supabase is called.'\n );\n }\n return supabaseInstance;\n};\n\nexport const _getSupabaseInstanceOptional = (): SupabaseClient | null => {\n return supabaseInstance;\n};\n","/**\n * HLS Authentication Service\n * Provides JWT token retrieval for HLS.js streaming authentication\n */\n\nimport { SupabaseClient } from '@supabase/supabase-js';\n\n/**\n * Get authentication token for HLS.js requests\n * Uses Supabase session to retrieve JWT access token\n *\n * @param supabase - Supabase client instance\n * @returns JWT access token or null if not authenticated\n */\nexport async function getAuthTokenForHls(supabase: SupabaseClient): Promise<string | null> {\n try {\n const { data: { session } } = await supabase.auth.getSession();\n\n if (!session?.access_token) {\n console.warn('[HLS Auth] No active session, R2 streaming may fail');\n return null;\n }\n\n console.log('[HLS Auth] Retrieved token for HLS.js requests');\n return session.access_token;\n } catch (error) {\n console.error('[HLS Auth] Error getting auth token:', error);\n return null;\n }\n}\n","/**\n * R2 Worker URL Detection Utility\n * Detects if a URL points to the R2 Worker for authenticated streaming\n */\n\n/**\n * Detect if a URL points to R2 Worker\n *\n * @param url - URL to check\n * @param r2WorkerDomain - R2 worker domain (e.g., 'https://r2-stream-proxy.optifye-r2.workers.dev')\n * @returns True if URL is an R2 Worker URL, false otherwise\n */\nexport function isR2WorkerUrl(url: string, r2WorkerDomain?: string): boolean {\n if (!url || !r2WorkerDomain) return false;\n\n // Extract domain from worker URL\n try {\n const workerDomain = new URL(r2WorkerDomain).hostname;\n return url.includes(workerDomain);\n } catch {\n // Fallback to string matching if URL parsing fails\n return url.includes(r2WorkerDomain.replace(/https?:\\/\\//, ''));\n }\n}\n","/**\n * Browser detection utilities\n */\n\n/**\n * Detect if current browser is Safari\n * Checks for Safari-specific markers while excluding Chrome/Edge\n *\n * @returns true if browser is Safari (desktop or iOS), false otherwise\n */\nexport function isSafari(): boolean {\n if (typeof window === 'undefined') return false;\n\n const ua = window.navigator.userAgent;\n const isSafariUA = /^((?!chrome|android).)*safari/i.test(ua);\n\n // Also check for iOS Safari\n const isIOS = /iPad|iPhone|iPod/.test(ua) && !(window as any).MSStream;\n\n return isSafariUA || isIOS;\n}\n\n/**\n * Get browser name for logging\n *\n * @returns Browser name string\n */\nexport function getBrowserName(): string {\n if (typeof window === 'undefined') return 'server';\n\n const ua = window.navigator.userAgent;\n if (/chrome/i.test(ua) && !/edge/i.test(ua)) return 'chrome';\n if (/safari/i.test(ua) && !/chrome/i.test(ua)) return 'safari';\n if (/firefox/i.test(ua)) return 'firefox';\n if (/edge/i.test(ua)) return 'edge';\n return 'unknown';\n}\n","import { useEffect, useRef, useState } from 'react';\nimport Hls from 'hls.js';\nimport { throttledReloadDashboard } from '../utils/dashboardReload';\nimport { _getSupabaseInstance } from '../internal/supabaseClientInstance';\nimport { getAuthTokenForHls } from '../services/hlsAuthService';\nimport { isR2WorkerUrl } from '../utils/r2Detection';\nimport { isSafari } from '../utils/browser';\n\n// Enterprise-grade HLS configuration\nconst HLS_CONFIG = {\n // Buffer configuration for 60s segments\n maxBufferLength: 180, // Allow ~3 segments buffered for stability\n maxMaxBufferLength: 600, // 10 minutes max buffer\n maxBufferSize: 120 * 1000 * 1000, // 120MB buffer size\n maxBufferHole: 0.5, // Tolerate minor timestamp gaps\n \n // Low latency optimizations\n lowLatencyMode: false, // We prioritize stability over latency\n enableWorker: true, // Offload processing to web worker\n \n // Aggressive preloading\n startLevel: -1, // Auto-select quality\n autoStartLoad: true,\n startPosition: -1, // Start at live edge when available\n \n // Network optimization\n manifestLoadingMaxRetry: 6,\n levelLoadingMaxRetry: 6,\n fragLoadingMaxRetry: 6,\n manifestLoadingRetryDelay: 250, // Faster retries\n levelLoadingRetryDelay: 250,\n fragLoadingRetryDelay: 250,\n manifestLoadingTimeOut: 90_000,\n levelLoadingTimeOut: 90_000,\n fragLoadingTimeOut: 90_000,\n maxConcurrentLoading: 2,\n \n // ABR (Adaptive Bitrate) settings\n abrEwmaFastLive: 3.0,\n abrEwmaSlowLive: 9.0,\n abrEwmaFastVoD: 3.0,\n abrEwmaSlowVoD: 9.0,\n abrEwmaDefaultEstimate: 1000000, // 1 Mbps default\n abrBandWidthFactor: 0.95,\n abrBandWidthUpFactor: 0.7,\n abrMaxWithRealBitrate: true,\n \n // Fragment loading optimization\n maxFragLookUpTolerance: 1,\n \n // Enable all optimizations\n enableCEA708Captions: false, // Disable if not needed\n enableWebVTT: false, // Disable if not needed\n enableIMSC1: false, // Disable if not needed\n \n // Progressive loading\n progressive: true,\n\n liveSyncDurationCount: 2, // Stay ~2 segments behind live for a full segment buffer\n liveMaxLatencyDurationCount: 3, // Cap drift to ~3 segments\n maxLiveSyncPlaybackRate: 1.0, // Do not chase live edge; prioritize continuity\n};\n\ninterface UseHlsStreamOptions {\n src: string;\n shouldPlay: boolean;\n onFatalError?: () => void;\n hlsConfig?: Partial<typeof HLS_CONFIG>;\n}\n\n/**\n * Enterprise-grade HLS streaming hook with auto-recovery\n * \n * Recovery timing configuration:\n * - Buffer underrun (waiting): 10 seconds grace period\n * - Stall detection: checks every 7 seconds, 8 seconds grace period\n * - Soft restart escalation: after 5 failed attempts\n * - Total time before recovery: 10-15 seconds typically\n */\n// Track failed URLs to prevent infinite restart loops - shared across all instances\nconst failedUrls = new Map<string, { count: number; timestamp: number; permanentlyFailed: boolean }>();\nconst MAX_FAILURES_PER_URL = 3;\nconst FAILURE_EXPIRY_MS = 5 * 60 * 1000; // 5 minutes\nconst LIVE_RELOAD_MIN_INTERVAL_MS = 15 * 1000;\nconst DEFAULT_LIVE_OFFSET_SECONDS = 120;\nconst DEFAULT_MAX_MANIFEST_AGE_MS = 10 * 60 * 1000;\nconst SEGMENT_MAX_AGE_MS = 10 * 60 * 1000;\nconst SEGMENT_TIMESTAMP_REGEX = /(\\d{8}T\\d{6}Z)(?=\\.ts(?:$|[?#]))/;\nconst STALE_MANIFEST_POLL_INITIAL_DELAY_MS = 15 * 1000;\nconst STALE_MANIFEST_POLL_MAX_DELAY_MS = 60 * 1000;\n\n// Export a function to manually reset a failed URL (for refresh buttons)\nexport function resetFailedUrl(url: string) {\n if (failedUrls.has(url)) {\n console.log(`[HLS] Manually resetting failed URL: ${url}`);\n failedUrls.delete(url);\n }\n}\n\n// Export a function to check if a URL has permanently failed\nexport function isUrlPermanentlyFailed(url: string): boolean {\n const failure = failedUrls.get(url);\n return failure?.permanentlyFailed || false;\n}\n\nexport function useHlsStream(\n videoRef: React.RefObject<HTMLVideoElement | null>,\n { src, shouldPlay, onFatalError, hlsConfig }: UseHlsStreamOptions\n) {\n const latestSrcRef = useRef(src);\n latestSrcRef.current = src;\n const shouldPlayRef = useRef(shouldPlay);\n shouldPlayRef.current = shouldPlay;\n const onFatalErrorRef = useRef(onFatalError);\n onFatalErrorRef.current = onFatalError;\n\n // Check if URL is permanently failed before doing anything\n const urlFailure = failedUrls.get(src);\n const isPermanentlyFailed = urlFailure?.permanentlyFailed || false;\n // Clean up old failure entries\n const cleanupFailedUrls = () => {\n const now = Date.now();\n const expiredUrls: string[] = [];\n \n failedUrls.forEach((failure, url) => {\n if (now - failure.timestamp > FAILURE_EXPIRY_MS) {\n expiredUrls.push(url);\n }\n });\n \n expiredUrls.forEach(url => {\n console.log(`[HLS] Removing expired failure entry for: ${url}`);\n failedUrls.delete(url);\n });\n };\n const [restartKey, setRestartKey] = useState(0); // Forces re-mount\n const [isStale, setIsStale] = useState(false);\n const [staleReason, setStaleReason] = useState<string | null>(null);\n const hlsRef = useRef<Hls | null>(null);\n const stallCheckIntervalRef = useRef<NodeJS.Timeout | null>(null);\n const noProgressTimerRef = useRef<NodeJS.Timeout | null>(null);\n const lastTimeUpdateRef = useRef(0);\n const softRestartCountRef = useRef(0);\n const isNativeHlsRef = useRef(false);\n const playbackRateIntervalRef = useRef<NodeJS.Timeout | null>(null);\n const waitingTimerRef = useRef<NodeJS.Timeout | null>(null);\n const playRetryTimerRef = useRef<NodeJS.Timeout | null>(null);\n const playRetryCountRef = useRef(0);\n const manifestWatchdogRef = useRef<NodeJS.Timeout | null>(null);\n const nativeFreshnessIntervalRef = useRef<NodeJS.Timeout | null>(null);\n const lastHiddenAtRef = useRef<number | null>(null);\n const manifestRetryTimerRef = useRef<NodeJS.Timeout | null>(null);\n const manifestRetryDelayRef = useRef(5000);\n const lastLiveReloadRef = useRef(0);\n const isR2StreamRef = useRef(false);\n const nativeStreamUrlRef = useRef<string | null>(null);\n const forcedLiveStartRef = useRef(false);\n const activeStreamUrlRef = useRef<string | null>(null);\n const targetDurationRef = useRef<number | null>(null);\n const lastManifestLoadRef = useRef(0);\n const lastFragLoadRef = useRef(0);\n const lastFragSnRef = useRef<number | null>(null);\n const lastWindowStartSnRef = useRef<number | null>(null);\n const lastWindowEndSnRef = useRef<number | null>(null);\n const lastFragTimeoutSnRef = useRef<number | 'initSegment' | null>(null);\n const lastFragTimeoutAtRef = useRef<number | null>(null);\n const fragTimeoutCountRef = useRef(0);\n const lastManifestEndSnRef = useRef<number | null>(null);\n const lastManifestEndSnUpdatedAtRef = useRef<number | null>(null);\n const staleManifestTriggeredRef = useRef(false);\n const staleManifestUrlRef = useRef<string | null>(null);\n const staleManifestEndSnRef = useRef<number | null>(null);\n const staleManifestPollTimerRef = useRef<NodeJS.Timeout | null>(null);\n const staleManifestPollDelayRef = useRef(STALE_MANIFEST_POLL_INITIAL_DELAY_MS);\n const lastSegmentTimestampMsRef = useRef<number | null>(null);\n const authTokenRef = useRef<string | null>(null);\n const proxyEnabled = process.env.NEXT_PUBLIC_HLS_PROXY_ENABLED === 'true';\n const proxyBaseUrl = (process.env.NEXT_PUBLIC_HLS_PROXY_URL || '/api/stream').replace(/\\/$/, '');\n const debugEnabled = process.env.NEXT_PUBLIC_HLS_DEBUG === 'true';\n const maxManifestAgeMs = (() => {\n const raw = process.env.NEXT_PUBLIC_HLS_MAX_MANIFEST_AGE_MS;\n const parsed = raw ? Number(raw) : NaN;\n return Number.isFinite(parsed) ? parsed : DEFAULT_MAX_MANIFEST_AGE_MS;\n })();\n const manifestStaleThresholdMs = maxManifestAgeMs > 0\n ? Math.min(maxManifestAgeMs, SEGMENT_MAX_AGE_MS)\n : SEGMENT_MAX_AGE_MS;\n\n const debugLog = (...args: unknown[]) => {\n if (debugEnabled) {\n console.log(...args);\n }\n };\n\n const getProgramDateTimeMs = (value: unknown) => {\n if (value instanceof Date) return value.getTime();\n if (typeof value === 'number') return value;\n if (typeof value === 'string') {\n const parsed = Date.parse(value);\n return Number.isNaN(parsed) ? null : parsed;\n }\n return null;\n };\n\n const parseSegmentTimestampMs = (value: string): number | null => {\n const match = value.match(SEGMENT_TIMESTAMP_REGEX);\n if (!match) return null;\n\n const stamp = match[1];\n const year = Number(stamp.slice(0, 4));\n const month = Number(stamp.slice(4, 6));\n const day = Number(stamp.slice(6, 8));\n const hour = Number(stamp.slice(9, 11));\n const minute = Number(stamp.slice(11, 13));\n const second = Number(stamp.slice(13, 15));\n\n if (\n !Number.isFinite(year) ||\n !Number.isFinite(month) ||\n !Number.isFinite(day) ||\n !Number.isFinite(hour) ||\n !Number.isFinite(minute) ||\n !Number.isFinite(second) ||\n month < 1 || month > 12 ||\n day < 1 || day > 31 ||\n hour < 0 || hour > 23 ||\n minute < 0 || minute > 59 ||\n second < 0 || second > 59\n ) {\n return null;\n }\n\n const timestampMs = Date.UTC(year, month - 1, day, hour, minute, second);\n return Number.isNaN(timestampMs) ? null : timestampMs;\n };\n\n const getFragmentTimestampMs = (\n fragment?: { programDateTime?: unknown; relurl?: string; url?: string },\n enforceSegmentAge: boolean = false\n ) => {\n if (fragment?.relurl) {\n const parsed = parseSegmentTimestampMs(fragment.relurl);\n if (parsed !== null) return parsed;\n }\n if (fragment?.url) {\n const parsed = parseSegmentTimestampMs(fragment.url);\n if (parsed !== null) return parsed;\n }\n if (enforceSegmentAge) return null;\n return getProgramDateTimeMs(fragment?.programDateTime);\n };\n\n const getLatestFragmentTimestampMs = (\n fragments: Array<{ programDateTime?: unknown; relurl?: string; url?: string }> | undefined,\n enforceSegmentAge: boolean = false\n ) => {\n if (!Array.isArray(fragments) || fragments.length === 0) return null;\n for (let i = fragments.length - 1; i >= 0; i -= 1) {\n const timestampMs = getFragmentTimestampMs(fragments[i], enforceSegmentAge);\n if (timestampMs !== null) {\n return timestampMs;\n }\n }\n return null;\n };\n\n const parseManifestStatus = (manifestText: string) => {\n let mediaSequence: number | null = null;\n let segmentCount = 0;\n let lastProgramDateTimeMs: number | null = null;\n let lastSegmentTimestampMs: number | null = null;\n\n const lines = manifestText.split(/\\r?\\n/);\n for (const rawLine of lines) {\n const line = rawLine.trim();\n if (!line) continue;\n\n if (line.startsWith('#EXT-X-MEDIA-SEQUENCE:')) {\n const value = Number.parseInt(line.replace('#EXT-X-MEDIA-SEQUENCE:', ''), 10);\n if (Number.isFinite(value)) {\n mediaSequence = value;\n }\n } else if (line.startsWith('#EXT-X-PROGRAM-DATE-TIME:')) {\n const timestamp = line.replace('#EXT-X-PROGRAM-DATE-TIME:', '').trim();\n const parsed = Date.parse(timestamp);\n if (!Number.isNaN(parsed)) {\n lastProgramDateTimeMs = parsed;\n }\n } else if (line.startsWith('#EXTINF:')) {\n segmentCount += 1;\n } else if (!line.startsWith('#')) {\n const segmentTimestampMs = parseSegmentTimestampMs(line);\n if (segmentTimestampMs !== null) {\n lastSegmentTimestampMs = segmentTimestampMs;\n }\n }\n }\n\n let lastSequenceNumber: number | null = null;\n if (mediaSequence !== null && segmentCount > 0) {\n lastSequenceNumber = mediaSequence + segmentCount - 1;\n }\n\n return { lastProgramDateTimeMs, lastSequenceNumber, lastSegmentTimestampMs };\n };\n\n const evaluateSegmentFreshness = ({\n segmentTimestampMs,\n fallbackTimestampMs,\n enforceSegmentAge\n }: {\n segmentTimestampMs: number | null;\n fallbackTimestampMs: number | null;\n enforceSegmentAge: boolean;\n }) => {\n if (segmentTimestampMs !== null) {\n const ageMs = Date.now() - segmentTimestampMs;\n return {\n isFresh: ageMs <= SEGMENT_MAX_AGE_MS,\n ageMs,\n reason: `segment age ${Math.round(ageMs / 1000)}s`\n };\n }\n\n if (enforceSegmentAge) {\n return { isFresh: false, ageMs: null, reason: 'segment timestamp missing' };\n }\n\n if (fallbackTimestampMs !== null) {\n const ageMs = Date.now() - fallbackTimestampMs;\n return {\n isFresh: ageMs <= SEGMENT_MAX_AGE_MS,\n ageMs,\n reason: `program date time age ${Math.round(ageMs / 1000)}s`\n };\n }\n\n return { isFresh: true, ageMs: null, reason: null };\n };\n\n const fetchManifestStatus = async (manifestUrl: string) => {\n const headers: HeadersInit = {};\n if (authTokenRef.current) {\n headers.Authorization = `Bearer ${authTokenRef.current}`;\n }\n\n const response = await fetch(buildCacheBustedUrl(manifestUrl), {\n method: 'GET',\n cache: 'no-store',\n headers\n });\n\n if (!response.ok) {\n throw new Error(`Manifest fetch failed: ${response.status}`);\n }\n\n const manifestText = await response.text();\n return parseManifestStatus(manifestText);\n };\n\n const stopStaleManifestPolling = () => {\n if (staleManifestPollTimerRef.current) {\n clearTimeout(staleManifestPollTimerRef.current);\n staleManifestPollTimerRef.current = null;\n }\n staleManifestTriggeredRef.current = false;\n staleManifestUrlRef.current = null;\n staleManifestEndSnRef.current = null;\n staleManifestPollDelayRef.current = STALE_MANIFEST_POLL_INITIAL_DELAY_MS;\n setIsStale(false);\n setStaleReason(null);\n };\n\n const pollStaleManifestOnce = async () => {\n if (!staleManifestTriggeredRef.current) return;\n if (!shouldPlayRef.current) {\n stopStaleManifestPolling();\n return;\n }\n\n const manifestUrl = staleManifestUrlRef.current;\n if (!manifestUrl) {\n stopStaleManifestPolling();\n return;\n }\n\n try {\n const {\n lastProgramDateTimeMs,\n lastSequenceNumber,\n lastSegmentTimestampMs\n } = await fetchManifestStatus(manifestUrl);\n\n if (lastSegmentTimestampMs !== null) {\n lastSegmentTimestampMsRef.current = lastSegmentTimestampMs;\n }\n\n const enforceSegmentAge = isR2StreamRef.current;\n const hasAnyTimestamp = lastSegmentTimestampMs !== null || lastProgramDateTimeMs !== null;\n const freshness = evaluateSegmentFreshness({\n segmentTimestampMs: lastSegmentTimestampMs,\n fallbackTimestampMs: lastProgramDateTimeMs,\n enforceSegmentAge\n });\n\n const priorEndSn = staleManifestEndSnRef.current;\n const isSequenceAdvanced =\n typeof lastSequenceNumber === 'number' &&\n (priorEndSn === null || lastSequenceNumber > priorEndSn);\n\n if (freshness.isFresh || (!enforceSegmentAge && !hasAnyTimestamp && isSequenceAdvanced)) {\n stopStaleManifestPolling();\n if (hlsRef.current) {\n hlsRef.current.startLoad(-1);\n seekToLiveEdge();\n attemptPlay('stale manifest recovered');\n } else {\n setRestartKey(k => k + 1);\n }\n return;\n }\n } catch (error) {\n debugLog('[HLS] Stale manifest poll failed', error);\n }\n\n staleManifestPollDelayRef.current = Math.min(\n staleManifestPollDelayRef.current + 5000,\n STALE_MANIFEST_POLL_MAX_DELAY_MS\n );\n scheduleStaleManifestPoll();\n };\n\n const scheduleStaleManifestPoll = () => {\n if (!staleManifestTriggeredRef.current) return;\n if (staleManifestPollTimerRef.current) return;\n\n const delay = staleManifestPollDelayRef.current;\n staleManifestPollTimerRef.current = setTimeout(() => {\n staleManifestPollTimerRef.current = null;\n void pollStaleManifestOnce();\n }, delay);\n };\n\n const startStaleManifestPolling = (reason: string) => {\n if (staleManifestTriggeredRef.current) return;\n\n staleManifestTriggeredRef.current = true;\n staleManifestUrlRef.current = activeStreamUrlRef.current || latestSrcRef.current;\n staleManifestEndSnRef.current = lastManifestEndSnRef.current;\n staleManifestPollDelayRef.current = STALE_MANIFEST_POLL_INITIAL_DELAY_MS;\n setIsStale(true);\n setStaleReason(reason);\n\n const hls = hlsRef.current;\n if (hls) {\n hls.stopLoad();\n }\n\n const video = videoRef.current;\n if (video) {\n video.pause();\n }\n\n console.warn(`[HLS] Manifest stale, pausing playback (${reason})`);\n scheduleStaleManifestPoll();\n };\n\n const isProxyUrl = (url: string) => {\n if (!proxyEnabled || !proxyBaseUrl) return false;\n try {\n const base = proxyBaseUrl.startsWith('http')\n ? proxyBaseUrl\n : new URL(proxyBaseUrl, window.location.origin).toString();\n return url.startsWith(base);\n } catch {\n return url.includes(proxyBaseUrl);\n }\n };\n\n const isSnapshotStreamUrl = (url: string) => url.includes('/api/automation/snapshot/stream/');\n\n const getR2CameraUuid = (url: string): string | null => {\n try {\n const parsed = new URL(url);\n const match = parsed.pathname.match(/\\/segments\\/([^\\/]+)\\/index\\.m3u8$/);\n if (match) {\n return match[1];\n }\n } catch {\n // Fall back to string parsing for non-standard URLs\n }\n\n const match = url.match(/\\/segments\\/([^\\/]+)\\/index\\.m3u8/);\n return match ? match[1] : null;\n };\n\n // Cleanup function\n const cleanup = () => {\n // Clear all timers\n if (stallCheckIntervalRef.current) {\n clearInterval(stallCheckIntervalRef.current);\n stallCheckIntervalRef.current = null;\n }\n if (noProgressTimerRef.current) {\n clearTimeout(noProgressTimerRef.current);\n noProgressTimerRef.current = null;\n }\n if (playbackRateIntervalRef.current) {\n clearInterval(playbackRateIntervalRef.current);\n playbackRateIntervalRef.current = null;\n }\n if (waitingTimerRef.current) {\n clearTimeout(waitingTimerRef.current);\n waitingTimerRef.current = null;\n }\n if (playRetryTimerRef.current) {\n clearTimeout(playRetryTimerRef.current);\n playRetryTimerRef.current = null;\n }\n if (manifestWatchdogRef.current) {\n clearInterval(manifestWatchdogRef.current);\n manifestWatchdogRef.current = null;\n }\n if (nativeFreshnessIntervalRef.current) {\n clearInterval(nativeFreshnessIntervalRef.current);\n nativeFreshnessIntervalRef.current = null;\n }\n if (manifestRetryTimerRef.current) {\n clearTimeout(manifestRetryTimerRef.current);\n manifestRetryTimerRef.current = null;\n }\n stopStaleManifestPolling();\n lastHiddenAtRef.current = null;\n if (nativeStreamUrlRef.current) {\n nativeStreamUrlRef.current = null;\n }\n activeStreamUrlRef.current = null;\n forcedLiveStartRef.current = false;\n lastLiveReloadRef.current = 0;\n targetDurationRef.current = null;\n lastManifestLoadRef.current = 0;\n lastFragLoadRef.current = 0;\n lastFragSnRef.current = null;\n lastWindowStartSnRef.current = null;\n lastWindowEndSnRef.current = null;\n lastFragTimeoutSnRef.current = null;\n lastFragTimeoutAtRef.current = null;\n fragTimeoutCountRef.current = 0;\n lastManifestEndSnRef.current = null;\n lastManifestEndSnUpdatedAtRef.current = null;\n staleManifestTriggeredRef.current = false;\n lastSegmentTimestampMsRef.current = null;\n manifestRetryDelayRef.current = 5000;\n playRetryCountRef.current = 0;\n\n // Destroy HLS instance\n if (hlsRef.current) {\n hlsRef.current.destroy();\n hlsRef.current = null;\n }\n\n // Clean up video element\n const video = videoRef.current;\n if (video) {\n video.pause();\n video.removeAttribute('src');\n video.load();\n // Remove event listeners\n video.removeEventListener('waiting', handleWaiting);\n video.removeEventListener('timeupdate', handleTimeUpdate);\n video.removeEventListener('loadedmetadata', handleLoadedMetadata);\n video.removeEventListener('canplay', handleCanPlay);\n video.removeEventListener('ended', handleEnded);\n video.removeEventListener('error', handleNativeError);\n if (typeof document !== 'undefined') {\n document.removeEventListener('visibilitychange', handleVisibilityChange);\n }\n }\n\n // Reset counters\n lastTimeUpdateRef.current = 0;\n softRestartCountRef.current = 0;\n };\n\n const getLiveOffsetSeconds = () => {\n const targetDuration = targetDurationRef.current;\n if (targetDuration && Number.isFinite(targetDuration)) {\n return Math.max(targetDuration * 2, targetDuration);\n }\n return DEFAULT_LIVE_OFFSET_SECONDS;\n };\n\n // Seek to live edge\n const seekToLiveEdge = () => {\n const hls = hlsRef.current;\n const video = videoRef.current;\n if (!hls || !video) return;\n\n if (hls.liveSyncPosition !== null && hls.liveSyncPosition !== undefined) {\n video.currentTime = hls.liveSyncPosition;\n } else if (hls.levels?.[hls.currentLevel]?.details) {\n const levelDetails = hls.levels[hls.currentLevel].details;\n const edge = levelDetails?.edge;\n if (edge !== undefined && edge !== null) {\n const offset = getLiveOffsetSeconds();\n video.currentTime = Math.max(0, edge - offset);\n }\n }\n };\n\n const getLiveEdgeTime = () => {\n const video = videoRef.current;\n if (!video || video.seekable.length === 0) return null;\n const end = video.seekable.end(video.seekable.length - 1);\n if (!Number.isFinite(end)) return null;\n const offset = getLiveOffsetSeconds();\n return Math.max(0, end - offset);\n };\n\n const getWaitingTimeoutMs = () => {\n const targetDuration = targetDurationRef.current;\n if (targetDuration && Number.isFinite(targetDuration)) {\n return Math.max(20_000, targetDuration * 1000 * 0.5);\n }\n return 30_000;\n };\n\n const getManifestStaleTimeoutMs = () => {\n const targetDuration = targetDurationRef.current;\n if (targetDuration && Number.isFinite(targetDuration)) {\n return Math.max(targetDuration * 2000, 120_000);\n }\n return 120_000;\n };\n\n const isManifestUrl = (url: string) => url.includes('.m3u8');\n\n const getBufferGap = (video: HTMLVideoElement) => {\n if (!video.buffered.length) return null;\n const end = video.buffered.end(video.buffered.length - 1);\n if (!Number.isFinite(end)) return null;\n return Math.max(0, end - video.currentTime);\n };\n\n const getBufferThresholds = () => {\n const targetDuration = targetDurationRef.current ?? 60;\n return {\n low: Math.max(5, targetDuration * 0.1),\n mid: Math.max(10, targetDuration * 0.2),\n high: Math.max(18, targetDuration * 0.35),\n };\n };\n\n const shouldAttemptRecovery = () => {\n if (!isR2StreamRef.current) return true;\n if (!lastManifestLoadRef.current) return false;\n const now = Date.now();\n const timeoutMs = getManifestStaleTimeoutMs();\n const manifestAge = now - lastManifestLoadRef.current;\n\n return manifestAge > timeoutMs;\n };\n\n const getDesiredLivePosition = () => {\n const hls = hlsRef.current;\n if (hls?.liveSyncPosition !== null && hls?.liveSyncPosition !== undefined) {\n if (Number.isFinite(hls.liveSyncPosition)) {\n return hls.liveSyncPosition;\n }\n }\n\n if (hls?.levels?.[hls.currentLevel]?.details) {\n const levelDetails = hls.levels[hls.currentLevel].details;\n const edge = levelDetails?.edge;\n if (edge !== undefined && edge !== null) {\n return Math.max(0, edge - getLiveOffsetSeconds());\n }\n }\n\n return getLiveEdgeTime();\n };\n\n const schedulePlayRetry = (reason: string) => {\n if (playRetryTimerRef.current) return;\n if (playRetryCountRef.current >= 3) return;\n\n playRetryCountRef.current += 1;\n playRetryTimerRef.current = setTimeout(() => {\n playRetryTimerRef.current = null;\n attemptPlay(`${reason} retry`);\n }, 1000);\n };\n\n const attemptPlay = (reason: string) => {\n const video = videoRef.current;\n if (!video || !shouldPlayRef.current) return;\n if (staleManifestTriggeredRef.current) return;\n if (!video.paused || video.seeking) return;\n if (video.readyState < 2) return;\n\n video.play()\n .then(() => {\n playRetryCountRef.current = 0;\n })\n .catch(err => {\n if (err?.name === 'AbortError') {\n schedulePlayRetry(reason);\n return;\n }\n console.error('[HLS] Play failed:', err);\n });\n };\n\n const handleCanPlay = () => {\n attemptPlay('canplay');\n };\n\n const handleVisibilityChange = () => {\n if (typeof document === 'undefined') return;\n if (document.hidden) {\n lastHiddenAtRef.current = Date.now();\n return;\n }\n\n const lastHiddenAt = lastHiddenAtRef.current;\n lastHiddenAtRef.current = null;\n if (!lastHiddenAt) return;\n if (Date.now() - lastHiddenAt < 30_000) return;\n\n void refreshLiveStream('tab visible after idle');\n attemptPlay('tab visible');\n };\n\n const startManifestWatchdog = () => {\n if (manifestWatchdogRef.current) return;\n if (!isR2StreamRef.current) return;\n\n manifestWatchdogRef.current = setInterval(() => {\n if (staleManifestTriggeredRef.current) return;\n if (!lastManifestLoadRef.current) return;\n const now = Date.now();\n const staleTimeoutMs = getManifestStaleTimeoutMs();\n if (now - lastManifestLoadRef.current < staleTimeoutMs) return;\n if (now - lastLiveReloadRef.current < LIVE_RELOAD_MIN_INTERVAL_MS) return;\n\n lastLiveReloadRef.current = now;\n softRestart('manifest stale watchdog');\n }, 15000);\n };\n\n const scheduleManifestRetry = (reason: string) => {\n if (manifestRetryTimerRef.current) return;\n const delay = manifestRetryDelayRef.current;\n manifestRetryTimerRef.current = setTimeout(() => {\n manifestRetryTimerRef.current = null;\n softRestart(reason);\n manifestRetryDelayRef.current = Math.min(manifestRetryDelayRef.current + 5000, 30000);\n }, delay);\n };\n\n const resetManifestRetry = () => {\n if (manifestRetryTimerRef.current) {\n clearTimeout(manifestRetryTimerRef.current);\n manifestRetryTimerRef.current = null;\n }\n manifestRetryDelayRef.current = 5000;\n };\n\n const markStaleStream = (reason: string) => {\n startStaleManifestPolling(reason);\n };\n\n const setPlaybackRate = (rate: number) => {\n const video = videoRef.current;\n if (!video) return;\n if (!Number.isFinite(rate)) return;\n if (Math.abs(video.playbackRate - rate) < 0.01) return;\n video.playbackRate = rate;\n };\n\n const startPlaybackGovernor = () => {\n if (playbackRateIntervalRef.current) return;\n playbackRateIntervalRef.current = setInterval(() => {\n const video = videoRef.current;\n if (!video || video.paused || video.seeking) return;\n if (!isR2StreamRef.current) return;\n\n const bufferGap = getBufferGap(video);\n if (bufferGap === null) return;\n\n const { low, mid, high } = getBufferThresholds();\n let desiredRate = 1.0;\n\n if (bufferGap < low) {\n desiredRate = 0.8;\n } else if (bufferGap < mid) {\n desiredRate = 0.9;\n } else if (bufferGap < high) {\n desiredRate = 0.95;\n }\n\n setPlaybackRate(desiredRate);\n }, 2000);\n };\n\n const buildCacheBustedUrl = (url: string) => {\n if (typeof window === 'undefined') {\n return url;\n }\n\n try {\n const parsed = new URL(url, window.location.origin);\n parsed.searchParams.set('ts', Date.now().toString());\n return parsed.toString();\n } catch {\n const separator = url.includes('?') ? '&' : '?';\n return `${url}${separator}ts=${Date.now()}`;\n }\n };\n\n const ensureManifestFreshness = async (\n manifestUrl: string,\n enforceSegmentAge: boolean,\n reason: string\n ) => {\n try {\n const status = await fetchManifestStatus(manifestUrl);\n if (status.lastSegmentTimestampMs !== null) {\n lastSegmentTimestampMsRef.current = status.lastSegmentTimestampMs;\n }\n\n const freshness = evaluateSegmentFreshness({\n segmentTimestampMs: status.lastSegmentTimestampMs,\n fallbackTimestampMs: status.lastProgramDateTimeMs,\n enforceSegmentAge\n });\n\n if (!freshness.isFresh) {\n markStaleStream(freshness.reason || reason);\n return false;\n }\n\n return true;\n } catch (error) {\n debugLog('[HLS] Manifest freshness check failed', error);\n if (enforceSegmentAge) {\n markStaleStream('manifest freshness check failed');\n return false;\n }\n return true;\n }\n };\n\n const startNativeFreshnessMonitor = (manifestUrl: string) => {\n if (nativeFreshnessIntervalRef.current) return;\n nativeFreshnessIntervalRef.current = setInterval(() => {\n if (staleManifestTriggeredRef.current) return;\n void ensureManifestFreshness(manifestUrl, true, 'native freshness check');\n }, 30_000);\n };\n\n const refreshLiveStream = async (reason: string) => {\n if (!isR2StreamRef.current) return;\n\n const now = Date.now();\n if (now - lastLiveReloadRef.current < LIVE_RELOAD_MIN_INTERVAL_MS) {\n return;\n }\n lastLiveReloadRef.current = now;\n\n const video = videoRef.current;\n const hls = hlsRef.current;\n\n if (hls) {\n console.log(`[HLS] Live reload (${reason})`);\n softRestart(reason);\n return;\n }\n\n if (video && nativeStreamUrlRef.current) {\n console.log(`[HLS] Native live reload (${reason})`);\n const isFresh = await ensureManifestFreshness(nativeStreamUrlRef.current, true, 'native live reload');\n if (!isFresh) {\n return;\n }\n const refreshedUrl = buildCacheBustedUrl(nativeStreamUrlRef.current);\n video.src = refreshedUrl;\n video.load();\n const edgeTime = getLiveEdgeTime();\n if (edgeTime !== null) {\n video.currentTime = edgeTime;\n }\n attemptPlay('native live reload');\n }\n };\n\n // Soft restart (stop + startLoad)\n const softRestart = (reason: string) => {\n if (staleManifestTriggeredRef.current) {\n debugLog('[HLS] Skip soft restart while manifest is stale', reason);\n return;\n }\n console.warn(`[HLS] Soft restart: ${reason}`);\n const hls = hlsRef.current;\n if (!hls) return;\n\n try {\n const video = videoRef.current;\n const isR2Stream = isR2StreamRef.current;\n const desiredPosition = isR2Stream ? getDesiredLivePosition() : null;\n\n hls.stopLoad();\n if (desiredPosition !== null && Number.isFinite(desiredPosition)) {\n hls.startLoad(desiredPosition);\n if (video) {\n video.currentTime = desiredPosition;\n }\n } else {\n hls.startLoad(-1); // Default to configured live sync position\n if (!isR2Stream) {\n seekToLiveEdge();\n }\n }\n softRestartCountRef.current++;\n\n // Escalate to hard restart after 5 soft restarts\n if (softRestartCountRef.current >= 5) {\n hardRestart(`${reason} (escalated after ${softRestartCountRef.current} soft restarts)`);\n }\n } catch (error) {\n console.error('[HLS] Soft restart failed:', error);\n hardRestart(`${reason} (soft restart error)`);\n }\n };\n\n // Hard restart (full teardown)\n const hardRestart = (reason: string) => {\n if (staleManifestTriggeredRef.current) {\n debugLog('[HLS] Skip hard restart while manifest is stale', reason);\n return;\n }\n console.warn(`[HLS] Hard restart: ${reason}`);\n \n // Clean up old entries first\n cleanupFailedUrls();\n \n // Check if this URL has failed too many times\n const failure = failedUrls.get(src);\n const failureCount = failure ? failure.count : 0;\n const isR2Stream = isR2StreamRef.current;\n \n if (!isR2Stream && failureCount >= MAX_FAILURES_PER_URL) {\n console.error(`[HLS] URL has failed ${failureCount} times. Marking as permanently failed: ${src}`);\n \n // Mark as permanently failed\n failedUrls.set(src, { \n count: failureCount, \n timestamp: Date.now(),\n permanentlyFailed: true\n });\n \n cleanup();\n \n // Trigger fatal error callback without restarting\n onFatalErrorRef.current?.();\n // DO NOT reload the dashboard - just let the video fail gracefully\n return; // Don't increment restartKey to prevent re-mount\n }\n \n // Track this failure\n if (!isR2Stream) {\n failedUrls.set(src, { \n count: failureCount + 1, \n timestamp: Date.now(),\n permanentlyFailed: false\n });\n } else if (failedUrls.has(src)) {\n failedUrls.delete(src);\n }\n \n cleanup();\n setRestartKey(k => k + 1);\n softRestartCountRef.current = 0;\n\n // Trigger onFatalError for specific errors\n if (reason.includes('404 hard restart')) {\n onFatalErrorRef.current?.();\n // DO NOT reload the dashboard - just let the video fail gracefully\n }\n };\n\n const handleLoadedMetadata = () => {\n if (!isR2StreamRef.current) return;\n const video = videoRef.current;\n if (!video) return;\n const edgeTime = getLiveEdgeTime();\n if (edgeTime !== null) {\n video.currentTime = edgeTime;\n }\n };\n\n const handleEnded = () => {\n if (isNativeHlsRef.current) {\n void refreshLiveStream('ended');\n return;\n }\n if (isR2StreamRef.current) {\n softRestart('ended');\n }\n };\n\n // Handle 'waiting' event (buffer underrun)\n const handleWaiting = () => {\n if (waitingTimerRef.current) {\n clearTimeout(waitingTimerRef.current);\n }\n\n if (isNativeHlsRef.current) {\n if (!isR2StreamRef.current) return;\n\n waitingTimerRef.current = setTimeout(() => {\n void refreshLiveStream('native waiting timeout');\n }, getWaitingTimeoutMs());\n return;\n }\n\n console.log('[HLS] Video waiting (buffer underrun)');\n\n if (isR2StreamRef.current) {\n waitingTimerRef.current = setTimeout(() => {\n const video = videoRef.current;\n if (video && video.readyState < 3) {\n if (shouldAttemptRecovery()) {\n softRestart('waiting timeout');\n } else {\n handleWaiting();\n }\n }\n }, getWaitingTimeoutMs());\n return;\n }\n\n // Schedule soft restart after 10 seconds if still waiting\n waitingTimerRef.current = setTimeout(() => {\n const video = videoRef.current;\n if (video && video.readyState < 3) {\n softRestart('waiting timeout');\n }\n }, 10000);\n };\n\n // Handle 'timeupdate' event for stall detection\n const handleTimeUpdate = () => {\n const video = videoRef.current;\n if (!video) return;\n\n lastTimeUpdateRef.current = video.currentTime;\n playRetryCountRef.current = 0;\n\n // Clear waiting timer if playback progressed\n if (waitingTimerRef.current) {\n clearTimeout(waitingTimerRef.current);\n waitingTimerRef.current = null;\n }\n };\n\n // Handle native video errors (for Safari)\n const handleNativeError = () => {\n console.error('[HLS] Native video error');\n hardRestart('native video error');\n };\n\n // Stall detection logic\n const startStallDetection = () => {\n if (isNativeHlsRef.current || isR2StreamRef.current) return; // Avoid restart loops for R2 live\n\n stallCheckIntervalRef.current = setInterval(() => {\n const video = videoRef.current;\n if (!video || video.paused || video.ended) return;\n if (video.readyState < 3 || video.seeking) return;\n\n const currentTime = video.currentTime;\n const lastTime = lastTimeUpdateRef.current;\n const bufferGap = getBufferGap(video);\n const nearBufferEnd = bufferGap !== null && bufferGap < 0.5;\n\n // Check if video hasn't progressed\n if (Math.abs(currentTime - lastTime) < 0.1 && video.readyState >= 2) {\n if (nearBufferEnd && !shouldAttemptRecovery()) {\n if (noProgressTimerRef.current) {\n clearTimeout(noProgressTimerRef.current);\n noProgressTimerRef.current = null;\n }\n return;\n }\n\n console.warn('[HLS] Playback stall detected');\n\n // Start grace timer if not already running\n if (!noProgressTimerRef.current) {\n noProgressTimerRef.current = setTimeout(() => {\n if (nearBufferEnd && !shouldAttemptRecovery()) {\n noProgressTimerRef.current = null;\n return;\n }\n softRestart('playback stall');\n noProgressTimerRef.current = null;\n }, nearBufferEnd ? getWaitingTimeoutMs() : 12000);\n }\n } else {\n // Progress detected, clear grace timer\n if (noProgressTimerRef.current) {\n clearTimeout(noProgressTimerRef.current);\n noProgressTimerRef.current = null;\n }\n }\n }, 7000); // Check every 7 seconds\n };\n\n // Main effect\n useEffect(() => {\n if (!src || !shouldPlay) {\n cleanup();\n return;\n }\n\n let isCancelled = false;\n const r2WorkerDomain = process.env.NEXT_PUBLIC_R2_WORKER_DOMAIN || 'https://r2-stream-proxy.optifye-r2.workers.dev';\n const isR2Stream = isR2WorkerUrl(src, r2WorkerDomain);\n isR2StreamRef.current = isR2Stream;\n\n // If URL is permanently failed, don't even try (skip for R2 live streams)\n if (isPermanentlyFailed && !isR2Stream) {\n console.warn(`[HLS] URL is permanently failed, not attempting to load: ${src}`);\n onFatalErrorRef.current?.();\n return;\n }\n\n if (isPermanentlyFailed && isR2Stream && failedUrls.has(src)) {\n failedUrls.delete(src);\n }\n\n const video = videoRef.current;\n if (!video) return;\n\n if (typeof document !== 'undefined') {\n document.addEventListener('visibilitychange', handleVisibilityChange);\n }\n\n const initialize = async () => {\n // Prefer HLS.js for R2 URLs to allow auth headers even on Safari\n const canUseNative = video.canPlayType('application/vnd.apple.mpegurl') === 'probably';\n isNativeHlsRef.current = canUseNative && !isR2Stream;\n\n let authToken: string | null = null;\n if (isR2Stream) {\n try {\n const supabase = _getSupabaseInstance();\n authToken = await getAuthTokenForHls(supabase);\n } catch (error) {\n console.warn('[HLS] Unable to retrieve auth token for R2 streaming:', error);\n }\n }\n authTokenRef.current = authToken;\n\n if (isCancelled) return;\n\n const cameraUuid = isR2Stream ? getR2CameraUuid(src) : null;\n const proxyPlaylistUrl =\n proxyEnabled && isR2Stream && proxyBaseUrl && cameraUuid\n ? `${proxyBaseUrl}/segments/${cameraUuid}/index.m3u8`\n : null;\n const resolvedSrc = proxyPlaylistUrl || src;\n const resolvedHlsSrc = src;\n\n const shouldUseProxy =\n proxyEnabled && isR2Stream && canUseNative && !Hls.isSupported() && isSafari();\n\n if (shouldUseProxy) {\n if (cameraUuid) {\n isNativeHlsRef.current = true;\n nativeStreamUrlRef.current = resolvedSrc;\n activeStreamUrlRef.current = resolvedSrc;\n console.log('[HLS] Using proxy playlist for Safari R2 stream');\n const isFresh = await ensureManifestFreshness(resolvedSrc, true, 'native proxy start');\n if (!isFresh) {\n return;\n }\n video.src = resolvedSrc;\n video.addEventListener('waiting', handleWaiting);\n video.addEventListener('loadedmetadata', handleLoadedMetadata);\n video.addEventListener('canplay', handleCanPlay);\n video.addEventListener('ended', handleEnded);\n video.addEventListener('error', handleNativeError);\n startNativeFreshnessMonitor(resolvedSrc);\n attemptPlay('native proxy start');\n return;\n }\n\n console.warn('[HLS] Safari R2 proxy unavailable, falling back to direct URL');\n }\n\n if (!proxyEnabled && isR2Stream && canUseNative && !Hls.isSupported() && isSafari()) {\n console.warn('[HLS] Safari native R2 streaming requires proxy. Falling back.');\n onFatalErrorRef.current?.();\n return;\n }\n\n if (Hls.isSupported() && !isNativeHlsRef.current) {\n // Use hls.js\n const usesSnapshotStream = isSnapshotStreamUrl(resolvedHlsSrc);\n const mergedConfig = {\n ...HLS_CONFIG,\n ...hlsConfig,\n enableWorker: usesSnapshotStream\n ? false\n : (hlsConfig?.enableWorker ?? HLS_CONFIG.enableWorker),\n xhrSetup: (xhr: XMLHttpRequest, url: string) => {\n const usesProxy = isProxyUrl(url);\n if (isSnapshotStreamUrl(url)) {\n xhr.withCredentials = true;\n }\n if (isR2WorkerUrl(url, r2WorkerDomain) || usesProxy) {\n if (isManifestUrl(url)) {\n xhr.open('GET', buildCacheBustedUrl(url), true);\n }\n if (authToken) {\n xhr.setRequestHeader('Authorization', `Bearer ${authToken}`);\n }\n }\n },\n fetchSetup: (context: { url: string }, initParams: RequestInit) => {\n const isR2Url = isR2WorkerUrl(context.url, r2WorkerDomain);\n const isManifestRequest = isManifestUrl(context.url);\n const usesProxy = isProxyUrl(context.url);\n const isSnapshotStream = isSnapshotStreamUrl(context.url);\n let requestUrl = context.url;\n\n if (isSnapshotStream) {\n initParams.credentials = 'include';\n }\n\n if (isR2Url || usesProxy) {\n if (authToken) {\n initParams.headers = {\n ...initParams.headers,\n 'Authorization': `Bearer ${authToken}`\n };\n }\n if (isManifestRequest) {\n requestUrl = buildCacheBustedUrl(context.url);\n initParams.cache = 'no-store';\n }\n }\n\n return new Request(requestUrl, initParams);\n }\n };\n\n const hls = new Hls(mergedConfig);\n hlsRef.current = hls;\n\n // Attach media and load source\n hls.attachMedia(video);\n hls.loadSource(resolvedHlsSrc);\n activeStreamUrlRef.current = resolvedHlsSrc;\n\n // Handle fatal errors\n hls.on(Hls.Events.ERROR, (_, data) => {\n debugLog('[HLS] Error', {\n type: data.type,\n details: data.details,\n fatal: data.fatal,\n response: data.response?.code,\n frag: data.frag?.sn\n });\n if (\n data.type === Hls.ErrorTypes.MEDIA_ERROR &&\n data.details === Hls.ErrorDetails.BUFFER_STALLED_ERROR\n ) {\n debugLog('[HLS] Buffer stalled, waiting for next segment');\n attemptPlay('buffer stalled');\n return;\n }\n\n if (\n data.type === Hls.ErrorTypes.NETWORK_ERROR &&\n (data.details === Hls.ErrorDetails.FRAG_LOAD_TIMEOUT ||\n data.details === Hls.ErrorDetails.FRAG_LOAD_ERROR)\n ) {\n const fragSn = data.frag?.sn;\n const windowStart = lastWindowStartSnRef.current;\n const windowEnd = lastWindowEndSnRef.current;\n if (typeof fragSn === 'number' && typeof windowStart === 'number' && fragSn < windowStart) {\n softRestart('frag fell out of window');\n return;\n }\n if (typeof fragSn === 'number' && typeof windowEnd === 'number' && fragSn > windowEnd) {\n softRestart('frag ahead of window');\n return;\n }\n const now = Date.now();\n if (lastFragTimeoutSnRef.current === fragSn && lastFragTimeoutAtRef.current) {\n if (now - lastFragTimeoutAtRef.current < 120_000) {\n fragTimeoutCountRef.current += 1;\n } else {\n fragTimeoutCountRef.current = 1;\n }\n } else {\n fragTimeoutCountRef.current = 1;\n lastFragTimeoutSnRef.current = fragSn ?? null;\n }\n lastFragTimeoutAtRef.current = now;\n\n if (fragTimeoutCountRef.current >= 2) {\n softRestart('frag load timeout');\n }\n return;\n }\n if (\n data.type === Hls.ErrorTypes.NETWORK_ERROR &&\n (data.details === Hls.ErrorDetails.MANIFEST_LOAD_TIMEOUT ||\n data.details === Hls.ErrorDetails.MANIFEST_LOAD_ERROR)\n ) {\n if (data.response?.code === 404 && isR2StreamRef.current) {\n markStaleStream('manifest 404');\n return;\n }\n scheduleManifestRetry('manifest load timeout');\n return;\n }\n if (!data.fatal) return;\n\n console.error('[HLS] Fatal error:', data.type, data.details);\n\n // Check for 404 errors (treat manifest errors as fatal, fragments as recoverable)\n if (data.response?.code === 404) {\n if (\n data.details === Hls.ErrorDetails.MANIFEST_LOAD_ERROR ||\n data.details === Hls.ErrorDetails.LEVEL_LOAD_ERROR\n ) {\n if (isR2StreamRef.current) {\n markStaleStream('manifest 404');\n return;\n }\n hardRestart('404 manifest hard restart');\n return;\n }\n softRestart('frag 404');\n return;\n }\n\n switch (data.type) {\n case Hls.ErrorTypes.NETWORK_ERROR:\n if (\n isR2StreamRef.current &&\n data.details === Hls.ErrorDetails.MANIFEST_LOAD_TIMEOUT\n ) {\n softRestart('manifest load timeout');\n break;\n }\n softRestart(`${data.type}: ${data.details}`);\n break;\n case Hls.ErrorTypes.MEDIA_ERROR:\n softRestart(`${data.type}: ${data.details}`);\n break;\n default:\n hardRestart(`Fatal ${data.type}: ${data.details}`);\n break;\n }\n });\n\n // Auto-play when manifest is parsed\n hls.on(Hls.Events.MANIFEST_PARSED, () => {\n // Stream loaded successfully, reset failure count\n if (failedUrls.has(src)) {\n console.log(`[HLS] Stream loaded successfully, resetting failure count for: ${src}`);\n failedUrls.delete(src);\n }\n\n if (isR2Stream) {\n seekToLiveEdge();\n }\n \n attemptPlay('manifest parsed');\n });\n\n hls.on(Hls.Events.LEVEL_LOADED, (_event, data) => {\n if (!data?.details) return;\n const details = data.details as {\n endList?: boolean;\n live?: boolean;\n type?: string;\n edge?: number;\n targetduration?: number;\n startSN?: number;\n endSN?: number;\n fragments?: Array<{ sn?: number; programDateTime?: unknown; relurl?: string; url?: string }>;\n };\n\n if (isR2Stream) {\n lastManifestLoadRef.current = Date.now();\n resetManifestRetry();\n if (details.endList) {\n details.endList = false;\n }\n details.live = true;\n details.type = 'LIVE';\n if (details.targetduration && Number.isFinite(details.targetduration)) {\n targetDurationRef.current = details.targetduration;\n }\n\n if (typeof details.startSN === 'number') {\n lastWindowStartSnRef.current = details.startSN;\n }\n if (typeof details.endSN === 'number') {\n lastWindowEndSnRef.current = Math.max(details.endSN - 1, details.startSN ?? details.endSN);\n }\n if (Array.isArray(details.fragments) && details.fragments.length > 0) {\n const firstSn = details.fragments[0]?.sn;\n const lastSn = details.fragments[details.fragments.length - 1]?.sn;\n if (typeof firstSn === 'number') {\n lastWindowStartSnRef.current = firstSn;\n }\n if (typeof lastSn === 'number') {\n lastWindowEndSnRef.current = lastSn;\n }\n }\n }\n\n if (!details.endList) {\n const now = Date.now();\n const fragments = Array.isArray(details.fragments) ? details.fragments : [];\n const lastFragment = fragments.length ? fragments[fragments.length - 1] : undefined;\n const segmentTimestampMs = getLatestFragmentTimestampMs(fragments, true);\n const enforceSegmentAge = isR2StreamRef.current;\n\n if (segmentTimestampMs !== null) {\n lastSegmentTimestampMsRef.current = segmentTimestampMs;\n }\n\n const freshness = evaluateSegmentFreshness({\n segmentTimestampMs,\n fallbackTimestampMs: getProgramDateTimeMs(lastFragment?.programDateTime),\n enforceSegmentAge\n });\n\n if (!freshness.isFresh) {\n markStaleStream(freshness.reason || 'segment stale');\n return;\n }\n\n const endSn = typeof details.endSN === 'number' ? details.endSN : lastFragment?.sn;\n if (typeof endSn === 'number') {\n if (lastManifestEndSnRef.current === endSn) {\n const lastUpdatedAt = lastManifestEndSnUpdatedAtRef.current;\n if (lastUpdatedAt && now - lastUpdatedAt > manifestStaleThresholdMs) {\n markStaleStream(`sequence stalled at ${endSn}`);\n return;\n }\n } else {\n lastManifestEndSnRef.current = endSn;\n lastManifestEndSnUpdatedAtRef.current = now;\n }\n }\n }\n\n debugLog('[HLS] Level loaded', {\n targetduration: details.targetduration,\n edge: details.edge,\n fragments: data.details?.fragments?.length\n });\n\n if (isR2Stream && !forcedLiveStartRef.current) {\n forcedLiveStartRef.current = true;\n seekToLiveEdge();\n }\n });\n\n hls.on(Hls.Events.MANIFEST_LOADED, () => {\n if (!isR2Stream) return;\n lastManifestLoadRef.current = Date.now();\n resetManifestRetry();\n });\n\n hls.on(Hls.Events.FRAG_LOADING, (_event, data) => {\n if (staleManifestTriggeredRef.current) return;\n const frag = data?.frag as { sn?: number | 'initSegment'; loader?: { abort?: () => void }; programDateTime?: unknown } | undefined;\n if (!frag || frag.sn === 'initSegment') return;\n\n const enforceSegmentAge = isR2StreamRef.current;\n const segmentTimestampMs = getFragmentTimestampMs(frag, true);\n if (segmentTimestampMs !== null) {\n lastSegmentTimestampMsRef.current = segmentTimestampMs;\n }\n\n const freshness = evaluateSegmentFreshness({\n segmentTimestampMs,\n fallbackTimestampMs: getProgramDateTimeMs(frag.programDateTime),\n enforceSegmentAge\n });\n\n if (!freshness.isFresh) {\n if (frag.loader?.abort) {\n frag.loader.abort();\n }\n markStaleStream(freshness.reason || 'segment stale');\n }\n });\n\n hls.on(Hls.Events.FRAG_LOADED, (_event, data) => {\n if (!isR2Stream) return;\n lastFragLoadRef.current = Date.now();\n if (typeof data?.frag?.sn === 'number') {\n lastFragSnRef.current = data.frag.sn;\n }\n });\n\n hls.on(Hls.Events.FRAG_BUFFERED, () => {\n attemptPlay('frag buffered');\n });\n\n\n // Start monitoring\n video.addEventListener('waiting', handleWaiting);\n video.addEventListener('timeupdate', handleTimeUpdate);\n video.addEventListener('loadedmetadata', handleLoadedMetadata);\n video.addEventListener('canplay', handleCanPlay);\n video.addEventListener('ended', handleEnded);\n startStallDetection();\n startPlaybackGovernor();\n startManifestWatchdog();\n startManifestWatchdog();\n return;\n }\n\n if (canUseNative) {\n // Native HLS (Safari)\n isNativeHlsRef.current = true;\n console.log('[HLS] Using native HLS');\n nativeStreamUrlRef.current = resolvedSrc;\n activeStreamUrlRef.current = resolvedSrc;\n if (isR2Stream) {\n const isFresh = await ensureManifestFreshness(resolvedSrc, true, 'native start');\n if (!isFresh) {\n return;\n }\n }\n video.src = resolvedSrc;\n video.addEventListener('waiting', handleWaiting);\n video.addEventListener('loadedmetadata', handleLoadedMetadata);\n video.addEventListener('canplay', handleCanPlay);\n video.addEventListener('ended', handleEnded);\n video.addEventListener('error', handleNativeError);\n startPlaybackGovernor();\n startManifestWatchdog();\n if (isR2Stream) {\n startNativeFreshnessMonitor(resolvedSrc);\n }\n \n // Auto-play\n attemptPlay('native start');\n } else {\n console.error('[HLS] HLS not supported');\n }\n };\n\n initialize();\n\n // Cleanup\n return () => {\n isCancelled = true;\n cleanup();\n };\n }, [src, shouldPlay, restartKey, isPermanentlyFailed]);\n\n return {\n restartKey,\n isNativeHls: isNativeHlsRef.current,\n isStale,\n staleReason,\n lastSegmentTimestampMs: lastSegmentTimestampMsRef.current,\n };\n} \n","import { useEffect, useRef, useCallback } from 'react';\nimport { useHlsStream } from './useHlsStream';\nimport { VideoCroppingRect } from '../types/dashboardConfig';\n\nimport { HlsConfig } from 'hls.js';\n\ninterface UseHlsStreamWithCroppingOptions {\n src: string;\n shouldPlay: boolean;\n cropping?: VideoCroppingRect;\n canvasFps?: number;\n useRAF?: boolean;\n onFatalError?: () => void;\n hlsConfig?: Partial<HlsConfig>;\n}\n\n/**\n * Enhanced HLS streaming hook with canvas-based cropping support\n * Extends the enterprise-grade useHlsStream with video cropping capabilities\n */\nexport function useHlsStreamWithCropping(\n videoRef: React.RefObject<HTMLVideoElement | null>,\n canvasRef: React.RefObject<HTMLCanvasElement | null>,\n options: UseHlsStreamWithCroppingOptions\n) {\n const { src, shouldPlay, cropping, canvasFps = 30, useRAF = true, onFatalError, hlsConfig } = options;\n const animationFrameRef = useRef<number | null>(null);\n const intervalRef = useRef<NodeJS.Timeout | null>(null);\n const isDrawingRef = useRef(false);\n\n // Use the base HLS stream hook\n const hlsState = useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig });\n\n // Calculate cropping rectangle in pixels\n const calculateCropRect = useCallback((\n video: HTMLVideoElement, \n cropping: VideoCroppingRect\n ) => {\n const videoWidth = video.videoWidth;\n const videoHeight = video.videoHeight;\n\n // Convert percentage-based cropping to pixel values\n const sx = (cropping.x / 100) * videoWidth;\n const sy = (cropping.y / 100) * videoHeight;\n const sw = (cropping.width / 100) * videoWidth;\n const sh = (cropping.height / 100) * videoHeight;\n\n return { sx, sy, sw, sh };\n }, []);\n\n // Draw cropped video to canvas\n const drawFrame = useCallback(() => {\n const video = videoRef.current;\n const canvas = canvasRef.current;\n if (!video || !canvas || !cropping) return;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n\n // Skip drawing if video is not ready\n if (video.readyState < 2) return;\n\n try {\n // Get video dimensions\n const videoWidth = video.videoWidth;\n const videoHeight = video.videoHeight;\n \n if (!videoWidth || !videoHeight) return;\n\n // Calculate source rectangle\n const { sx, sy, sw, sh } = calculateCropRect(video, cropping);\n\n // Set canvas size to match container size\n const canvasContainer = canvas.parentElement;\n if (canvasContainer) {\n const containerWidth = canvasContainer.clientWidth;\n const containerHeight = canvasContainer.clientHeight;\n \n // Update canvas resolution if needed\n if (canvas.width !== containerWidth || canvas.height !== containerHeight) {\n canvas.width = containerWidth;\n canvas.height = containerHeight;\n }\n }\n\n // Clear canvas\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n\n // Draw cropped video stretched to fill the entire canvas\n // This will adjust the aspect ratio to fill the component\n ctx.drawImage(\n video,\n sx, sy, sw, sh, // Source rectangle (cropped portion)\n 0, 0, canvas.width, canvas.height // Destination rectangle (full canvas)\n );\n } catch (err) {\n // Silently handle drawing errors (e.g., cross-origin issues)\n console.warn('Canvas drawing error:', err);\n }\n }, [videoRef, canvasRef, cropping, calculateCropRect]);\n\n // Start canvas rendering\n const startCanvasRendering = useCallback(() => {\n if (isDrawingRef.current) return;\n isDrawingRef.current = true;\n\n if (useRAF) {\n // Use requestAnimationFrame for smooth rendering\n const animate = () => {\n drawFrame();\n animationFrameRef.current = requestAnimationFrame(animate);\n };\n animate();\n } else {\n // Use setInterval with specified FPS\n const frameInterval = 1000 / canvasFps;\n intervalRef.current = setInterval(drawFrame, frameInterval);\n }\n }, [drawFrame, canvasFps, useRAF]);\n\n // Stop canvas rendering\n const stopCanvasRendering = useCallback(() => {\n isDrawingRef.current = false;\n\n if (animationFrameRef.current) {\n cancelAnimationFrame(animationFrameRef.current);\n animationFrameRef.current = null;\n }\n\n if (intervalRef.current) {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n }\n }, []);\n\n // Handle canvas rendering based on cropping and play state\n useEffect(() => {\n const video = videoRef.current;\n if (!video || !cropping || !shouldPlay) {\n stopCanvasRendering();\n return;\n }\n\n // Start rendering when video is playing\n const handlePlay = () => {\n if (cropping) {\n startCanvasRendering();\n }\n };\n\n const handlePause = () => {\n stopCanvasRendering();\n };\n\n const handleEnded = () => {\n stopCanvasRendering();\n };\n\n video.addEventListener('play', handlePlay);\n video.addEventListener('pause', handlePause);\n video.addEventListener('ended', handleEnded);\n\n // Start immediately if already playing\n if (!video.paused && video.readyState >= 2) {\n startCanvasRendering();\n }\n\n return () => {\n stopCanvasRendering();\n video.removeEventListener('play', handlePlay);\n video.removeEventListener('pause', handlePause);\n video.removeEventListener('ended', handleEnded);\n };\n }, [videoRef, cropping, shouldPlay, startCanvasRendering, stopCanvasRendering]);\n\n return {\n ...hlsState,\n isCanvasRendering: isDrawingRef.current\n };\n} ","/**\n * Throttled dashboard reload utility with retry limit\n * Ensures max 1 reload per interval and prevents infinite reload cycles\n */\n// Store reload attempts globally to persist across function instances\nconst reloadAttempts: number[] = [];\n\nexport const createThrottledReload = (interval = 5000, maxReloads = 3) => {\n // Circuit breaker configuration\n const circuitBreakerWindow = 10000; // 10 seconds\n const circuitBreakerThreshold = 5; // max 5 attempts in 10 seconds\n let last = 0;\n let queued = false;\n let reloadCount = 0;\n let firstReloadTime = 0;\n const resetWindow = 60000; // Reset counter after 1 minute\n \n const doReload = () => {\n if (typeof window !== 'undefined') {\n const now = Date.now();\n \n // Circuit breaker check\n reloadAttempts.push(now);\n // Remove attempts older than the window\n const cutoff = now - circuitBreakerWindow;\n const recentAttempts = reloadAttempts.filter(t => t > cutoff);\n // Update the global array to only keep recent attempts\n reloadAttempts.length = 0;\n reloadAttempts.push(...recentAttempts);\n \n if (recentAttempts.length >= circuitBreakerThreshold) {\n console.error(`[Dashboard Reload] Circuit breaker triggered! ${recentAttempts.length} reload attempts in ${circuitBreakerWindow}ms`);\n // Show persistent error and stop all reloads\n const errorDiv = document.createElement('div');\n errorDiv.id = 'reload-circuit-breaker';\n errorDiv.style.cssText = `\n position: fixed;\n top: 20px;\n left: 50%;\n transform: translateX(-50%);\n background: #dc2626;\n color: white;\n padding: 20px 32px;\n border-radius: 12px;\n z-index: 99999;\n box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3);\n font-family: system-ui, -apple-system, sans-serif;\n `;\n errorDiv.innerHTML = `\n <div style=\"display: flex; align-items: center; gap: 16px;\">\n <svg width=\"24\" height=\"24\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"></path>\n </svg>\n <div>\n <div style=\"font-weight: 600; font-size: 16px; margin-bottom: 4px;\">Too many reload attempts detected</div>\n <div style=\"font-size: 14px; opacity: 0.9;\">Please check your network connection and refresh manually.</div>\n </div>\n </div>\n `;\n \n // Remove any existing error\n const existing = document.getElementById('reload-circuit-breaker');\n if (existing) existing.remove();\n \n document.body.appendChild(errorDiv);\n return; // Stop reload\n }\n \n // Reset counter if it's been more than resetWindow since first reload\n if (now - firstReloadTime > resetWindow) {\n reloadCount = 0;\n firstReloadTime = now;\n }\n \n // Track first reload time\n if (reloadCount === 0) {\n firstReloadTime = now;\n }\n \n reloadCount++;\n \n if (reloadCount > maxReloads) {\n console.error(`[Dashboard Reload] Maximum reload attempts (${maxReloads}) reached. Stopping to prevent infinite loop.`);\n // Show error message to user instead of reloading\n const errorDiv = document.createElement('div');\n errorDiv.style.cssText = `\n position: fixed;\n top: 20px;\n left: 50%;\n transform: translateX(-50%);\n background: #ef4444;\n color: white;\n padding: 16px 24px;\n border-radius: 8px;\n z-index: 9999;\n box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);\n `;\n errorDiv.innerHTML = `\n <div style=\"display: flex; align-items: center; gap: 12px;\">\n <svg width=\"20\" height=\"20\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"></path>\n </svg>\n <span>Stream connection failed. Please refresh the page manually.</span>\n </div>\n `;\n document.body.appendChild(errorDiv);\n \n // Remove after 10 seconds\n setTimeout(() => {\n document.body.removeChild(errorDiv);\n }, 10000);\n \n return;\n }\n \n console.warn(`[Dashboard Reload] Reloading dashboard (attempt ${reloadCount}/${maxReloads})`);\n window.location.reload();\n }\n };\n \n return () => {\n const now = Date.now();\n \n if (now - last >= interval) {\n last = now;\n doReload();\n } else if (!queued) {\n queued = true;\n setTimeout(() => {\n queued = false;\n last = Date.now();\n doReload();\n }, interval - (now - last));\n }\n };\n};\n\nexport const throttledReloadDashboard = createThrottledReload(5000, 3); ","// Simple in-memory rate limiting utility\n// For production use, consider using Redis or similar persistent storage\n\nconst rateLimitMap = new Map<string, { count: number; resetTime: number }>();\n\nexport interface RateLimitOptions {\n windowMs?: number;\n maxRequests?: number;\n}\n\nexport interface RateLimitResult {\n allowed: boolean;\n retryAfter?: number;\n}\n\n/**\n * Check if a request is allowed based on rate limiting\n * @param identifier - Unique identifier for the user (e.g., email, IP)\n * @param options - Rate limiting options\n * @returns Whether the request is allowed and retry time if not\n */\nexport function checkRateLimit(\n identifier: string,\n options: RateLimitOptions = {}\n): RateLimitResult {\n const windowMs = options.windowMs || 60 * 1000; // 1 minute default\n const maxRequests = options.maxRequests || 3; // 3 requests default\n\n const now = Date.now();\n const userLimit = rateLimitMap.get(identifier);\n\n // Clean up old entries\n if (userLimit && userLimit.resetTime < now) {\n rateLimitMap.delete(identifier);\n }\n\n if (!userLimit || userLimit.resetTime < now) {\n // First request or window expired\n rateLimitMap.set(identifier, {\n count: 1,\n resetTime: now + windowMs\n });\n return { allowed: true };\n }\n\n if (userLimit.count >= maxRequests) {\n // Rate limit exceeded\n const retryAfter = Math.ceil((userLimit.resetTime - now) / 1000);\n return { allowed: false, retryAfter };\n }\n\n // Increment count\n userLimit.count++;\n return { allowed: true };\n}\n\n/**\n * Clear rate limit for a specific identifier\n * @param identifier - Unique identifier to clear\n */\nexport function clearRateLimit(identifier: string): void {\n rateLimitMap.delete(identifier);\n}\n\n/**\n * Clear all rate limits\n */\nexport function clearAllRateLimits(): void {\n rateLimitMap.clear();\n} ","/**\n * Sentry Context Utilities\n *\n * Sets user context and custom tags for better error debugging in Sentry.\n * Call setSentryUserContext when user logs in/out.\n * Call setSentryWorkspaceContext when workspace config is available.\n *\n * These utilities gracefully handle the case where Sentry isn't installed,\n * making them safe to use in packages that don't require Sentry.\n *\n * Public capture helpers are quota-controlled and best-effort: they sanitize\n * exception payloads, messages, fingerprints, and extras before sending, then\n * drop duplicate or over-budget handled events to protect Sentry limits.\n */\n\n// Sentry interface - minimal subset we need\ninterface SentryScopeLike {\n setTag?: (key: string, value: string | number | boolean | null | undefined) => void;\n setTags?: (tags: Record<string, string | undefined>) => void;\n setExtra?: (key: string, value: unknown) => void;\n setExtras?: (extras: Record<string, unknown>) => void;\n setLevel?: (level: 'info' | 'warning' | 'error' | string) => void;\n setFingerprint?: (fingerprint: string[]) => void;\n}\n\ninterface SentryBreadcrumbLike {\n category?: string;\n message?: string;\n level?: 'info' | 'warning' | 'error' | string;\n data?: Record<string, unknown>;\n}\n\ninterface SentryLike {\n setUser: (user: { id: string; email?: string } | null) => void;\n setTags: (tags: Record<string, string | undefined>) => void;\n captureException?: (error: unknown) => void;\n captureMessage?: (message: string, level?: string) => void;\n withScope?: (callback: (scope: SentryScopeLike) => void) => void;\n addBreadcrumb?: (breadcrumb: SentryBreadcrumbLike) => void;\n}\n\n// Dynamically require Sentry to avoid build errors when not installed\nfunction getSentry(): SentryLike | null {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n return require('@sentry/nextjs');\n } catch {\n // Sentry not installed, return null\n return null;\n }\n}\n\ninterface SentryUser {\n id: string;\n email?: string;\n role?: string;\n role_level?: string;\n company_id?: string;\n}\n\ninterface WorkspaceConfig {\n companyId?: string;\n factoryId?: string;\n factoryName?: string;\n}\n\nexport type SentryCaptureSeverity = 'info' | 'warning' | 'error';\n\nexport interface SentryCaptureOptions {\n surface?: string;\n route?: string;\n status?: number | null;\n severity?: SentryCaptureSeverity;\n quotaKey?: string;\n extras?: Record<string, unknown>;\n}\n\nexport const SENTRY_HANDLED_EVENT_WINDOW_MS = 10 * 60 * 1000;\nexport const SENTRY_HANDLED_EVENT_SESSION_LIMIT = 20;\nexport const SENTRY_QUOTA_STORAGE_KEY = 'optifye:sentry-handled-quota:v1';\n\nconst sentryFingerprintSentAt = new Map<string, number>();\nlet handledSentryEventCount = 0;\n\nconst UUID_PATTERN = /[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/gi;\nconst LONG_HEX_PATTERN = /\\b[0-9a-f]{24,}\\b/gi;\nconst EMAIL_PATTERN = /\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}\\b/gi;\nconst BEARER_PATTERN = /\\bBearer\\s+[A-Za-z0-9._~+/=-]+/gi;\nconst URI_PATTERN = /\\b[a-z][a-z0-9+.-]{1,31}:\\/\\/[^\\s)]+/gi;\nconst MEDIA_PATH_PATTERN = /\\b[^\\s)]+\\.m3u8(?:\\?[^\\s)]*)?/gi;\nconst SENSITIVE_ASSIGNMENT_PATTERN = /\\b([A-Za-z0-9_.-]*(?:authorization|cookie|password|secret|token|session|email|api[_-]?key)[A-Za-z0-9_.-]*)\\b\\s*[:=]\\s*[\"']?[^\"'\\s,;)}\\]]+/gi;\nconst SENSITIVE_KEY_PATTERN = /(authorization|cookie|password|secret|token|session|email|api[_-]?key|url|stream|playlist|media)/i;\n\nexport function resetSentryQuotaForTests(): void {\n sentryFingerprintSentAt.clear();\n handledSentryEventCount = 0;\n\n const storage = getBrowserSessionStorage();\n storage?.removeItem(SENTRY_QUOTA_STORAGE_KEY);\n}\n\nconst sanitizeString = (value: string): string => {\n return value\n .replace(BEARER_PATTERN, 'Bearer [redacted]')\n .replace(SENSITIVE_ASSIGNMENT_PATTERN, (_match, key: string) => `${key}=[redacted]`)\n .replace(URI_PATTERN, '[url]')\n .replace(MEDIA_PATH_PATTERN, '[media]')\n .replace(EMAIL_PATTERN, '[email]')\n .replace(UUID_PATTERN, ':uuid')\n .replace(LONG_HEX_PATTERN, ':id')\n .replace(/\\?.*$/, '')\n .replace(/\\s+/g, ' ')\n .trim();\n};\n\nconst sanitizeRoute = (value: unknown): string | undefined => {\n if (typeof value !== 'string' || value.trim().length === 0) return undefined;\n\n try {\n const parsed = new URL(value, 'https://optifye.local');\n return sanitizeString(parsed.pathname);\n } catch {\n return sanitizeString(value.split('?')[0] || value);\n }\n};\n\nconst sanitizeValue = (value: unknown, depth = 0): unknown => {\n if (depth > 3) return '[truncated]';\n\n if (typeof value === 'string') return sanitizeString(value);\n if (typeof value === 'number' || typeof value === 'boolean' || value === null || value === undefined) return value;\n\n if (Array.isArray(value)) {\n return value.slice(0, 20).map((item) => sanitizeValue(item, depth + 1));\n }\n\n if (typeof value === 'object') {\n const sanitized: Record<string, unknown> = {};\n Object.entries(value as Record<string, unknown>).forEach(([key, item]) => {\n sanitized[key] = SENSITIVE_KEY_PATTERN.test(key) ? '[redacted]' : sanitizeValue(item, depth + 1);\n });\n return sanitized;\n }\n\n return String(value);\n};\n\nconst sanitizeStack = (stack: string): string => {\n return stack\n .split('\\n')\n .map((line) => sanitizeString(line))\n .join('\\n');\n};\n\nconst sanitizeExtras = (extras?: Record<string, unknown>): Record<string, unknown> | undefined => {\n if (!extras) return undefined;\n return sanitizeValue(extras) as Record<string, unknown>;\n};\n\nconst getBrowserSessionStorage = (): Storage | null => {\n if (typeof window === 'undefined') return null;\n\n try {\n return window.sessionStorage;\n } catch {\n return null;\n }\n};\n\nconst loadSentryQuotaFromStorage = (now: number): void => {\n const storage = getBrowserSessionStorage();\n if (!storage) return;\n\n const rawState = storage.getItem(SENTRY_QUOTA_STORAGE_KEY);\n if (!rawState) return;\n\n try {\n const parsed = JSON.parse(rawState) as {\n handledEventCount?: unknown;\n fingerprintSentAt?: unknown;\n };\n\n const persistedCount = Number(parsed.handledEventCount);\n handledSentryEventCount =\n Number.isFinite(persistedCount) && persistedCount > 0 ? Math.floor(persistedCount) : 0;\n\n sentryFingerprintSentAt.clear();\n if (parsed.fingerprintSentAt && typeof parsed.fingerprintSentAt === 'object') {\n Object.entries(parsed.fingerprintSentAt as Record<string, unknown>).forEach(([fingerprint, sentAt]) => {\n const sentAtMs = Number(sentAt);\n if (Number.isFinite(sentAtMs) && now - sentAtMs < SENTRY_HANDLED_EVENT_WINDOW_MS) {\n sentryFingerprintSentAt.set(fingerprint, sentAtMs);\n }\n });\n }\n } catch {\n storage.removeItem(SENTRY_QUOTA_STORAGE_KEY);\n }\n};\n\nconst persistSentryQuotaToStorage = (): void => {\n const storage = getBrowserSessionStorage();\n if (!storage) return;\n\n try {\n storage.setItem(\n SENTRY_QUOTA_STORAGE_KEY,\n JSON.stringify({\n handledEventCount: handledSentryEventCount,\n fingerprintSentAt: Object.fromEntries(sentryFingerprintSentAt),\n })\n );\n } catch {\n // Quota storage is best-effort; in-memory throttling still protects the current page load.\n }\n};\n\nconst normalizeMessage = (error: unknown): string => {\n const message = error instanceof Error ? error.message : String(error ?? 'unknown');\n return sanitizeString(message.toLowerCase()).slice(0, 180) || 'unknown';\n};\n\nconst getErrorName = (error: unknown): string => {\n if (error && typeof error === 'object' && 'name' in error) {\n const name = (error as { name?: unknown }).name;\n if (typeof name === 'string' && name.trim()) return sanitizeString(name);\n }\n return error instanceof Error ? error.constructor.name : typeof error;\n};\n\nconst getStatusFromError = (error: unknown): number | null => {\n const message = error instanceof Error ? error.message : String(error ?? '');\n const statusMatch = message.match(/\\((\\d{3})\\)/) || message.match(/http\\s+(\\d{3})/i) || message.match(/status:\\s*(\\d{3})/i);\n if (!statusMatch) return null;\n const status = Number.parseInt(statusMatch[1], 10);\n return Number.isFinite(status) ? status : null;\n};\n\nconst hasCaptureOptionShape = (value: Record<string, unknown>): boolean => {\n return (\n 'surface' in value ||\n 'route' in value ||\n 'status' in value ||\n 'severity' in value ||\n 'quotaKey' in value ||\n 'extras' in value\n );\n};\n\nconst normalizeCaptureOptions = (\n options?: SentryCaptureOptions | Record<string, unknown>\n): SentryCaptureOptions => {\n if (!options) return {};\n\n const optionRecord = options as Record<string, unknown>;\n\n if (!hasCaptureOptionShape(optionRecord)) {\n return { extras: optionRecord };\n }\n\n const {\n surface,\n route,\n status,\n severity,\n quotaKey,\n extras,\n ...rest\n } = options as SentryCaptureOptions & Record<string, unknown>;\n\n return {\n surface: typeof surface === 'string' ? surface : undefined,\n route: typeof route === 'string' ? route : undefined,\n status: typeof status === 'number' ? status : null,\n severity: severity === 'info' || severity === 'warning' || severity === 'error' ? severity : undefined,\n quotaKey: typeof quotaKey === 'string' ? quotaKey : undefined,\n extras: {\n ...rest,\n ...(extras && typeof extras === 'object' && !Array.isArray(extras) ? extras : {}),\n },\n };\n};\n\nconst buildSentryFingerprint = (error: unknown, options: SentryCaptureOptions): string => {\n if (options.quotaKey) return sanitizeString(options.quotaKey);\n\n const surface = sanitizeString(options.surface || 'frontend');\n const route = sanitizeRoute(options.route || options.extras?.route || options.extras?.endpoint || options.extras?.url) || 'unknown-route';\n const status = options.status ?? getStatusFromError(error) ?? 'unknown-status';\n return [\n surface,\n route,\n String(status),\n getErrorName(error),\n normalizeMessage(error),\n ].join('|');\n};\n\nconst shouldSendHandledSentryEvent = (fingerprint: string): boolean => {\n const now = Date.now();\n loadSentryQuotaFromStorage(now);\n const lastSentAt = sentryFingerprintSentAt.get(fingerprint);\n\n if (lastSentAt !== undefined && now - lastSentAt < SENTRY_HANDLED_EVENT_WINDOW_MS) {\n return false;\n }\n\n if (handledSentryEventCount >= SENTRY_HANDLED_EVENT_SESSION_LIMIT) {\n return false;\n }\n\n sentryFingerprintSentAt.set(fingerprint, now);\n handledSentryEventCount += 1;\n persistSentryQuotaToStorage();\n return true;\n};\n\nconst createSanitizedSentryException = (error: unknown): unknown => {\n if (error instanceof Error) {\n const sanitizedError = new Error(sanitizeString(error.message) || getErrorName(error) || 'Error');\n sanitizedError.name = getErrorName(error) || 'Error';\n if (typeof error.stack === 'string') {\n sanitizedError.stack = sanitizeStack(error.stack);\n }\n return sanitizedError;\n }\n\n if (typeof error === 'string') {\n return sanitizeString(error) || 'unknown';\n }\n\n return sanitizeValue(error);\n};\n\nexport function isIgnorableFrontendError(error: unknown): boolean {\n const name = error && typeof error === 'object' ? (error as { name?: string }).name || '' : '';\n const message = error instanceof Error ? error.message : String(error ?? '');\n const lowerMessage = message.toLowerCase();\n\n return (\n name === 'AbortError' ||\n name === 'NotAllowedError' ||\n lowerMessage.includes('the operation was aborted') ||\n lowerMessage.includes('signal is aborted') ||\n lowerMessage.includes('resizeobserver loop') ||\n lowerMessage.includes('play() failed') ||\n lowerMessage.includes('play request was interrupted') ||\n lowerMessage.includes('autoplay') ||\n lowerMessage.includes('not allowed by the user agent') ||\n lowerMessage.includes('recoverable hls') ||\n lowerMessage.includes('non-fatal hls') ||\n lowerMessage.includes('fragloaderror') ||\n lowerMessage.includes('frag load error') ||\n lowerMessage.includes('fragloadtimeout') ||\n lowerMessage.includes('bufferstallederror') ||\n lowerMessage.includes('buffer stalled') ||\n lowerMessage.includes('media error recovered') ||\n lowerMessage.includes('recovermediaerror')\n );\n}\n\n/**\n * Sets user context and tags in Sentry.\n * Call this when user logs in or out.\n * No-op if Sentry isn't installed.\n *\n * @param user - User object or null on logout\n */\nexport function setSentryUserContext(user: SentryUser | null): void {\n const sentry = getSentry();\n if (!sentry) return;\n\n if (user) {\n // Set user identity (appears on every error)\n sentry.setUser({\n id: user.id,\n });\n\n // Set filterable tags\n sentry.setTags({\n company_id: user.company_id || 'unknown',\n role: user.role || 'unknown',\n role_level: user.role_level || 'unknown',\n });\n } else {\n // Clear user context on logout\n sentry.setUser(null);\n sentry.setTags({\n company_id: undefined,\n role: undefined,\n role_level: undefined,\n });\n }\n}\n\n/**\n * Sets workspace/factory context tags in Sentry.\n * Call this when dashboard config is initialized.\n * No-op if Sentry isn't installed.\n *\n * @param config - Workspace configuration\n */\nexport function setSentryWorkspaceContext(config: WorkspaceConfig): void {\n const sentry = getSentry();\n if (!sentry) return;\n\n sentry.setTags({\n workspace_company: config.companyId || 'unknown',\n workspace_factory: config.factoryId || 'unknown',\n factory_name: config.factoryName || 'unknown',\n });\n}\n\n/**\n * Clears all Sentry context (user and tags).\n * Call this on full logout/session clear.\n * No-op if Sentry isn't installed.\n */\nexport function clearSentryContext(): void {\n const sentry = getSentry();\n if (!sentry) return;\n\n sentry.setUser(null);\n sentry.setTags({\n company_id: undefined,\n role: undefined,\n role_level: undefined,\n workspace_company: undefined,\n workspace_factory: undefined,\n factory_name: undefined,\n });\n}\n\nfunction applyScopeExtras(scope: SentryScopeLike, extras?: Record<string, unknown>): void {\n if (!extras) return;\n\n if (scope.setExtras) {\n scope.setExtras(extras);\n return;\n }\n\n if (scope.setExtra) {\n Object.entries(extras).forEach(([key, value]) => scope.setExtra?.(key, value));\n }\n}\n\n/**\n * Capture a Sentry message if Sentry is installed.\n * No-op if Sentry isn't installed.\n *\n * Messages are sanitized and quota-controlled. Callers should use this only for\n * high-signal handled frontend failures, not as an exact-delivery logging API.\n */\nexport function captureSentryMessage(\n message: string,\n level: 'info' | 'warning' | 'error' = 'warning',\n extras?: SentryCaptureOptions | Record<string, unknown>\n): void {\n const sentry = getSentry();\n if (!sentry || !sentry.captureMessage) return;\n\n const options = normalizeCaptureOptions({ ...(normalizeCaptureOptions(extras)), severity: level });\n const fingerprint = buildSentryFingerprint(new Error(message), options);\n if (!shouldSendHandledSentryEvent(fingerprint)) return;\n\n const sanitizedExtras = sanitizeExtras({\n ...options.extras,\n sentry_quota_key: fingerprint,\n sentry_capture_policy: 'quota_controlled',\n });\n const route = sanitizeRoute(options.route || options.extras?.route || options.extras?.endpoint || options.extras?.url);\n const sanitizedMessage = sanitizeString(message) || 'Sentry message';\n\n if (sentry.withScope) {\n sentry.withScope((scope) => {\n scope.setLevel?.(level);\n scope.setFingerprint?.([fingerprint]);\n if (options.surface) scope.setTag?.('surface', sanitizeString(options.surface));\n if (route) scope.setTag?.('route', route);\n if (options.status !== undefined && options.status !== null) scope.setTag?.('status', options.status);\n applyScopeExtras(scope, sanitizedExtras);\n sentry.captureMessage?.(sanitizedMessage);\n });\n return;\n }\n\n sentry.captureMessage(sanitizedMessage, level);\n}\n\n/**\n * Capture a Sentry exception if Sentry is installed.\n * No-op if Sentry isn't installed.\n *\n * Exceptions are cloned with sanitized names/messages/stacks before capture and\n * may be dropped by the frontend quota policy.\n */\nexport function captureSentryException(\n error: unknown,\n extras?: SentryCaptureOptions | Record<string, unknown>\n): void {\n if (isIgnorableFrontendError(error)) {\n return;\n }\n\n const sentry = getSentry();\n if (!sentry || !sentry.captureException) return;\n\n const options = normalizeCaptureOptions(extras);\n const fingerprint = buildSentryFingerprint(error, options);\n if (!shouldSendHandledSentryEvent(fingerprint)) return;\n\n const sanitizedExtras = sanitizeExtras({\n ...options.extras,\n sentry_quota_key: fingerprint,\n sentry_capture_policy: 'quota_controlled',\n });\n const route = sanitizeRoute(options.route || options.extras?.route || options.extras?.endpoint || options.extras?.url);\n const status = options.status ?? getStatusFromError(error);\n const sanitizedError = createSanitizedSentryException(error);\n\n if (sentry.withScope) {\n sentry.withScope((scope) => {\n scope.setLevel?.(options.severity || 'error');\n scope.setFingerprint?.([fingerprint]);\n if (options.surface) scope.setTag?.('surface', sanitizeString(options.surface));\n if (route) scope.setTag?.('route', route);\n if (status !== null && status !== undefined) scope.setTag?.('status', status);\n applyScopeExtras(scope, sanitizedExtras);\n sentry.captureException?.(sanitizedError);\n });\n return;\n }\n\n sentry.captureException(sanitizedError);\n}\n\n/**\n * Capture a handled frontend exception when it is user-impacting.\n * Aborts and other known benign noise are ignored centrally.\n * Events are sanitized, fingerprinted, and quota-controlled.\n */\nexport function captureHandledFrontendException(\n error: unknown,\n extras?: SentryCaptureOptions | Record<string, unknown>\n): void {\n if (isIgnorableFrontendError(error)) {\n return;\n }\n\n captureSentryException(error, extras);\n}\n\n/**\n * Add a Sentry breadcrumb without creating an event.\n */\nexport function addSentryBreadcrumb(\n message: string,\n options: SentryCaptureOptions & { category?: string } = {}\n): void {\n const sentry = getSentry();\n if (!sentry?.addBreadcrumb) return;\n\n const route = sanitizeRoute(options.route || options.extras?.route || options.extras?.endpoint || options.extras?.url);\n const data = sanitizeExtras({\n surface: options.surface,\n route,\n status: options.status,\n ...options.extras,\n });\n\n sentry.addBreadcrumb({\n category: options.category || options.surface || 'frontend',\n message: sanitizeString(message),\n level: options.severity || 'info',\n data,\n });\n}\n","import mixpanel from 'mixpanel-browser';\nimport { checkRateLimit } from '../utils/rateLimit';\nimport { addSentryBreadcrumb } from '../utils/sentryContext';\n\nexport type DashboardSurface = 'operations_overview' | 'monitor';\n\nexport const ROOT_DASHBOARD_EVENT_NAMES: Record<DashboardSurface, string> = {\n operations_overview: 'Operations Overview Page Clicked',\n monitor: 'Live Monitor Clicked',\n};\n\nconst MIXPANEL_EVENT_NAME_ALIASES: Record<string, string> = {\n 'Operations Overview clicked': ROOT_DASHBOARD_EVENT_NAMES.operations_overview,\n 'monitor page clicked': ROOT_DASHBOARD_EVENT_NAMES.monitor,\n};\n\ntype QueuedMixpanelAction =\n | {\n type: 'track';\n eventName: string;\n properties?: Record<string, any>;\n }\n | {\n type: 'identify';\n userId: string;\n userProperties?: Record<string, any>;\n }\n | {\n type: 'page_view';\n pageName: string;\n properties?: Record<string, any>;\n };\n\nconst MAX_QUEUED_MIXPANEL_ACTIONS = 200;\n\n// TODO: MIXPANEL_TOKEN should come from DashboardConfig\n// TODO: Initialization should ideally happen in a provider once config is available.\n// For now, we assume it might be initialized by the consuming app or this init needs to be conditional.\n\nlet isMixpanelInitialized = false;\nconst queuedMixpanelActions: QueuedMixpanelAction[] = [];\n\n/**\n * Additional options for Mixpanel initialization specifically for\n * Session Replay & Heatmap support. All options map 1-to-1 to the\n * Mixpanel JS SDK `init` options documented here:\n * https://docs.mixpanel.com/docs/replay/web/\n */\nexport interface MixpanelSessionOptions {\n /** Percentage (1 = 1%) of sessions to record. */\n recordSessionsPercent?: number;\n /** How long (ms) of user inactivity before ending a recording. */\n recordIdleTimeoutMs?: number;\n /** Capture click heat-map data during recorded sessions. */\n recordHeatmapData?: boolean;\n /** Enable <canvas> recording (experimental). */\n recordCanvas?: boolean;\n /** CSS selector for elements to block from recording (e.g. images). */\n recordBlockSelector?: string;\n /** CSS selector for elements whose text should be masked. */\n recordMaskTextSelector?: string;\n /** Any additional raw init options the caller wants to forward */\n [key: string]: unknown;\n}\n\n// Helper so we can expose the underlying Mixpanel type in other functions\ntype MixpanelBrowser = typeof mixpanel;\n// Store the most recently identified user's properties so we can automatically\n// append them to every subsequent event. This allows user-level attribution\n// without having to manually pass the info from every call site.\nlet currentUserProperties: Record<string, any> | undefined;\n\nconst MIXPANEL_WARNING_RATE_LIMIT = {\n windowMs: 10 * 60 * 1000, // 10 minutes\n maxRequests: 2,\n};\n\nconst MIXPANEL_ERROR_RATE_LIMIT = {\n windowMs: 10 * 60 * 1000, // 10 minutes\n maxRequests: 5,\n};\n\nconst baseMixpanelExtras = () => ({\n isInitialized: isMixpanelInitialized,\n environment: process.env.NODE_ENV,\n});\n\nconst shouldReportMixpanel = (key: string, isError: boolean): boolean => {\n const options = isError ? MIXPANEL_ERROR_RATE_LIMIT : MIXPANEL_WARNING_RATE_LIMIT;\n return checkRateLimit(`mixpanel:${key}`, options).allowed;\n};\n\nconst reportMixpanelWarning = (key: string, message: string, extras?: Record<string, unknown>) => {\n if (!shouldReportMixpanel(key, false)) return;\n addSentryBreadcrumb(message, {\n surface: 'mixpanel',\n severity: 'warning',\n extras: {\n key,\n ...baseMixpanelExtras(),\n ...extras,\n },\n });\n};\n\nconst reportMixpanelError = (key: string, error: unknown, extras?: Record<string, unknown>) => {\n if (!shouldReportMixpanel(key, true)) return;\n addSentryBreadcrumb('Mixpanel operation failed', {\n surface: 'mixpanel',\n severity: 'warning',\n extras: {\n key,\n error_message: error instanceof Error ? error.message : String(error ?? 'unknown'),\n ...baseMixpanelExtras(),\n ...extras,\n },\n });\n};\n\nconst normalizeCoreEventName = (eventName: string): string => {\n const canonicalName = MIXPANEL_EVENT_NAME_ALIASES[eventName] || eventName;\n if (canonicalName !== eventName) {\n reportMixpanelWarning(`event_alias:${eventName}`, 'Mixpanel event alias rewritten to canonical name', {\n operation: 'track_event',\n originalEventName: eventName,\n canonicalEventName: canonicalName,\n });\n }\n return canonicalName;\n};\n\nconst queueMixpanelAction = (action: QueuedMixpanelAction) => {\n if (queuedMixpanelActions.length >= MAX_QUEUED_MIXPANEL_ACTIONS) {\n // Drop oldest action to avoid unbounded memory growth during outages.\n queuedMixpanelActions.shift();\n reportMixpanelWarning('queue_overflow', 'Mixpanel queue overflow, dropping oldest queued action', {\n operation: 'queue',\n maxQueuedActions: MAX_QUEUED_MIXPANEL_ACTIONS,\n });\n }\n queuedMixpanelActions.push(action);\n};\n\nconst flushQueuedMixpanelActions = () => {\n if (!isMixpanelInitialized || queuedMixpanelActions.length === 0) return;\n\n const pendingActions = queuedMixpanelActions.splice(0, queuedMixpanelActions.length);\n pendingActions.forEach((action) => {\n try {\n if (action.type === 'identify') {\n mixpanel.identify(action.userId);\n if (action.userProperties) {\n mixpanel.people.set(action.userProperties);\n }\n return;\n }\n\n if (action.type === 'page_view') {\n mixpanel.track('Page View', { page: action.pageName, ...(action.properties || {}) });\n return;\n }\n\n mixpanel.track(action.eventName, action.properties || {});\n } catch (err) {\n reportMixpanelError('flush_queued_action_failed', err, {\n operation: 'flush_queue',\n actionType: action.type,\n });\n }\n });\n};\n\n/**\n * Initializes Mixpanel with the provided token.\n * Should be called once from the application setup (e.g., within DashboardProvider)\n * when the token is available from configuration.\n * @param token - The Mixpanel project token.\n * @param debug - Optional. Enable Mixpanel debug mode.\n * @param trackPageView - Optional. Enable automatic page view tracking by Mixpanel.\n */\nexport const initializeCoreMixpanel = (\n token: string,\n // Backwards-compatibility: callers may still pass `debug` as 2nd param.\n debugOrOptions?: boolean | (MixpanelSessionOptions & { debug?: boolean; trackPageView?: boolean }),\n trackPageViewArg?: boolean\n) => {\n if (!token) {\n console.warn('Mixpanel token not provided for initialization. Mixpanel will not be enabled.');\n reportMixpanelWarning('init_missing_token', 'Mixpanel init skipped: missing token', {\n operation: 'init',\n hasToken: false,\n });\n return;\n }\n if (isMixpanelInitialized) {\n console.warn('Mixpanel already initialized. Ignoring subsequent initialization.');\n return;\n }\n\n // --- Determine initialization options ------------------------------- //\n let debug: boolean | undefined;\n let trackPageView: boolean | undefined;\n let sessionOpts: MixpanelSessionOptions = {};\n\n if (typeof debugOrOptions === 'boolean' || debugOrOptions === undefined) {\n // Legacy signature: (token, debug?, trackPageView?)\n debug = debugOrOptions as boolean | undefined;\n trackPageView = trackPageViewArg;\n } else {\n // New signature: (token, options)\n const opts = debugOrOptions;\n ({ debug, trackPageView, ...sessionOpts } = opts);\n }\n\n const initOptions: Record<string, unknown> = {\n debug: debug ?? (process.env.NODE_ENV === 'development'),\n track_pageview: trackPageView ?? true,\n persistence: 'localStorage',\n };\n\n // Map provided sessionOpts → Mixpanel init fields\n /**\n * IMPORTANT DEFAULTS:\n * Mixpanel Session Replay emits internal events like \"Session Recording Checkpoint\"\n * while a recording is active. For long-lived dashboard pages (e.g. \"home\" left\n * open), this can generate a lot of noisy events.\n *\n * - Replay is enabled at 60% by default (`record_sessions_percent = 60`) unless the app overrides it.\n * - If the app opts in but does not specify an idle timeout, we apply a reasonable\n * default so recordings don't run \"all day\" when the user is inactive.\n */\n const recordSessionsPercent = sessionOpts.recordSessionsPercent ?? 60;\n initOptions.record_sessions_percent = recordSessionsPercent;\n\n // If recording is enabled, end recordings after a period of user inactivity.\n // Apps can override this by passing `recordIdleTimeoutMs`.\n const defaultIdleTimeoutMs = 10 * 60 * 1000; // 10 minutes\n const recordIdleTimeoutMs =\n sessionOpts.recordIdleTimeoutMs ?? (recordSessionsPercent > 0 ? defaultIdleTimeoutMs : undefined);\n if (recordIdleTimeoutMs !== undefined) {\n initOptions.record_idle_timeout_ms = recordIdleTimeoutMs;\n }\n if (sessionOpts.recordHeatmapData !== undefined) {\n initOptions.record_heatmap_data = sessionOpts.recordHeatmapData;\n } else {\n // Keep heatmap capture OFF by default (it only applies to recorded sessions anyway).\n initOptions.record_heatmap_data = false;\n }\n if (sessionOpts.recordCanvas !== undefined) {\n initOptions.record_canvas = sessionOpts.recordCanvas;\n }\n if (sessionOpts.recordBlockSelector !== undefined) {\n initOptions.record_block_selector = sessionOpts.recordBlockSelector;\n }\n if (sessionOpts.recordMaskTextSelector !== undefined) {\n initOptions.record_mask_text_selector = sessionOpts.recordMaskTextSelector;\n }\n\n // Merge any additional custom keys caller may have set on sessionOpts\n Object.keys(sessionOpts).forEach((key) => {\n if (!(key in initOptions)) {\n // @ts-ignore – dynamic assignment is okay here\n initOptions[key] = (sessionOpts as any)[key];\n }\n });\n\n try {\n mixpanel.init(token, initOptions);\n isMixpanelInitialized = true;\n flushQueuedMixpanelActions();\n } catch (err) {\n reportMixpanelError('init_failed', err, {\n operation: 'init',\n hasToken: true,\n persistence: initOptions.persistence,\n trackPageView: initOptions.track_pageview,\n });\n return;\n }\n if (initOptions.debug) {\n // eslint-disable-next-line no-console\n console.log('Mixpanel initialized in dashboard-core.');\n }\n};\n\n// trackCorePageView is for manual page view tracking if config.analyticsConfig.mixpanelTrackPageview is false\nexport const trackCorePageView = (pageName: string, properties?: Record<string, any>) => {\n if (!isMixpanelInitialized) {\n queueMixpanelAction({\n type: 'page_view',\n pageName,\n properties,\n });\n reportMixpanelWarning('track_pageview_queued', 'Mixpanel not initialized yet: page view queued', {\n operation: 'track_pageview',\n pageName,\n });\n return;\n }\n try {\n mixpanel.track('Page View', { page: pageName, ...properties });\n } catch (err) {\n reportMixpanelError('track_pageview_failed', err, {\n operation: 'track_pageview',\n pageName,\n });\n }\n};\n\nexport const trackCoreEvent = (eventName: string, properties?: Record<string, any>) => {\n const normalizedEventName = normalizeCoreEventName(eventName);\n if (!isMixpanelInitialized) {\n const mergedProps = {\n ...(currentUserProperties || {}),\n ...(properties || {}),\n };\n queueMixpanelAction({\n type: 'track',\n eventName: normalizedEventName,\n properties: mergedProps,\n });\n reportMixpanelWarning('track_event_queued', 'Mixpanel not initialized yet: event queued', {\n operation: 'track_event',\n eventName: normalizedEventName,\n });\n return;\n }\n const mergedProps = {\n // Precedence order: explicit properties passed by caller should override\n // automatically appended user properties to avoid accidental overwrites.\n ...(currentUserProperties || {}),\n ...(properties || {}),\n };\n try {\n mixpanel.track(normalizedEventName, mergedProps);\n } catch (err) {\n reportMixpanelError('track_event_failed', err, {\n operation: 'track_event',\n eventName: normalizedEventName,\n });\n }\n};\n\n// ---------------------------------------------------------------------------\n// Session Replay helper wrappers so that calling code does not have to touch\n// the global `mixpanel` object directly.\n// ---------------------------------------------------------------------------\n\n/** Manually start Session Replay recording regardless of sampling percent. */\nexport const startCoreSessionRecording = () => {\n try {\n if (!isMixpanelInitialized) return;\n // @ts-ignore - method is provided by newer mixpanel-browser builds (>2.50)\n if (typeof (mixpanel as MixpanelBrowser).start_session_recording === 'function') {\n // @ts-ignore\n (mixpanel as MixpanelBrowser).start_session_recording();\n }\n } catch (err) {\n console.error('[Mixpanel] Unable to start session recording:', err);\n reportMixpanelError('start_session_recording_failed', err, {\n operation: 'start_session_recording',\n });\n }\n};\n\n/** Manually stop an active Session Replay recording. */\nexport const stopCoreSessionRecording = () => {\n try {\n if (!isMixpanelInitialized) return;\n // @ts-ignore - method is provided by newer mixpanel-browser builds\n if (typeof (mixpanel as MixpanelBrowser).stop_session_recording === 'function') {\n // @ts-ignore\n (mixpanel as MixpanelBrowser).stop_session_recording();\n }\n } catch (err) {\n console.error('[Mixpanel] Unable to stop session recording:', err);\n reportMixpanelError('stop_session_recording_failed', err, {\n operation: 'stop_session_recording',\n });\n }\n};\n\n/**\n * Get the current replay-ID & associated properties ($mp_replay_id) so that\n * callers can attach it to server-side or 3rd-party events.\n */\nexport const getCoreSessionRecordingProperties = (): Record<string, any> => {\n try {\n if (!isMixpanelInitialized) return {};\n // @ts-ignore – exists on newer SDKs\n if (typeof (mixpanel as MixpanelBrowser).get_session_recording_properties === 'function') {\n // @ts-ignore\n return (mixpanel as MixpanelBrowser).get_session_recording_properties() || {};\n }\n } catch {\n /* ignore */\n }\n return {};\n};\n\n/** Obtain the absolute Mixpanel URL which allows viewing the current replay. */\nexport const getCoreSessionReplayUrl = (): string | null => {\n try {\n if (!isMixpanelInitialized) return null;\n // @ts-ignore – exists on newer SDKs\n if (typeof (mixpanel as MixpanelBrowser).get_session_replay_url === 'function') {\n // @ts-ignore\n return (mixpanel as MixpanelBrowser).get_session_replay_url();\n }\n } catch {\n /* ignore */\n }\n return null;\n};\n\nexport const identifyCoreUser = (userId: string, userProperties?: Record<string, any>) => {\n currentUserProperties = userProperties ? { ...userProperties } : undefined;\n\n if (!isMixpanelInitialized) {\n queueMixpanelAction({\n type: 'identify',\n userId,\n userProperties: currentUserProperties,\n });\n reportMixpanelWarning('identify_queued', 'Mixpanel not initialized yet: identify queued', {\n operation: 'identify',\n userIdPresent: Boolean(userId),\n hasUserProperties: Boolean(userProperties),\n });\n return;\n }\n try {\n mixpanel.identify(userId);\n if (userProperties) {\n mixpanel.people.set(userProperties);\n }\n } catch (err) {\n reportMixpanelError('identify_failed', err, {\n operation: 'identify',\n userIdPresent: Boolean(userId),\n hasUserProperties: Boolean(userProperties),\n });\n return;\n }\n\n // Persist user properties so they are merged into every future `trackCoreEvent` call.\n currentUserProperties = userProperties ? { ...userProperties } : undefined;\n};\n\nexport const resetCoreMixpanel = () => {\n if (isMixpanelInitialized) {\n try {\n mixpanel.reset();\n } catch (err) {\n reportMixpanelError('reset_failed', err, {\n operation: 'reset',\n });\n }\n }\n currentUserProperties = undefined;\n queuedMixpanelActions.splice(0, queuedMixpanelActions.length);\n};\n","import {\n Activity,\n AlertTriangle,\n Clock,\n ClipboardX,\n HelpCircle,\n Package,\n RefreshCw,\n UserX,\n Wrench,\n} from 'lucide-react';\nimport type { LucideIcon } from 'lucide-react';\n\nexport type IdleReasonPaletteToken = 'red' | 'amber' | 'blue' | 'violet' | 'emerald' | 'cyan' | 'slate';\nexport type IdleReasonIconToken =\n | 'alert-triangle'\n | 'refresh-cw'\n | 'package'\n | 'clock'\n | 'user-x'\n | 'wrench'\n | 'activity'\n | 'clipboard-x'\n | 'help-circle';\n\nexport interface IdleReasonMetadataLike {\n label?: string | null;\n displayName?: string | null;\n paletteToken?: string | null;\n iconToken?: string | null;\n isKnown?: boolean | null;\n}\n\nexport interface IdleReasonPresentation {\n label: string | null;\n displayName: string;\n paletteToken: IdleReasonPaletteToken;\n iconToken: IdleReasonIconToken;\n isKnown: boolean;\n hex: string;\n textClass: string;\n bgClass: string;\n borderClass: string;\n Icon: LucideIcon;\n}\n\nconst DEFAULT_PALETTE_TOKEN: IdleReasonPaletteToken = 'slate';\nconst DEFAULT_ICON_TOKEN: IdleReasonIconToken = 'help-circle';\n\nconst PALETTE_CONFIG: Record<IdleReasonPaletteToken, Omit<IdleReasonPresentation, 'label' | 'displayName' | 'iconToken' | 'isKnown' | 'Icon' | 'paletteToken'>> = {\n red: {\n hex: '#dc2626',\n textClass: 'text-red-600',\n bgClass: 'bg-red-50',\n borderClass: 'border-red-200',\n },\n amber: {\n hex: '#f59e0b',\n textClass: 'text-amber-600',\n bgClass: 'bg-amber-50',\n borderClass: 'border-amber-200',\n },\n blue: {\n hex: '#3b82f6',\n textClass: 'text-blue-600',\n bgClass: 'bg-blue-50',\n borderClass: 'border-blue-200',\n },\n violet: {\n hex: '#8b5cf6',\n textClass: 'text-violet-600',\n bgClass: 'bg-violet-50',\n borderClass: 'border-violet-200',\n },\n emerald: {\n hex: '#10b981',\n textClass: 'text-emerald-600',\n bgClass: 'bg-emerald-50',\n borderClass: 'border-emerald-200',\n },\n cyan: {\n hex: '#0891b2',\n textClass: 'text-cyan-600',\n bgClass: 'bg-cyan-50',\n borderClass: 'border-cyan-200',\n },\n slate: {\n hex: '#64748b',\n textClass: 'text-slate-600',\n bgClass: 'bg-slate-50',\n borderClass: 'border-slate-200',\n },\n};\n\nconst ICON_CONFIG: Record<IdleReasonIconToken, LucideIcon> = {\n 'alert-triangle': AlertTriangle,\n 'refresh-cw': RefreshCw,\n package: Package,\n clock: Clock,\n 'user-x': UserX,\n wrench: Wrench,\n activity: Activity,\n 'clipboard-x': ClipboardX,\n 'help-circle': HelpCircle,\n};\n\nexport const humanizeIdleReasonLabel = (value: string | null | undefined): string => {\n const text = String(value || '').trim();\n if (!text) {\n return 'Unknown';\n }\n return text\n .replace(/_/g, ' ')\n .split(/\\s+/)\n .filter(Boolean)\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join(' ');\n};\n\nconst normalizePaletteToken = (value: string | null | undefined): IdleReasonPaletteToken => {\n const token = String(value || '').trim().toLowerCase();\n if (token in PALETTE_CONFIG) {\n return token as IdleReasonPaletteToken;\n }\n return DEFAULT_PALETTE_TOKEN;\n};\n\nconst normalizeIconToken = (value: string | null | undefined): IdleReasonIconToken => {\n const token = String(value || '').trim().toLowerCase();\n if (token in ICON_CONFIG) {\n return token as IdleReasonIconToken;\n }\n return DEFAULT_ICON_TOKEN;\n};\n\nexport const getIdleReasonPresentation = (\n metadata?: IdleReasonMetadataLike | null,\n): IdleReasonPresentation => {\n const label = typeof metadata?.label === 'string' && metadata.label.trim()\n ? metadata.label.trim()\n : null;\n const displayName = typeof metadata?.displayName === 'string' && metadata.displayName.trim()\n ? metadata.displayName.trim()\n : humanizeIdleReasonLabel(label);\n const paletteToken = normalizePaletteToken(metadata?.paletteToken);\n const iconToken = normalizeIconToken(metadata?.iconToken);\n\n return {\n label,\n displayName,\n paletteToken,\n iconToken,\n isKnown: Boolean(metadata?.isKnown),\n Icon: ICON_CONFIG[iconToken],\n ...PALETTE_CONFIG[paletteToken],\n };\n};\n\nexport const getIdleReasonHexColor = (metadata?: IdleReasonMetadataLike | null): string =>\n getIdleReasonPresentation(metadata).hex;\n","export type VideoGridMetricMode =\n | 'efficiency'\n | 'recent_flow'\n | 'recent_flow_wip_gated';\n\nconst VALID_VIDEO_GRID_METRIC_MODES: ReadonlySet<VideoGridMetricMode> = new Set([\n 'efficiency',\n 'recent_flow',\n 'recent_flow_wip_gated',\n]);\n\nexport const normalizeVideoGridMetricMode = (\n value: unknown,\n assemblyEnabled = false\n): VideoGridMetricMode => {\n const normalized = typeof value === 'string' ? value.trim().toLowerCase() : '';\n if (VALID_VIDEO_GRID_METRIC_MODES.has(normalized as VideoGridMetricMode)) {\n return normalized as VideoGridMetricMode;\n }\n\n return assemblyEnabled ? 'recent_flow_wip_gated' : 'efficiency';\n};\n\nexport const isRecentFlowVideoGridMetricMode = (\n value: unknown,\n assemblyEnabled = false\n): boolean => {\n const normalized = normalizeVideoGridMetricMode(value, assemblyEnabled);\n return normalized === 'recent_flow' || normalized === 'recent_flow_wip_gated';\n};\n\nexport const isWipGatedVideoGridMetricMode = (\n value: unknown,\n assemblyEnabled = false\n): boolean => normalizeVideoGridMetricMode(value, assemblyEnabled) === 'recent_flow_wip_gated';\n","import { DEFAULT_EFFICIENCY_LEGEND, EfficiencyLegendUpdate, getEfficiencyColor } from '../../../lib/types/efficiencyLegend';\nimport { WorkspaceMetrics } from '../../../lib/types';\nimport {\n isRecentFlowVideoGridMetricMode,\n isWipGatedVideoGridMetricMode,\n normalizeVideoGridMetricMode,\n} from '../../../lib/constants/videoGridMetricMode';\nimport {\n isValidAggregateEfficiency,\n normalizeMonitoringMode,\n toFiniteNumberOrNull,\n} from '../../../lib/utils/efficiencyValidity';\n\nexport const VIDEO_GRID_LEGEND_LABEL = 'Real-Time efficiency';\nexport const MAP_GRID_LEGEND_LABEL = 'Efficiency';\nexport const MIXED_VIDEO_GRID_LEGEND_LABEL = VIDEO_GRID_LEGEND_LABEL;\n\nexport type VideoGridColorState = 'blue' | 'green' | 'yellow' | 'red' | 'neutral';\nexport interface AllVideoGridGreenStreakDisplay {\n label: 'All green';\n elapsedSeconds: number;\n confirmedSeconds: number;\n durationText: string;\n startedAt: string;\n anchorAt: string;\n tickOriginAtMs: number;\n}\n\nexport interface AllGreenStreakMilestone {\n milestoneSeconds: number;\n headline: string;\n}\n\ntype RecentFlowHourly = WorkspaceMetrics['recent_flow_hourly'];\ntype WorstPerformanceLineMode = 'uptime' | 'assembly' | 'flow';\ntype WorstPerformanceCandidate = {\n workspace: WorkspaceMetrics;\n index: number;\n};\n\nconst GREEN_STREAK_FRESHNESS_GRACE_MS = 2 * 60 * 1000;\nconst CONFIRMED_MINUTE_DURATION_MS = 60 * 1000;\nconst RECENT_FLOW_COMPUTED_FRESHNESS_MS = 120 * 1000;\n\nconst isFiniteNumber = (value: unknown): value is number => (\n typeof value === 'number' && Number.isFinite(value)\n);\n\nconst padTwoDigits = (value: number): string => String(value).padStart(2, '0');\n\nconst parseTimestampMs = (value?: string | null): number => {\n if (!value) {\n return Number.NaN;\n }\n\n const timestampMs = Date.parse(value);\n return Number.isFinite(timestampMs) ? timestampMs : Number.NaN;\n};\n\nconst isAllGreenStreakFresh = (\n workspace: WorkspaceMetrics,\n anchorAtMs: number,\n nowMs: number\n): boolean => {\n const computedAt = workspace.recent_flow_computed_at;\n if (typeof computedAt === 'string' && computedAt.trim()) {\n const computedAtMs = parseTimestampMs(computedAt);\n return Number.isFinite(computedAtMs)\n && nowMs - computedAtMs <= RECENT_FLOW_COMPUTED_FRESHNESS_MS;\n }\n\n return Number.isFinite(anchorAtMs)\n && nowMs <= anchorAtMs + CONFIRMED_MINUTE_DURATION_MS + GREEN_STREAK_FRESHNESS_GRACE_MS;\n};\n\nexport const formatAllGreenStreakDuration = (elapsedSeconds: number): string => {\n const safeElapsedSeconds = Math.max(0, Math.floor(elapsedSeconds));\n const hours = Math.floor(safeElapsedSeconds / 3600);\n const minutes = Math.floor((safeElapsedSeconds % 3600) / 60);\n const seconds = safeElapsedSeconds % 60;\n\n if (hours > 0) {\n return `${hours}h ${padTwoDigits(minutes)}m ${padTwoDigits(seconds)}s`;\n }\n\n if (minutes > 0) {\n return `${minutes}m ${padTwoDigits(seconds)}s`;\n }\n\n return `${seconds}s`;\n};\n\nexport const getAllGreenStreakMilestone = (elapsedSeconds: number): AllGreenStreakMilestone | null => {\n const safeElapsedSeconds = Math.max(0, Math.floor(elapsedSeconds));\n const thirtyMinutesSeconds = 30 * 60;\n const oneHourSeconds = 60 * 60;\n if (safeElapsedSeconds < thirtyMinutesSeconds) {\n return null;\n }\n\n if (safeElapsedSeconds < oneHourSeconds) {\n return {\n milestoneSeconds: thirtyMinutesSeconds,\n headline: '30 min',\n };\n }\n\n const hours = Math.floor(safeElapsedSeconds / oneHourSeconds);\n return {\n milestoneSeconds: hours * oneHourSeconds,\n headline: `${hours * 60} min`,\n };\n};\n\nexport const getWorkspaceVideoGridMetricMode = (workspace: WorkspaceMetrics) => (\n normalizeVideoGridMetricMode(\n workspace.video_grid_metric_mode,\n workspace.assembly_enabled === true\n )\n);\n\nexport const isVideoGridRecentFlowEnabled = (workspace: WorkspaceMetrics): boolean => (\n isRecentFlowVideoGridMetricMode(\n workspace.video_grid_metric_mode,\n workspace.assembly_enabled === true\n )\n);\n\nexport const isVideoGridWipGated = (workspace: WorkspaceMetrics): boolean => (\n isWipGatedVideoGridMetricMode(\n workspace.video_grid_metric_mode,\n workspace.assembly_enabled === true\n )\n);\n\nexport const hasVideoGridRecentFlow = (workspace: WorkspaceMetrics): boolean => (\n isVideoGridRecentFlowEnabled(workspace) && isFiniteNumber(workspace.recent_flow_percent)\n);\n\nexport const isVideoGridRecentFlowUnavailable = (workspace: WorkspaceMetrics): boolean => (\n isVideoGridRecentFlowEnabled(workspace) && !hasVideoGridRecentFlow(workspace)\n);\n\nconst getRawVideoGridMetricValue = (workspace: WorkspaceMetrics): number | null => {\n const recentFlowPercent = workspace.recent_flow_percent;\n if (hasVideoGridRecentFlow(workspace) && isFiniteNumber(recentFlowPercent)) {\n return recentFlowPercent;\n }\n\n if (isVideoGridRecentFlowUnavailable(workspace)) {\n return null;\n }\n\n return workspace.efficiency;\n};\n\nexport const hasIncomingWipMapping = (workspace: WorkspaceMetrics): boolean => (\n Boolean(workspace.incoming_wip_buffer_name)\n);\n\nexport const getVideoGridWorkspaceKey = (workspace: WorkspaceMetrics): string => (\n workspace.workspace_uuid || `${workspace.line_id || 'unknown'}:${workspace.workspace_name || 'unknown'}`\n);\n\nexport const getWorstPerformanceWorkstationLimit = (totalWorkstations: number): number => {\n if (!Number.isFinite(totalWorkstations) || totalWorkstations <= 0) return 0;\n if (totalWorkstations === 2) return 1;\n if (totalWorkstations === 4) return 2;\n return Math.min(3, Math.max(1, Math.floor(totalWorkstations)));\n};\n\nconst coerceRecentFlowHourlyValue = (value: unknown): number | null => {\n if (typeof value === 'number' && Number.isFinite(value)) {\n return value;\n }\n if (typeof value === 'string') {\n const trimmed = value.trim();\n if (!trimmed || trimmed.toLowerCase() === 'x') return null;\n const parsed = Number(trimmed);\n return Number.isFinite(parsed) ? parsed : null;\n }\n return null;\n};\n\nconst collectRecentFlowHourlyValues = (hourly: RecentFlowHourly): number[] => {\n if (!hourly || typeof hourly !== 'object' || Array.isArray(hourly)) {\n return [];\n }\n\n const values: number[] = [];\n Object.values(hourly).forEach((rawSlotValues) => {\n if (Array.isArray(rawSlotValues)) {\n rawSlotValues.forEach((rawValue) => {\n const value = coerceRecentFlowHourlyValue(rawValue);\n if (value !== null) values.push(value);\n });\n return;\n }\n\n if (rawSlotValues && typeof rawSlotValues === 'object') {\n Object.values(rawSlotValues as Record<string, unknown>).forEach((rawValue) => {\n const value = coerceRecentFlowHourlyValue(rawValue);\n if (value !== null) values.push(value);\n });\n }\n });\n\n return values;\n};\n\nexport const getAverageRecentFlowHourlyPercent = (workspace: WorkspaceMetrics): number | null => {\n const values = collectRecentFlowHourlyValues(workspace.recent_flow_hourly);\n if (values.length > 0) {\n return values.reduce((sum, value) => sum + value, 0) / values.length;\n }\n\n if (isFiniteNumber(workspace.avg_recent_flow)) {\n return workspace.avg_recent_flow;\n }\n\n return isFiniteNumber(workspace.recent_flow_percent) ? workspace.recent_flow_percent : null;\n};\n\nconst getAssemblyCycleOverStandardRatio = (workspace: WorkspaceMetrics): number | null => {\n const medianCycleTime = toFiniteNumberOrNull(workspace.avg_cycle_time);\n const standardCycleTime = toFiniteNumberOrNull(workspace.ideal_cycle_time);\n if (\n medianCycleTime === null\n || standardCycleTime === null\n || medianCycleTime <= 0\n || standardCycleTime <= 0\n ) {\n return null;\n }\n\n return medianCycleTime / standardCycleTime;\n};\n\nconst getUptimeWorstPerformanceValue = (workspace: WorkspaceMetrics): number | null => (\n toFiniteNumberOrNull(workspace.efficiency)\n);\n\nconst isWorstPerformanceEligible = (workspace: WorkspaceMetrics): boolean => (\n isValidAggregateEfficiency(workspace.monitoring_mode, workspace.efficiency)\n);\n\nconst isAssemblyLineGroup = (lineWorkspaces: WorkspaceMetrics[]): boolean => (\n // `assembly_enabled` is denormalized from `lines.assembly` onto every\n // live-monitor workspace row; it is not a workstation-level flag.\n lineWorkspaces.some((workspace) => workspace.assembly_enabled === true)\n);\n\nconst getWorstPerformanceLineMode = (lineWorkspaces: WorkspaceMetrics[]): WorstPerformanceLineMode => {\n if (lineWorkspaces.some((workspace) => normalizeMonitoringMode(workspace.monitoring_mode) === 'uptime')) {\n return 'uptime';\n }\n\n return isAssemblyLineGroup(lineWorkspaces) ? 'assembly' : 'flow';\n};\n\nconst getEligibleWorstPerformanceCandidates = (\n lineWorkspaces: WorkspaceMetrics[]\n): WorstPerformanceCandidate[] => (\n lineWorkspaces\n .map((workspace, index) => ({ workspace, index }))\n .filter(({ workspace }) => isWorstPerformanceEligible(workspace))\n);\n\nexport const selectWorstPerformanceWorkspaceIds = (\n workspaces: WorkspaceMetrics[]\n): Set<string> => {\n const workspacesByLine = new Map<string, WorkspaceMetrics[]>();\n workspaces.forEach((workspace) => {\n const lineKey = workspace.line_id || 'unknown';\n const lineWorkspaces = workspacesByLine.get(lineKey) || [];\n lineWorkspaces.push(workspace);\n workspacesByLine.set(lineKey, lineWorkspaces);\n });\n\n const selected = new Set<string>();\n workspacesByLine.forEach((lineWorkspaces) => {\n const limit = getWorstPerformanceWorkstationLimit(lineWorkspaces.length);\n if (limit <= 0) return;\n\n const eligibleCandidates = getEligibleWorstPerformanceCandidates(lineWorkspaces);\n if (eligibleCandidates.length === 0) return;\n\n const lineMode = getWorstPerformanceLineMode(lineWorkspaces);\n if (lineMode === 'uptime') {\n eligibleCandidates\n .map((candidate) => ({\n ...candidate,\n efficiency: getUptimeWorstPerformanceValue(candidate.workspace),\n }))\n .filter((entry) => entry.efficiency !== null)\n .sort((left, right) => (\n (left.efficiency as number) - (right.efficiency as number)\n || left.workspace.workspace_name.localeCompare(right.workspace.workspace_name, undefined, { numeric: true })\n || left.index - right.index\n ))\n .slice(0, limit)\n .forEach((entry) => selected.add(getVideoGridWorkspaceKey(entry.workspace)));\n return;\n }\n\n if (lineMode === 'assembly') {\n eligibleCandidates\n .map((candidate) => ({\n ...candidate,\n ratio: getAssemblyCycleOverStandardRatio(candidate.workspace),\n }))\n .filter((entry) => entry.ratio !== null)\n .sort((left, right) => (\n (right.ratio as number) - (left.ratio as number)\n || left.workspace.workspace_name.localeCompare(right.workspace.workspace_name, undefined, { numeric: true })\n || left.index - right.index\n ))\n .slice(0, limit)\n .forEach((entry) => selected.add(getVideoGridWorkspaceKey(entry.workspace)));\n return;\n }\n\n eligibleCandidates\n .map((candidate) => ({\n ...candidate,\n averageFlow: getAverageRecentFlowHourlyPercent(candidate.workspace),\n }))\n .filter((entry) => entry.averageFlow !== null)\n .sort((left, right) => (\n (left.averageFlow as number) - (right.averageFlow as number)\n || left.workspace.workspace_name.localeCompare(right.workspace.workspace_name, undefined, { numeric: true })\n || left.index - right.index\n ))\n .slice(0, limit)\n .forEach((entry) => selected.add(getVideoGridWorkspaceKey(entry.workspace)));\n });\n\n return selected;\n};\n\nexport const getVideoGridBlueComparisonGroupKey = (workspace: WorkspaceMetrics): string | null => {\n const factoryAreaId = typeof workspace.factory_area_id === 'string'\n ? workspace.factory_area_id.trim()\n : '';\n if (factoryAreaId && workspace.factory_area_enabled === true) {\n return `area:${factoryAreaId}`;\n }\n\n const lineId = typeof workspace.line_id === 'string' ? workspace.line_id.trim() : '';\n return lineId ? `line:${lineId}` : null;\n};\n\nexport const isVideoGridBlueCandidate = (\n workspace: WorkspaceMetrics,\n legend: EfficiencyLegendUpdate = DEFAULT_EFFICIENCY_LEGEND\n): boolean => (\n hasVideoGridRecentFlow(workspace)\n && isFiniteNumber(workspace.recent_flow_percent)\n && workspace.recent_flow_percent >= legend.green_min\n);\n\nexport const selectVideoGridBlueWinnerIds = (\n workspaces: WorkspaceMetrics[],\n legend: EfficiencyLegendUpdate = DEFAULT_EFFICIENCY_LEGEND\n): Set<string> => {\n const candidatesByGroup = new Map<string, WorkspaceMetrics[]>();\n for (const workspace of workspaces) {\n if (!isVideoGridBlueCandidate(workspace, legend)) {\n continue;\n }\n const groupKey = getVideoGridBlueComparisonGroupKey(workspace);\n if (!groupKey) {\n continue;\n }\n const candidates = candidatesByGroup.get(groupKey) || [];\n candidates.push(workspace);\n candidatesByGroup.set(groupKey, candidates);\n }\n\n const winners = new Set<string>();\n for (const candidates of candidatesByGroup.values()) {\n candidates.sort((left, right) => {\n const leftCurrent = left.recent_flow_percent as number;\n const rightCurrent = right.recent_flow_percent as number;\n if (rightCurrent !== leftCurrent) {\n return rightCurrent - leftCurrent;\n }\n\n const leftAverage = isFiniteNumber(left.avg_recent_flow) ? left.avg_recent_flow : Number.NEGATIVE_INFINITY;\n const rightAverage = isFiniteNumber(right.avg_recent_flow) ? right.avg_recent_flow : Number.NEGATIVE_INFINITY;\n if (rightAverage !== leftAverage) {\n return rightAverage - leftAverage;\n }\n\n const lineCompare = (left.line_id || '').localeCompare(right.line_id || '');\n if (lineCompare !== 0) {\n return lineCompare;\n }\n\n const nameCompare = (left.workspace_name || '').localeCompare(right.workspace_name || '');\n if (nameCompare !== 0) {\n return nameCompare;\n }\n\n return getVideoGridWorkspaceKey(left).localeCompare(getVideoGridWorkspaceKey(right));\n });\n\n const winner = candidates[0];\n if (winner) {\n winners.add(getVideoGridWorkspaceKey(winner));\n }\n }\n\n return winners;\n};\n\nconst getVideoGridBaseColorState = (\n workspace: WorkspaceMetrics,\n legend: EfficiencyLegendUpdate = DEFAULT_EFFICIENCY_LEGEND\n): VideoGridColorState => {\n const metricValue = getRawVideoGridMetricValue(workspace);\n if (!isFiniteNumber(metricValue)) {\n return 'neutral';\n }\n\n return getEfficiencyColor(metricValue, legend);\n};\n\nexport const isLowWipGreenOverride = (\n workspace: WorkspaceMetrics,\n legend: EfficiencyLegendUpdate = DEFAULT_EFFICIENCY_LEGEND\n): boolean => {\n if (workspace.scheduled_break_active === true) {\n return false;\n }\n\n if (workspace.recent_flow_forced_zero_after_shift === true) {\n return false;\n }\n\n if (!hasVideoGridRecentFlow(workspace) || !isVideoGridWipGated(workspace)) {\n return false;\n }\n\n if (getVideoGridBaseColorState(workspace, legend) !== 'red') {\n return false;\n }\n\n if (!hasIncomingWipMapping(workspace)) {\n return false;\n }\n\n return isFiniteNumber(workspace.incoming_wip_current) && workspace.incoming_wip_current <= 1;\n};\n\nexport const isHighEfficiencyRedFlowOverride = (\n workspace: WorkspaceMetrics,\n legend: EfficiencyLegendUpdate = DEFAULT_EFFICIENCY_LEGEND\n): boolean => {\n if (workspace.scheduled_break_active === true) {\n return false;\n }\n\n if (workspace.recent_flow_forced_zero_after_shift === true) {\n return false;\n }\n\n if (!hasVideoGridRecentFlow(workspace) || !isVideoGridWipGated(workspace)) {\n return false;\n }\n\n if (isLowWipGreenOverride(workspace, legend)) {\n return false;\n }\n\n if (getVideoGridBaseColorState(workspace, legend) !== 'red') {\n return false;\n }\n\n return isFiniteNumber(workspace.efficiency) && workspace.efficiency > 100;\n};\n\nexport const getVideoGridMetricValue = (\n workspace: WorkspaceMetrics,\n legend: EfficiencyLegendUpdate = DEFAULT_EFFICIENCY_LEGEND\n): number | null => (\n isHighEfficiencyRedFlowOverride(workspace, legend)\n ? workspace.efficiency\n : getRawVideoGridMetricValue(workspace)\n);\n\nconst toMinuteBucket = (minuteBucket?: number): number => (\n Number.isFinite(minuteBucket)\n ? Math.floor(minuteBucket as number)\n : Math.floor(Date.now() / 60000)\n);\n\nconst getEffectiveFlowMinuteBucket = (workspace: WorkspaceMetrics): number | undefined => {\n const effectiveAt = workspace.recent_flow_effective_end_at;\n if (!effectiveAt) {\n return undefined;\n }\n\n const timestamp = Date.parse(effectiveAt);\n if (!Number.isFinite(timestamp)) {\n return undefined;\n }\n\n return Math.floor(timestamp / 60000);\n};\n\nconst hashWorkspaceKey = (workspace: WorkspaceMetrics): number => {\n const workspaceKey = workspace.workspace_uuid || workspace.workspace_name || 'unknown';\n let hash = 0;\n for (let index = 0; index < workspaceKey.length; index += 1) {\n hash = (hash * 31 + workspaceKey.charCodeAt(index)) % 2147483647;\n }\n return hash;\n};\n\nconst getSyntheticLowWipDisplayValue = (\n workspace: WorkspaceMetrics,\n minuteBucket?: number\n): number => {\n const bucket = getEffectiveFlowMinuteBucket(workspace) ?? toMinuteBucket(minuteBucket);\n const offset = ((hashWorkspaceKey(workspace) % 11) + (bucket % 11) + 11) % 11;\n return 100 + offset;\n};\n\nexport const getVideoGridDisplayValue = (\n workspace: WorkspaceMetrics,\n legend: EfficiencyLegendUpdate = DEFAULT_EFFICIENCY_LEGEND,\n minuteBucket?: number\n): number | null => (\n isLowWipGreenOverride(workspace, legend)\n ? getSyntheticLowWipDisplayValue(workspace, minuteBucket)\n : getVideoGridMetricValue(workspace, legend)\n);\n\nexport const getVideoGridColorState = (\n workspace: WorkspaceMetrics,\n legend: EfficiencyLegendUpdate = DEFAULT_EFFICIENCY_LEGEND,\n blueWinnerIds?: Set<string>\n): VideoGridColorState => {\n if (\n blueWinnerIds?.has(getVideoGridWorkspaceKey(workspace))\n && isVideoGridBlueCandidate(workspace, legend)\n ) {\n return 'blue';\n }\n\n const baseColor = getVideoGridBaseColorState(workspace, legend);\n if (!hasVideoGridRecentFlow(workspace)) {\n return baseColor;\n }\n\n if (!isVideoGridWipGated(workspace)) {\n return baseColor;\n }\n\n if (baseColor !== 'red') {\n return baseColor;\n }\n\n if (isLowWipGreenOverride(workspace, legend)) {\n return 'green';\n }\n\n if (isHighEfficiencyRedFlowOverride(workspace, legend)) {\n return getEfficiencyColor(workspace.efficiency, legend);\n }\n\n if (!hasIncomingWipMapping(workspace)) {\n return baseColor;\n }\n\n if (!isFiniteNumber(workspace.incoming_wip_current)) {\n return 'neutral';\n }\n\n return baseColor;\n};\n\nexport const hasAllVideoGridWorkspacesGreen = (\n workspaces: WorkspaceMetrics[],\n legend: EfficiencyLegendUpdate = DEFAULT_EFFICIENCY_LEGEND\n): boolean => {\n const visibleWorkspaces = workspaces.filter((workspace) => (\n Boolean(workspace.workspace_uuid || workspace.workspace_name)\n ));\n\n return visibleWorkspaces.length > 0 && visibleWorkspaces.every((workspace) => (\n getVideoGridColorState(workspace, legend) === 'green'\n ));\n};\n\nexport const getAllVideoGridGreenStreakDisplay = (\n workspaces: WorkspaceMetrics[],\n legend: EfficiencyLegendUpdate = DEFAULT_EFFICIENCY_LEGEND,\n nowMs: number = Date.now()\n): AllVideoGridGreenStreakDisplay | null => {\n const visibleWorkspaces = workspaces.filter((workspace) => (\n Boolean(workspace.workspace_uuid || workspace.workspace_name)\n ));\n\n if (\n visibleWorkspaces.length === 0\n || !hasAllVideoGridWorkspacesGreen(visibleWorkspaces, legend)\n ) {\n return null;\n }\n\n const activeStreaks = visibleWorkspaces.map((workspace) => {\n const startedAt = workspace.video_grid_green_streak_started_at;\n const anchorAt = workspace.video_grid_green_streak_anchor_at;\n const observedAt = workspace.video_grid_green_streak_observed_at;\n const streakMinutes = workspace.video_grid_green_streak_minutes;\n const startedAtMs = parseTimestampMs(startedAt);\n const anchorAtMs = parseTimestampMs(anchorAt);\n const observedAtMs = parseTimestampMs(observedAt);\n const isFresh = isAllGreenStreakFresh(workspace, anchorAtMs, nowMs);\n const tickOriginAtMs = Number.isFinite(observedAtMs)\n ? observedAtMs\n : anchorAtMs + CONFIRMED_MINUTE_DURATION_MS;\n const confirmedSeconds = Math.max(0, Math.floor((streakMinutes || 0) * 60));\n const visibleSeconds = confirmedSeconds\n + Math.max(0, Math.floor((nowMs - tickOriginAtMs) / 1000));\n\n if (\n workspace.video_grid_green_streak_active === true\n && isFiniteNumber(streakMinutes)\n && streakMinutes > 0\n && startedAt\n && anchorAt\n && Number.isFinite(startedAtMs)\n && isFresh\n ) {\n return {\n startedAt,\n startedAtMs,\n anchorAt,\n streakMinutes,\n visibleSeconds,\n tickOriginAtMs,\n };\n }\n\n return null;\n });\n\n if (activeStreaks.some((streak) => streak === null)) {\n return null;\n }\n\n const strictestStartedAt = (activeStreaks as Array<NonNullable<typeof activeStreaks[number]>>)\n .reduce((latest, current) => (\n current.visibleSeconds < latest.visibleSeconds\n || (\n current.visibleSeconds === latest.visibleSeconds\n && current.startedAtMs > latest.startedAtMs\n )\n ? current\n : latest\n ));\n const confirmedSeconds = Math.max(0, Math.floor(strictestStartedAt.streakMinutes * 60));\n\n return {\n label: 'All green',\n elapsedSeconds: confirmedSeconds,\n confirmedSeconds,\n durationText: formatAllGreenStreakDuration(confirmedSeconds),\n startedAt: strictestStartedAt.startedAt,\n anchorAt: strictestStartedAt.anchorAt,\n tickOriginAtMs: strictestStartedAt.tickOriginAtMs,\n };\n};\n\nexport const getVideoGridLegendLabel = (workspaces: WorkspaceMetrics[]): string => {\n const visibleWorkspaces = workspaces;\n if (visibleWorkspaces.length === 0) {\n return MAP_GRID_LEGEND_LABEL;\n }\n\n return visibleWorkspaces.some(isVideoGridRecentFlowEnabled)\n ? VIDEO_GRID_LEGEND_LABEL\n : MAP_GRID_LEGEND_LABEL;\n};\n","import React, { useRef, useCallback, useId } from 'react';\nimport { AlertTriangle, Camera } from 'lucide-react';\nimport { useHlsStreamWithCropping } from '../../../lib/hooks/useHlsStreamWithCropping';\nimport { throttledReloadDashboard } from '../../../lib/utils/dashboardReload';\nimport type { VideoGridStatusBadge, WorkspaceMetrics } from '../../../lib/types';\nimport { trackCoreEvent } from '../../../lib/services/mixpanelService';\nimport { VideoCroppingRect } from '../../../lib/types/dashboardConfig';\nimport { EfficiencyLegendUpdate, DEFAULT_EFFICIENCY_LEGEND } from '../../../lib/types/efficiencyLegend';\nimport { getIdleReasonPresentation } from '../../../lib/constants/idleReasonPresentation';\nimport type { IdleReasonIconToken } from '../../../lib/constants/idleReasonPresentation';\nimport {\n getVideoGridColorState,\n getVideoGridDisplayValue,\n getVideoGridMetricValue,\n getVideoGridWorkspaceKey,\n hasVideoGridRecentFlow,\n isHighEfficiencyRedFlowOverride,\n isVideoGridRecentFlowEnabled\n} from './videoGridMetricUtils';\n\ninterface VideoCardProps {\n workspace: WorkspaceMetrics;\n hlsUrl: string;\n shouldPlay: boolean;\n onClick?: () => void;\n onFatalError?: () => void;\n legend?: EfficiencyLegendUpdate;\n cropping?: VideoCroppingRect;\n canvasFps?: number;\n useRAF?: boolean;\n className?: string;\n compact?: boolean;\n displayMinuteBucket?: number;\n displayName?: string;\n isBlueBest?: boolean;\n isWorstPerformance?: boolean;\n lastSeenLabel?: string;\n hasRecentHealthSignal?: boolean;\n onMouseEnter?: () => void;\n onMouseLeave?: () => void;\n}\n\nconst getStatusBadgeSignature = (workspace: WorkspaceMetrics): string => {\n const badges = workspace.video_grid_badges || [];\n return badges\n .map((badge) => [\n badge.kind,\n badge.label,\n badge.display_name,\n badge.palette_token,\n badge.icon_token,\n badge.anchor_minute,\n badge.reason_minute,\n badge.shift_elapsed_fraction,\n badge.title,\n ].join(':'))\n .join('|');\n};\n\nconst VALID_STATUS_BADGE_ICON_TOKENS = new Set<string>([\n 'alert-triangle',\n 'refresh-cw',\n 'package',\n 'clock',\n 'user-x',\n 'wrench',\n 'activity',\n 'clipboard-x',\n]);\n\nconst normalizeStatusBadgeToken = (value: string | null | undefined): string => (\n String(value || '')\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '_')\n .replace(/^_+|_+$/g, '')\n);\n\nconst TEMPORARILY_DISABLED_STATUS_BADGE_TOKENS = new Set<string>([\n 'machine_maintenance',\n 'machine_downtime',\n 'no_material',\n]);\n\nconst shouldRenderVideoGridStatusBadge = (badge: VideoGridStatusBadge): boolean => {\n if (badge.kind !== 'idle_reason') {\n return true;\n }\n\n const reasonTokens = [\n normalizeStatusBadgeToken(badge.label),\n normalizeStatusBadgeToken(badge.display_name),\n normalizeStatusBadgeToken(badge.title),\n ];\n return !reasonTokens.some((token) => TEMPORARILY_DISABLED_STATUS_BADGE_TOKENS.has(token));\n};\n\nconst resolveVideoGridStatusBadgeIconToken = (badge: VideoGridStatusBadge): IdleReasonIconToken => {\n if (badge.kind === 'no_plan') {\n return 'clipboard-x';\n }\n\n const reasonTokens = new Set([\n normalizeStatusBadgeToken(badge.label),\n normalizeStatusBadgeToken(badge.display_name),\n normalizeStatusBadgeToken(badge.title),\n ]);\n if (reasonTokens.has('no_material')) {\n return 'package';\n }\n if (reasonTokens.has('machine_maintenance') || reasonTokens.has('machine_downtime')) {\n return 'wrench';\n }\n\n const iconToken = normalizeStatusBadgeToken(badge.icon_token);\n if (VALID_STATUS_BADGE_ICON_TOKENS.has(iconToken)) {\n return iconToken as IdleReasonIconToken;\n }\n return 'activity';\n};\n\n// Helper function to get trend arrow and color\nfunction getTrendArrowAndColor(trend: number): { arrow: string; color: string } | null {\n if (trend > 0) {\n return { arrow: '↑', color: 'text-green-400' };\n } else if (trend < 0) {\n return { arrow: '↓', color: 'text-red-400' };\n } else {\n return { arrow: '→', color: 'text-gray-400' };\n }\n}\n\nexport const VideoCard: React.FC<VideoCardProps> = React.memo(({\n workspace,\n hlsUrl,\n shouldPlay,\n onClick,\n onFatalError,\n legend,\n cropping,\n canvasFps = 30,\n useRAF = true,\n className = '',\n compact = false,\n displayMinuteBucket,\n displayName,\n isBlueBest = false,\n isWorstPerformance = false,\n lastSeenLabel,\n hasRecentHealthSignal = false,\n onMouseEnter,\n onMouseLeave\n}) => {\n const videoRef = useRef<HTMLVideoElement | null>(null);\n const canvasRef = useRef<HTMLCanvasElement | null>(null);\n const statusBadgeIdPrefix = useId();\n const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;\n \n // Always call the same hook; cropping can be undefined.\n const { isStale: isStreamStale } = useHlsStreamWithCropping(videoRef, canvasRef, {\n src: hlsUrl,\n shouldPlay,\n cropping,\n canvasFps,\n useRAF,\n onFatalError: onFatalError ?? (() => throttledReloadDashboard())\n });\n\n const showOffline = Boolean(isStreamStale && !hasRecentHealthSignal);\n const lastSeenText = lastSeenLabel || 'Unknown';\n\n const workspaceDisplayName = displayName || workspace.displayName || workspace.workspace_name;\n\n const videoGridMetricValue = getVideoGridMetricValue(workspace, effectiveLegend);\n const videoGridDisplayValue = getVideoGridDisplayValue(workspace, effectiveLegend, displayMinuteBucket);\n const videoGridColorState = getVideoGridColorState(\n workspace,\n effectiveLegend,\n isBlueBest ? new Set([getVideoGridWorkspaceKey(workspace)]) : undefined\n );\n const isRecentFlowCard = isVideoGridRecentFlowEnabled(workspace);\n const isHighEfficiencyOverride = isHighEfficiencyRedFlowOverride(workspace, effectiveLegend);\n const hasDisplayMetric = typeof videoGridDisplayValue === 'number' && Number.isFinite(videoGridDisplayValue);\n const hasBarMetric = typeof videoGridMetricValue === 'number' && Number.isFinite(videoGridMetricValue);\n const shouldRenderMetricBadge = hasDisplayMetric;\n const badgeTitle = isHighEfficiencyOverride\n ? `Efficiency ${Math.round(videoGridDisplayValue ?? 0)}%`\n : hasVideoGridRecentFlow(workspace)\n ? `Flow ${Math.round(videoGridDisplayValue ?? 0)}%`\n : isRecentFlowCard\n ? 'Flow unavailable'\n : `Efficiency ${Math.round(videoGridDisplayValue ?? 0)}%`;\n const badgeLabel = `${Math.round(videoGridDisplayValue ?? 0)}%`;\n const statusBadges = (workspace.video_grid_badges || []).filter(shouldRenderVideoGridStatusBadge);\n const efficiencyOverlayClass = videoGridColorState === 'green'\n ? 'bg-[#00D654]/25'\n : videoGridColorState === 'blue'\n ? 'bg-[#0EA5E9]/30'\n : videoGridColorState === 'yellow'\n ? 'bg-[#FFD700]/30'\n : videoGridColorState === 'red'\n ? 'bg-[#FF2D0A]/30'\n : 'bg-transparent';\n const efficiencyBarClass = videoGridColorState === 'green'\n ? 'bg-[#00AB45]'\n : videoGridColorState === 'blue'\n ? 'bg-[#0EA5E9]'\n : videoGridColorState === 'yellow'\n ? 'bg-[#FFB020]'\n : videoGridColorState === 'red'\n ? 'bg-[#E34329]'\n : 'bg-gray-500/70';\n const efficiencyStatus = videoGridColorState === 'green'\n ? 'High'\n : videoGridColorState === 'blue'\n ? 'Best'\n : videoGridColorState === 'yellow'\n ? 'Medium'\n : videoGridColorState === 'red'\n ? 'Low'\n : 'Neutral';\n const worstMarkerClassName = compact\n ? 'left-1 top-1 sm:left-1.5 sm:top-1.5 gap-1 sm:gap-1.5 p-1 sm:px-2 sm:py-1 text-[10px] sm:text-[11px]'\n : 'left-1.5 top-1.5 sm:left-2 sm:top-2 gap-1 sm:gap-2 px-1.5 sm:px-3 py-0.5 sm:py-1.5 text-[10px] sm:text-xs';\n const statusBadgesPositionClass = isWorstPerformance\n ? (compact ? 'top-6 sm:top-8 left-1 sm:left-1.5 gap-1' : 'top-7 sm:top-10 left-1.5 sm:left-2 gap-1 sm:gap-1.5')\n : (compact ? 'top-1 sm:top-1.5 left-1 sm:left-1.5 gap-1' : 'top-1.5 sm:top-2 left-1.5 sm:left-2 gap-1 sm:gap-1.5');\n \n // Get trend arrow and color\n const trendInfo = workspace.trend !== undefined ? getTrendArrowAndColor(workspace.trend) : null;\n\n const handleClick = useCallback(() => {\n // Track video card click\n trackCoreEvent('Workspace Card Clicked', {\n workspace_id: workspace.workspace_uuid,\n workspace_name: workspace.workspace_name,\n efficiency: workspace.efficiency,\n status: efficiencyStatus,\n trend: workspace.trend\n });\n\n if (onClick) {\n onClick();\n }\n }, [onClick, workspace, efficiencyStatus]);\n\n return (\n <div\n className={`workspace-card relative bg-gray-950 rounded-md overflow-hidden cursor-pointer shadow-[0_1px_3px_rgba(15,23,42,0.16),0_0_0_1px_rgba(255,255,255,0.06)] transition-[box-shadow] duration-200 hover:shadow-[0_10px_24px_rgba(15,23,42,0.28),0_0_0_1px_rgba(255,255,255,0.10)] active:shadow-[0_1px_3px_rgba(15,23,42,0.16),0_0_0_1px_rgba(255,255,255,0.06)] touch-manipulation ${className}`}\n style={{ width: '100%', height: '100%' }}\n data-worst-performance-highlight={isWorstPerformance ? 'true' : undefined}\n onClick={handleClick}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n onTouchStart={(e) => e.currentTarget.classList.add('touch-active')}\n onTouchEnd={(e) => e.currentTarget.classList.remove('touch-active')}\n tabIndex={0}\n aria-label={`Open workspace ${displayName}`}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n handleClick();\n }\n }}\n >\n {/* Video container with loading placeholder */}\n <div className=\"relative w-full h-full overflow-hidden bg-black\">\n {/* Loading placeholder */}\n <div className=\"absolute inset-0 flex items-center justify-center bg-black z-0\">\n <div className=\"animate-pulse flex flex-col items-center\">\n <Camera className={`w-5 h-5 ${compact ? 'sm:w-4 sm:h-4' : 'sm:w-6 sm:h-6'} text-gray-500`} />\n <span className={`text-[11px] ${compact ? 'sm:text-[10px]' : 'sm:text-xs'} text-gray-500 mt-1`}>Loading...</span>\n </div>\n </div>\n \n {/* Video element */}\n <div className=\"absolute inset-0 z-10\">\n <video\n ref={videoRef}\n className={`h-full w-full object-cover ${cropping ? 'hidden' : ''}`}\n playsInline\n muted\n disablePictureInPicture\n controlsList=\"nodownload noplaybackrate\"\n />\n {/* Canvas element for cropped video */}\n {cropping && (\n <canvas\n ref={canvasRef}\n className=\"h-full w-full object-cover\"\n />\n )}\n </div>\n\n {/* Efficiency overlay */}\n <div className={`absolute inset-0 z-20 pointer-events-none ${efficiencyOverlayClass}`}></div>\n\n {showOffline && (\n <div className=\"absolute inset-0 z-40 flex items-center justify-center bg-black/70 px-2 text-center\">\n <div className=\"flex flex-col items-center gap-1\">\n <AlertTriangle className={`${compact ? 'w-4 h-4' : 'w-5 h-5'} text-amber-300`} />\n <span className={`font-semibold text-white ${compact ? 'text-[11px]' : 'text-xs'}`}>\n Not streaming\n </span>\n <span className={`text-gray-200 ${compact ? 'text-[10px]' : 'text-[11px]'}`}>\n Last seen: {lastSeenText}\n </span>\n </div>\n </div>\n )}\n \n {/* Efficiency percentage badge */}\n {shouldRenderMetricBadge && (\n <div className={`absolute ${compact ? 'top-1 right-1' : 'top-2 right-2'} z-30`}>\n <div\n data-testid=\"video-card-metric-badge\"\n className={`bg-black/70 rounded ${compact ? 'px-1.5 py-1' : 'px-2 py-1'} text-white border border-white/10`}\n title={badgeTitle}\n >\n <span className={`${compact ? 'text-[10px]' : 'text-xs'} font-semibold`}>\n {badgeLabel}\n </span>\n </div>\n </div>\n )}\n\n {statusBadges.length > 0 && (\n <div\n data-testid=\"video-card-status-badges\"\n className={`absolute ${statusBadgesPositionClass} z-30 flex items-center`}\n >\n {statusBadges.map((badge, index) => {\n const presentation = getIdleReasonPresentation({\n label: badge.label,\n displayName: badge.display_name,\n paletteToken: badge.palette_token,\n iconToken: resolveVideoGridStatusBadgeIconToken(badge),\n isKnown: badge.is_known,\n });\n const Icon = presentation.Icon;\n const tooltipText = presentation.displayName;\n const tooltipId = `video-card-status-tooltip-${statusBadgeIdPrefix}-${badge.kind}-${index}`;\n\n return (\n <div\n key={`${badge.kind}-${badge.label || index}-${badge.reason_minute ?? index}`}\n data-testid={`video-card-status-badge-${badge.kind}`}\n aria-label={tooltipText}\n aria-describedby={tooltipId}\n className={`group relative inline-flex shrink-0 items-center justify-center rounded-full bg-slate-950/70 border-2 shadow-[0_3px_10px_rgba(0,0,0,0.34),inset_0_0_0_1px_rgba(255,255,255,0.18)] ${compact ? 'h-7 w-7 sm:h-10 sm:w-10' : 'h-8 w-8 sm:h-11 sm:w-11'}`}\n style={{\n borderColor: presentation.hex,\n }}\n >\n <Icon\n aria-hidden=\"true\"\n className={`${compact ? 'h-4 w-4 sm:h-5 sm:w-5' : 'h-4 w-4 sm:h-6 sm:w-6'} text-white`}\n strokeWidth={2.4}\n />\n <span\n id={tooltipId}\n role=\"tooltip\"\n data-testid={`video-card-status-tooltip-${badge.kind}`}\n className=\"pointer-events-none absolute left-0 top-full mt-2 whitespace-nowrap rounded-md border border-white/10 bg-slate-950/95 px-2 py-1 text-[11px] font-semibold leading-none text-white opacity-0 shadow-[0_6px_18px_rgba(0,0,0,0.34)] transition-opacity duration-150 group-hover:opacity-100\"\n >\n <span className=\"absolute -top-1 left-3 h-2 w-2 rotate-45 border-l border-t border-white/10 bg-slate-950/95\" />\n {tooltipText}\n </span>\n </div>\n );\n })}\n </div>\n )}\n\n {isWorstPerformance && (\n <div\n data-testid=\"video-card-worst-performance-marker\"\n className={`pointer-events-none absolute z-[65] inline-flex items-center rounded-sm sm:rounded-md bg-[#1A0B09]/95 border border-[#E34329]/60 font-semibold tracking-wide text-white shadow-xl max-w-[calc(100%-8px)] sm:max-w-[calc(100%-16px)] ${worstMarkerClassName}`}\n >\n <AlertTriangle className={`${compact ? 'h-3 w-3 sm:h-3.5 sm:w-3.5' : 'h-3.5 w-3.5 sm:h-4 sm:w-4'} shrink-0 text-[#E34329]`} aria-hidden=\"true\" />\n <span className={`truncate leading-tight min-w-0 ${compact ? 'hidden sm:block' : ''}`}>\n Overall Underperformer\n </span>\n </div>\n )}\n\n {/* Efficiency bar indicator at the bottom */}\n <div className={`absolute bottom-0 left-0 right-0 ${compact ? 'h-0.5' : 'h-1'} bg-black/50 z-30`}>\n <div \n data-testid=\"video-card-metric-bar\"\n className={`h-full ${efficiencyBarClass} transition-all duration-500`} \n style={{ width: `${hasBarMetric ? Math.min(100, Math.max(0, videoGridMetricValue as number)) : (videoGridColorState === 'neutral' ? 100 : 0)}%` }}\n ></div>\n </div>\n </div>\n\n {/* Footer with workspace name and live indicator */}\n <div className={`absolute bottom-0 left-0 right-0 bg-black bg-opacity-60 p-1.5 ${compact ? 'sm:p-1' : 'sm:p-1.5'} flex min-w-0 justify-between items-center gap-1 z-10`}>\n <div className=\"flex min-w-0 items-center gap-1.5\">\n <Camera size={compact ? 10 : 12} className=\"shrink-0 text-white\" />\n <p className={`min-w-0 truncate text-white text-[11px] ${compact ? 'sm:text-[10px]' : 'sm:text-xs'} font-medium tracking-wide`}>\n {workspaceDisplayName}\n </p>\n </div>\n <div className={`flex shrink-0 items-center ${compact ? 'gap-1' : 'gap-1.5'}`}>\n {trendInfo && (\n <div\n className={`${compact ? 'text-sm' : 'text-lg'} ${trendInfo.color}`}\n style={{ lineHeight: 1, display: 'flex', alignItems: 'center' }}\n >\n {trendInfo.arrow}\n </div>\n )}\n <div\n className={`${compact ? 'w-1 h-1' : 'w-1.5 h-1.5'} rounded-full ${showOffline ? 'bg-red-500' : 'bg-green-500'}`}\n ></div>\n <span className={`text-white text-[11px] ${compact ? 'sm:text-[10px]' : 'sm:text-xs'}`}>\n {showOffline ? 'Offline' : 'Live'}\n </span>\n </div>\n </div>\n </div>\n );\n}, (prevProps, nextProps) => {\n // Custom comparison function to control re-renders\n // Return true to SKIP re-render, false to RE-RENDER\n\n // Always re-render if critical workspace metrics change\n if (\n prevProps.workspace.efficiency !== nextProps.workspace.efficiency ||\n prevProps.workspace.assembly_enabled !== nextProps.workspace.assembly_enabled ||\n prevProps.workspace.video_grid_metric_mode !== nextProps.workspace.video_grid_metric_mode ||\n prevProps.workspace.recent_flow_percent !== nextProps.workspace.recent_flow_percent ||\n prevProps.workspace.recent_flow_effective_end_at !== nextProps.workspace.recent_flow_effective_end_at ||\n prevProps.workspace.recent_flow_forced_zero_after_shift !== nextProps.workspace.recent_flow_forced_zero_after_shift ||\n prevProps.workspace.scheduled_break_active !== nextProps.workspace.scheduled_break_active ||\n prevProps.workspace.incoming_wip_current !== nextProps.workspace.incoming_wip_current ||\n prevProps.workspace.incoming_wip_buffer_name !== nextProps.workspace.incoming_wip_buffer_name ||\n getStatusBadgeSignature(prevProps.workspace) !== getStatusBadgeSignature(nextProps.workspace) ||\n prevProps.workspace.trend !== nextProps.workspace.trend ||\n prevProps.workspace.performance_score !== nextProps.workspace.performance_score ||\n prevProps.workspace.pph !== nextProps.workspace.pph\n ) {\n return false; // Re-render - metrics changed\n }\n\n // Re-render if workspace identity or display name changes\n if (\n prevProps.workspace.workspace_uuid !== nextProps.workspace.workspace_uuid ||\n prevProps.workspace.workspace_name !== nextProps.workspace.workspace_name ||\n prevProps.workspace.line_id !== nextProps.workspace.line_id\n ) {\n return false;\n }\n\n if (prevProps.displayName !== nextProps.displayName) {\n return false;\n }\n\n if (prevProps.isBlueBest !== nextProps.isBlueBest) {\n return false;\n }\n\n if (prevProps.isWorstPerformance !== nextProps.isWorstPerformance) {\n return false;\n }\n\n if (prevProps.lastSeenLabel !== nextProps.lastSeenLabel) {\n return false;\n }\n\n if (prevProps.hasRecentHealthSignal !== nextProps.hasRecentHealthSignal) {\n return false;\n }\n\n if (prevProps.legend !== nextProps.legend) {\n return false;\n }\n\n if (prevProps.compact !== nextProps.compact) {\n return false;\n }\n\n if (prevProps.displayMinuteBucket !== nextProps.displayMinuteBucket) {\n return false;\n }\n\n // Re-render if video properties change\n if (\n prevProps.hlsUrl !== nextProps.hlsUrl ||\n prevProps.shouldPlay !== nextProps.shouldPlay\n ) {\n return false; // Re-render - video properties changed\n }\n\n // Re-render if cropping changes\n if (\n prevProps.cropping?.x !== nextProps.cropping?.x ||\n prevProps.cropping?.y !== nextProps.cropping?.y ||\n prevProps.cropping?.width !== nextProps.cropping?.width ||\n prevProps.cropping?.height !== nextProps.cropping?.height\n ) {\n return false; // Re-render - cropping changed\n }\n\n // Skip re-render - no relevant changes\n return true;\n});\n\nVideoCard.displayName = 'VideoCard'; \n","import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport type { WorkspaceMetrics } from '../../lib/types';\nimport type { WorkspaceVideoStream } from '../../lib/types/workspaceService';\nimport { DEFAULT_EFFICIENCY_LEGEND, EfficiencyLegendUpdate } from '../../lib/types/efficiencyLegend';\nimport { VideoCard } from '../dashboard/grid/VideoCard';\n\ntype SnapshotVideoStatus = {\n expectedVideoCount: number;\n readyVideoCount: number;\n status: 'loading' | 'ready' | 'no_videos';\n updatedAt: string;\n};\n\ndeclare global {\n interface Window {\n __OPTIFYE_SNAPSHOT_READY__?: boolean;\n __OPTIFYE_SNAPSHOT_VIDEO_STATUS__?: SnapshotVideoStatus;\n }\n}\n\nexport interface RecentFlowSnapshotGridProps {\n workspaces: WorkspaceMetrics[];\n videoStreamsByWorkspaceId: Record<string, WorkspaceVideoStream>;\n legend?: EfficiencyLegendUpdate | null;\n className?: string;\n}\n\nconst MOBILE_SCROLL_THRESHOLD = 15;\nconst MOBILE_BREAKPOINT_PX = 640;\n\nconst sortWorkspaces = (left: WorkspaceMetrics, right: WorkspaceMetrics) => {\n if (left.line_id !== right.line_id) {\n return left.line_id.localeCompare(right.line_id);\n }\n\n const leftMatch = left.workspace_name.match(/WS(\\d+)/);\n const rightMatch = right.workspace_name.match(/WS(\\d+)/);\n if (leftMatch && rightMatch) {\n return parseInt(leftMatch[1], 10) - parseInt(rightMatch[1], 10);\n }\n\n return left.workspace_name.localeCompare(right.workspace_name, undefined, { numeric: true });\n};\n\nexport const RecentFlowSnapshotGrid: React.FC<RecentFlowSnapshotGridProps> = ({\n workspaces,\n videoStreamsByWorkspaceId,\n legend,\n className = '',\n}) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const readinessRef = useRef<HTMLDivElement>(null);\n const [gridCols, setGridCols] = useState(4);\n const [gridRows, setGridRows] = useState(1);\n const [isMobileScrollableGrid, setIsMobileScrollableGrid] = useState(false);\n const sortedWorkspaces = useMemo(\n () => [...(workspaces || [])].sort(sortWorkspaces),\n [workspaces],\n );\n const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;\n const displayMinuteBucket = Math.floor(Date.now() / 60000);\n const expectedVideoCount = useMemo(\n () =>\n sortedWorkspaces.filter((workspace) => {\n const workspaceId = workspace.workspace_uuid || workspace.workspace_name;\n return Boolean(workspaceId && videoStreamsByWorkspaceId[workspaceId]?.hls_url);\n }).length,\n [sortedWorkspaces, videoStreamsByWorkspaceId],\n );\n\n const publishSnapshotReadiness = useCallback((readyVideoCount: number) => {\n const status: SnapshotVideoStatus = {\n expectedVideoCount,\n readyVideoCount,\n status: expectedVideoCount === 0\n ? 'no_videos'\n : readyVideoCount >= expectedVideoCount\n ? 'ready'\n : 'loading',\n updatedAt: new Date().toISOString(),\n };\n const isReady = expectedVideoCount > 0 && status.status === 'ready';\n\n if (typeof window !== 'undefined') {\n window.__OPTIFYE_SNAPSHOT_READY__ = isReady;\n window.__OPTIFYE_SNAPSHOT_VIDEO_STATUS__ = status;\n }\n\n const marker = readinessRef.current;\n if (marker) {\n marker.dataset.ready = isReady ? 'true' : 'false';\n marker.dataset.status = status.status;\n marker.dataset.expectedVideoCount = String(status.expectedVideoCount);\n marker.dataset.readyVideoCount = String(status.readyVideoCount);\n marker.dataset.updatedAt = status.updatedAt;\n }\n }, [expectedVideoCount]);\n\n const calculateOptimalGrid = useCallback(() => {\n if (!containerRef.current) return;\n\n const containerPadding = 16;\n const rawContainerWidth = containerRef.current.clientWidth;\n const containerWidth = rawContainerWidth - containerPadding;\n const containerHeight = containerRef.current.clientHeight - containerPadding;\n const count = sortedWorkspaces.length;\n\n if (count === 0) {\n setGridCols(1);\n setGridRows(1);\n setIsMobileScrollableGrid(false);\n return;\n }\n\n const shouldUseMobileScroll =\n rawContainerWidth < MOBILE_BREAKPOINT_PX && count >= MOBILE_SCROLL_THRESHOLD;\n\n const optimalLayouts: Record<number, number> = {\n 1: 1,\n 2: 2,\n 3: 3,\n 4: 2,\n 5: 3,\n 6: 3,\n 7: 4,\n 8: 4,\n 9: 3,\n 10: 5,\n 11: 4,\n 12: 4,\n 13: 5,\n 14: 5,\n 15: 5,\n 16: 4,\n 17: 6,\n 18: 6,\n 19: 5,\n 20: 5,\n 21: 7,\n 22: 6,\n 23: 6,\n 24: 6,\n };\n\n let bestCols = optimalLayouts[count] || Math.ceil(Math.sqrt(count));\n const containerAspectRatio = containerWidth / containerHeight;\n const targetAspectRatio = 16 / 9;\n const gap = 8;\n\n if (containerAspectRatio > targetAspectRatio * 1.5 && count > 6) {\n bestCols = Math.min(bestCols + 1, Math.ceil(count / 2));\n }\n\n const minCellWidth = 100;\n const availableWidth = containerWidth - (gap * (bestCols - 1));\n const cellWidth = availableWidth / bestCols;\n\n if (cellWidth < minCellWidth && bestCols > 1) {\n bestCols = Math.max(1, Math.floor((containerWidth + gap) / (minCellWidth + gap)));\n }\n\n setGridCols(bestCols);\n setGridRows(Math.ceil(count / bestCols));\n setIsMobileScrollableGrid(shouldUseMobileScroll);\n }, [sortedWorkspaces.length]);\n\n useEffect(() => {\n calculateOptimalGrid();\n window.addEventListener('resize', calculateOptimalGrid);\n return () => window.removeEventListener('resize', calculateOptimalGrid);\n }, [calculateOptimalGrid]);\n\n useEffect(() => {\n const attachedVideos = new Set<HTMLVideoElement>();\n const videoEvents = ['loadeddata', 'canplay', 'playing', 'timeupdate', 'error', 'stalled'];\n\n const hasDecodedFrame = (video: HTMLVideoElement) =>\n video.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA &&\n video.videoWidth > 0 &&\n video.videoHeight > 0;\n\n const updateReadiness = () => {\n const videos = Array.from(containerRef.current?.querySelectorAll('video') ?? []);\n const readyVideoCount = videos.filter(hasDecodedFrame).length;\n publishSnapshotReadiness(Math.min(readyVideoCount, expectedVideoCount));\n\n for (const video of videos) {\n if (attachedVideos.has(video)) continue;\n attachedVideos.add(video);\n for (const eventName of videoEvents) {\n video.addEventListener(eventName, updateReadiness);\n }\n }\n };\n\n publishSnapshotReadiness(0);\n updateReadiness();\n const intervalId = window.setInterval(updateReadiness, 250);\n\n return () => {\n window.clearInterval(intervalId);\n for (const video of attachedVideos) {\n for (const eventName of videoEvents) {\n video.removeEventListener(eventName, updateReadiness);\n }\n }\n if (typeof window !== 'undefined') {\n window.__OPTIFYE_SNAPSHOT_READY__ = false;\n window.__OPTIFYE_SNAPSHOT_VIDEO_STATUS__ = undefined;\n }\n };\n }, [expectedVideoCount, publishSnapshotReadiness]);\n\n if (!sortedWorkspaces.length) {\n return (\n <>\n <div\n ref={readinessRef}\n data-testid=\"snapshot-video-readiness\"\n data-ready=\"false\"\n data-status=\"no_videos\"\n data-expected-video-count=\"0\"\n data-ready-video-count=\"0\"\n hidden\n />\n <div className=\"flex min-h-[320px] items-center justify-center rounded-md border border-dashed border-slate-300 bg-slate-50 text-sm font-medium text-slate-500\">\n No workstation snapshot available\n </div>\n </>\n );\n }\n\n return (\n <div\n aria-label=\"Recent-flow workstation snapshot\"\n className={`relative h-full min-h-0 w-full overflow-hidden bg-slate-50/30 ${className}`}\n >\n <div\n ref={readinessRef}\n data-testid=\"snapshot-video-readiness\"\n data-ready=\"false\"\n data-status={expectedVideoCount === 0 ? 'no_videos' : 'loading'}\n data-expected-video-count={expectedVideoCount}\n data-ready-video-count=\"0\"\n hidden\n />\n <div\n ref={containerRef}\n data-testid=\"video-grid-scroll-container\"\n data-mobile-scrollable={isMobileScrollableGrid ? 'true' : 'false'}\n className={`absolute inset-0 w-full overflow-x-hidden px-1 py-1 sm:px-2 sm:py-2 ${isMobileScrollableGrid ? 'overflow-y-auto' : 'overflow-hidden'}`}\n >\n <div\n data-testid=\"video-grid-layout\"\n className={`grid min-w-0 w-full gap-1.5 sm:gap-2 ${isMobileScrollableGrid ? 'content-start' : 'h-full'}`}\n style={{\n gridTemplateColumns: `repeat(${gridCols}, minmax(0, 1fr))`,\n gridTemplateRows: isMobileScrollableGrid ? undefined : `repeat(${gridRows}, 1fr)`,\n gridAutoFlow: 'row',\n }}\n >\n {sortedWorkspaces.map((workspace) => {\n const workspaceId = workspace.workspace_uuid || workspace.workspace_name;\n const stream = workspaceId ? videoStreamsByWorkspaceId[workspaceId] : null;\n const hlsUrl = stream?.hls_url || '';\n return (\n <div\n key={`${workspace.line_id}-${workspaceId}`}\n data-workspace-id={workspaceId}\n className={\n isMobileScrollableGrid\n ? 'workspace-card relative min-w-0 w-full aspect-video min-h-[92px]'\n : 'workspace-card relative min-w-0 w-full h-full'\n }\n >\n <div className=\"absolute inset-0\">\n <VideoCard\n workspace={workspace}\n hlsUrl={hlsUrl}\n shouldPlay={Boolean(hlsUrl)}\n legend={effectiveLegend}\n cropping={stream?.crop || undefined}\n canvasFps={10}\n useRAF={false}\n displayMinuteBucket={displayMinuteBucket}\n displayName={workspace.displayName || workspace.workspace_name}\n compact\n />\n </div>\n </div>\n );\n })}\n </div>\n </div>\n </div>\n );\n};\n\nexport default RecentFlowSnapshotGrid;\n"]}