@flamingo-stack/openframe-frontend-core 0.0.581-snapshot.20260901141954 → 0.0.581
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-CR7GPMCM.js → chunk-3GXNCJZI.js} +3 -3
- package/dist/{chunk-A5VD6R7N.cjs → chunk-6U45VX5J.cjs} +43 -43
- package/dist/{chunk-A5VD6R7N.cjs.map → chunk-6U45VX5J.cjs.map} +1 -1
- package/dist/{chunk-SVQVAGZK.js → chunk-7CP5KY5W.js} +3 -3
- package/dist/{chunk-WQ236GO3.cjs → chunk-AIALG25H.cjs} +69 -69
- package/dist/{chunk-WQ236GO3.cjs.map → chunk-AIALG25H.cjs.map} +1 -1
- package/dist/{chunk-ALSLHGBH.cjs → chunk-BTKMTVN5.cjs} +4 -4
- package/dist/{chunk-ALSLHGBH.cjs.map → chunk-BTKMTVN5.cjs.map} +1 -1
- package/dist/{chunk-FYVL6A4Z.cjs → chunk-DSPQVZFE.cjs} +24 -24
- package/dist/{chunk-FYVL6A4Z.cjs.map → chunk-DSPQVZFE.cjs.map} +1 -1
- package/dist/{chunk-HQF7VE7B.js → chunk-ESWXXWCL.js} +2 -2
- package/dist/{chunk-FRWQ67UM.js → chunk-EZVABFEO.js} +2 -2
- package/dist/{chunk-6EUYXDV3.js → chunk-GAHKWML7.js} +6 -6
- package/dist/{chunk-L4E2TOSF.cjs → chunk-GMQEHDN2.cjs} +5 -5
- package/dist/{chunk-L4E2TOSF.cjs.map → chunk-GMQEHDN2.cjs.map} +1 -1
- package/dist/{chunk-K5SCFOD5.cjs → chunk-GUC4JK6O.cjs} +9 -9
- package/dist/{chunk-K5SCFOD5.cjs.map → chunk-GUC4JK6O.cjs.map} +1 -1
- package/dist/{chunk-U2ZRM3YB.js → chunk-GVM37TRU.js} +4 -4
- package/dist/{chunk-F227VMPL.js → chunk-HJVKPG55.js} +15 -125
- package/dist/{chunk-F227VMPL.js.map → chunk-HJVKPG55.js.map} +1 -1
- package/dist/{chunk-7HHRJTIY.js → chunk-HVSZYUGP.js} +5 -5
- package/dist/{chunk-KBOBPKMU.js → chunk-IHFQ5VVR.js} +5 -5
- package/dist/{chunk-A3SVEK7A.js → chunk-JW53WPB5.js} +3 -3
- package/dist/{chunk-646JEAY4.js → chunk-KAEVE7W2.js} +2 -2
- package/dist/{chunk-R3J2ERH2.cjs → chunk-KNBXZ22O.cjs} +3 -3
- package/dist/{chunk-R3J2ERH2.cjs.map → chunk-KNBXZ22O.cjs.map} +1 -1
- package/dist/{chunk-BUOYMIFJ.js → chunk-LWDIDSCF.js} +3 -3
- package/dist/{chunk-ORQQZQBB.cjs → chunk-M4KQWGEL.cjs} +32 -32
- package/dist/{chunk-ORQQZQBB.cjs.map → chunk-M4KQWGEL.cjs.map} +1 -1
- package/dist/{chunk-SKGMU7TZ.js → chunk-OKA3DJJ3.js} +2 -2
- package/dist/{chunk-DQVM4FTL.cjs → chunk-PBRXT7TD.cjs} +10 -10
- package/dist/{chunk-DQVM4FTL.cjs.map → chunk-PBRXT7TD.cjs.map} +1 -1
- package/dist/{chunk-4KWAHIYA.js → chunk-R5JVRFSW.js} +3 -3
- package/dist/{chunk-BQPCCBGO.cjs → chunk-SEZ25E22.cjs} +10 -10
- package/dist/{chunk-BQPCCBGO.cjs.map → chunk-SEZ25E22.cjs.map} +1 -1
- package/dist/{chunk-DGZPX2G3.cjs → chunk-U3W2ZVN4.cjs} +159 -159
- package/dist/{chunk-DGZPX2G3.cjs.map → chunk-U3W2ZVN4.cjs.map} +1 -1
- package/dist/{chunk-KAOXOYRT.cjs → chunk-UKHM2S6I.cjs} +15 -15
- package/dist/{chunk-KAOXOYRT.cjs.map → chunk-UKHM2S6I.cjs.map} +1 -1
- package/dist/{chunk-J5I3TKAH.cjs → chunk-VDRCKHML.cjs} +3 -3
- package/dist/{chunk-J5I3TKAH.cjs.map → chunk-VDRCKHML.cjs.map} +1 -1
- package/dist/{chunk-GD3OAM4T.js → chunk-VXUUSLD5.js} +2 -2
- package/dist/{chunk-T32XVOFP.cjs → chunk-W2TXSZTM.cjs} +21 -21
- package/dist/{chunk-T32XVOFP.cjs.map → chunk-W2TXSZTM.cjs.map} +1 -1
- package/dist/{chunk-RMOUILHT.cjs → chunk-WHFEG6UU.cjs} +22 -22
- package/dist/{chunk-RMOUILHT.cjs.map → chunk-WHFEG6UU.cjs.map} +1 -1
- package/dist/{chunk-LOJ5KQ6B.cjs → chunk-YU2UJPAT.cjs} +356 -466
- package/dist/chunk-YU2UJPAT.cjs.map +1 -0
- package/dist/{chunk-QDSVTIO5.js → chunk-ZEZMGXYV.js} +5 -5
- package/dist/components/case-studies/index.cjs +9 -9
- package/dist/components/case-studies/index.js +3 -3
- package/dist/components/chat/index.cjs +2 -2
- package/dist/components/chat/index.js +1 -1
- package/dist/components/contact/index.cjs +4 -4
- package/dist/components/contact/index.js +3 -3
- package/dist/components/docs/index.cjs +7 -7
- package/dist/components/docs/index.js +6 -6
- package/dist/components/embeds/index.cjs +4 -4
- package/dist/components/embeds/index.js +3 -3
- package/dist/components/faq/index.cjs +4 -4
- package/dist/components/faq/index.js +3 -3
- package/dist/components/features/index.cjs +3 -5
- package/dist/components/features/index.cjs.map +1 -1
- package/dist/components/features/index.js +2 -4
- package/dist/components/features/video.d.ts +0 -12
- package/dist/components/features/video.d.ts.map +1 -1
- package/dist/components/help-center-pages/index.cjs +23 -23
- package/dist/components/help-center-pages/index.js +14 -14
- package/dist/components/index.cjs +121 -123
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.js +13 -15
- package/dist/components/index.js.map +1 -1
- package/dist/components/meeting-scheduler/index.cjs +21 -21
- package/dist/components/meeting-scheduler/index.js +2 -2
- package/dist/components/navigation/index.cjs +6 -6
- package/dist/components/navigation/index.js +5 -5
- package/dist/components/onboarding-guides/index.cjs +6 -6
- package/dist/components/onboarding-guides/index.js +5 -5
- package/dist/components/tickets/index.cjs +7 -7
- package/dist/components/tickets/index.js +6 -6
- package/dist/components/ui/index.cjs +3 -3
- package/dist/components/ui/index.js +2 -2
- package/dist/index.cjs +7 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +6 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/features/video.tsx +16 -198
- package/dist/chunk-LOJ5KQ6B.cjs.map +0 -1
- package/src/components/features/__tests__/video-playback-failure.test.tsx +0 -250
- /package/dist/{chunk-CR7GPMCM.js.map → chunk-3GXNCJZI.js.map} +0 -0
- /package/dist/{chunk-SVQVAGZK.js.map → chunk-7CP5KY5W.js.map} +0 -0
- /package/dist/{chunk-HQF7VE7B.js.map → chunk-ESWXXWCL.js.map} +0 -0
- /package/dist/{chunk-FRWQ67UM.js.map → chunk-EZVABFEO.js.map} +0 -0
- /package/dist/{chunk-6EUYXDV3.js.map → chunk-GAHKWML7.js.map} +0 -0
- /package/dist/{chunk-U2ZRM3YB.js.map → chunk-GVM37TRU.js.map} +0 -0
- /package/dist/{chunk-7HHRJTIY.js.map → chunk-HVSZYUGP.js.map} +0 -0
- /package/dist/{chunk-KBOBPKMU.js.map → chunk-IHFQ5VVR.js.map} +0 -0
- /package/dist/{chunk-A3SVEK7A.js.map → chunk-JW53WPB5.js.map} +0 -0
- /package/dist/{chunk-646JEAY4.js.map → chunk-KAEVE7W2.js.map} +0 -0
- /package/dist/{chunk-BUOYMIFJ.js.map → chunk-LWDIDSCF.js.map} +0 -0
- /package/dist/{chunk-SKGMU7TZ.js.map → chunk-OKA3DJJ3.js.map} +0 -0
- /package/dist/{chunk-4KWAHIYA.js.map → chunk-R5JVRFSW.js.map} +0 -0
- /package/dist/{chunk-GD3OAM4T.js.map → chunk-VXUUSLD5.js.map} +0 -0
- /package/dist/{chunk-QDSVTIO5.js.map → chunk-ZEZMGXYV.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
SoftwareInfo,
|
|
10
10
|
SoftwareSourceBadge,
|
|
11
11
|
ToolBadge
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-3GXNCJZI.js";
|
|
13
13
|
import {
|
|
14
14
|
AppHeader,
|
|
15
15
|
AppLayout,
|
|
@@ -28,14 +28,14 @@ import {
|
|
|
28
28
|
SlidingSidebar,
|
|
29
29
|
TicketAlertsButton,
|
|
30
30
|
TopNavigation
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-GVM37TRU.js";
|
|
32
32
|
import {
|
|
33
33
|
MobileNavigationDropdown,
|
|
34
34
|
MultiLevelNavigation,
|
|
35
35
|
StickySectionNav,
|
|
36
36
|
useSectionNavigation
|
|
37
|
-
} from "./chunk-
|
|
38
|
-
import "./chunk-
|
|
37
|
+
} from "./chunk-KAEVE7W2.js";
|
|
38
|
+
import "./chunk-R5JVRFSW.js";
|
|
39
39
|
import {
|
|
40
40
|
ADMIN_APPROVAL_REQUEST_CONTEXT_TYPE,
|
|
41
41
|
AIEnrichButton,
|
|
@@ -577,7 +577,7 @@ import {
|
|
|
577
577
|
withFirstTouchAttribution,
|
|
578
578
|
withWalkthroughOpenParam,
|
|
579
579
|
writeDismissCookie
|
|
580
|
-
} from "./chunk-
|
|
580
|
+
} from "./chunk-VXUUSLD5.js";
|
|
581
581
|
import "./chunk-L2SLFCXP.js";
|
|
582
582
|
import "./chunk-BJXDHKUX.js";
|
|
583
583
|
import {
|
|
@@ -892,7 +892,6 @@ import {
|
|
|
892
892
|
TooltipProvider,
|
|
893
893
|
TooltipTrigger,
|
|
894
894
|
UnarchiveChatModal,
|
|
895
|
-
VIDEO_PLAYBACK_FAILED_EVENT,
|
|
896
895
|
Video,
|
|
897
896
|
VideoBiteCard,
|
|
898
897
|
VideoBitesStrip,
|
|
@@ -1055,7 +1054,7 @@ import {
|
|
|
1055
1054
|
useVideoOriginPreconnect,
|
|
1056
1055
|
useVideoWarmup,
|
|
1057
1056
|
usesCanonicalStatusStyle
|
|
1058
|
-
} from "./chunk-
|
|
1057
|
+
} from "./chunk-HJVKPG55.js";
|
|
1059
1058
|
import {
|
|
1060
1059
|
HEADING_EMOJI_RE,
|
|
1061
1060
|
HUB_HEADER_OFFSET_PX,
|
|
@@ -2505,7 +2504,6 @@ export {
|
|
|
2505
2504
|
UserIcon,
|
|
2506
2505
|
UsersGroupIcon,
|
|
2507
2506
|
UsersIcon,
|
|
2508
|
-
VIDEO_PLAYBACK_FAILED_EVENT,
|
|
2509
2507
|
VendorDirectoryIcon,
|
|
2510
2508
|
VendorShowcaseMainIcon,
|
|
2511
2509
|
VendorsIcon,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/logs-list.tsx","../src/components/log-severity-dot.tsx","../src/types/logs.types.ts","../src/assets/index.ts"],"sourcesContent":["import { type FC, forwardRef, useRef } from 'react';\nimport type { LogsListProps, LogEntry } from '../types/logs.types';\nimport { cn } from '../utils/cn';\nimport { LogSeverityDot } from './log-severity-dot';\nimport { ToolIcon } from './tool-icon';\n\nconst formatTimestamp = (timestamp: string | Date): string => {\n const date = timestamp instanceof Date ? timestamp : new Date(timestamp);\n\n // UTC getters so the timestamp is identical on server (UTC) and client\n // (local) — otherwise React #418 hydration mismatch.\n const year = date.getUTCFullYear();\n const month = String(date.getUTCMonth() + 1).padStart(2, '0');\n const day = String(date.getUTCDate()).padStart(2, '0');\n const hours = String(date.getUTCHours()).padStart(2, '0');\n const minutes = String(date.getUTCMinutes()).padStart(2, '0');\n\n return `${year}/${month}/${day},${hours}:${minutes}`;\n};\n\nconst LogCard: FC<{\n log: LogEntry;\n isLast: boolean;\n showConnector: boolean;\n onClick?: () => void;\n}> = ({ log, isLast, showConnector, onClick }) => {\n return (\n <div className=\"relative\">\n <div\n className={cn(\n 'relative box-border flex w-full items-start gap-3 rounded px-1 py-2',\n 'cursor-pointer transition-colors hover:bg-ods-bg-hover/50',\n )}\n onClick={onClick}\n role=\"button\"\n tabIndex={0}\n onKeyDown={e => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n onClick?.();\n }\n }}\n >\n <LogSeverityDot severity={log.severity} size=\"md\" />\n\n <div className=\"min-w-0 flex-1 space-y-1\">\n <p className=\"text-ods-text-primary text-h6\">{log.title}</p>\n <div className=\"flex items-center gap-2\">\n <p className=\"text-ods-text-secondary text-h5\">{formatTimestamp(log.timestamp)}</p>\n {log.toolType && <ToolIcon toolType={log.toolType} size={16} />}\n </div>\n </div>\n </div>\n\n {showConnector && !isLast && (\n <div\n className=\"absolute left-[15px] w-[2px] bg-ods-border\"\n style={{\n top: '28px',\n bottom: '-10px',\n }}\n aria-hidden=\"true\"\n />\n )}\n </div>\n );\n};\n\nexport const LogsList = forwardRef<HTMLDivElement, LogsListProps>(\n (\n {\n logs,\n maxHeight = '400px',\n showConnector = true,\n onLogClick,\n loading = false,\n emptyMessage = 'No logs to display',\n className,\n },\n ref,\n ) => {\n const containerRef = useRef<HTMLDivElement>(null);\n\n const isFullHeight = maxHeight === '100%';\n\n const getContainerStyles = () => {\n if (isFullHeight) return undefined;\n return { maxHeight, minHeight: '200px' };\n };\n\n const getContainerClasses = () => {\n if (isFullHeight) return 'h-full';\n return '';\n };\n\n if (loading) {\n return (\n <div\n ref={ref}\n className={cn(\n 'rounded-lg border border-ods-border bg-ods-card p-4',\n 'flex items-center justify-center',\n getContainerClasses(),\n className,\n )}\n style={getContainerStyles()}\n >\n <div className=\"text-ods-text-muted text-h6\">Loading logs...</div>\n </div>\n );\n }\n\n if (logs.length === 0) {\n return (\n <div\n ref={ref}\n className={cn(\n 'rounded-lg border border-ods-border bg-ods-card p-4',\n 'flex items-center justify-center',\n getContainerClasses(),\n className,\n )}\n style={getContainerStyles()}\n >\n <div className=\"text-ods-text-muted text-h6\">{emptyMessage}</div>\n </div>\n );\n }\n\n return (\n <div\n ref={ref}\n className={cn(\n 'relative rounded-lg border border-ods-border bg-ods-card',\n 'flex flex-col overflow-hidden',\n getContainerClasses(),\n className,\n )}\n style={getContainerStyles()}\n >\n <div ref={containerRef} className=\"flex-1 overflow-y-auto px-4 py-3\">\n {logs.map((log, index) => (\n <LogCard\n key={log.id}\n log={log}\n isLast={index === logs.length - 1}\n showConnector={showConnector}\n onClick={() => onLogClick?.(log)}\n />\n ))}\n </div>\n </div>\n );\n },\n);\n\nLogsList.displayName = 'LogsList';\n","import { forwardRef } from 'react';\nimport { type LogSeverityDotProps, LOG_SEVERITY_COLORS, LOG_SEVERITY_LABELS } from '../types/logs.types';\nimport { cn } from '../utils/cn';\n\nexport const LogSeverityDot = forwardRef<HTMLDivElement, LogSeverityDotProps>(\n ({ severity, size = 'md', className }, ref) => {\n const sizeClasses = {\n sm: 'w-4 h-4',\n md: 'w-6 h-6',\n lg: 'w-8 h-8',\n };\n\n const dotSizeClasses = {\n sm: 'w-2 h-2',\n md: 'w-3 h-3',\n lg: 'w-4 h-4',\n };\n\n return (\n <div\n ref={ref}\n className={cn('relative flex shrink-0 items-center justify-center', sizeClasses[size], className)}\n aria-label={`${LOG_SEVERITY_LABELS[severity]} severity`}\n >\n <div\n className={cn('rounded-full', dotSizeClasses[size])}\n style={{ backgroundColor: LOG_SEVERITY_COLORS[severity] }}\n />\n </div>\n );\n },\n);\n\nLogSeverityDot.displayName = 'LogSeverityDot';\n","import type { ToolType } from './tool.types';\n\nexport type LogSeverity = 'DEBUG' | 'INFO' | 'WARNING' | 'ERROR' | 'CRITICAL';\n\nexport interface LogEntry {\n id: string;\n ingestDay: string;\n eventType: string;\n severity: LogSeverity;\n title: string;\n timestamp: string | Date;\n toolType?: ToolType;\n toolIcon?: string;\n message?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface LogsListProps {\n logs: LogEntry[];\n maxHeight?: string | number;\n showConnector?: boolean;\n onLogClick?: (log: LogEntry) => void;\n loading?: boolean;\n emptyMessage?: string;\n className?: string;\n}\n\nexport interface LogSeverityDotProps {\n severity: LogSeverity;\n size?: 'sm' | 'md' | 'lg';\n className?: string;\n}\n\nexport const LOG_SEVERITY_COLORS: Record<LogSeverity, string> = {\n DEBUG: '#888888',\n INFO: '#888888',\n WARNING: '#e1b32f',\n ERROR: '#f36666',\n CRITICAL: '#b43b3b',\n};\n\nexport const LOG_SEVERITY_LABELS: Record<LogSeverity, string> = {\n DEBUG: 'Debug',\n INFO: 'Info',\n WARNING: 'Warning',\n ERROR: 'Error',\n CRITICAL: 'Critical',\n};\n","// Assets exports\n// Font files and other static assets\n// Platform utilities will be available when needed\n\nexport const assets = {} as const;\nexport * from './fae-avatar';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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","names":["forwardRef","jsx","forwardRef"]}
|
|
1
|
+
{"version":3,"sources":["../src/components/logs-list.tsx","../src/components/log-severity-dot.tsx","../src/types/logs.types.ts","../src/assets/index.ts"],"sourcesContent":["import { type FC, forwardRef, useRef } from 'react';\nimport type { LogsListProps, LogEntry } from '../types/logs.types';\nimport { cn } from '../utils/cn';\nimport { LogSeverityDot } from './log-severity-dot';\nimport { ToolIcon } from './tool-icon';\n\nconst formatTimestamp = (timestamp: string | Date): string => {\n const date = timestamp instanceof Date ? timestamp : new Date(timestamp);\n\n // UTC getters so the timestamp is identical on server (UTC) and client\n // (local) — otherwise React #418 hydration mismatch.\n const year = date.getUTCFullYear();\n const month = String(date.getUTCMonth() + 1).padStart(2, '0');\n const day = String(date.getUTCDate()).padStart(2, '0');\n const hours = String(date.getUTCHours()).padStart(2, '0');\n const minutes = String(date.getUTCMinutes()).padStart(2, '0');\n\n return `${year}/${month}/${day},${hours}:${minutes}`;\n};\n\nconst LogCard: FC<{\n log: LogEntry;\n isLast: boolean;\n showConnector: boolean;\n onClick?: () => void;\n}> = ({ log, isLast, showConnector, onClick }) => {\n return (\n <div className=\"relative\">\n <div\n className={cn(\n 'relative box-border flex w-full items-start gap-3 rounded px-1 py-2',\n 'cursor-pointer transition-colors hover:bg-ods-bg-hover/50',\n )}\n onClick={onClick}\n role=\"button\"\n tabIndex={0}\n onKeyDown={e => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n onClick?.();\n }\n }}\n >\n <LogSeverityDot severity={log.severity} size=\"md\" />\n\n <div className=\"min-w-0 flex-1 space-y-1\">\n <p className=\"text-ods-text-primary text-h6\">{log.title}</p>\n <div className=\"flex items-center gap-2\">\n <p className=\"text-ods-text-secondary text-h5\">{formatTimestamp(log.timestamp)}</p>\n {log.toolType && <ToolIcon toolType={log.toolType} size={16} />}\n </div>\n </div>\n </div>\n\n {showConnector && !isLast && (\n <div\n className=\"absolute left-[15px] w-[2px] bg-ods-border\"\n style={{\n top: '28px',\n bottom: '-10px',\n }}\n aria-hidden=\"true\"\n />\n )}\n </div>\n );\n};\n\nexport const LogsList = forwardRef<HTMLDivElement, LogsListProps>(\n (\n {\n logs,\n maxHeight = '400px',\n showConnector = true,\n onLogClick,\n loading = false,\n emptyMessage = 'No logs to display',\n className,\n },\n ref,\n ) => {\n const containerRef = useRef<HTMLDivElement>(null);\n\n const isFullHeight = maxHeight === '100%';\n\n const getContainerStyles = () => {\n if (isFullHeight) return undefined;\n return { maxHeight, minHeight: '200px' };\n };\n\n const getContainerClasses = () => {\n if (isFullHeight) return 'h-full';\n return '';\n };\n\n if (loading) {\n return (\n <div\n ref={ref}\n className={cn(\n 'rounded-lg border border-ods-border bg-ods-card p-4',\n 'flex items-center justify-center',\n getContainerClasses(),\n className,\n )}\n style={getContainerStyles()}\n >\n <div className=\"text-ods-text-muted text-h6\">Loading logs...</div>\n </div>\n );\n }\n\n if (logs.length === 0) {\n return (\n <div\n ref={ref}\n className={cn(\n 'rounded-lg border border-ods-border bg-ods-card p-4',\n 'flex items-center justify-center',\n getContainerClasses(),\n className,\n )}\n style={getContainerStyles()}\n >\n <div className=\"text-ods-text-muted text-h6\">{emptyMessage}</div>\n </div>\n );\n }\n\n return (\n <div\n ref={ref}\n className={cn(\n 'relative rounded-lg border border-ods-border bg-ods-card',\n 'flex flex-col overflow-hidden',\n getContainerClasses(),\n className,\n )}\n style={getContainerStyles()}\n >\n <div ref={containerRef} className=\"flex-1 overflow-y-auto px-4 py-3\">\n {logs.map((log, index) => (\n <LogCard\n key={log.id}\n log={log}\n isLast={index === logs.length - 1}\n showConnector={showConnector}\n onClick={() => onLogClick?.(log)}\n />\n ))}\n </div>\n </div>\n );\n },\n);\n\nLogsList.displayName = 'LogsList';\n","import { forwardRef } from 'react';\nimport { type LogSeverityDotProps, LOG_SEVERITY_COLORS, LOG_SEVERITY_LABELS } from '../types/logs.types';\nimport { cn } from '../utils/cn';\n\nexport const LogSeverityDot = forwardRef<HTMLDivElement, LogSeverityDotProps>(\n ({ severity, size = 'md', className }, ref) => {\n const sizeClasses = {\n sm: 'w-4 h-4',\n md: 'w-6 h-6',\n lg: 'w-8 h-8',\n };\n\n const dotSizeClasses = {\n sm: 'w-2 h-2',\n md: 'w-3 h-3',\n lg: 'w-4 h-4',\n };\n\n return (\n <div\n ref={ref}\n className={cn('relative flex shrink-0 items-center justify-center', sizeClasses[size], className)}\n aria-label={`${LOG_SEVERITY_LABELS[severity]} severity`}\n >\n <div\n className={cn('rounded-full', dotSizeClasses[size])}\n style={{ backgroundColor: LOG_SEVERITY_COLORS[severity] }}\n />\n </div>\n );\n },\n);\n\nLogSeverityDot.displayName = 'LogSeverityDot';\n","import type { ToolType } from './tool.types';\n\nexport type LogSeverity = 'DEBUG' | 'INFO' | 'WARNING' | 'ERROR' | 'CRITICAL';\n\nexport interface LogEntry {\n id: string;\n ingestDay: string;\n eventType: string;\n severity: LogSeverity;\n title: string;\n timestamp: string | Date;\n toolType?: ToolType;\n toolIcon?: string;\n message?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface LogsListProps {\n logs: LogEntry[];\n maxHeight?: string | number;\n showConnector?: boolean;\n onLogClick?: (log: LogEntry) => void;\n loading?: boolean;\n emptyMessage?: string;\n className?: string;\n}\n\nexport interface LogSeverityDotProps {\n severity: LogSeverity;\n size?: 'sm' | 'md' | 'lg';\n className?: string;\n}\n\nexport const LOG_SEVERITY_COLORS: Record<LogSeverity, string> = {\n DEBUG: '#888888',\n INFO: '#888888',\n WARNING: '#e1b32f',\n ERROR: '#f36666',\n CRITICAL: '#b43b3b',\n};\n\nexport const LOG_SEVERITY_LABELS: Record<LogSeverity, string> = {\n DEBUG: 'Debug',\n INFO: 'Info',\n WARNING: 'Warning',\n ERROR: 'Error',\n CRITICAL: 'Critical',\n};\n","// Assets exports\n// Font files and other static assets\n// Platform utilities will be available when needed\n\nexport const assets = {} as const;\nexport * from './fae-avatar';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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","names":["forwardRef","jsx","forwardRef"]}
|
package/package.json
CHANGED
|
@@ -35,7 +35,6 @@ import type React from 'react';
|
|
|
35
35
|
import { useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
|
|
36
36
|
import { useAuthedAssetSrc } from '../../hooks/use-authed-asset-src';
|
|
37
37
|
import { fetchPriorityProp } from '../../utils/fetch-priority';
|
|
38
|
-
import { Button } from '../ui/button';
|
|
39
38
|
import { useIosNativeVideoFullscreen } from './use-ios-native-video-fullscreen';
|
|
40
39
|
import { saveDataEnabled } from './use-video-warmup';
|
|
41
40
|
import { VideoPlayBadge, VideoUnmuteGlyph } from './video-center-badge';
|
|
@@ -281,76 +280,6 @@ export function extractYouTubeId(url: string): string | null {
|
|
|
281
280
|
return m ? m[1] : null;
|
|
282
281
|
}
|
|
283
282
|
|
|
284
|
-
// =============================================================================
|
|
285
|
-
// Playback-failure handling
|
|
286
|
-
// =============================================================================
|
|
287
|
-
//
|
|
288
|
-
// A stalled or errored player leaves the user on an endless spinner with no way
|
|
289
|
-
// out. Every failure surfaces a retry (`VideoErrorOverlay`) and is reported on
|
|
290
|
-
// a `window` CustomEvent the host subscribes to — no vendor analytics global is
|
|
291
|
-
// touched from here; this package ships to consumers that load different (or
|
|
292
|
-
// no) analytics.
|
|
293
|
-
|
|
294
|
-
export type VideoFailureKind = 'file' | 'youtube';
|
|
295
|
-
/** `stall`: buffering that never cleared while playback was expected. */
|
|
296
|
-
export type VideoFailureReason = 'error' | 'stall';
|
|
297
|
-
|
|
298
|
-
export interface VideoPlaybackFailureDetail {
|
|
299
|
-
kind: VideoFailureKind;
|
|
300
|
-
/** File URL or YouTube video id — enough to group failures by source. */
|
|
301
|
-
src: string;
|
|
302
|
-
reason: VideoFailureReason;
|
|
303
|
-
/** YouTube IFrame API error code, when the embed reported one. */
|
|
304
|
-
errorCode?: number;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
export const VIDEO_PLAYBACK_FAILED_EVENT = 'flamingo:video-playback-failed';
|
|
308
|
-
|
|
309
|
-
function reportPlaybackFailure(detail: VideoPlaybackFailureDetail): void {
|
|
310
|
-
if (typeof window === 'undefined') return;
|
|
311
|
-
try {
|
|
312
|
-
window.dispatchEvent(new CustomEvent(VIDEO_PLAYBACK_FAILED_EVENT, { detail }));
|
|
313
|
-
} catch {
|
|
314
|
-
// Analytics must never break playback recovery.
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
// Buffering longer than this while playback is expected reads as "broken", not
|
|
319
|
-
// "loading" — surface a retry instead of spinning forever. This is what an
|
|
320
|
-
// `error` callback alone misses: a video that played, then stalled mid-way.
|
|
321
|
-
const VIDEO_STALL_TIMEOUT_MS = 15_000;
|
|
322
|
-
|
|
323
|
-
/** `watchOnYouTubeId` adds the one recovery that survives an embed the owner
|
|
324
|
-
* disabled: opening the video on YouTube itself. */
|
|
325
|
-
function VideoErrorOverlay({
|
|
326
|
-
onRetry,
|
|
327
|
-
watchOnYouTubeId,
|
|
328
|
-
}: {
|
|
329
|
-
onRetry: () => void;
|
|
330
|
-
watchOnYouTubeId?: string;
|
|
331
|
-
}): React.ReactElement {
|
|
332
|
-
return (
|
|
333
|
-
<div className="absolute inset-0 z-10 flex flex-col items-center justify-center gap-[var(--spacing-system-sf)] bg-ods-overlay p-[var(--spacing-system-lf)] text-center">
|
|
334
|
-
<p className="text-ods-text-secondary text-h6">This video failed to play.</p>
|
|
335
|
-
<div className="flex items-center gap-[var(--spacing-system-sf)]">
|
|
336
|
-
<Button variant="outline" size="small" onClick={onRetry}>
|
|
337
|
-
Try again
|
|
338
|
-
</Button>
|
|
339
|
-
{watchOnYouTubeId ? (
|
|
340
|
-
<Button
|
|
341
|
-
variant="transparent"
|
|
342
|
-
size="small"
|
|
343
|
-
href={`https://www.youtube.com/watch?v=${watchOnYouTubeId}`}
|
|
344
|
-
openInNewTab
|
|
345
|
-
>
|
|
346
|
-
Watch on YouTube
|
|
347
|
-
</Button>
|
|
348
|
-
) : null}
|
|
349
|
-
</div>
|
|
350
|
-
</div>
|
|
351
|
-
);
|
|
352
|
-
}
|
|
353
|
-
|
|
354
283
|
// =============================================================================
|
|
355
284
|
// Props
|
|
356
285
|
// =============================================================================
|
|
@@ -761,13 +690,11 @@ function FilePlayer({
|
|
|
761
690
|
const hoverPlayerRef = useRef<{
|
|
762
691
|
play?: () => Promise<void> | void;
|
|
763
692
|
pause?: () => void;
|
|
764
|
-
load?: () => void;
|
|
765
693
|
muted?: boolean;
|
|
766
694
|
volume?: number;
|
|
767
695
|
currentTime?: number;
|
|
768
696
|
duration?: number;
|
|
769
697
|
paused?: boolean;
|
|
770
|
-
ended?: boolean;
|
|
771
698
|
addEventListener?: (type: string, listener: () => void) => void;
|
|
772
699
|
removeEventListener?: (type: string, listener: () => void) => void;
|
|
773
700
|
} | null>(null);
|
|
@@ -1173,93 +1100,9 @@ function FilePlayer({
|
|
|
1173
1100
|
);
|
|
1174
1101
|
}
|
|
1175
1102
|
|
|
1176
|
-
// Only chrome-bearing surfaces (a user actively watching) monitor health —
|
|
1177
|
-
// decorative first-frame and hover previews pass `chromeless` and stay silent.
|
|
1178
|
-
const monitorHealth = !chromeless;
|
|
1179
|
-
const [playbackFailed, setPlaybackFailed] = useState(false);
|
|
1180
|
-
const stallTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
1181
|
-
const clearStallTimer = useCallback(() => {
|
|
1182
|
-
if (stallTimerRef.current !== null) {
|
|
1183
|
-
clearTimeout(stallTimerRef.current);
|
|
1184
|
-
stallTimerRef.current = null;
|
|
1185
|
-
}
|
|
1186
|
-
}, []);
|
|
1187
|
-
useEffect(() => clearStallTimer, [clearStallTimer]);
|
|
1188
|
-
|
|
1189
|
-
const failPlayback = useCallback(
|
|
1190
|
-
(reason: VideoFailureReason) => {
|
|
1191
|
-
clearStallTimer();
|
|
1192
|
-
setPlaybackFailed(true);
|
|
1193
|
-
reportPlaybackFailure({ kind: 'file', src: url, reason });
|
|
1194
|
-
},
|
|
1195
|
-
[clearStallTimer, url],
|
|
1196
|
-
);
|
|
1197
|
-
|
|
1198
|
-
// A manually paused or finished video is never a stall.
|
|
1199
|
-
const armStallTimer = useCallback(() => {
|
|
1200
|
-
if (!monitorHealth) return;
|
|
1201
|
-
const el = hoverPlayerRef.current;
|
|
1202
|
-
if (!el || el.paused || el.ended) return;
|
|
1203
|
-
if (stallTimerRef.current !== null) return;
|
|
1204
|
-
stallTimerRef.current = setTimeout(() => {
|
|
1205
|
-
stallTimerRef.current = null;
|
|
1206
|
-
failPlayback('stall');
|
|
1207
|
-
}, VIDEO_STALL_TIMEOUT_MS);
|
|
1208
|
-
}, [monitorHealth, failPlayback]);
|
|
1209
|
-
|
|
1210
|
-
// Progress means the player is alive — a stall that self-heals clears its own
|
|
1211
|
-
// overlay.
|
|
1212
|
-
const handlePlaybackProgress = useCallback(() => {
|
|
1213
|
-
clearStallTimer();
|
|
1214
|
-
setPlaybackFailed(false);
|
|
1215
|
-
}, [clearStallTimer]);
|
|
1216
|
-
|
|
1217
|
-
const handleMediaError = useCallback(() => {
|
|
1218
|
-
if (!monitorHealth) return;
|
|
1219
|
-
failPlayback('error');
|
|
1220
|
-
}, [monitorHealth, failPlayback]);
|
|
1221
|
-
|
|
1222
|
-
// `load()` re-inits the HLS pipeline (`play()` alone can't) but rewinds to
|
|
1223
|
-
// zero — stash the position and seek back on `loadedmetadata`, since an
|
|
1224
|
-
// earlier assignment is dropped while the fresh source has no seekable range.
|
|
1225
|
-
const retryPlayback = useCallback(() => {
|
|
1226
|
-
setPlaybackFailed(false);
|
|
1227
|
-
clearStallTimer();
|
|
1228
|
-
const el = hoverPlayerRef.current;
|
|
1229
|
-
if (!el) return;
|
|
1230
|
-
const resumeAt = typeof el.currentTime === 'number' ? el.currentTime : 0;
|
|
1231
|
-
try {
|
|
1232
|
-
el.load?.();
|
|
1233
|
-
if (resumeAt > 0 && el.addEventListener && el.removeEventListener) {
|
|
1234
|
-
const seekBack = () => {
|
|
1235
|
-
el.removeEventListener?.('loadedmetadata', seekBack);
|
|
1236
|
-
try {
|
|
1237
|
-
el.currentTime = resumeAt;
|
|
1238
|
-
} catch {
|
|
1239
|
-
/* source shorter than the stashed position — start from the top */
|
|
1240
|
-
}
|
|
1241
|
-
};
|
|
1242
|
-
el.addEventListener('loadedmetadata', seekBack);
|
|
1243
|
-
}
|
|
1244
|
-
(el.play?.() as Promise<void> | undefined)?.catch?.(() => {
|
|
1245
|
-
/* a re-rejected play surfaces again via the error/stall handlers */
|
|
1246
|
-
});
|
|
1247
|
-
} catch {
|
|
1248
|
-
/* element torn down */
|
|
1249
|
-
}
|
|
1250
|
-
}, [clearStallTimer]);
|
|
1251
|
-
|
|
1252
|
-
const errorOverlay = monitorHealth && playbackFailed ? <VideoErrorOverlay onRetry={retryPlayback} /> : null;
|
|
1253
|
-
|
|
1254
1103
|
const player = (
|
|
1255
1104
|
<MuxPlayer
|
|
1256
1105
|
ref={hoverPlayerRef as React.Ref<never>}
|
|
1257
|
-
onError={handleMediaError}
|
|
1258
|
-
onWaiting={armStallTimer}
|
|
1259
|
-
onStalled={armStallTimer}
|
|
1260
|
-
onPlaying={handlePlaybackProgress}
|
|
1261
|
-
onTimeUpdate={handlePlaybackProgress}
|
|
1262
|
-
onPause={clearStallTimer}
|
|
1263
1106
|
src={url}
|
|
1264
1107
|
poster={poster || undefined}
|
|
1265
1108
|
streamType="on-demand"
|
|
@@ -1344,18 +1187,26 @@ function FilePlayer({
|
|
|
1344
1187
|
<div className="relative h-full w-full" onPointerEnter={handleHoverEnter} onPointerLeave={handleHoverLeave}>
|
|
1345
1188
|
{player}
|
|
1346
1189
|
{unmuteBadge}
|
|
1347
|
-
{errorOverlay}
|
|
1348
1190
|
</div>
|
|
1349
1191
|
);
|
|
1350
1192
|
}
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1193
|
+
if (hoverControlled) {
|
|
1194
|
+
return (
|
|
1195
|
+
<div className="relative h-full w-full">
|
|
1196
|
+
{player}
|
|
1197
|
+
{unmuteBadge}
|
|
1198
|
+
</div>
|
|
1199
|
+
);
|
|
1200
|
+
}
|
|
1201
|
+
// Handoff surfaces (autoPlayUnmuted / startMuted) need the relative wrapper so
|
|
1202
|
+
// the internal center unmute badge can dock — the bare branch has none. Hosts
|
|
1203
|
+
// that render their OWN control pass hideMutedBadge (unmuteBadge is null then,
|
|
1204
|
+
// but the wrapper is harmless).
|
|
1205
|
+
if (autoPlayUnmuted || startMuted) {
|
|
1354
1206
|
return (
|
|
1355
1207
|
<div className="relative h-full w-full">
|
|
1356
1208
|
{player}
|
|
1357
1209
|
{unmuteBadge}
|
|
1358
|
-
{errorOverlay}
|
|
1359
1210
|
</div>
|
|
1360
1211
|
);
|
|
1361
1212
|
}
|
|
@@ -1437,9 +1288,6 @@ const YT_PLAYING_BLUR_DELAY_MS = 1000;
|
|
|
1437
1288
|
interface YouTubeInfoDeliveryMessage {
|
|
1438
1289
|
event?: string;
|
|
1439
1290
|
info?: { playerState?: number };
|
|
1440
|
-
/** `onError` carries its code as a bare number in `info`, not an object:
|
|
1441
|
-
* `{"event":"onError","info":150}` (101/150 = embedding disabled). */
|
|
1442
|
-
errorCode?: number;
|
|
1443
1291
|
}
|
|
1444
1292
|
|
|
1445
1293
|
/**
|
|
@@ -1459,7 +1307,6 @@ function toYouTubeMessage(parsed: unknown): YouTubeInfoDeliveryMessage | null {
|
|
|
1459
1307
|
return {
|
|
1460
1308
|
event: typeof event === 'string' ? event : undefined,
|
|
1461
1309
|
info: { playerState: typeof playerState === 'number' ? playerState : undefined },
|
|
1462
|
-
errorCode: typeof info === 'number' ? info : undefined,
|
|
1463
1310
|
};
|
|
1464
1311
|
}
|
|
1465
1312
|
|
|
@@ -1473,18 +1320,8 @@ function YouTubeFacadeInner({
|
|
|
1473
1320
|
suspended,
|
|
1474
1321
|
}: YouTubeFacadeInnerProps): React.ReactElement {
|
|
1475
1322
|
const [activated, setActivated] = useState(Boolean(autoActivate));
|
|
1476
|
-
// The embed reported a hard player error (see the `onError` branch below).
|
|
1477
|
-
// Retry remounts the iframe via `reloadNonce` — a fresh element, since there
|
|
1478
|
-
// is no way to re-init a cross-origin player from outside.
|
|
1479
|
-
const [failed, setFailed] = useState(false);
|
|
1480
|
-
const [reloadNonce, setReloadNonce] = useState(0);
|
|
1481
1323
|
const iframeRef = useRef<HTMLIFrameElement | null>(null);
|
|
1482
1324
|
|
|
1483
|
-
const retry = useCallback(() => {
|
|
1484
|
-
setFailed(false);
|
|
1485
|
-
setReloadNonce(n => n + 1);
|
|
1486
|
-
}, []);
|
|
1487
|
-
|
|
1488
1325
|
// Embed URL + poster URLs only change when `videoId` or `minimalControls`
|
|
1489
1326
|
// do — memoize so we don't rebuild URLSearchParams on every render.
|
|
1490
1327
|
//
|
|
@@ -1577,7 +1414,7 @@ function YouTubeFacadeInner({
|
|
|
1577
1414
|
// end-of-video (ENDED).
|
|
1578
1415
|
// ---------------------------------------------------------------------------
|
|
1579
1416
|
useEffect(() => {
|
|
1580
|
-
if (!activated
|
|
1417
|
+
if (!activated) return undefined;
|
|
1581
1418
|
const iframe = iframeRef.current;
|
|
1582
1419
|
if (!iframe) return undefined;
|
|
1583
1420
|
|
|
@@ -1590,10 +1427,6 @@ function YouTubeFacadeInner({
|
|
|
1590
1427
|
|
|
1591
1428
|
let blurTimer: ReturnType<typeof setTimeout> | null = null;
|
|
1592
1429
|
|
|
1593
|
-
// No "never reached PLAYING within N seconds" watchdog on purpose: an embed
|
|
1594
|
-
// whose unmuted autoplay Safari/iOS blocked, or one whose jsapi channel is
|
|
1595
|
-
// dead, is healthy and silent — a timeout would tear those down, and retry
|
|
1596
|
-
// would re-arm it. Only an explicit `onError` is a definitive failure.
|
|
1597
1430
|
function handleMessage(event: MessageEvent) {
|
|
1598
1431
|
if (event.origin !== YT_NOCOOKIE_ORIGIN) return;
|
|
1599
1432
|
if (typeof event.data !== 'string') return;
|
|
@@ -1603,13 +1436,7 @@ function YouTubeFacadeInner({
|
|
|
1603
1436
|
} catch {
|
|
1604
1437
|
return;
|
|
1605
1438
|
}
|
|
1606
|
-
if (!payload) return;
|
|
1607
|
-
if (payload.event === 'onError') {
|
|
1608
|
-
setFailed(true);
|
|
1609
|
-
reportPlaybackFailure({ kind: 'youtube', src: videoId, reason: 'error', errorCode: payload.errorCode });
|
|
1610
|
-
return;
|
|
1611
|
-
}
|
|
1612
|
-
if (payload.event !== 'infoDelivery') return;
|
|
1439
|
+
if (!payload || payload.event !== 'infoDelivery') return;
|
|
1613
1440
|
const state = payload.info?.playerState;
|
|
1614
1441
|
if (typeof state !== 'number') return;
|
|
1615
1442
|
|
|
@@ -1632,7 +1459,7 @@ function YouTubeFacadeInner({
|
|
|
1632
1459
|
window.removeEventListener('message', handleMessage);
|
|
1633
1460
|
if (blurTimer !== null) clearTimeout(blurTimer);
|
|
1634
1461
|
};
|
|
1635
|
-
}, [activated
|
|
1462
|
+
}, [activated]);
|
|
1636
1463
|
|
|
1637
1464
|
// Close-side pause: a closing dialog flips `suspended` false→true. Post the
|
|
1638
1465
|
// pauseVideo command over the same enablejsapi channel so the iframe stops
|
|
@@ -1655,19 +1482,10 @@ function YouTubeFacadeInner({
|
|
|
1655
1482
|
const wrapperClass = `relative w-full ${className ?? ''}`;
|
|
1656
1483
|
const wrapperStyle = { paddingBottom: '56.25%' as const };
|
|
1657
1484
|
|
|
1658
|
-
if (activated && failed) {
|
|
1659
|
-
return (
|
|
1660
|
-
<div className={wrapperClass} style={wrapperStyle}>
|
|
1661
|
-
<VideoErrorOverlay onRetry={retry} watchOnYouTubeId={videoId} />
|
|
1662
|
-
</div>
|
|
1663
|
-
);
|
|
1664
|
-
}
|
|
1665
|
-
|
|
1666
1485
|
if (activated) {
|
|
1667
1486
|
return (
|
|
1668
1487
|
<div className={wrapperClass} style={wrapperStyle}>
|
|
1669
1488
|
<iframe
|
|
1670
|
-
key={reloadNonce}
|
|
1671
1489
|
ref={iframeRef}
|
|
1672
1490
|
src={embedUrl}
|
|
1673
1491
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|