@swan-admin/swan-web-component 1.0.121 → 1.0.123
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/BodyScan-CCRXkFFL.mjs +3 -0
- package/dist/BodyScan-CCRXkFFL.mjs.map +1 -0
- package/dist/BodyScan-RBxSYdGc.js +3 -0
- package/dist/BodyScan-RBxSYdGc.js.map +1 -0
- package/dist/FaceScan-DQvhrp3K.mjs +3 -0
- package/dist/FaceScan-DQvhrp3K.mjs.map +1 -0
- package/dist/{FaceScan-BRdpV_Yn.js → FaceScan-Dva9De8x.js} +3 -2
- package/dist/{FaceScan-BRdpV_Yn.js.map → FaceScan-Dva9De8x.js.map} +1 -1
- package/dist/{Modal-BPtRtRVC.js → Modal-CZgyeNwW.mjs} +3 -2
- package/dist/Modal-CZgyeNwW.mjs.map +1 -0
- package/dist/{Modal-HTYYx7sM.js → Modal-CmbYIXgn.js} +3 -2
- package/dist/{Modal-YZScjwEm.js.map → Modal-CmbYIXgn.js.map} +1 -1
- package/dist/bodyScan.d.ts +2 -2
- package/dist/bodyScan.js +2 -1
- package/dist/bodyScan.mjs +2 -1
- package/dist/faceScan.js +2 -1
- package/dist/faceScan.mjs +2 -1
- package/dist/index.css +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -1
- package/dist/index.mjs.map +1 -1
- package/dist/{pose-detection.esm-Bc8T4UAD.js → pose-detection.esm-DFB9u8DH.js} +2 -1
- package/dist/{pose-detection.esm-Bc8T4UAD.js.map → pose-detection.esm-DFB9u8DH.js.map} +1 -1
- package/dist/{pose-detection.esm-DC0_P6R7.js → pose-detection.esm-EvS-BndH.mjs} +2 -1
- package/dist/pose-detection.esm-EvS-BndH.mjs.map +1 -0
- package/package.json +3 -2
- package/dist/BodyScan-3YoMZG7n.js +0 -2
- package/dist/BodyScan-8qgqyl5-.js +0 -2
- package/dist/BodyScan-B0qqqK3C.js +0 -2
- package/dist/BodyScan-B1VBoA6S.js +0 -2
- package/dist/BodyScan-B1afPG1Q.js +0 -2
- package/dist/BodyScan-B1afPG1Q.js.map +0 -1
- package/dist/BodyScan-B4-sO3R2.js +0 -2
- package/dist/BodyScan-B87W6dpH.js +0 -2
- package/dist/BodyScan-B8pglBAp.js +0 -2
- package/dist/BodyScan-BEFwluXG.js +0 -2
- package/dist/BodyScan-BHgnqB6A.js +0 -2
- package/dist/BodyScan-BLvWlYnA.js +0 -2
- package/dist/BodyScan-BMAkl5Iw.js +0 -2
- package/dist/BodyScan-BMTK6e-E.js +0 -2
- package/dist/BodyScan-BQri8pAO.js +0 -2
- package/dist/BodyScan-BVNPC7bJ.js +0 -2
- package/dist/BodyScan-BfLKF5PU.js +0 -2
- package/dist/BodyScan-BjYHCskz.js +0 -2
- package/dist/BodyScan-BoAbG01d.js +0 -2
- package/dist/BodyScan-Bq64fsmL.js +0 -2
- package/dist/BodyScan-Bre1BIHK.js +0 -2
- package/dist/BodyScan-BsDJmy0G.js +0 -2
- package/dist/BodyScan-ByMGumuH.js +0 -2
- package/dist/BodyScan-C0V-EDdB.js +0 -2
- package/dist/BodyScan-C1dWJ2qu.js +0 -2
- package/dist/BodyScan-C43x2o2T.js +0 -2
- package/dist/BodyScan-C57PuQgF.js +0 -2
- package/dist/BodyScan-C9hQU-dK.js +0 -2
- package/dist/BodyScan-CA4rNyqL.js +0 -2
- package/dist/BodyScan-CH-_x55M.js +0 -2
- package/dist/BodyScan-CKH_WyTt.js +0 -2
- package/dist/BodyScan-CKmPcrkx.js +0 -2
- package/dist/BodyScan-CLpL3Uwx.js +0 -2
- package/dist/BodyScan-CM12T_-T.js +0 -2
- package/dist/BodyScan-CNhb6UOX.js +0 -2
- package/dist/BodyScan-CWsaPB8W.js +0 -2
- package/dist/BodyScan-CXwtxfOd.js +0 -2
- package/dist/BodyScan-CYn7eLoz.js +0 -2
- package/dist/BodyScan-CYnHFqsR.js +0 -2
- package/dist/BodyScan-CZTiQzpy.js +0 -2
- package/dist/BodyScan-Cc68SnU1.js +0 -2
- package/dist/BodyScan-CfS4bPCN.js +0 -2
- package/dist/BodyScan-CiG19l_n.js +0 -2
- package/dist/BodyScan-CjvyMLT9.js +0 -2
- package/dist/BodyScan-CkmrPAq9.js +0 -2
- package/dist/BodyScan-CoObDhac.js +0 -2
- package/dist/BodyScan-Cyfo46EK.js +0 -2
- package/dist/BodyScan-Cz9ggKm6.js +0 -2
- package/dist/BodyScan-D6Y_W5e2.js +0 -2
- package/dist/BodyScan-DCEgmVbG.js +0 -2
- package/dist/BodyScan-DEDdENv7.js +0 -2
- package/dist/BodyScan-DEdrK-ld.js +0 -2
- package/dist/BodyScan-DHMf2V5R.js +0 -2
- package/dist/BodyScan-DK9UmNBr.js +0 -2
- package/dist/BodyScan-DPCd26i-.js +0 -2
- package/dist/BodyScan-DPCd26i-.js.map +0 -1
- package/dist/BodyScan-DPSQ1hKh.js +0 -2
- package/dist/BodyScan-DVKnmJuJ.js +0 -2
- package/dist/BodyScan-DX_KbG5U.js +0 -2
- package/dist/BodyScan-DbfP9-G3.js +0 -2
- package/dist/BodyScan-DeBJWo0L.js +0 -2
- package/dist/BodyScan-DgZpiChn.js +0 -2
- package/dist/BodyScan-DkPwUgVu.js +0 -2
- package/dist/BodyScan-DntA0MBx.js +0 -2
- package/dist/BodyScan-DxidLEhk.js +0 -2
- package/dist/BodyScan-DyDxGqfI.js +0 -2
- package/dist/BodyScan-Dysj7b77.js +0 -2
- package/dist/BodyScan-DzyamZmb.js +0 -2
- package/dist/BodyScan-Ehsr_cYc.js +0 -2
- package/dist/BodyScan-MSB66I12.js +0 -2
- package/dist/BodyScan-Ot6nAH1j.js +0 -2
- package/dist/BodyScan-Srq29PK8.js +0 -2
- package/dist/BodyScan-SzaquTgH.js +0 -2
- package/dist/BodyScan-TPpfyief.js +0 -2
- package/dist/BodyScan-XW2vsiE3.js +0 -2
- package/dist/BodyScan-eUuIC3jY.js +0 -2
- package/dist/BodyScan-enDyqSjO.js +0 -2
- package/dist/BodyScan-iOXndWaO.js +0 -2
- package/dist/BodyScan-qVHOWabf.js +0 -2
- package/dist/BodyScan-t9qdQAL-.js +0 -2
- package/dist/BodyScan-twE2YOwy.js +0 -2
- package/dist/BodyScan-z6h5fueJ.js +0 -2
- package/dist/FaceScan-3rVSqkKB.js +0 -2
- package/dist/FaceScan-6Aoz5uUy.js +0 -2
- package/dist/FaceScan-7fl5pLnb.js +0 -2
- package/dist/FaceScan-9kUfXRSv.js +0 -2
- package/dist/FaceScan-AHZ-ajep.js +0 -2
- package/dist/FaceScan-B0pFv4Us.js +0 -2
- package/dist/FaceScan-BE0PB0fZ.js +0 -2
- package/dist/FaceScan-BWSeJEqI.js +0 -2
- package/dist/FaceScan-BdjT7nMG.js +0 -2
- package/dist/FaceScan-Bji5lGcQ.js +0 -2
- package/dist/FaceScan-Bji5lGcQ.js.map +0 -1
- package/dist/FaceScan-BmDNM8tt.js +0 -2
- package/dist/FaceScan-CNoW9DIE.js +0 -2
- package/dist/FaceScan-CPf6-siO.js +0 -2
- package/dist/FaceScan-CQayY4q-.js +0 -2
- package/dist/FaceScan-CQkfSMC2.js +0 -2
- package/dist/FaceScan-CaxQ6fGF.js +0 -2
- package/dist/FaceScan-CxDcvM4f.js +0 -2
- package/dist/FaceScan-D2PL8KpZ.js +0 -2
- package/dist/FaceScan-D3T_l4nN.js +0 -2
- package/dist/FaceScan-DCm43Nrq.js +0 -2
- package/dist/FaceScan-DKP93c3l.js +0 -2
- package/dist/FaceScan-DW6Ix1a1.js +0 -2
- package/dist/FaceScan-DYJVZoZ4.js +0 -2
- package/dist/FaceScan-Da2XEApH.js +0 -2
- package/dist/FaceScan-DrW2yUSA.js +0 -2
- package/dist/FaceScan-DxnydFcx.js +0 -2
- package/dist/FaceScan-Gm4F4cO1.js +0 -2
- package/dist/FaceScan-Nhc7myLG.js +0 -2
- package/dist/FaceScan-VFLJ9h_T.js +0 -2
- package/dist/FaceScan-iUXyDuza.js +0 -2
- package/dist/LoadingScreen-BILrJVZm.js +0 -2
- package/dist/LoadingScreen-FKDLJRUj.js +0 -2
- package/dist/Modal-4T_aWWPQ.js +0 -2
- package/dist/Modal-BHfJVhRi.js +0 -2
- package/dist/Modal-BitizIvu.js +0 -2
- package/dist/Modal-BpgpPVxR.js +0 -2
- package/dist/Modal-BzH-_ch3.js +0 -2
- package/dist/Modal-CB_MHF7l.js +0 -2
- package/dist/Modal-CDiBoVXR.js +0 -2
- package/dist/Modal-CWoUZTh4.js +0 -2
- package/dist/Modal-CaPtqgjI.js +0 -2
- package/dist/Modal-CbZJwbLZ.js +0 -2
- package/dist/Modal-DRjJKUrw.js +0 -2
- package/dist/Modal-DU2Jymve.js +0 -2
- package/dist/Modal-D_XGokez.js +0 -2
- package/dist/Modal-DnweWp6L.js +0 -2
- package/dist/Modal-Do--CErH.js +0 -2
- package/dist/Modal-Do--CErH.js.map +0 -1
- package/dist/Modal-YZScjwEm.js +0 -2
- package/dist/fonts/Switzer-Regular.eot +0 -0
- package/dist/fonts/Switzer-Regular.svg +0 -0
- package/dist/fonts/Switzer-Regular.ttf +0 -0
- package/dist/fonts/Switzer-Regular.woff +0 -0
- package/dist/fonts/Switzer-Regular.woff2 +0 -0
- package/dist/interfaces-B5ysRXpa.d.ts +0 -209
- package/dist/interfaces-CaaERZjZ.d.ts +0 -209
- package/dist/pose-detection.esm-D08ZA0zl.js +0 -18
- package/dist/pose-detection.esm-DejsYGYM.js +0 -18
- package/dist/pose-detection.esm-DejsYGYM.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BodyScan-RBxSYdGc.js","sources":["../src/utils/context/mediaContext.tsx","../src/Icons/SwitchIcon.tsx","../src/components/bodyScan/LevelScreen.tsx","../src/components/bodyScan/CameraScanChild.tsx","../src/customHooks/useTensorFlow.ts","../src/components/bodyScan/ScanningComponent.tsx","../src/customHooks/useWatchdog.ts","../src/components/bodyScan/AngleDetector.tsx","../src/components/bodyScan/CameraPermission.tsx","../src/components/bodyScan/VideoPlayer.tsx","../src/components/bodyScan/ScanErrorMessage.tsx","../src/components/Signup.tsx","../src/components/bodyScan/BodyScanSteps.tsx","../src/components/bodyScan/BodyScan.tsx","../src/customHooks/useGyroSensor.ts"],"sourcesContent":["\"use client\"\nimport React, {\n createContext,\n Dispatch,\n ReactNode,\n SetStateAction,\n useEffect,\n useLayoutEffect,\n useMemo,\n useState,\n} from \"react\";\n\nexport const MEDIA_TYPES = {\n DESKTOP: \"DESKTOP\",\n TAB: \"TAB\",\n MOBILE: \"MOBILE\",\n};\n\ninterface MediaContextType {\n size: number[];\n setSize: Dispatch<SetStateAction<number[]>>;\n clearInputs: boolean;\n setClearInputs: Dispatch<SetStateAction<boolean>>;\n media: string;\n}\n\nexport const MediaContext = createContext<MediaContextType | undefined>(\n undefined\n);\n\nfunction MediaContextProvider({ children }: { children: ReactNode }) {\n const [size, setSize] = useState([window?.innerWidth, window?.innerHeight]);\n const [clearInputs, setClearInputs] = useState(false);\n\n const updateSize = () => {\n setSize([window?.innerWidth, window?.innerHeight]);\n };\n useEffect(() => {\n updateSize();\n }, []);\n useLayoutEffect(() => {\n window.addEventListener(\"resize\", updateSize);\n return () => window.removeEventListener(\"resize\", updateSize);\n }, [updateSize]);\n let media = MEDIA_TYPES.DESKTOP;\n if (size[0] > 768 && size[0] < 1024) {\n media = MEDIA_TYPES.TAB;\n }\n if (size[0] < 768) {\n media = MEDIA_TYPES.MOBILE;\n }\n\n const mediaDetails = useMemo(\n () => ({\n size,\n setSize,\n clearInputs,\n setClearInputs,\n media,\n }),\n [size, clearInputs, media]\n );\n\n return (\n <MediaContext.Provider value={mediaDetails}>\n {children}\n </MediaContext.Provider>\n );\n}\n\nexport default MediaContextProvider;\n","import React from \"react\";\n\nfunction SwitchIcon({ size = 16 }: { size?: number }) {\n return (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 25 25\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M22.6968 14.6968C22.6968 16.8185 21.8539 18.8533 20.3536 20.3536C18.8533 21.8539 16.8185 22.6968 14.6968 22.6968\"\n stroke=\"white\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n <path\n d=\"M18.6968 11.6968V10.6968C18.6968 10.1663 18.4861 9.65764 18.111 9.28256C17.7359 8.90749 17.2272 8.69678 16.6968 8.69678C16.1663 8.69678 15.6576 8.90749 15.2826 9.28256C14.9075 9.65764 14.6968 10.1663 14.6968 10.6968\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n <path\n d=\"M14.6968 10.6968V9.69678C14.6968 9.16634 14.4861 8.65764 14.111 8.28256C13.7359 7.90749 13.2272 7.69678 12.6968 7.69678C12.1663 7.69678 11.6576 7.90749 11.2826 8.28256C10.9075 8.65764 10.6968 9.16634 10.6968 9.69678V10.6968\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n <path\n d=\"M10.6968 10.1968V4.69678C10.6968 4.16634 10.4861 3.65764 10.111 3.28256C9.73592 2.90749 9.22721 2.69678 8.69678 2.69678C8.16634 2.69678 7.65764 2.90749 7.28256 3.28256C6.90749 3.65764 6.69678 4.16634 6.69678 4.69678V14.6968\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n <path\n d=\"M18.6969 11.6968C18.6969 11.1663 18.9076 10.6576 19.2827 10.2826C19.6577 9.90749 20.1664 9.69678 20.6969 9.69678C21.2273 9.69678 21.736 9.90749 22.1111 10.2826C22.4862 10.6576 22.6969 11.1663 22.6969 11.6968V14.6968C22.6969 16.8185 21.854 18.8533 20.3537 20.3536C18.8534 21.8539 16.8186 22.6968 14.6969 22.6968H12.6969C9.89688 22.6968 8.19688 21.8368 6.70688 20.3568L3.10688 16.7568C2.76282 16.3757 2.57847 15.8769 2.592 15.3637C2.60554 14.8505 2.81593 14.3621 3.1796 13.9997C3.54327 13.6373 4.03238 13.4287 4.54565 13.417C5.05892 13.4053 5.55704 13.5914 5.93688 13.9368L7.69688 15.6968\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n );\n}\n\nexport default React.memo(SwitchIcon);\n","/* eslint-disable no-nested-ternary */\n\nimport React, { useCallback, useContext, useMemo } from \"react\";\nimport Header from \"../Header\";\nimport cn from \"clsx\";\nimport { LevelScreenProps } from \"../../types/interfaces\";\nimport SwitchIcon from \"../../Icons/SwitchIcon\";\nimport { LanguageContext } from \"../../utils/context/languageContext\";\nimport { LanguageKeys } from \"../../utils/languageKeys\";\nimport { useConfig } from \"../../utils/context/configContext\";\n\nfunction LevelScreen({ angle, countdown, isScanning, isInTargetRange, stabilityScore, children }: Omit<LevelScreenProps, \"config\">) {\n\tconst { translate } = useContext(LanguageContext) || {};\n\tconst config = useConfig();\n\n\tconst getProximityToTarget = useCallback(() => {\n\t\tif (angle < 80) return Math.max(0, Math.min(100, (angle - 60) * 10));\n\t\tif (angle > 95) return Math.max(0, Math.min(100, (105 - angle) * 10));\n\t\treturn 100;\n\t}, [angle]);\n\n\tconst backgroundColor = useMemo(() => {\n\t\tif (isScanning) return config?.style?.angleDetector?.successAngleBackground || \"#4f46e5\";\n\n\t\tconst proximity = getProximityToTarget();\n\t\tif (proximity === 0) return config?.style?.angleDetector?.successAngleLowBackground || \"#ffffff\";\n\t\tif (proximity === 100) return config?.style?.angleDetector?.successAngleBackground || \"#4f46e5\";\n\n const r = Math.round(255 - (255 - 139) * (proximity / 100));\n const g = Math.round(255 - (255 - 92) * (proximity / 100));\n const b = Math.round(255 - (255 - 246) * (proximity / 100));\n return `rgb(${r}, ${g}, ${b})`;\n }, [isScanning, getProximityToTarget]);\n\n\tconst handelTextColour = useCallback((isLight: boolean, proximity: number) => {\n\t\tif (proximity > 70) {\n\t\t\treturn isLight ? `text-[${config?.style?.angleDetector?.successAngleTextLightColor}]/70` : `text-[${config?.style?.angleDetector?.successAngleTextLightColor}]`;\n\t\t}\n\t\treturn isLight ? `text-[${config?.style?.angleDetector?.successAngleTextLightColor}]/40` : `text-[${config?.style?.angleDetector?.successAngleTextDarkColor}]/70`;\n\t}, []);\n\n\tconst getTextColor = useCallback(\n\t\t(isLight = false) => {\n\t\t\tconst proximity = getProximityToTarget();\n\t\t\tif (isScanning) return `text-[${config?.style?.angleDetector?.successAngleTextLightColor}]`;\n\t\t\treturn handelTextColour(isLight, proximity);\n\t\t},\n\t\t[isScanning, getProximityToTarget],\n\t);\n\n\tconst handelColourCode = useCallback((isLight: boolean, proximity: number) => {\n\t\tif (proximity > 70) {\n\t\t\treturn isLight ? config?.style?.angleDetector?.successAngleTextLightColor : config?.style?.angleDetector?.successAngleTextLightColor;\n\t\t}\n\t\treturn isLight ? config?.style?.angleDetector?.successAngleTextLightColor : config?.style?.angleDetector?.successAngleTextDarkColor;\n\t}, []);\n\tconst getColorCode = useCallback(\n\t\t(isLight = false) => {\n\t\t\tconst proximity = getProximityToTarget();\n\t\t\tif (isScanning) return config?.style?.angleDetector?.successAngleTextLightColor;\n\t\t\treturn handelColourCode(isLight, proximity);\n\t\t},\n\t\t[isScanning, getProximityToTarget],\n\t);\n\tconst cd = countdown;\n\tfunction fixVh() {\n\t\tdocument.documentElement.style.setProperty(\"--real-vh\", window.innerHeight + \"px\");\n\t}\n\tfixVh();\n\twindow.addEventListener(\"resize\", fixVh);\n\treturn (\n\t\t<div\n\t\t\tclassName=\"flex common-ui-main w-screen flex-col items-center h-[var(--real-vh)] overflow-hidden touch-none justify-center transition-all duration-300 max-w-[28rem] mx-auto\"\n\t\t\tstyle={{ backgroundColor }}\n\t\t\tdata-testid=\"level-screen\"\n\t\t\tdata-angle-state={isScanning ? \"locked\" : isInTargetRange ? \"target\" : \"adjusting\"}\n\t\t>\n\t\t\t<div className=\"flex justify-start fixed top-[.5rem] max-w-[28rem] mx-auto w-full px-[1rem]\">\n\t\t\t\t<div className=\"flex justify-start \">\n\t\t\t\t\t<Header noTitle />\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t{cd !== null ? (\n\t\t\t\t<div\n\t\t\t\t\tclassName=\"relative flex h-[6rem] w-[6rem] items-center justify-center rounded-[9999px] bg-[#fff]/20 transition-all duration-300\"\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tborder: `2px solid ${getColorCode()}`,\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName={`text-[3rem] font-bold text-[${getColorCode()}]`}\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\tfontFamily: config?.style?.base?.baseFontFamily || \"Inter, sans-serif\",\n color: getColorCode(),\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t{cd}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t) : isScanning ? (\n\t\t\t\t<div className=\"relative flex flex-col items-center justify-center\">\n\t\t\t\t\t<div className=\"relative flex h-16 w-16 items-center justify-center rounded-[9999px] border-2 bg-[#fff]/30 border-[#fff]\">\n\t\t\t\t\t\t<div className=\"h-4 w-4 rounded-[9999px] animate-pulse bg-[#fff]/80\" />\n\t\t\t\t\t</div>\n\t\t\t\t\t{children}\n\t\t\t\t</div>\n\t\t\t) : (\n\t\t\t\t<div\n\t\t\t\t\tclassName={cn(\"relative flex h-[4rem] w-[4rem] items-center justify-center rounded-[9999px] \", isInTargetRange ? \"bg-[#fff]/20 border-[#fff]\" : \"bg-[#fff]/10 border-[#fff]\")}\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\ttransform: `translateY(${(90 - angle) * 3}px)`,\n\t\t\t\t\t\ttransition: \"transform 0.2s ease-out\",\n\t\t\t\t\t\tborder: `2px solid ${getColorCode()}`,\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<div className={`h-[1rem] w-[1rem] rounded-[9999px] bg-[${getColorCode()}]/80`}\n style={{\n\t\t\t\t\t\tbackgroundColor: `${getColorCode()}B3`,\n\t\t\t\t\t}}\n />\n\t\t\t\t\t<div\n\t\t\t\t\t\t// className=\" text-[#fff]\"\n\t\t\t\t\t\tclassName={cn(\"mt-[.5rem] text-center w-[180px] flex-col flex items-center absolute top-[60px]\", getTextColor())}\n style={{\n\t\t\t\t\t\tcolor: getColorCode(),\n\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t<SwitchIcon size={30} />\n\t\t\t\t\t\t<p\n\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\tfontFamily: config?.style?.base?.baseFontFamily || \"Inter, sans-serif\",\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{\" \"}\n\t\t\t\t\t\t\t{translate?.(LanguageKeys.startLevelCheck)}\n\t\t\t\t\t\t\t<br />\n\t\t\t\t\t\t</p>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{cd !== null && (\n\t\t\t\t<div className=\"absolute bottom-[8rem] text-center\">\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName={cn(\"text-sm font-medium px-4 py-1.5 bg-black/20 rounded-[9999px] mt-8\", getTextColor())}\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\tfontFamily: config?.style?.base?.baseFontFamily || \"Inter, sans-serif\",\n color: getColorCode(),\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t{translate?.(LanguageKeys.leavePhone)}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t\t{isInTargetRange && cd === null && !isScanning && (\n\t\t\t\t<div className=\"absolute bottom-[8rem] w-[12rem]\">\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName={`h-[.375rem] w-full bg-[${getColorCode()}]/20 rounded-[9999px] overflow-hidden`}\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\tbackgroundColor: `${getColorCode()}33`, // 20% opacity\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName={`h-full bg-[${getColorCode()}]/70 transition-all duration-300 rounded-[9999px]`}\n\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\twidth: `${angle}%`,\n\t\t\t\t\t\t\t\tbackgroundColor: `${getColorCode()}B3`,\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\n {cd === null && !isScanning && (\n <div className=\"absolute bottom-[5rem] text-center\">\n <div className={cn(\"text-[1.5rem] font-light\", getTextColor())}\n style={{\n\t\t\t\t\t\tcolor: getColorCode(),\n\t\t\t\t\t}}\n >\n {Math.round(angle)}°\n </div>\n </div>\n )}\n\n\t\t\t{cd === null && !isScanning && (\n\t\t\t\t<div className=\"absolute bottom-[2rem] text-center max-w-[20rem] mx-auto\">\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName={cn(\"text-[.75rem] opacity-50\", getTextColor())}\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\tfontFamily: config?.style?.base?.baseFontFamily || \"Inter, sans-serif\",\n color: getColorCode(),\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t{translate?.(LanguageKeys.placePhoneUpright)}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t);\n}\nexport default React.memo(LevelScreen);\n","import { useContext } from \"react\";\nimport Webcam from \"react-webcam\";\nimport { CameraScanChildProps } from \"../../types/interfaces\";\nimport { MEDIA_TYPES, MediaContext } from \"../../utils/context/mediaContext\";\nimport { LanguageContext } from \"../../utils/context/languageContext\";\nimport { pauseIcon, voiceOverAssetsPath } from \"../../utils/constants\";\nimport SpecificButton from \"../../atoms/specificButton/SpecificButton\";\nimport { LanguageKeys } from \"../../utils/languageKeys\";\nimport { X } from \"lucide-react\";\n\n\n\nfunction CameraScanChild({\n resetScan,\n loadingCam,\n onUserMedia,\n onPause,\n pause,\n onReScan,\n recordingStarted,\n startSendingVideoFrames,\n showPause,\n webcamRef,\n onUserMediaError,\n resetDetector,\n videoConstraints,\n webcamKey,\n faceDone,\n isScanning,\n}: Omit<CameraScanChildProps, \"config\">) {\n const { media } = useContext(MediaContext) || {};\n const { translate } = useContext(LanguageContext) || {};\n\n return (\n <div className=\"App w-screen h-[100vh] relative common-ui-main\" data-testid=\"camera-scan\" data-scan-phase={faceDone ? \"spinning\" : recordingStarted ? \"pose-detecting\" : isScanning ? \"starting\" : \"ready\"}>\n <span\n onClick={() => {\n resetScan();\n resetDetector();\n }}\n className=\"fixed right-[20px] top-[20px] z-[999]\"\n >\n <X className=\"text-[#fff]\" />\n </span>\n\n <div className=\"w-full h-full overflow-hidden \">\n {media === MEDIA_TYPES.MOBILE && (\n <Webcam\n key={webcamKey}\n audio={false}\n ref={webcamRef}\n screenshotQuality={1}\n videoConstraints={videoConstraints}\n mirrored\n screenshotFormat=\"image/jpeg\"\n onUserMedia={onUserMedia}\n onUserMediaError={onUserMediaError}\n style={{\n position: \"fixed\",\n top: 0,\n bottom: 0,\n zIndex: 0,\n width: \"100%\",\n height: \"100%\",\n objectFit: \"cover\",\n }}\n />\n )}\n </div>\n\n <div className=\"fixed bottom-[30px] w-full z-[999] flex flex-col gap-4 items-center justify-center\">\n {showPause && (\n <SpecificButton\n className=\"!w-[180px] !h-[40px] !py-[0] mx-auto \"\n prefix={pauseIcon}\n buttonText={translate?.(LanguageKeys.pause)}\n buttonFunc={onPause}\n btnSecondary\n />\n )}\n\n {pause ? (\n <div>\n <SpecificButton\n className=\"!w-[180px] !h-[40px] !py-[0] mx-auto\"\n buttonText={translate?.(LanguageKeys.restart)}\n buttonFunc={onReScan}\n btnSecondary\n />\n </div>\n ) : !recordingStarted ? (\n <SpecificButton\n data-testid=\"start-scan-btn\"\n className={`!w-[180px] !h-[40px] !py-[0] mx-auto !bg-[#ffffff] !text-[#000000] ${\n loadingCam ? \"!opacity-50\" : \"\"\n }`}\n buttonText={translate?.(LanguageKeys.startScan)}\n buttonFunc={startSendingVideoFrames}\n disabled={loadingCam}\n />\n ) : null}\n </div>\n\n <audio\n id=\"audioElement\"\n crossOrigin=\"anonymous\"\n preload=\"auto\"\n style={{\n position: \"absolute\",\n zIndex: -99999,\n }}\n src={`${voiceOverAssetsPath}scanAudioInstructions/silence.mp3`}\n />\n </div>\n );\n}\n\nexport default CameraScanChild;\n","import { useState, useRef, useEffect } from \"react\";\n// We keep this, but we will make it optional in the logic below\n\ntype KeypointTuple = [number, number, number];\n\n// GLOBAL SINGLETONS (Persist across component re-mounts)\n// This prevents reloading the heavy model if the user navigates away and back.\nlet globalLoadingPromise: Promise<boolean> | null = null;\nlet globalDetector: any = null;\nlet globalPoseLib: any = null;\n\nexport default function useTensorFlow() {\n const [consecutiveFronts, setConsecutiveFronts] = useState(0);\n const [spinPhase, setSpinPhase] = useState(0);\n const [isLoaded, setIsLoaded] = useState(false);\n\n const spinPhaseRef = useRef(0);\n const consecutiveFrontsRef = useRef(0);\n const mounted = useRef(true);\n\n useEffect(() => {\n mounted.current = true; // Reset on mount\n\n // 1. If already loaded globally, just use it.\n if (globalDetector) {\n setIsLoaded(true);\n return;\n }\n\n // 2. If not loading, start the process.\n if (!globalLoadingPromise) {\n globalLoadingPromise = loadDependenciesGlobal();\n }\n\n // 3. Wait for the global promise to resolve.\n globalLoadingPromise.then((success) => {\n if (success && mounted.current) {\n setIsLoaded(true);\n }\n });\n\n return () => {\n mounted.current = false;\n };\n }, []);\n\n /**\n * Loads TensorFlow/MediaPipe dynamically.\n * This logic is ISOLATED from the server.\n */\n async function loadDependenciesGlobal(): Promise<boolean> {\n // GUARDRAIL 1: Strict Environment Check\n // Ensure we are in a browser environment with media capabilities\n if (\n typeof window === 'undefined' || \n typeof navigator === 'undefined'\n ) {\n return false;\n }\n \n try {\n console.log(\"Starting TensorFlow preload...\");\n \n // GUARDRAIL 2: Dynamic Imports\n // These heavy modules are only fetched by the browser, never the server.\n const [poseLib, tfjsCore, tfjsBackend] = await Promise.all([\n import(\"@tensorflow-models/pose-detection\"),\n import(\"@tensorflow/tfjs-core\"),\n import(\"@tensorflow/tfjs-backend-webgl\") // Triggers backend registration side-effect\n ]);\n \n globalPoseLib = poseLib;\n \n // Explicitly set the backend to WebGL for performance\n await tfjsCore.setBackend('webgl');\n await tfjsCore.ready();\n console.log(\"TensorFlow backend ready (WebGL)\");\n\n // GUARDRAIL 3: Zero-Config Asset Loading\n // If TENSORFLOW_SOLUTION_PATH fails or is missing, we must fallback \n // to a CDN so the user doesn't have to copy files manually.\n const modelConfig = {\n runtime: \"mediapipe\",\n modelType: \"full\",\n solutionPath:\"https://cdn.jsdelivr.net/npm/@mediapipe/pose\", // Fallback for Zero Config\n } as const;\n\n try {\n globalDetector = await globalPoseLib.createDetector(\n globalPoseLib.SupportedModels.BlazePose,\n modelConfig\n );\n console.log(\"MediaPipe detector created successfully\");\n } catch (mediapipeError) {\n console.warn(\"MediaPipe failed, falling back to TFJS runtime:\", mediapipeError);\n \n // Fallback to TFJS runtime (slower but works without external assets)\n globalDetector = await globalPoseLib.createDetector(\n globalPoseLib.SupportedModels.BlazePose,\n {\n runtime: \"tfjs\",\n modelType: \"full\",\n }\n );\n console.log(\"TFJS detector created successfully\");\n }\n \n return true;\n } catch (error) {\n console.error(\"Failed to load TensorFlow dependencies:\", error);\n globalLoadingPromise = null; // Reset so retry is possible\n return false;\n }\n }\n\n function isFrontFrame(body: KeypointTuple[]) {\n // Guardrail: Ensure points exist before filtering\n if (!body || body.length === 0) return false;\n return body.filter((p) => p[2] > 0.7).length === 22;\n }\n\n const poseDetector = async (callback: () => void, webcamRef: any) => {\n // GUARDRAIL 4: Runtime Safety\n // Check if everything is truly ready before calculating\n if (!globalDetector || !mounted.current || !webcamRef?.current?.video) {\n return;\n }\n \n // Check if video is actually playing and has data\n const video = webcamRef.current.video;\n if (video.readyState < 2) return; \n\n try {\n const poses = await globalDetector.estimatePoses(\n video,\n { flipHorizontal: false }\n );\n\n if (!poses || !poses.length) return;\n\n const keypoints = poses[0].keypoints;\n \n // Standardize keypoints\n const body: KeypointTuple[] = keypoints\n .slice(11) // Ignore face keypoints (0-10)\n .map((kp: any) => [\n kp.x > 0 && kp.x < 720 ? kp.x : -1,\n kp.y > 0 && kp.y < 1280 ? kp.y : -1,\n kp.score ?? 0,\n ]);\n\n if (spinPhaseRef.current === 0 && !isFrontFrame(body)) {\n setConsecutiveFronts((prev) => prev + 1);\n }\n\n if (spinPhaseRef.current === 1 && isFrontFrame(body)) {\n setConsecutiveFronts((prev) => prev + 1);\n }\n\n if (spinPhaseRef.current === 2) callback();\n } catch (err) {\n console.error(\"Pose detection error:\", err);\n }\n };\n\n useEffect(() => {\n consecutiveFrontsRef.current = consecutiveFronts;\n if (consecutiveFronts > 6 && spinPhaseRef.current < 2) {\n setSpinPhase((s) => s + 1);\n setConsecutiveFronts(0);\n }\n }, [consecutiveFronts]);\n\n useEffect(() => {\n spinPhaseRef.current = spinPhase;\n }, [spinPhase]);\n\n const resetDetector = () => {\n spinPhaseRef.current = 0;\n consecutiveFrontsRef.current = 0;\n setConsecutiveFronts(0);\n setSpinPhase(0);\n };\n\n const retryLoading = async () => {\n if (!globalDetector && !globalLoadingPromise) {\n globalLoadingPromise = loadDependenciesGlobal();\n const success = await globalLoadingPromise;\n if (success && mounted.current) {\n setIsLoaded(true);\n }\n }\n };\n\n return { \n poseDetector, \n isLoaded,\n spinPhase,\n resetDetector,\n retryLoading\n };\n}","/* eslint-disable no-use-before-define */\nimport React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from \"react\";\nimport CameraScanChild from \"./CameraScanChild\";\nimport posthog from \"../../utils/posthog\";\nimport Webcam from \"react-webcam\";\nimport { ScanningComponentProps } from \"../../types/interfaces\";\nimport useTensorFlow from \"../../customHooks/useTensorFlow\";\nimport useWatchdog from \"../../customHooks/useWatchdog\";\nimport ParamsContext from \"../../utils/context/paramsContext\";\nimport {\n\tgenerateUuid,\n\tgetCurrentTimeInSeconds,\n\tgetPreferredMediaRecorderTypes,\n\tgetRecordingExtension,\n\tgetRecordingMimeType,\n\tgetSupportedMediaRecorderTypes,\n\thandleScanTimeCapture,\n\trescanSupportCaptureEvent,\n} from \"../../utils/utils\";\nimport speechService from \"../../utils/service/speechService\";\nimport { videoConstraintsExact, videoConstraintsFallback, videoTypes, voiceOverAssetsPath } from \"../../utils/constants\";\n\n// If frames are streaming to the pose-detection backend but no instruction comes\n// back within this window, treat the scan as stalled and surface a retry instead\n// of freezing forever (see the watchdog wiring below).\nconst POSE_FEEDBACK_TIMEOUT_MS = 12000;\n\nlet audioTimeoutId: ReturnType<typeof setTimeout> | undefined;\nlet lastVideoPlayed: {\n\tskipCount: number;\n\tno_of_times_skipped: number;\n\taudioName: string;\n} | null = null;\n\nfunction ScanningComponent({ setIsScanLocked, resetDetector, scanID, setIsVideoUploaded, setScanFailsError, setScanStartTime, setScanUniqueKey, userDetails }: Omit<ScanningComponentProps, \"config\">) {\n\tconst { email, shopDomain, gender, heightInCm } = userDetails;\n\tconst webcamRef = useRef<Webcam | null>(null);\n\tconst mediaRecorderRef = useRef<MediaRecorder | null>(null);\n\tconst recordedFpsRef = useRef<number | null>(null);\n\tconst [recordedChunks, setRecordedChunks] = useState<Blob[]>([]);\n\tconst [loadingCam, setLoadingCam] = useState(true);\n\tconst [recordingStarted, setRecordingStarted] = useState(false);\n\tconst [faceDone, setFaceDone] = useState(false);\n\tconst [mediaRecorderStopped, setMediaRecorderStopped] = useState(true);\n\tconst [isScanning, setIsScanning] = useState(false);\n\tconst captureVideoTimeOutHandle = useRef<ReturnType<typeof setTimeout> | null>(null);\n\tconst [audioSource, setAudioSource] = useState(\"\");\n\tconst [audioSourceList, setAudioSourceList] = useState<string[]>([]);\n\tconst [emptyAudioSource, setEmptyAudioSource] = useState(\"\");\n\tconst [startAgain, setStartAgain] = useState(false);\n\tconst [showRestart, setRestart] = useState(false);\n\tconst [pause, setPause] = useState(false);\n\tconst [events, setEvents] = useState<any>([]);\n\tconst [showPause, setShowPause] = useState(false);\n\tconst allowAudioToPlay = useRef(true);\n\tconst { poseDetector } = useTensorFlow();\n\tconst firstScan = useRef(true);\n\tconst poseStoppedRef = useRef(false);\n\tconst counterRef = useRef(0);\n\tconst consecutiveFalseRef = useRef(0);\n\t// Diagnostics: count frames actually emitted to the BE vs. skipped because the\n\t// webcam screenshot came back null (a silent cause of \"scan frozen\" with no feedback).\n\tconst framesSentRef = useRef(0);\n\tconst framesSkippedRef = useRef(0);\n\tconst poseListenerRegistered = useRef(false);\n\tconst isConnectingRef = useRef(false);\n\tconst brilliantPlayedRef = useRef(false);\n\tconst postPoseProcessRef = useRef<() => void>(() => {});\n\tconst startSendingVideoFramesRef = useRef<() => void>(() => {});\n\tconst idRef = useRef<string | null>(null);\n\tconst [supportedTypes, setSupportedTypes] = useState<string[]>([]);\n\tconst [webcamKey, setWebcamKey] = useState(0);\n\tconst [currentVideoConstraints, setCurrentVideoConstraints] = useState<MediaTrackConstraints>(videoConstraintsExact);\n\tconst { setStartGyro, uploadScanFile, setUploadLoading, swan, onScanStart, onCaptureComplete } = useContext(ParamsContext);\n\n\t// Refs to avoid stale closures in callbacks after pause/restart\n\tconst scanIDRef = useRef(scanID);\n\tuseEffect(() => {\n\t\tscanIDRef.current = scanID;\n\t}, [scanID]);\n\n\tconst eventsRef = useRef(events);\n\tuseEffect(() => {\n\t\teventsRef.current = events;\n\t}, [events]);\n\n\t// Watchdog (LOG-ONLY): the scan depends on the pose-detection backend sending\n\t// guidance back while we stream frames. If frames are flowing but nothing comes\n\t// back for POSE_FEEDBACK_TIMEOUT_MS, this fires a single telemetry event so the\n\t// stall is visible in analytics. It deliberately does NOT change the UX — the\n\t// screen behaves exactly as before — so this build is pure measurement. Once the\n\t// data confirms the pattern, the recovery action (stop + retry UI) can be enabled.\n\t// The dog is started when the first frame goes out, petted on every backend\n\t// message, and stopped on pause/confirm/restart/unmount.\n\tconst handlePoseFeedbackTimeout = useCallback(() => {\n\t\tposthog.capture(`${shopDomain}/pose_feedback_timeout`, {\n\t\t\tscanID: scanIDRef.current,\n\t\t\temail,\n\t\t\tframesSent: framesSentRef.current,\n\t\t\tid: idRef.current,\n\t\t});\n\t\t// --- Recovery action (disabled for this measurement-only build) ---\n\t\t// Uncomment to turn the stall into a recoverable retry instead of a freeze:\n\t\t// allowAudioToPlay.current = false;\n\t\t// speechService.stopAudio();\n\t\t// clearTimeout(audioTimeoutId);\n\t\t// if (captureVideoTimeOutHandle.current) clearTimeout(captureVideoTimeOutHandle.current);\n\t\t// if (mediaRecorderRef.current) {\n\t\t// \ttry {\n\t\t// \t\tmediaRecorderRef.current.stop();\n\t\t// \t} catch (e) {\n\t\t// \t\tconsole.log(\"watchdog: error stopping recorder\", e);\n\t\t// \t}\n\t\t// }\n\t\t// swan.poseDetection.disconnect();\n\t\t// poseListenerRegistered.current = false;\n\t\t// setScanFailsError(\"Scan timed out. Please try again.\");\n\t}, [shopDomain, email]);\n\n\tconst poseWatchdog = useWatchdog({ timeoutMs: POSE_FEEDBACK_TIMEOUT_MS, onTimeout: handlePoseFeedbackTimeout });\n\n\tconst resetScan = () => {\n\t\tposeStoppedRef.current = false;\n\t\tposeWatchdog.stop();\n\t\tclearTimeout(audioTimeoutId);\n\t\tif (captureVideoTimeOutHandle.current) clearTimeout(captureVideoTimeOutHandle.current);\n\t\tsetScanUniqueKey(generateUuid());\n\t\tsetScanFailsError(\"\");\n\t\tsetUploadLoading?.(false);\n\t\tsetRecordedChunks([]);\n\t\trecordedFpsRef.current = null;\n\t\tsetLoadingCam(true);\n\t\tsetRecordingStarted(false);\n\t\tsetFaceDone(false);\n\t\tsetMediaRecorderStopped(true);\n\t\tsetIsScanning(false);\n\t\tcaptureVideoTimeOutHandle.current = null;\n\t\tsetAudioSource(\"\");\n\t\tsetAudioSourceList([]);\n\t\tsetEmptyAudioSource(\"\");\n\t\tsetStartAgain(!startAgain);\n\t\tsetStartGyro(false);\n\t\tspeechService.stopAudio();\n\t\tsetRestart(false);\n\t\tsetPause(false);\n\t\tcounterRef.current = 0;\n\t\tconsecutiveFalseRef.current = 0;\n\t\tframesSentRef.current = 0;\n\t\tframesSkippedRef.current = 0;\n\t\tposeListenerRegistered.current = false;\n\t\tbrilliantPlayedRef.current = false;\n\t\tif (mediaRecorderRef.current !== null) {\n\t\t\tmediaRecorderRef.current.stop();\n\t\t}\n\n\t\tevents.forEach((el: any) => {\n\t\t\tif (mediaRecorderRef.current) mediaRecorderRef.current.removeEventListener(\"dataavailable\", el);\n\t\t});\n\n\t\tsetEvents([]);\n\t\tallowAudioToPlay.current = true;\n\t\tsetShowPause(false);\n\t\tsetIsVideoUploaded(false);\n\t\tif (webcamRef.current?.stream) {\n\t\t\tsetLoadingCam(false);\n\t\t}\n\t};\n\n\tconst handleRestart = async () => {\n\t\trescanSupportCaptureEvent({\n\t\t\teventName: `${shopDomain}/rescan`,\n\t\t\temail,\n\t\t\tscanID: scanIDRef.current,\n\t\t\theight: heightInCm,\n\t\t\tgender,\n\t\t\tstatus: false,\n\t\t});\n\t\tswan.poseDetection.disconnect();\n\t\tresetScan();\n\t\tawait handleSocket();\n\t};\n\n\tconst handleUserMedia = useCallback(() => {\n\t\tsetTimeout(() => {\n\t\t\tsetLoadingCam(false);\n\t\t}, 1000);\n\t}, []);\n\n\tconst handleUserMediaError = useCallback(\n\t\t(error: string | DOMException) => {\n\t\t\tconsole.log(\"camera error\", error);\n\t\t\t// Retry once with relaxed constraints for broader device compatibility.\n\t\t\tif (currentVideoConstraints === videoConstraintsExact) {\n\t\t\t\tsetCurrentVideoConstraints(videoConstraintsFallback);\n\t\t\t\tsetWebcamKey((prev) => prev + 1);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsetLoadingCam(false);\n\t\t},\n\t\t[currentVideoConstraints],\n\t);\n\n\tconst handlePause = useCallback(() => {\n\t\tallowAudioToPlay.current = false;\n\t\tposeWatchdog.stop();\n\t\tsetPause(true);\n\t\tsetShowPause(false);\n\t\tif (captureVideoTimeOutHandle.current) clearTimeout(captureVideoTimeOutHandle.current);\n\t\tif (mediaRecorderRef.current) {\n\t\t\tmediaRecorderRef.current.pause();\n\t\t}\n\t\tspeechService.stopAudio();\n\t\tswan.poseDetection.disconnect();\n\t\tposthog.capture(`${shopDomain}/pose_detection_disconnected`, {\n\t\t\tscanID: scanID,\n\t\t\temail,\n\t\t\tid: idRef.current,\n\t\t\treason: \"pause\",\n\t\t});\n\t\tcounterRef.current = 0;\n\t\tconsecutiveFalseRef.current = 0;\n\t\tposeListenerRegistered.current = false;\n\t\tbrilliantPlayedRef.current = false;\n\t\tif (mediaRecorderRef.current) {\n\t\t\tmediaRecorderRef.current.stop();\n\t\t}\n\t\tevents.forEach((el: any) => {\n\t\t\tif (mediaRecorderRef.current) mediaRecorderRef.current.removeEventListener(\"dataavailable\", el);\n\t\t});\n\t\tsetEvents([]);\n\t\tclearTimeout(audioTimeoutId);\n\t}, [mediaRecorderRef, events, speechService, allowAudioToPlay]);\n\n\t// const supportedTypes = videoTypes.filter((type) => MediaRecorder.isTypeSupported(type));\n\tconst preferredVideoTypes = useMemo(() => getPreferredMediaRecorderTypes(), []);\n\tconst recordingMimeType = getRecordingMimeType(supportedTypes);\n\tconst recordingExtension = getRecordingExtension(recordingMimeType);\n\n\tconst stopRecording = useCallback(async () => {\n\t\tallowAudioToPlay.current = true;\n\t\tawait speechService.playAudio(`${voiceOverAssetsPath}SpotOn.mp3`);\n\t\tsetPause(false);\n\t\tsetShowPause(false);\n\t\tif (captureVideoTimeOutHandle.current) clearTimeout(captureVideoTimeOutHandle.current);\n\t\tsetRecordingStarted(false);\n\t}, [captureVideoTimeOutHandle, speechService]);\n\n\tconst handleSocket = useCallback(async () => {\n\t\t// Guard against React StrictMode double-mount: skip if already connecting or connected\n\t\tif (isConnectingRef.current || idRef.current) return;\n\t\tisConnectingRef.current = true;\n\t\ttry {\n\t\t\tidRef.current = await swan.poseDetection.connect();\n\n\t\t\tposthog.capture(`${shopDomain}/pose_detection_connected`, {\n\t\t\t\tscanID: scanIDRef.current,\n\t\t\t\temail,\n\t\t\t\tid: idRef.current,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconsole.log(error, \"while connecting websocket\");\n\t\t\tposthog.capture(`${shopDomain}/pose_detection_connect_failed`, {\n\t\t\t\tscanID: scanID,\n\t\t\t\temail,\n\t\t\t\terror: String(error),\n\t\t\t});\n\t\t} finally {\n\t\t\tisConnectingRef.current = false;\n\t\t}\n\t}, [scanID, shopDomain, email]);\n\nconst handleSpinDataAvailable = useCallback(\n\t\t({ data }: BlobEvent) => {\n\t\t\tif (data && data.size > 0 && allowAudioToPlay.current) {\n\t\t\t\tsetRecordedChunks((prev) => prev.concat(data));\n\t\t\t\tif (!poseStoppedRef.current && webcamRef.current) {\n\t\t\t\t\tposeStoppedRef.current = true;\n\t\t\t\t\tposeDetector(() => {\n\t\t\t\t\t\tstopRecording();\n\t\t\t\t\t\thandleScanTimeCapture({\n\t\t\t\t\t\t\teventName: `${shopDomain}/tensorFlow`,\n\t\t\t\t\t\t\tscanID: scanIDRef.current,\n\t\t\t\t\t\t\temail,\n\t\t\t\t\t\t\tmessage: \"recording stopped by tensorflow \",\n\t\t\t\t\t\t});\n\t\t\t\t\t}, webcamRef);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t[stopRecording, handleScanTimeCapture, shopDomain, email, webcamRef],\n\t);\n\n\tconst playAudio = useCallback(async () => {\n\t\tconst lastAudio = audioSourceList[audioSourceList.length - 1];\n\t\tif (lastAudio && allowAudioToPlay.current) {\n\t\t\tawait speechService.playAudio(voiceOverAssetsPath + lastAudio);\n\t\t\tif (allowAudioToPlay.current) {\n\t\t\t\taudioTimeoutId = setTimeout(playAudio, 2000);\n\t\t\t}\n\t\t}\n\t}, [audioSourceList, allowAudioToPlay]);\n\n\tconst handleStartCaptureClick = useCallback(() => {\n\t\tif (mediaRecorderRef && mediaRecorderRef.current) {\n\t\t\tmediaRecorderRef.current.stop();\n\t\t}\n\t\tsetRecordingStarted(true);\n\t\ttry {\n\t\t\tif (webcamRef && webcamRef.current && webcamRef.current.stream) {\n\t\t\t\t// Capture FPS from stream settings\n\t\t\t\tconst videoTrack = webcamRef.current.stream.getVideoTracks()[0];\n\t\t\t\tif (videoTrack) {\n\t\t\t\t\tconst settings = videoTrack.getSettings();\n\t\t\t\t\trecordedFpsRef.current = settings.frameRate || null;\n\t\t\t\t}\n\n\t\t\t\tconst mediaRecorderOptions: MediaRecorderOptions = {\n\t\t\t\t\tvideoBitsPerSecond: 1_500_000,\n\t\t\t\t};\n\t\t\t\tif (supportedTypes[0]) {\n\t\t\t\t\tmediaRecorderOptions.mimeType = supportedTypes[0];\n\t\t\t\t}\n\t\t\t\tmediaRecorderRef.current = new MediaRecorder(webcamRef.current.stream, mediaRecorderOptions);\n\t\t\t\tmediaRecorderRef.current.addEventListener(\"dataavailable\", handleSpinDataAvailable);\n\n\t\t\t\t// Mark recorder as fully stopped only after all dataavailable events have fired\n\t\t\t\tmediaRecorderRef.current.addEventListener(\"stop\", () => {\n\t\t\t\t\tsetMediaRecorderStopped(true);\n\t\t\t\t});\n\n\t\t\t\tsetEvents([...events, handleSpinDataAvailable]);\n\t\t\t\tmediaRecorderRef.current.start(1000);\n\t\t\t\tsetMediaRecorderStopped(false);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tconsole.log(\"error while using media recorder\", e);\n\t\t}\n\t}, [webcamRef, supportedTypes, handleSpinDataAvailable, events]);\n\n\tconst postPoseProcess = useCallback(async () => {\n\t\tif (captureVideoTimeOutHandle.current) {\n\t\t\tclearTimeout(captureVideoTimeOutHandle.current);\n\t\t}\n\t\thandleStartCaptureClick();\n\t\tif (allowAudioToPlay.current) {\n\t\t\tcaptureVideoTimeOutHandle.current = setTimeout(async () => {\n\t\t\t\tif (allowAudioToPlay.current) {\n\t\t\t\t\tawait speechService.playAudio(`${voiceOverAssetsPath}SpotOn.mp3`);\n\t\t\t\t\tsetRecordingStarted(false);\n\t\t\t\t}\n\t\t\t}, 15000);\n\t\t}\n\t\tsetFaceDone(true);\n\t\tif (allowAudioToPlay.current) {\n\t\t\tawait speechService.playAudio(`${voiceOverAssetsPath}Spin.mp3`);\n\t\t}\n\t}, [handleStartCaptureClick, allowAudioToPlay, speechService]);\n\n\tuseEffect(() => {\n\t\tpostPoseProcessRef.current = postPoseProcess;\n\t}, [postPoseProcess]);\n\n\tconst handleDataAvailable = useCallback(\n\t\t({ data }: BlobEvent) => {\n\t\t\tconst connected = swan.poseDetection.connected();\n\t\t\tif (data.size > 0 && connected) {\n\t\t\t\tif (!poseListenerRegistered.current) {\n\t\t\t\t\tposeListenerRegistered.current = true;\n\t\t\t\t\tswan.poseDetection.poseStatus(async (data: any) => {\n\t\t\t\t\t\t// The backend is alive and talking — reset the stall watchdog.\n\t\t\t\t\t\tposeWatchdog.pet();\n\t\t\t\t\t\t// Capture EVERY backend pose message BEFORE the audio gate below, so\n\t\t\t\t\t\t// telemetry can distinguish \"BE silent\" from \"BE sending audio-less\n\t\t\t\t\t\t// frames\". The latter would silently stall the scan, since all of the\n\t\t\t\t\t\t// handling below is gated on data.audio being non-empty.\n\t\t\t\t\t\tposthog.capture(`${shopDomain}/pose_instruction_received`, {\n\t\t\t\t\t\t\tscanID: scanIDRef.current,\n\t\t\t\t\t\t\temail,\n\t\t\t\t\t\t\taudio: data?.audio ?? \"\",\n\t\t\t\t\t\t\taudioEmpty: !(data?.audio && data.audio.length > 0),\n\t\t\t\t\t\t\tstatus: data?.status,\n\t\t\t\t\t\t\tsid: data?.sid,\n\t\t\t\t\t\t\tsidMatchesConnection: data?.sid === idRef.current,\n\t\t\t\t\t\t\tcounter: counterRef.current,\n\t\t\t\t\t\t\tconsecutiveFalse: consecutiveFalseRef.current,\n\t\t\t\t\t\t\ttype: \"body_scan_instruction\",\n\t\t\t\t\t\t});\n\t\t\t\t\t\tif (data && data.audio && data.audio.length > 0) {\n\t\t\t\t\t\t\tconst audio = document.querySelector(\"#audioElement\") as HTMLAudioElement | null;\n\n\t\t\t\t\t\t\tif (data.status === true && data.sid === idRef.current) {\n\t\t\t\t\t\t\t\tconsecutiveFalseRef.current = 0;\n\t\t\t\t\t\t\t\tif (counterRef.current < 2) {\n\t\t\t\t\t\t\t\t\tcounterRef.current += 1;\n\t\t\t\t\t\t\t\t\tif (counterRef.current === 1 && !brilliantPlayedRef.current) {\n\t\t\t\t\t\t\t\t\t\tbrilliantPlayedRef.current = true;\n\t\t\t\t\t\t\t\t\t\tclearTimeout(audioTimeoutId);\n\t\t\t\t\t\t\t\t\t\tspeechService.stopAudio();\n\t\t\t\t\t\t\t\t\t\tawait speechService.playAudio(voiceOverAssetsPath + data.audio);\n\t\t\t\t\t\t\t\t\t} else if (counterRef.current === 2 && audio?.paused) {\n\t\t\t\t\t\t\t\t\t\tclearTimeout(audioTimeoutId);\n\t\t\t\t\t\t\t\t\t\tspeechService.playAudio(voiceOverAssetsPath + data.audio);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t// Alignment confirmed — no more guidance expected, disarm watchdog.\n\t\t\t\t\t\t\t\t\tposeWatchdog.stop();\n\t\t\t\t\t\t\t\t\tsetEmptyAudioSource(data.audio);\n\t\t\t\t\t\t\t\t\tclearTimeout(audioTimeoutId);\n\t\t\t\t\t\t\t\t\tswan.poseDetection.disconnect();\n\t\t\t\t\t\t\t\t\tposthog.capture(`${shopDomain}/pose_detection_disconnected`, {\n\t\t\t\t\t\t\t\t\t\tscanID: scanID,\n\t\t\t\t\t\t\t\t\t\temail,\n\t\t\t\t\t\t\t\t\t\tid: idRef.current,\n\t\t\t\t\t\t\t\t\t\treason: \"pose_confirmed\",\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\tposeListenerRegistered.current = false;\n\t\t\t\t\t\t\t\t\tsetTimeout(() => postPoseProcessRef.current(), 1000);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tconsecutiveFalseRef.current += 1;\n\t\t\t\t\t\t\t\tif (consecutiveFalseRef.current >= 3) {\n\t\t\t\t\t\t\t\t\tcounterRef.current = 0;\n\t\t\t\t\t\t\t\t\tbrilliantPlayedRef.current = false;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (audio?.paused && (!lastVideoPlayed || lastVideoPlayed?.audioName !== data.audio)) {\n\t\t\t\t\t\t\t\t\tlastVideoPlayed = {\n\t\t\t\t\t\t\t\t\t\tskipCount: 2,\n\t\t\t\t\t\t\t\t\t\tno_of_times_skipped: 0,\n\t\t\t\t\t\t\t\t\t\taudioName: data.audio,\n\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\tspeechService.playAudio(voiceOverAssetsPath + data.audio);\n\t\t\t\t\t\t\t\t} else if (lastVideoPlayed?.audioName === data.audio && audio?.paused) {\n\t\t\t\t\t\t\t\t\tif (lastVideoPlayed && lastVideoPlayed.no_of_times_skipped >= lastVideoPlayed.skipCount) {\n\t\t\t\t\t\t\t\t\t\tlastVideoPlayed.no_of_times_skipped = 0;\n\t\t\t\t\t\t\t\t\t\tspeechService.playAudio(voiceOverAssetsPath + data.audio);\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tif (lastVideoPlayed) lastVideoPlayed.no_of_times_skipped += 1;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tconst frame = webcamRef?.current ? webcamRef.current.getScreenshot() : null;\n\t\t\t\tif (frame) {\n\t\t\t\t\tframesSentRef.current += 1;\n\t\t\t\t\t// First frame out -> start the stall watchdog; backend messages pet it.\n\t\t\t\t\tif (framesSentRef.current === 1) poseWatchdog.start();\n\t\t\t\t\tswan.poseDetection.videoEmit({\n\t\t\t\t\t\timage: frame,\n\t\t\t\t\t\tscanId: scanIDRef.current,\n\t\t\t\t\t});\n\t\t\t\t\t// Track frame delivery so a \"connected but no frames\" stall is visible in\n\t\t\t\t\t// telemetry. Log the first frame, then every 5th to bound event volume.\n\t\t\t\t\tif (framesSentRef.current === 1 || framesSentRef.current % 5 === 0) {\n\t\t\t\t\t\tposthog.capture(`${shopDomain}/pose_frame_sent`, {\n\t\t\t\t\t\t\tscanID: scanIDRef.current,\n\t\t\t\t\t\t\temail,\n\t\t\t\t\t\t\tsid: idRef.current,\n\t\t\t\t\t\t\tframesSent: framesSentRef.current,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Screenshot came back null/empty -> frames are NOT reaching the BE, which\n\t\t\t\t\t// silently stalls the scan with no pose feedback. Track it (throttled).\n\t\t\t\t\tframesSkippedRef.current += 1;\n\t\t\t\t\tif (framesSkippedRef.current === 1 || framesSkippedRef.current % 5 === 0) {\n\t\t\t\t\t\tposthog.capture(`${shopDomain}/pose_frame_skipped_null`, {\n\t\t\t\t\t\t\tscanID: scanIDRef.current,\n\t\t\t\t\t\t\temail,\n\t\t\t\t\t\t\tframesSkipped: framesSkippedRef.current,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t[webcamRef, scanID, swan, speechService, shopDomain, email],\n\t);\n\n\tconst startSendingVideoFrames = useCallback(async () => {\n\t\tsetIsScanning(true);\n\t\tsetStartGyro(true);\n\t\tonScanStart?.();\n\t\tif (firstScan.current) {\n\t\t\tfirstScan.current = false;\n\t\t\thandleScanTimeCapture({\n\t\t\t\teventName: \"scan started\",\n\t\t\t\tscanID: scanIDRef.current,\n\t\t\t\tstatus: \"success\",\n\t\t\t\temail,\n\t\t\t});\n\t\t}\n\t\tsetScanStartTime(getCurrentTimeInSeconds());\n\t\tsetLoadingCam(true);\n\t\tif (allowAudioToPlay.current) {\n\t\t\tawait speechService.playAudio(`${voiceOverAssetsPath}StartScan.mp3`);\n\t\t}\n\t\tif (allowAudioToPlay.current) {\n\t\t\tawait speechService.playAudio(`${voiceOverAssetsPath}LiftArmsAndHoldAtHip.mp3`);\n\t\t}\n\t\tif (allowAudioToPlay.current) {\n\t\t\tsetIsScanning(false);\n\t\t\tsetRecordingStarted(true);\n\t\t\tsetRestart(true);\n\t\t\tsetShowPause(true);\n\t\t\thandleUserMedia();\n\t\t}\n\t\ttry {\n\t\t\tif (webcamRef && webcamRef.current && webcamRef.current.stream && allowAudioToPlay.current) {\n\t\t\t\t// Capture FPS from stream settings\n\t\t\t\tconst videoTrack = webcamRef.current.stream.getVideoTracks()[0];\n\t\t\t\tif (videoTrack) {\n\t\t\t\t\tconst settings = videoTrack.getSettings();\n\t\t\t\t\trecordedFpsRef.current = settings.frameRate || null;\n\t\t\t\t}\n\n\t\t\t\tconst mediaRecorderOptions: MediaRecorderOptions = {\n\t\t\t\t\tvideoBitsPerSecond: 1_500_000,\n\t\t\t\t};\n\t\t\t\tif (supportedTypes[0]) {\n\t\t\t\t\tmediaRecorderOptions.mimeType = supportedTypes[0];\n\t\t\t\t}\n\t\t\t\tif (allowAudioToPlay.current) {\n\t\t\t\t\tmediaRecorderRef.current = new MediaRecorder(webcamRef.current.stream, mediaRecorderOptions);\n\t\t\t\t}\n\t\t\t\tif (allowAudioToPlay.current && mediaRecorderRef.current) {\n\t\t\t\t\tmediaRecorderRef.current.addEventListener(\"dataavailable\", handleDataAvailable);\n\t\t\t\t}\n\n\t\t\t\tsetEvents([...eventsRef.current, handleDataAvailable]);\n\t\t\t\tif (mediaRecorderRef.current) mediaRecorderRef.current.start(1000);\n\t\t\t\tsetMediaRecorderStopped(false);\n\t\t\t\tif (allowAudioToPlay.current) {\n\t\t\t\t\taudioTimeoutId = setTimeout(playAudio, 2000);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconsole.log(\"error ----------\", err);\n\t\t}\n\t}, [webcamRef, supportedTypes, handleDataAvailable, playAudio, allowAudioToPlay, onScanStart]);\n\n\tuseEffect(() => {\n\t\tstartSendingVideoFramesRef.current = startSendingVideoFrames;\n\t}, [startSendingVideoFrames]);\n\n\tuseEffect(() => {\n\t\tif (shopDomain) {\n\t\t\tsetIsScanLocked(true);\n\t\t\thandleSocket();\n\t\t}\n\t\treturn () => {\n\t\t\t// Capture then reset id so next genuine mount can reconnect.\n\t\t\t// Do NOT reset isConnectingRef here — keeping it true blocks the\n\t\t\t// StrictMode second-mount call while the first connect is still awaiting.\n\t\t\tconst prevId = idRef.current;\n\t\t\tidRef.current = null;\n\t\t\tif (prevId) {\n\t\t\t\tswan.poseDetection.disconnect();\n\t\t\t\tposthog.capture(`${shopDomain}/pose_detection_disconnected`, {\n\t\t\t\t\tscanID: scanIDRef.current,\n\t\t\t\t\temail,\n\t\t\t\t\tid: prevId,\n\t\t\t\t\treason: \"unmount\",\n\t\t\t\t});\n\t\t\t}\n\t\t\tevents.forEach((el: any) => {\n\t\t\t\tmediaRecorderRef?.current?.removeEventListener(\"dataavailable\", el);\n\t\t\t});\n\t\t};\n\t}, [shopDomain]);\n\tuseEffect(() => {\n\t\taudioSourceList.push(audioSource);\n\t}, [audioSource]);\n\n\tuseEffect(() => {\n\t\tsetAudioSourceList([]);\n\t}, [emptyAudioSource]);\n\tuseEffect(() => {\n\t\tconst bypassChecksToBackup = recordedChunks.length && recordedChunks.length > 0;\n\t\tif (!mediaRecorderStopped && bypassChecksToBackup && !recordingStarted && allowAudioToPlay.current && !pause) {\n\t\t\tif (mediaRecorderRef && mediaRecorderRef.current) {\n\t\t\t\tif (!recordingStarted) {\n\t\t\t\t\tmediaRecorderRef.current.stop();\n\t\t\t\t\t// mediaRecorderStopped will be set by the \"stop\" event listener\n\t\t\t\t\t// which fires AFTER all dataavailable events are done\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}, [mediaRecorderStopped, recordingStarted, recordedChunks, pause]);\n\n\tuseEffect(() => {\n\t\tconst bypassChecksToBackup = recordedChunks.length && recordedChunks.length > 0;\n\t\tif (mediaRecorderStopped && bypassChecksToBackup && !recordingStarted) {\n\t\t\tif (mediaRecorderRef && mediaRecorderRef.current && allowAudioToPlay.current && !pause) {\n\t\t\t\tif (!recordingStarted) {\n\t\t\t\t\tconst videoFile = new File(recordedChunks, `${scanID}.${recordingExtension}`, {\n\t\t\t\t\t\ttype: recordingMimeType,\n\t\t\t\t\t});\n\t\t\t\t\tonCaptureComplete?.();\n\t\t\t\t\tsetUploadLoading?.(true);\n\t\t\t\t\tsetScanFailsError(\"\");\n\t\t\t\t\t// Pass FPS captured at recording time\n\t\t\t\t\tuploadScanFile?.(videoFile, recordedFpsRef.current);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}, [mediaRecorderStopped, recordingStarted, recordedChunks, pause, mediaRecorderRef, allowAudioToPlay]);\n\tuseEffect(() => {\n\t\tstartSendingVideoFramesRef.current = startSendingVideoFrames;\n\t}, [startSendingVideoFrames]);\n\n\tuseEffect(() => {\n\t\tconst supported = getSupportedMediaRecorderTypes(preferredVideoTypes, videoTypes);\n\t\tsetSupportedTypes(supported);\n\t}, [preferredVideoTypes]);\n\tuseEffect(() => {\n\t\tresetScan();\n\t}, []);\n\n\treturn (\n\t\t<CameraScanChild\n\t\t\tresetDetector={resetDetector}\n\t\t\tresetScan={resetScan}\n\t\t\tloadingCam={loadingCam}\n\t\t\tonPause={handlePause}\n\t\t\tshowRestart={showRestart}\n\t\t\tpause={pause}\n\t\t\tonReScan={handleRestart}\n\t\t\trecordingStarted={recordingStarted}\n\t\t\tisScanning={isScanning}\n\t\t\tstartSendingVideoFrames={startSendingVideoFrames}\n\t\t\tfaceDone={faceDone}\n\t\t\tstopRecording={stopRecording}\n\t\t\tshowPause={showPause}\n\t\t\twebcamRef={webcamRef}\n\t\t\tonUserMedia={handleUserMedia}\n\t\t\tonUserMediaError={handleUserMediaError}\n\t\t\tvideoConstraints={currentVideoConstraints}\n\t\t\twebcamKey={webcamKey}\n\t\t/>\n\t);\n}\n\nexport default React.memo(ScanningComponent);\n","import { useCallback, useEffect, useRef } from \"react\";\n\ninterface UseWatchdogOptions {\n\t/** How long (ms) to wait without a `pet()` before `onTimeout` fires. */\n\ttimeoutMs: number;\n\t/** Invoked once when the watchdog expires (no `pet()` within `timeoutMs`). */\n\tonTimeout: () => void;\n}\n\nexport interface WatchdogControls {\n\t/** Arm (or re-arm) the countdown. Call when the watched activity begins. */\n\tstart: () => void;\n\t/** Reset the countdown. Call on every progress signal to keep it alive. */\n\tpet: () => void;\n\t/** Disarm the watchdog so no timeout fires until `start()`/`pet()` again. */\n\tstop: () => void;\n}\n\n/**\n * Generic watchdog timer. Fires `onTimeout` if `pet()` is not called within\n * `timeoutMs` of the last `start()`/`pet()`.\n *\n * Use it to detect a stalled pipeline — e.g. video frames streaming out to a\n * server but no responses coming back — and recover gracefully (show a retry,\n * disconnect, log) instead of leaving the UI frozen forever.\n *\n * The pending timer is cleared automatically on unmount. `onTimeout` is read\n * through a ref, so passing a fresh inline callback each render never restarts\n * an already-armed timer.\n */\nfunction useWatchdog({ timeoutMs, onTimeout }: UseWatchdogOptions): WatchdogControls {\n\tconst timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\tconst onTimeoutRef = useRef(onTimeout);\n\n\tuseEffect(() => {\n\t\tonTimeoutRef.current = onTimeout;\n\t}, [onTimeout]);\n\n\tconst stop = useCallback(() => {\n\t\tif (timerRef.current) {\n\t\t\tclearTimeout(timerRef.current);\n\t\t\ttimerRef.current = null;\n\t\t}\n\t}, []);\n\n\tconst arm = useCallback(() => {\n\t\tif (timerRef.current) clearTimeout(timerRef.current);\n\t\ttimerRef.current = setTimeout(() => {\n\t\t\ttimerRef.current = null;\n\t\t\tonTimeoutRef.current();\n\t\t}, timeoutMs);\n\t}, [timeoutMs]);\n\n\t// Clear any pending timer when the consuming component unmounts.\n\tuseEffect(() => stop, [stop]);\n\n\treturn { start: arm, pet: arm, stop };\n}\n\nexport default useWatchdog;\n","/* eslint-disable no-use-before-define */\n\nimport { useEffect, useState, useRef } from \"react\";\nimport { IOSDeviceOrientationEvent } from \"../../customHooks/useGyroSensor\";\nimport LevelScreen from \"./LevelScreen\";\nimport ScanningComponent from \"./ScanningComponent\";\nimport { AngleDetectorProps } from \"../../types/interfaces\";\n\nexport default function AngleDetector({\n scanID,\n userDetails,\n setIsVideoUploaded,\n setScanFailsError,\n setScanStartTime,\n setScanUniqueKey,\n}: Omit<AngleDetectorProps, \"config\">) {\n const [angle, setAngle] = useState(90);\n const [calibrationOffset, setCalibrationOffset] = useState(0);\n const [countdown, setCountdown] = useState<number | null>(null);\n const [isScanning, setIsScanning] = useState(false);\n const [stabilityScore, setStabilityScore] = useState(0);\n const lastAnglesRef = useRef<number[]>([]);\n const [isScanLocked, setIsScanLocked] = useState(false);\n const calibratedAngle = angle - calibrationOffset;\n const isInTargetRange = calibratedAngle >= 80 && calibratedAngle <= 95;\n\n const resetDetector = () => {\n setAngle(90);\n setCalibrationOffset(0);\n setCountdown(null);\n setIsScanning(false);\n setStabilityScore(0);\n setIsScanLocked(false);\n lastAnglesRef.current = [];\n };\n\n useEffect(() => {\n if (typeof window !== \"undefined\" && window.DeviceOrientationEvent) {\n const handleOrientation = (event: DeviceOrientationEvent) => {\n if (event.beta !== null) {\n let newAngle = Math.abs(event.beta);\n if (event.gamma !== null) {\n newAngle *= Math.cos((event.gamma * Math.PI) / 180);\n }\n newAngle = Math.max(0, Math.min(180, newAngle));\n setAngle(newAngle);\n }\n };\n\n const requestPermission = async () => {\n const DeviceOrientation =\n DeviceOrientationEvent as unknown as IOSDeviceOrientationEvent;\n\n if (\n DeviceOrientation &&\n typeof DeviceOrientation.requestPermission === \"function\"\n ) {\n try {\n const permission = await DeviceOrientation.requestPermission();\n if (permission === \"granted\") {\n // wrappedHandler = timeout(handleOrientation, 2000);\n window.addEventListener(\"deviceorientation\", handleOrientation);\n }\n } catch (e) {\n console.error(\"Permission error\", e);\n }\n } else {\n // wrappedHandler = handleOrientation;\n window.addEventListener(\"deviceorientation\", handleOrientation);\n }\n\n return () => {\n if (handleOrientation) {\n window.removeEventListener(\"deviceorientation\", handleOrientation);\n }\n };\n };\n\n document.addEventListener(\"click\", requestPermission, { once: true });\n }\n }, []);\n\n useEffect(() => {\n lastAnglesRef.current = [\n ...lastAnglesRef.current.slice(-4),\n calibratedAngle,\n ];\n\n if (lastAnglesRef.current.length >= 5) {\n const variation =\n Math.max(...lastAnglesRef.current) - Math.min(...lastAnglesRef.current);\n if (isInTargetRange && variation < 2) {\n setStabilityScore((prev) => Math.min(100, prev + 10));\n } else {\n setStabilityScore((prev) => Math.max(0, prev - 20));\n }\n }\n\n if (!isInTargetRange && (countdown !== null || isScanning)) {\n resetCountdown();\n }\n }, [calibratedAngle, isInTargetRange, countdown, isScanning]);\n\n\tuseEffect(() => {\n\t\tif (stabilityScore >= 100 && countdown === null && !isScanning ) {\n\t\t\tstartCountdown();\n\t\t}\n\t\tif (stabilityScore < 50 && countdown !== null) {\n\t\t\tresetCountdown();\n\t\t}\n\t}, [stabilityScore, countdown, isScanning]);\n\n const startCountdown = () => {\n setCountdown(3);\n const timer = setInterval(() => {\n setCountdown((prev) => {\n if (prev === null || prev <= 1) {\n clearInterval(timer);\n setIsScanning(true);\n return null;\n }\n return prev - 1;\n });\n }, 1000);\n };\n\n const resetCountdown = () => {\n setCountdown(null);\n if (!isScanLocked) {\n setIsScanning(false);\n }\n };\n\n\n return (\n <LevelScreen\n angle={calibratedAngle}\n countdown={countdown}\n isScanning={isScanning}\n isInTargetRange={isInTargetRange}\n stabilityScore={stabilityScore}\n >\n {isScanning && (\n <ScanningComponent\n setIsScanLocked={setIsScanLocked}\n resetDetector={resetDetector}\n scanID={scanID}\n userDetails={userDetails}\n setIsVideoUploaded={setIsVideoUploaded}\n setScanFailsError={setScanFailsError}\n setScanStartTime={setScanStartTime}\n setScanUniqueKey={setScanUniqueKey}\n />\n )}\n </LevelScreen>\n );\n}\n","import { useEffect, useState, useRef, useCallback, Dispatch, SetStateAction } from \"react\";\nimport Webcam from \"react-webcam\";\nimport Modal from \"../Modal\";\nimport LoadingScreen from \"../LoadingScreen\";\nimport { checkCameraPermission } from \"../../utils/utils\";\nimport { videoConstraints } from \"../../utils/constants\";\n\n\nfunction CameraPermission({ setShowDrawer, config,loader }: { setShowDrawer?: Dispatch<SetStateAction<boolean>>; config?: any,loader?:string }) {\n\tconst [showDeniedModal, setShowDeniedModal] = useState({\n\t\tdisabled: false,\n\t\tmessage: \"\",\n\t});\n\tconst [loading, setLoading] = useState(true);\n\tconst webcamRef = useRef(null);\n\n\tconst handleCameraPermission = useCallback(async () => {\n\t\tconst permission = await checkCameraPermission();\n\t\tsetShowDeniedModal(permission);\n\t\tsetLoading(false);\n\t}, []);\n\n\tuseEffect(() => {\n\t\thandleCameraPermission();\n\t}, []);\n\n\tif (loading) {\n\t\treturn <LoadingScreen url={loader} loaderType=\"black\" />\n\t}\n\n\tif (showDeniedModal?.disabled) {\n\t\treturn <Modal config={config} message={showDeniedModal?.message} />;\n\t}\n\treturn (\n\t\t<Webcam\n\t\t\taudio={false}\n\t\t\tref={webcamRef}\n\t\t\tscreenshotQuality={1}\n\t\t\tvideoConstraints={videoConstraints}\n\t\t\tmirrored\n\t\t\tonUserMedia={() => setShowDrawer?.(true)}\n\t\t\tscreenshotFormat=\"image/jpeg\"\n\t\t\tstyle={{\n\t\t\t\tposition: \"relative\",\n\t\t\t\ttop: 0,\n\t\t\t\tbottom: 0,\n\t\t\t\tzIndex: 0,\n\t\t\t\twidth: \"100%\",\n\t\t\t\theight: \"100%\",\n\t\t\t\tobjectFit: \"cover\",\n\t\t\t}}\n\t\t/>\n\t);\n}\n\nexport default CameraPermission;\n","\nimport React, { useEffect, useRef } from \"react\";\nimport videojs from \"video.js\";\nimport type Player from \"video.js/dist/types/player\";\nimport { videoPoster } from \"../../utils/constants\";\n\nfunction VideoPlayer({ link, onReady, wrapperClassName = \"[&_video]:rounded-t-[20px] w-full h-full\" }: { link?: string; wrapperClassName?: string; onReady?: (args: Player) => void }) {\n\tconst videoReference = useRef<HTMLVideoElement | null>(null);\n\tconst playerReference = useRef<Player | null>(null);\n\n\tlet videoJsOptions: any = {\n\t\tautoplay: true,\n\t\tcontrols: false,\n\t\tresponsive: true,\n\t\tfluid: true,\n\t\tmuted: true,\n\t\tnavigationUI: \"hide\",\n\t\tpreload: \"metadata\",\n\t\tposter: videoPoster,\n\t};\n\n\tuseEffect(() => {\n\t\tif (!playerReference.current && link && videoReference?.current) {\n\t\t\tconst player = videojs(videoReference.current, {\n\t\t\t\t...videoJsOptions,\n\t\t\t});\n\t\t\tplayer.ready(() => {\n\t\t\t\tplayerReference.current = player;\n\n\t\t\t\tconst updatedOptions = {\n\t\t\t\t\t...videoJsOptions,\n\t\t\t\t\tsources: [{ src: link, type: \"application/x-mpegURL\" }],\n\t\t\t\t};\n\n\t\t\t\tplayer.autoplay(updatedOptions.autoplay);\n\t\t\t\tplayer.src(updatedOptions.sources);\n\n\t\t\t\tonReady?.(player);\n\t\t\t});\n\t\t}\n\t}, [link, videoReference]);\n\n\tuseEffect(() => {\n\t\tconst player = playerReference.current;\n\t\treturn () => {\n\t\t\tif (player && !player.isDisposed()) {\n\t\t\t\tplayer.dispose();\n\t\t\t\tplayerReference.current = null;\n\t\t\t}\n\t\t};\n\t}, [playerReference]);\n\n\treturn (\n\t\t<div className={wrapperClassName}>\n\t\t\t<video ref={videoReference} muted className=\"video-js\" playsInline onDrag={(e) => e.preventDefault()} />\n\t\t</div>\n\t);\n}\nexport default VideoPlayer;\n","import { useContext, useState } from \"react\";\nimport {Dialog} from \"@mui/material\";\nimport { LanguageContext } from \"../../utils/context/languageContext\";\nimport Header from \"../Header\";\nimport VideoPlayer from \"./VideoPlayer\";\nimport { ScanErrorMessageProps } from \"../../types/interfaces\";\nimport { generateUuid, handleErrorMessage } from \"../../utils/utils\";\nimport { GENDER, VIDEO_POSTER_GENDER_BASED } from \"../../utils/constants\";\nimport SpecificButton from \"../../atoms/specificButton/SpecificButton\";\nimport { LanguageKeys } from \"../../utils/languageKeys\";\nimport { X } from \"lucide-react\";\nimport { useConfig } from \"../../utils/context/configContext\";\n\nfunction ScanErrorMessage({ scanFailsError, onNext, gender, setScanUniqueKey, setIsVideoUploaded }: ScanErrorMessageProps) {\n\tconst { translate } = useContext(LanguageContext) || {};\n\tconst resolvedConfig = useConfig();\n\tconst [showModal, setShowModal] = useState(false);\n\n\tconst handleBtnClick = () => {\n\t\tif (onNext) {\n\t\t\tonNext?.();\n\t\t} else {\n\t\t\tsetScanUniqueKey?.(generateUuid());\n\t\t}\n\t\tsetIsVideoUploaded?.(false);\n\t};\n\n\tconst handleShowModal = () => {\n\t\tsetShowModal(!showModal);\n\t};\n\n\n\treturn (\n\t\t<>\n\t\t\t<div data-testid=\"scan-error-message\" className=\"flex common-ui-main flex-col h-full max-w-[28rem] mx-auto w-full rounded-t-[20px] overflow-y-auto\" style={{ background: resolvedConfig?.style?.base?.backgroundColor }}>\n\t\t\t\t<div className=\"w-full max-w-[28rem] mx-auto pt-[1rem] px-[1rem]\">\n\t\t\t\t\t<Header noTitle />\n\t\t\t\t</div>\n\t\t\t\t<div className=\"flex-1\">\n\t\t\t\t\t{scanFailsError && (\n\t\t\t\t\t\t<div className=\"px-[1rem]\">\n\t\t\t\t\t\t\t<h2\n\t\t\t\t\t\t\t\tclassName=\"text-center\"\n\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\tfontFamily: resolvedConfig?.style?.heading?.headingFontFamily || \"SeriouslyNostalgic Fn\",\n\t\t\t\t\t\t\t\t\tfontSize: resolvedConfig?.style?.heading?.headingFontSize || \"32px\",\n\t\t\t\t\t\t\t\t\tcolor: resolvedConfig?.style?.heading?.headingColor || \"#000\",\n\t\t\t\t\t\t\t\t\tfontWeight: resolvedConfig?.style?.heading?.headingFontWeight || \"normal\",\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{translate?.(LanguageKeys.issueWithScan)}\n\t\t\t\t\t\t\t</h2>\n\t\t\t\t\t\t\t<p\n\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\tfontFamily: resolvedConfig?.style?.base?.baseFontFamily || \"Inter, sans-serif\",\n\t\t\t\t\t\t\t\t\tfontSize: resolvedConfig?.style?.base?.baseFontSize || \"16px\",\n\t\t\t\t\t\t\t\t\tcolor: resolvedConfig?.style?.base?.baseTextColor || \"#1E1E1E\",\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{translate?.(LanguageKeys.reason)}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t<p\n\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\tfontFamily: resolvedConfig?.style?.base?.baseFontFamily || \"Inter, sans-serif\",\n\t\t\t\t\t\t\t\t\tfontSize: resolvedConfig?.style?.base?.baseFontSize || \"16px\",\n\t\t\t\t\t\t\t\t\tcolor: resolvedConfig?.style?.base?.baseTextColor || \"#1E1E1E\",\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{handleErrorMessage(scanFailsError)}\n\t\t\t\t\t\t\t</p>\n\n\t\t\t\t\t\t\t<img className=\"my-[0.5rem] aspect-[2/1.4] w-full object-cover\" onClick={handleShowModal} src={VIDEO_POSTER_GENDER_BASED[gender]} alt=\"icon\" />\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t\t{scanFailsError && (\n\t\t\t\t\t<div className=\"p-[1rem] flex gap-[0.5rem]\">\n\t\t\t\t\t\t<SpecificButton data-testid=\"scan-again-btn\" disabled={false} buttonText={translate?.(LanguageKeys.scanAgain)} className=\"!shadow-none\" buttonFunc={handleBtnClick} />\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t\t{showModal && (\n\t\t\t\t<Dialog className=\"w-screen h-screen video-modal\" onClose={handleShowModal} open={showModal}>\n\t\t\t\t\t<div className=\"flex justifyEnd \">\n\t\t\t\t\t\t<span className=\"closeBtn\" onClick={handleShowModal}>\n\t\t\t\t\t\t\t<X className=\"absolute right-[8px] top-[8px] text-white z-[9]\" />\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"aspect-video object-cover rounded-[20px] \">\n\t\t\t\t\t\t<VideoPlayer link={gender ? GENDER[gender].PRE_LINK : GENDER.male.PRE_LINK} wrapperClassName=\"w-screen h-screen fixed top-[0] left-[0]\" />\n\t\t\t\t\t</div>\n\t\t\t\t</Dialog>\n\t\t\t)}\n\t\t</>\n\t);\n}\n\nexport default ScanErrorMessage;\n","import { useCallback, useContext, useEffect, useState } from \"react\";\nimport {Box,Drawer} from \"@mui/material\";\nimport posthog from \"../utils/posthog\";\nimport Header from \"./Header\";\nimport CameraPermission from \"./bodyScan/CameraPermission\";\nimport { SignUpProps } from \"../types/interfaces\";\nimport { CLOTHING_BANNER_SCAN, CLOTHING_CUSTOM_FIT_SCAN, CLOTHING_CUSTOM_SCAN, maleMeasurementProgress, measurementProgress } from \"../utils/constants\";\nimport { GenderType } from \"../utils/enums\";\nimport SpecificButton from \"../atoms/specificButton/SpecificButton\";\nimport { LanguageContext } from \"../utils/context/languageContext\";\nimport { LanguageKeys } from \"../utils/languageKeys\";\n\nfunction SignUp({\n\tscanId,\n\tuserDetails,\n\tconfig,\n\tisVideoUploadedCorrect,\n\tisMeasurementAvailable,\n\tonComplete,\n\tisSuccess,\n\tonCustomScanSuccess,\n\tswan,\n}: SignUpProps) {\n\tconst { gender, shopDomain, heightInCm, deviceFocalLength, userName, email, scanType } = userDetails || {};\n\tconst isCustom = [CLOTHING_CUSTOM_SCAN, CLOTHING_BANNER_SCAN, CLOTHING_CUSTOM_FIT_SCAN].includes(scanType ?? \"\");\n\tconst [showDrawer, setShowDrawer] = useState(true);\n\tconst handlePostHogEvent = () => {\n\t\tposthog.capture(shopDomain??\"\", {\n\t\t\tscanID: scanId,\n\t\t\temail: email,\n\t\t\theight: heightInCm,\n\t\t\tfocalLength: deviceFocalLength,\n\t\t\tclothesFit: \"0\",\n\t\t\tgender: gender,\n\t\t});\n\t};\n\n\tconst handleSignUp = useCallback(async () => {\n\t\ttry {\n\t\t\tif (isCustom) {\n\t\t\t\tawait swan?.auth.addUser({\n\t\t\t\t\tscanId,\n\t\t\t\t\temail,\n\t\t\t\t\tname: userName,\n\t\t\t\t\tgender,\n\t\t\t\t\theight: heightInCm,\n\t\t\t\t});\n\t\t\t\tonCustomScanSuccess?.();\n\t\t\t}\n\t\t\thandlePostHogEvent();\n\t\t} catch (error) {\n\t\t\tconsole.log(error);\n\t\t}\n\t}, [isCustom]);\n\n\tconst { translate } = useContext(LanguageContext) || {};\n\n\tuseEffect(() => {\n\t\tif (isVideoUploadedCorrect || isMeasurementAvailable) {\n\t\t\thandleSignUp();\n\t\t}\n\t}, [isVideoUploadedCorrect, isMeasurementAvailable]);\n\n\t// For custom scans the flow auto-redirects via onCustomScanSuccess once addUser\n\t// resolves, so the manual Next button must never show (otherwise a stray\n\t// \"Continue\" appears during the addUser network window). The drawer's progress\n\t// video + \"measurements being taken\" header already serves as the processing\n\t// state while the redirect is in flight.\n\tconst showNextButton = (isSuccess || isVideoUploadedCorrect || isMeasurementAvailable) && !isCustom;\n\n\treturn (\n\t\t<Box className=\"flex h-full w-full flex-col common-ui-main\">\n\t\t\t<div className=\"h-full w-full flex-col items-center justify-center flex\">\n\t\t\t\t<CameraPermission loader={config?.loader} setShowDrawer={setShowDrawer} />\n\t\t\t\t<Drawer\n\t\t\t\t\topen={showDrawer}\n\t\t\t\t\tonClose={(_, reason) => {\n\t\t\t\t\t\tif (reason === \"backdropClick\") {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}}\n\t\t\t\t\tclassName=\"camera-drawer\"\n\t\t\t\t\tanchor=\"bottom\"\n\t\t\t\t>\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName=\"max-w-[28rem] mx-auto w-full h-full flex text-center flex-col justify-between items-center bg-primary rounded-t-[30px] p-[1rem]\"\n\t\t\t\t\t\tstyle={{ background: config?.style?.base?.backgroundColor }}\n\t\t\t\t\t>\n\t\t\t\t\t\t<div className=\"w-full h-full flex flex-col\">\n\t\t\t\t\t\t\t<Header title={translate?.(LanguageKeys.measurementsBeingTaken)} />\n\t\t\t\t\t\t\t<div className=\"flex items-center justify-center flex-1\">\n\t\t\t\t\t\t\t\t<video preload=\"auto\" className=\"max-h-[calc(100vh-450px)] mx-auto w-full object-contain border-none\" muted loop autoPlay playsInline>\n\t\t\t\t\t\t\t\t\t<source src={gender === GenderType.Male ? maleMeasurementProgress : measurementProgress} type=\"video/mp4\" />\n\t\t\t\t\t\t\t\t</video>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t{showNextButton && <SpecificButton className=\"!w-[180px] mx-auto\" buttonText={translate?.(LanguageKeys.next)} buttonFunc={()=>{onComplete?.()\n\t\t\t\t\t\t\t}} />}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</Drawer>\n\t\t\t</div>\n\t\t</Box>\n\t);\n}\nexport default SignUp;\n","\"use client\";\nimport { useContext } from \"react\";\nimport AngleDetector from \"./AngleDetector\";\nimport CameraPermission from \"./CameraPermission\";\nimport Modal from \"../Modal\";\nimport { Drawer } from \"@mui/material\";\nimport ScanErrorMessage from \"./ScanErrorMessage\";\nimport LoadingScreen from \"../LoadingScreen\";\nimport SignUp from \"../Signup\";\nimport { useConfig } from \"../../utils/context/configContext\";\nimport ParamsContext from \"../../utils/context/paramsContext\";\n\nexport const BodyScanSteps = () => {\n\tconst {\n\t\tuserDetails,\n\t\tonRetry,\n\t\tisError,\n\t\tisSuccess,\n\t\tgender,\n\t\tscanUniqueKey,\n\t\tscanFailsError,\n\t\tsetScanUniqueKey,\n\t\tsetIsVideoUploaded,\n\t\tisMeasurementAvailable,\n\t\tisVideoUploadedCorrect,\n\t\tloading,\n\t\tshowDeniedModal,\n\t\tuploadLoading,\n\t\tsetScanFailsError,\n\t\tsetScanStartTime,\n\t\tresetScan,\n\t\tonCustomScanSuccess,\n\t\tswan,\n\t\tonComplete,\n\t} = useContext(ParamsContext);\n\tconst resolvedConfig = useConfig();\n\n\tif (isError) {\n\t\treturn (\n\t\t\t<div data-scan-state=\"error\">\n\t\t\t\t<CameraPermission />\n\t\t\t\t<Drawer\n\t\t\t\t\tanchor=\"bottom\"\n\t\t\t\t\topen={true}\n\t\t\t\t\tclassName=\"camera-drawer\"\n\t\t\t\t\tonClose={(event, reason) => {\n\t\t\t\t\t\tif (reason === \"backdropClick\") {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<ScanErrorMessage scanFailsError onNext={onRetry} gender={gender} />\n\t\t\t\t</Drawer>\n\t\t\t</div>\n\t\t);\n\t}\n\n\tif (isSuccess) {\n\t\treturn <div data-scan-state=\"success\"><SignUp isSuccess onComplete={onComplete} /></div>;\n\t}\n\n\tif (loading) {\n\t\treturn (\n\t\t\t<div data-scan-state=\"loading\" className=\"flex top-0 !mt-0 left-0 z-[999] absolute justify-center items-center w-full h-full\" style={{ background: resolvedConfig?.style?.base?.backgroundColor }}>\n\t\t\t\t<LoadingScreen url={resolvedConfig?.loader} loaderType=\"black\" />\n\t\t\t</div>\n\t\t);\n\t}\n\tif (showDeniedModal.disabled) {\n\t\treturn <div data-scan-state=\"camera-denied\"><Modal /></div>;\n\t}\n\n\tif (!uploadLoading && !scanFailsError && !isMeasurementAvailable && !isVideoUploadedCorrect) {\n\t\treturn (\n\t\t\t<div data-scan-state=\"scanning\">\n\t\t\t<AngleDetector\n\t\t\t\tscanID={scanUniqueKey}\n\t\t\t\tuserDetails={userDetails}\n\t\t\t\tsetIsVideoUploaded={setIsVideoUploaded}\n\t\t\t\tsetScanFailsError={setScanFailsError}\n\t\t\t\tsetScanStartTime={setScanStartTime}\n\t\t\t\tsetScanUniqueKey={setScanUniqueKey}\n\t\t\t/>\n\t\t\t</div>\n\t\t);\n\t}\n\tif ((uploadLoading || isMeasurementAvailable || isVideoUploadedCorrect) && !scanFailsError) {\n\t\treturn (\n\t\t\t<div data-scan-state=\"processing\">\n\t\t\t<SignUp\n\t\t\t\tscanId={scanUniqueKey}\n\t\t\t\tisMeasurementAvailable={isMeasurementAvailable}\n\t\t\t\tuserDetails={userDetails}\n\t\t\t\tisVideoUploadedCorrect={isVideoUploadedCorrect}\n\t\t\t\tonCustomScanSuccess={onCustomScanSuccess}\n\t\t\t\tswan={swan}\n\t\t\t\tonComplete={onComplete}\n\t\t\t\tconfig={resolvedConfig}\n\t\t\t/>\n\t\t\t</div>\n\t\t);\n\t}\n\treturn (\n\t\t<div data-scan-state=\"error-retry\">\n\t\t\t<CameraPermission />\n\t\t\t<Drawer\n\t\t\t\tanchor=\"bottom\"\n\t\t\t\topen\n\t\t\t\tclassName=\"camera-drawer\"\n\t\t\t\tonClose={(event, reason) => {\n\t\t\t\t\tif (reason === \"backdropClick\") {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t<ScanErrorMessage\n\t\t\t\t\tscanFailsError={scanFailsError}\n\t\t\t\t\tonNext={() => {\n\t\t\t\t\t\tonRetry?.();\n\t\t\t\t\t\tresetScan();\n\t\t\t\t\t}}\n\t\t\t\t\tsetScanUniqueKey={setScanUniqueKey}\n\t\t\t\t\tgender={gender}\n\t\t\t\t\tsetIsVideoUploaded={setIsVideoUploaded}\n\t\t\t\t/>\n\t\t\t</Drawer>\n\t\t</div>\n\t);\n};\n","\"use client\";\nimport { useCallback, useEffect, useMemo, useState } from \"react\";\nimport useGyroSensor from \"../../customHooks/useGyroSensor\";\nimport usePosthogPageview from \"../../customHooks/usePosthogPageview\";\nimport {\n\tcheckCameraPermission,\n\tcreateObjectMetadataArray,\n\tgenerateUuid,\n\tgetCurrentTimeInSeconds,\n\tgetVideoFPS,\n\thandleErrorMessage,\n\thandleScanTimeCapture,\n\thandleWebSocketCapture,\n} from \"../../utils/utils\";\nimport LanguageContextProvider from \"../../utils/context/languageContext\";\nimport { ConfigProvider } from \"../../utils/context/configContext\";\nimport { getSwanService } from \"../../utils/service/swanService\";\nimport ParamsContext from \"../../utils/context/paramsContext\";\nimport MediaContextProvider from \"../../utils/context/mediaContext\";\nimport { BodyScanProps, UserDetails } from \"../../types/interfaces\";\nimport { BodyScanSteps } from \"./BodyScanSteps\";\n\nconst clothesFit = \"0\";\n\nconst BodyScan: React.FC<BodyScanProps> = (props) => {\n\tconst {\n\t\tconfig,\n\t\tonRetry,\n\t\tonScanStart,\n\t\tonCaptureComplete,\n\t\tonUploadStart,\n\t\tonUploadEnd,\n\t\tonMeasurementSocketStart,\n\t\tonMeasurementSocketClose,\n\t\tonIntermediateScanSuccess,\n\t\tonScanError,\n\t\tonScanSuccess,\n\t\tonCustomScanSuccess,\n\t\tonComplete,\n\t\tisError,\n\t\tisSuccess,\n\t} = props;\n\tconst userDetails: UserDetails = {\n\t\temail: \"\",\n\t\tshopDomain: \"\",\n\t\tgender: \"male\",\n\t\theightInCm: 0,\n\t\tscanType: \"\",\n\t\tdeviceFocalLength: 0,\n\t\tdeviceModelName: \"\",\n\t\tsourceTag: \"\",\n\t\tcallbackUrl: null,\n\t\t...(props.userDetails ?? {}),\n\t};\n\tconst token = props.token ?? \"\";\n\tconst { gender, scanType, shopDomain, heightInCm, email, deviceFocalLength, deviceModelName, callbackUrl, sourceTag } = userDetails;\n\tconst swan = useMemo(() => getSwanService(token), [token]);\n\tconst [uploadLoading, setUploadLoading] = useState(false);\n\tconst [isVideoUploadedCorrect, setIsVideoUploadedCorrect] = useState(false);\n\tconst [isMeasurementAvailable, setIsMeasurementAvailable] = useState(false);\n\tconst [showDeniedModal, setShowDeniedModal] = useState({\n\t\tdisabled: false,\n\t\tmessage: \"\",\n\t});\n\tconst [scanFailsError, setScanFailsError] = useState(\"\");\n\tconst [isVideoUploaded, setIsVideoUploaded] = useState(false);\n\tconst [loading, setLoading] = useState(true);\n\tconst [startGyro, setStartGyro] = useState(false);\n\tconst [scanUniqueKey, setScanUniqueKey] = useState(\"\");\n\tconst [scanStartTime, setScanStartTime] = useState<number | null>(getCurrentTimeInSeconds());\n\tconst { gyroData } = useGyroSensor(startGyro);\n\tusePosthogPageview();\n\n\tconst resetScan = useCallback(() => {\n\t\tsetScanUniqueKey(generateUuid());\n\t\tsetScanFailsError(\"\");\n\t\tsetUploadLoading(false);\n\t\tsetIsVideoUploaded(false);\n\t}, []);\n\n\tconst onError = (data: any) => {\n\t\tonScanError?.({ ...data, message: handleErrorMessage(data) });\n\t\tclearScanStartTimeAndScanId();\n\t\tsetIsMeasurementAvailable(false);\n\t\tsetScanFailsError(data);\n\t\tsetIsVideoUploadedCorrect(false);\n\t\thandleScanTimeCapture({\n\t\t\teventName: `${shopDomain}/measurement_failed/fit-view`,\n\t\t\tscanID: scanUniqueKey,\n\t\t\tstatus: \"failed\",\n\t\t\temail,\n\t\t\tmessage: handleErrorMessage(data),\n\t\t});\n\t\tif (scanStartTime)\n\t\t\thandleScanTimeCapture({\n\t\t\t\teventName: `${shopDomain}/scan_completion_time`,\n\t\t\t\tscanID: scanUniqueKey,\n\t\t\t\tstatus: \"failed\",\n\t\t\t\tcompletionTime: getCurrentTimeInSeconds() - scanStartTime,\n\t\t\t\temail,\n\t\t\t});\n\t};\n\n\tconst clearScanStartTimeAndScanId = () => {\n\t\tsetScanStartTime(null);\n\t\tsetScanUniqueKey(\"\");\n\t};\n\n\tconst onSuccess = useCallback(\n\t\tasync (data: any) => {\n\t\t\tif (data && data?.scanStatus === \"success\" && data?.resultType === \"intermediate\" && data?.code === 200) {\n\t\t\t\thandleScanTimeCapture({\n\t\t\t\t\teventName: `${shopDomain}/measurement_success/intermediate`,\n\t\t\t\t\tscanID: scanUniqueKey,\n\t\t\t\t\tstatus: \"success\",\n\t\t\t\t\temail,\n\t\t\t\t});\n\t\t\t\tsetIsVideoUploadedCorrect(true);\n\t\t\t\tonIntermediateScanSuccess?.(data);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tonScanSuccess?.(data);\n\t\t\tsetIsVideoUploadedCorrect(true);\n\t\t\tclearScanStartTimeAndScanId();\n\t\t\tconst scanIdToSet = scanUniqueKey;\n\t\t\tsetIsMeasurementAvailable(true);\n\t\t\thandleScanTimeCapture({\n\t\t\t\teventName: `${shopDomain}/measurement_success/fit-view`,\n\t\t\t\tscanID: scanIdToSet,\n\t\t\t\tstatus: \"success\",\n\t\t\t\temail,\n\t\t\t});\n\t\t\tif (scanStartTime)\n\t\t\t\thandleScanTimeCapture({\n\t\t\t\t\teventName: `${shopDomain}/scan_completion_time`,\n\t\t\t\t\tscanID: scanIdToSet,\n\t\t\t\t\tstatus: \"success\",\n\t\t\t\t\tcompletionTime: getCurrentTimeInSeconds() - scanStartTime,\n\t\t\t\t\temail,\n\t\t\t\t});\n\t\t},\n\t\t[scanType, shopDomain, scanUniqueKey],\n\t);\n\n\tconst handleMeasurementRecommendation = (scanId?: string) => {\n\t\tsetIsMeasurementAvailable(false);\n\t\tsetScanFailsError(\"\");\n\t\tsetIsVideoUploadedCorrect(false);\n\t\tswan.measurement.handleMeasurementSocket({\n\t\t\tscanId: scanId || scanUniqueKey,\n\t\t\tonPreopen: () => {\n\t\t\t\thandleWebSocketCapture({\n\t\t\t\t\teventName: `${shopDomain}/webSocket`,\n\t\t\t\t\tscanID: scanUniqueKey,\n\t\t\t\t\tconnection: \"pre_open\",\n\t\t\t\t\ttype: \"measurement_recommendation\",\n\t\t\t\t\temail,\n\t\t\t\t});\n\t\t\t},\n\t\t\tonOpen: () => {\n\t\t\t\tonMeasurementSocketStart?.();\n\t\t\t\thandleWebSocketCapture({\n\t\t\t\t\teventName: `${shopDomain}/webSocket`,\n\t\t\t\t\tscanID: scanUniqueKey,\n\t\t\t\t\tconnection: \"open\",\n\t\t\t\t\ttype: \"measurement_recommendation\",\n\t\t\t\t\temail,\n\t\t\t\t});\n\t\t\t},\n\t\t\tonClose: () => {\n\t\t\t\tonMeasurementSocketClose?.();\n\t\t\t\thandleWebSocketCapture({\n\t\t\t\t\teventName: `${shopDomain}/webSocket`,\n\t\t\t\t\tscanID: scanUniqueKey,\n\t\t\t\t\tconnection: \"close\",\n\t\t\t\t\ttype: \"measurement_recommendation\",\n\t\t\t\t\temail,\n\t\t\t\t});\n\t\t\t},\n\t\t\tonError: (err) => {\n\t\t\t\tonError(err);\n\t\t\t\thandleWebSocketCapture({\n\t\t\t\t\teventName: `${shopDomain}/webSocket`,\n\t\t\t\t\tscanID: scanUniqueKey,\n\t\t\t\t\tconnection: \"error\",\n\t\t\t\t\ttype: \"measurement_recommendation\",\n\t\t\t\t\temail,\n\t\t\t\t});\n\t\t\t},\n\t\t\tonSuccess: (data) => {\n\t\t\t\thandleWebSocketCapture({\n\t\t\t\t\teventName: `${shopDomain}/webSocket`,\n\t\t\t\t\tscanID: scanUniqueKey,\n\t\t\t\t\tconnection: \"success\",\n\t\t\t\t\ttype: \"measurement_recommendation\",\n\t\t\t\t\temail,\n\t\t\t\t});\n\t\t\t\tonSuccess(data);\n\t\t\t},\n\t\t});\n\t};\n\n\tconst uploadScanFile = useCallback(\n\t\tasync (file: File, recordedFps?: number | null) => {\n\t\t\tconst fileSizeInMB = Number((file.size / (1024 * 1024)).toFixed(2));\n\t\t\t// Use FPS captured at recording time, fallback to calculation if not available\n\t\t\tconst videoFps = recordedFps !== undefined && recordedFps !== null ? recordedFps : await getVideoFPS(file);\n\t\t\tconst arrayMetaData = createObjectMetadataArray({\n\t\t\t\tgender,\n\t\t\t\tfocal_length: `${deviceFocalLength}`,\n\t\t\t\theight: `${heightInCm}`,\n\t\t\t\tcustomer_store_url: shopDomain,\n\t\t\t\tclothes_fit: clothesFit,\n\t\t\t\tscan_type: scanType,\n\t\t\t\tcallback_url: callbackUrl,\n\t\t\t\tsource_tag: sourceTag,\n\t\t\t});\n\t\t\thandleScanTimeCapture({\n\t\t\t\teventName: `${shopDomain}/body_scan_meta_data`,\n\t\t\t\tscanID: scanUniqueKey,\n\t\t\t\temail,\n\t\t\t\tdata: JSON.stringify([...arrayMetaData, { fileSizeInMB, video_fps: videoFps || 0 }]),\n\t\t\t});\n\t\t\tonUploadStart?.();\n\t\t\ttry {\n\t\t\t\tawait swan.fileUpload.uploadFileFrontend({\n\t\t\t\t\tfile,\n\t\t\t\t\tarrayMetaData,\n\t\t\t\t\tscanId: scanUniqueKey,\n\t\t\t\t\temail,\n\t\t\t\t});\n\t\t\t\tawait swan.fileUpload.setDeviceInfo({\n\t\t\t\t\tmodel: deviceModelName,\n\t\t\t\t\tdetection: \"manual\",\n\t\t\t\t\tgyro: gyroData,\n\t\t\t\t\tscanId: scanUniqueKey,\n\t\t\t\t});\n\t\t\t\tconsole.log(\"video successfully uploaded\");\n\t\t\t\tsetIsVideoUploaded(false);\n\t\t\t\thandleScanTimeCapture({\n\t\t\t\t\teventName: `${shopDomain}/scan_success`,\n\t\t\t\t\tscanID: scanUniqueKey,\n\t\t\t\t\tstatus: \"success\",\n\t\t\t\t\temail,\n\t\t\t\t\tdata: JSON.stringify(arrayMetaData),\n\t\t\t\t});\n\t\t\t\thandleScanTimeCapture({\n\t\t\t\t\teventName: \"scan finished\",\n\t\t\t\t\tscanID: scanUniqueKey,\n\t\t\t\t\tstatus: \"success\",\n\t\t\t\t\temail,\n\t\t\t\t});\n\t\t\t\tsetScanStartTime(getCurrentTimeInSeconds());\n\t\t\t\tsetTimeout(() => {\n\t\t\t\t\tsetIsVideoUploaded(true);\n\t\t\t\t}, 3000);\n\t\t\t} catch (error:any) {\n\t\t\t\thandleScanTimeCapture({\n\t\t\t\t\teventName: \"scan finished\",\n\t\t\t\t\tscanID: scanUniqueKey,\n\t\t\t\t\tstatus: \"failed\",\n\t\t\t\t\temail,\n\t\t\t\t\tmessage: handleErrorMessage(error),\n\t\t\t\t});\n\t\t\t\thandleScanTimeCapture({\n\t\t\t\t\teventName: `${shopDomain}/scan_failed`,\n\t\t\t\t\tscanID: scanUniqueKey,\n\t\t\t\t\tstatus: \"failed\",\n\t\t\t\t\temail,\n\t\t\t\t\tmessage: handleErrorMessage(error),\n\t\t\t\t\tdata: JSON.stringify(arrayMetaData),\n\t\t\t\t});\n\t\t\t\tsetScanFailsError(handleErrorMessage(error));\n\t\t\t\tsetIsVideoUploaded(false);\n\t\t\t\tonScanError?.({...error, message: handleErrorMessage(error) });\n\t\t\t\tclearScanStartTimeAndScanId();\n\t\t\t} finally {\n\t\t\t\tsetStartGyro(false);\n\t\t\t\tonUploadEnd?.();\n\t\t\t}\n\t\t},\n\t\t[scanUniqueKey, gyroData, onUploadStart, onUploadEnd],\n\t);\n\t// const checkMeasurementStatus = useCallback(\n\t// \tasync (scanId: string) => {\n\t// \t\ttry {\n\t// \t\t\tconst res = await swan.measurement.getMeasurementResult(scanId);\n\t// \t\t\tconst isMeasured = res?.data?.isMeasured;\n\t// \t\t\tconst tempScanStartTime = scanStartTime || getCurrentTimeInSeconds();\n\t// \t\t\tconst currentTimeInSeconds = getCurrentTimeInSeconds();\n\t// \t\t\tconst fiveMinutesInSeconds = 5 * 60;\n\t// \t\t\tsetLoading(false);\n\t// \t\t\tif (isMeasured === false) {\n\t// \t\t\t\tclearScanStartTimeAndScanId();\n\t// \t\t\t\tsetScanUniqueKey(generateUuid());\n\t// \t\t\t\treturn;\n\t// \t\t\t}\n\t// \t\t\tsetUploadLoading(true);\n\t// \t\t\tif (isMeasured === true) {\n\t// \t\t\t\tawait onSuccess(null);\n\t// \t\t\t} else if (isMeasured === null && tempScanStartTime > currentTimeInSeconds + fiveMinutesInSeconds) {\n\t// \t\t\t\tonError(res?.data?.error);\n\t// \t\t\t} else {\n\t// \t\t\t\thandleMeasurementRecommendation(scanId);\n\t// \t\t\t}\n\t// \t\t} catch (error) {\n\t// \t\t\tconsole.log(error);\n\t// \t\t\tclearScanStartTimeAndScanId();\n\t// \t\t\tsetScanUniqueKey(generateUuid());\n\t// \t\t\tsetUploadLoading(false);\n\t// \t\t}\n\t// \t},\n\t// \t[onError, onSuccess, setScanUniqueKey],\n\t// );\n\n\tconst handleCameraPermission = useCallback(async () => {\n\t\tif (+heightInCm < 152.4 || +heightInCm > 213.36) {\n\t\t\tonScanError?.({\n\t\t\t\tmessage: \"Height must be between 152.4cm (5ft) and 213.36cm (7ft)\",\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tconst permission = await checkCameraPermission();\n\t\tif (permission.disabled) {\n\t\t\thandleScanTimeCapture({\n\t\t\t\teventName: `${shopDomain}/camera_activation`,\n\t\t\t\tscanID: scanUniqueKey,\n\t\t\t\tstatus: \"failed\",\n\t\t\t\temail,\n\t\t\t});\n\t\t\tsetLoading(false);\n\t\t} else {\n\t\t\t// const scanId = scanUniqueKey;\n\t\t\t// if (scanId) {\n\t\t\t// \tcheckMeasurementStatus(scanId);\n\t\t\t// } else {\n\t\t\t// \tsetLoading(false);\n\t\t\t// }\n\t\t\tsetLoading(false);\n\t\t\thandleScanTimeCapture({\n\t\t\t\teventName: `${shopDomain}/camera_activation`,\n\t\t\t\tscanID: scanUniqueKey,\n\t\t\t\tstatus: \"success\",\n\t\t\t\temail,\n\t\t\t});\n\t\t}\n\t\tsetShowDeniedModal(permission);\n\t\tsetScanFailsError(\"\");\n\t\tsetIsVideoUploaded(false);\n\t}, [email, scanUniqueKey, setScanUniqueKey, shopDomain]);\n\n\tuseEffect(() => {\n\t\tif (isError || isSuccess) return;\n\t\tif (shopDomain) {\n\t\t\thandleCameraPermission();\n\t\t}\n\t}, [shopDomain, heightInCm, isError, isSuccess]);\n\n\tuseEffect(() => {\n\t\tif (isError || isSuccess) return;\n\t\tif (isVideoUploaded && shopDomain && scanUniqueKey) {\n\t\t\thandleMeasurementRecommendation();\n\t\t}\n\t}, [isVideoUploaded, shopDomain, scanUniqueKey, isError, isSuccess]);\n\treturn (\n\t\t<div data-testid=\"body-scan-root\">\n\t\t<LanguageContextProvider>\n\t\t\t<ConfigProvider config={config}>\n\t\t\t\t<MediaContextProvider>\n\t\t\t\t\t<ParamsContext.Provider\n\t\t\t\t\t\tvalue={{\n\t\t\t\t\t\t\tuserDetails,\n\t\t\t\t\t\t\tonRetry,\n\t\t\t\t\t\t\tonScanError,\n\t\t\t\t\t\t\tisError,\n\t\t\t\t\t\t\tisSuccess,\n\t\t\t\t\t\t\tonScanSuccess,\n\t\t\t\t\t\t\tgender,\n\t\t\t\t\t\t\tscanUniqueKey,\n\t\t\t\t\t\t\tscanFailsError,\n\t\t\t\t\t\t\tsetScanUniqueKey,\n\t\t\t\t\t\t\tsetIsVideoUploaded,\n\t\t\t\t\t\t\tisMeasurementAvailable,\n\t\t\t\t\t\t\tisVideoUploadedCorrect,\n\t\t\t\t\t\t\tloading,\n\t\t\t\t\t\t\tshowDeniedModal,\n\t\t\t\t\t\t\tuploadLoading,\n\t\t\t\t\t\t\tsetStartGyro,\n\t\t\t\t\t\t\tuploadScanFile,\n\t\t\t\t\t\t\tsetUploadLoading,\n\t\t\t\t\t\t\tresetScan,\n\t\t\t\t\t\t\tsetScanFailsError,\n\t\t\t\t\t\t\tsetScanStartTime,\n\t\t\t\t\t\t\tonScanStart,\n\t\t\t\t\t\t\tonCaptureComplete,\n\t\t\t\t\t\t\tonCustomScanSuccess,\n\t\t\t\t\t\t\tonComplete,\n\t\t\t\t\t\t\tswan,\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t<BodyScanSteps />\n\t\t\t\t\t</ParamsContext.Provider>\n\t\t\t\t</MediaContextProvider>\n\t\t\t</ConfigProvider>\n\t\t</LanguageContextProvider>\n\t\t</div>\n\t);\n};\n\n// SSR mount-gate: the camera/scan tree is browser-only (touches window/document\n// during render and uses the webcam). This thin wrapper renders a neutral\n// placeholder on the server and the first client render, then mounts the real\n// component only after the effect fires in the browser. This lets consumers\n// import BodyScan into a server-rendered app (Next/Remix) without a crash or a\n// dynamic(ssr:false) wrapper. None of BodyScanInner's hooks run on the server.\nconst BodyScanClientGate: React.FC<BodyScanProps> = (props) => {\n\tconst [mounted, setMounted] = useState(false);\n\tuseEffect(() => {\n\t\tsetMounted(true);\n\t}, []);\n\n\tif (!mounted) {\n\t\treturn <div data-testid=\"body-scan-root\" />;\n\t}\n\n\treturn <BodyScan {...props} />;\n};\n\nexport { BodyScanClientGate as BodyScan };\n","import { useCallback, useEffect, useState } from \"react\";\n\ninterface GyroDataItem {\n alpha?: string;\n beta?: string;\n gamma?: string;\n timestamp?: string;\n}\n\nexport interface IOSDeviceOrientationEvent extends DeviceOrientationEvent {\n requestPermission?: () => Promise<PermissionState>;\n}\n\nfunction useGyroSensor(startGyro: boolean) {\n const [gyroData, setGyroData] = useState<GyroDataItem[]>([]);\n const [permissionGranted, setPermissionGranted] = useState(false);\n\n const handleOrientation = useCallback((event: DeviceOrientationEvent) => {\n try {\n const { alpha, beta, gamma } = event;\n setGyroData((prev: GyroDataItem[]) => [\n ...prev,\n {\n alpha: alpha?.toString() || undefined,\n beta: beta?.toString() || undefined,\n gamma: gamma?.toString() || undefined,\n timestamp: new Date().toISOString(),\n },\n ]);\n } catch (error) {\n console.log(error);\n }\n }, []);\n\n const requestPermission = useCallback(async () => {\n const DeviceOrientation =\n DeviceOrientationEvent as unknown as IOSDeviceOrientationEvent;\n\n if (\n typeof DeviceOrientation !== \"undefined\" &&\n typeof DeviceOrientation.requestPermission === \"function\"\n ) {\n try {\n const response = await DeviceOrientation.requestPermission();\n if (response === \"granted\") {\n setPermissionGranted(true);\n } else {\n console.warn(\"Device orientation permission denied.\");\n }\n } catch (error) {\n console.error(\"Error requesting device orientation permission:\", error);\n }\n } else {\n // Permission not required on non-iOS devices\n setPermissionGranted(true);\n }\n }, []);\n\n useEffect(() => {\n if (startGyro && permissionGranted) {\n window.addEventListener(\"deviceorientation\", handleOrientation);\n } else {\n setGyroData([]);\n }\n return () => {\n window.removeEventListener(\"deviceorientation\", handleOrientation);\n };\n }, [startGyro, permissionGranted, handleOrientation]);\n\n useEffect(() => {\n if (startGyro) {\n requestPermission();\n }\n }, [startGyro, requestPermission]);\n\n return { gyroData };\n}\n\nexport default useGyroSensor;\n"],"names":["MEDIA_TYPES","MediaContext","createContext","undefined","MediaContextProvider","children","size","setSize","useState","window","innerWidth","innerHeight","clearInputs","setClearInputs","updateSize","useEffect","useLayoutEffect","addEventListener","removeEventListener","media","mediaDetails","useMemo","_jsx","Provider","value","SwitchIcon$1","React","memo","_jsxs","width","height","viewBox","fill","xmlns","d","stroke","strokeWidth","strokeLinecap","strokeLinejoin","LevelScreen$1","angle","countdown","isScanning","isInTargetRange","stabilityScore","translate","useContext","LanguageContext","config","useConfig","getProximityToTarget","useCallback","Math","max","min","backgroundColor","style","angleDetector","successAngleBackground","proximity","successAngleLowBackground","round","handelTextColour","isLight","successAngleTextLightColor","successAngleTextDarkColor","getTextColor","handelColourCode","getColorCode","cd","fixVh","document","documentElement","setProperty","className","Header","noTitle","border","fontFamily","base","baseFontFamily","color","cn","transform","transition","SwitchIcon","LanguageKeys","startLevelCheck","leavePhone","placePhoneUpright","CameraScanChild","resetScan","loadingCam","onUserMedia","onPause","pause","onReScan","recordingStarted","startSendingVideoFrames","showPause","webcamRef","onUserMediaError","resetDetector","videoConstraints","webcamKey","faceDone","onClick","X","Webcam","audio","ref","screenshotQuality","mirrored","screenshotFormat","position","top","bottom","zIndex","objectFit","SpecificButton","prefix","pauseIcon","buttonText","buttonFunc","btnSecondary","restart","startScan","disabled","id","crossOrigin","preload","src","voiceOverAssetsPath","globalLoadingPromise","globalDetector","globalPoseLib","audioTimeoutId","lastVideoPlayed","ScanningComponent$1","setIsScanLocked","scanID","setIsVideoUploaded","setScanFailsError","setScanStartTime","setScanUniqueKey","userDetails","email","shopDomain","gender","heightInCm","useRef","mediaRecorderRef","recordedFpsRef","recordedChunks","setRecordedChunks","setLoadingCam","setRecordingStarted","setFaceDone","mediaRecorderStopped","setMediaRecorderStopped","setIsScanning","captureVideoTimeOutHandle","audioSource","setAudioSource","audioSourceList","setAudioSourceList","emptyAudioSource","setEmptyAudioSource","startAgain","setStartAgain","showRestart","setRestart","setPause","events","setEvents","setShowPause","allowAudioToPlay","poseDetector","consecutiveFronts","setConsecutiveFronts","spinPhase","setSpinPhase","isLoaded","setIsLoaded","spinPhaseRef","consecutiveFrontsRef","mounted","async","loadDependenciesGlobal","navigator","console","log","poseLib","tfjsCore","tfjsBackend","Promise","all","resolve","then","require","import","setBackend","ready","modelConfig","runtime","modelType","solutionPath","createDetector","SupportedModels","BlazePose","mediapipeError","warn","error","isFrontFrame","body","length","filter","p","current","success","s","callback","video","readyState","poses","estimatePoses","flipHorizontal","keypoints","slice","map","kp","x","y","score","prev","err","retryLoading","useTensorFlow","firstScan","poseStoppedRef","counterRef","consecutiveFalseRef","framesSentRef","framesSkippedRef","poseListenerRegistered","isConnectingRef","brilliantPlayedRef","postPoseProcessRef","startSendingVideoFramesRef","idRef","supportedTypes","setSupportedTypes","setWebcamKey","currentVideoConstraints","setCurrentVideoConstraints","videoConstraintsExact","setStartGyro","uploadScanFile","setUploadLoading","swan","onScanStart","onCaptureComplete","ParamsContext","scanIDRef","eventsRef","poseWatchdog","timeoutMs","onTimeout","timerRef","onTimeoutRef","stop","clearTimeout","arm","setTimeout","start","pet","useWatchdog","posthog","capture","framesSent","generateUuid","speechService","stopAudio","forEach","el","stream","handleUserMedia","handleUserMediaError","videoConstraintsFallback","handlePause","poseDetection","disconnect","reason","preferredVideoTypes","getPreferredMediaRecorderTypes","recordingMimeType","getRecordingMimeType","recordingExtension","getRecordingExtension","stopRecording","playAudio","handleSocket","connect","String","handleSpinDataAvailable","data","concat","handleScanTimeCapture","eventName","message","lastAudio","handleStartCaptureClick","videoTrack","getVideoTracks","settings","getSettings","frameRate","mediaRecorderOptions","videoBitsPerSecond","mimeType","MediaRecorder","e","postPoseProcess","handleDataAvailable","connected","poseStatus","audioEmpty","status","sid","sidMatchesConnection","counter","consecutiveFalse","type","querySelector","paused","audioName","no_of_times_skipped","skipCount","frame","getScreenshot","videoEmit","image","scanId","framesSkipped","getCurrentTimeInSeconds","prevId","push","bypassChecksToBackup","videoFile","File","supported","getSupportedMediaRecorderTypes","videoTypes","rescanSupportCaptureEvent","AngleDetector","setAngle","calibrationOffset","setCalibrationOffset","setCountdown","setStabilityScore","lastAnglesRef","isScanLocked","calibratedAngle","DeviceOrientationEvent","handleOrientation","event","beta","newAngle","abs","gamma","cos","PI","requestPermission","DeviceOrientation","once","variation","resetCountdown","startCountdown","timer","setInterval","clearInterval","LevelScreen","ScanningComponent","CameraPermission","setShowDrawer","loader","showDeniedModal","setShowDeniedModal","loading","setLoading","handleCameraPermission","permission","checkCameraPermission","LoadingScreen","url","loaderType","Modal","VideoPlayer","link","onReady","wrapperClassName","videoReference","playerReference","videoJsOptions","autoplay","controls","responsive","fluid","muted","navigationUI","poster","videoPoster","player","videojs","updatedOptions","sources","isDisposed","dispose","playsInline","onDrag","preventDefault","ScanErrorMessage","scanFailsError","onNext","resolvedConfig","showModal","setShowModal","handleShowModal","_Fragment","background","heading","headingFontFamily","fontSize","headingFontSize","headingColor","fontWeight","headingFontWeight","issueWithScan","baseFontSize","baseTextColor","handleErrorMessage","VIDEO_POSTER_GENDER_BASED","alt","scanAgain","Dialog","onClose","open","GENDER","PRE_LINK","male","SignUp","isVideoUploadedCorrect","isMeasurementAvailable","onComplete","isSuccess","onCustomScanSuccess","deviceFocalLength","userName","scanType","isCustom","CLOTHING_CUSTOM_SCAN","CLOTHING_BANNER_SCAN","CLOTHING_CUSTOM_FIT_SCAN","includes","showDrawer","handleSignUp","auth","addUser","name","focalLength","clothesFit","showNextButton","Box","Drawer","_","anchor","title","measurementsBeingTaken","loop","autoPlay","GenderType","Male","maleMeasurementProgress","measurementProgress","next","BodyScanSteps","onRetry","isError","scanUniqueKey","uploadLoading","BodyScan","props","onUploadStart","onUploadEnd","onMeasurementSocketStart","onMeasurementSocketClose","onIntermediateScanSuccess","onScanError","onScanSuccess","deviceModelName","sourceTag","callbackUrl","token","getSwanService","setIsVideoUploadedCorrect","setIsMeasurementAvailable","isVideoUploaded","startGyro","scanStartTime","gyroData","setGyroData","permissionGranted","setPermissionGranted","alpha","toString","timestamp","Date","toISOString","useGyroSensor","usePosthogPageview","clearScanStartTimeAndScanId","onSuccess","scanStatus","resultType","code","scanIdToSet","completionTime","handleMeasurementRecommendation","measurement","handleMeasurementSocket","onPreopen","handleWebSocketCapture","connection","onOpen","onError","file","recordedFps","fileSizeInMB","Number","toFixed","videoFps","getVideoFPS","arrayMetaData","createObjectMetadataArray","focal_length","customer_store_url","clothes_fit","scan_type","callback_url","source_tag","JSON","stringify","video_fps","fileUpload","uploadFileFrontend","setDeviceInfo","model","detection","gyro","LanguageContextProvider","ConfigProvider","setMounted"],"mappings":"2NAYO,MAAMA,EACF,UADEA,EAEN,MAFMA,EAGH,SAWGC,EAAeC,EAAAA,mBAC1BC,GAGF,SAASC,GAAqBC,SAAEA,IAC9B,MAAOC,EAAMC,GAAWC,EAAAA,SAAS,CAACC,QAAQC,WAAYD,QAAQE,eACvDC,EAAaC,GAAkBL,EAAAA,UAAS,GAEzCM,EAAa,KACjBP,EAAQ,CAACE,QAAQC,WAAYD,QAAQE,eAEvCI,EAAAA,UAAU,KACRD,KACC,IACHE,EAAAA,gBAAgB,KACdP,OAAOQ,iBAAiB,SAAUH,GAC3B,IAAML,OAAOS,oBAAoB,SAAUJ,IACjD,CAACA,IACJ,IAAIK,EAAQnB,EACRM,EAAK,GAAK,KAAOA,EAAK,GAAK,OAC7Ba,EAAQnB,GAENM,EAAK,GAAK,MACZa,EAAQnB,GAGV,MAAMoB,EAAeC,EAAAA,QACnB,KAAA,CACEf,OACAC,UACAK,cACAC,iBACAM,UAEF,CAACb,EAAMM,EAAaO,IAGtB,OACEG,EAAAA,IAACrB,EAAasB,SAAQ,CAACC,MAAOJ,EAAYf,SACvCA,GAGP,CClBA,IAAAoB,EAAeC,EAAMC,KAhDrB,UAAoBrB,KAAEA,EAAO,KAC3B,OACEsB,EAAAA,KAAA,MAAA,CACEC,MAAOvB,EACPwB,OAAQxB,EACRyB,QAAQ,YACRC,KAAK,OACLC,MAAM,6BAA4B5B,SAAA,CAElCiB,EAAAA,IAAA,OAAA,CACEY,EAAE,mHACFC,OAAO,QACPC,YAAY,IACZC,cAAc,QACdC,eAAe,UAEjBhB,EAAAA,IAAA,OAAA,CACEY,EAAE,0NACFC,OAAO,eACPC,YAAY,IACZC,cAAc,QACdC,eAAe,UAEjBhB,EAAAA,IAAA,OAAA,CACEY,EAAE,kOACFC,OAAO,eACPC,YAAY,IACZC,cAAc,QACdC,eAAe,UAEjBhB,MAAA,OAAA,CACEY,EAAE,kOACFC,OAAO,eACPC,YAAY,IACZC,cAAc,QACdC,eAAe,UAEjBhB,EAAAA,IAAA,OAAA,CACEY,EAAE,6kBACFC,OAAO,eACPC,YAAY,IACZC,cAAc,QACdC,eAAe,YAIvB,GCyJA,IAAAC,EAAeb,EAAMC,KA9LrB,UAAqBa,MAAEA,EAAKC,UAAEA,EAASC,WAAEA,EAAUC,gBAAEA,EAAeC,eAAEA,EAAcvC,SAAEA,IACrF,MAAMwC,UAAEA,GAAcC,aAAWC,EAAAA,kBAAoB,CAAA,EAC/CC,EAASC,EAAAA,YAETC,EAAuBC,EAAAA,YAAY,IACpCX,EAAQ,GAAWY,KAAKC,IAAI,EAAGD,KAAKE,IAAI,IAAoB,IAAdd,EAAQ,MACtDA,EAAQ,GAAWY,KAAKC,IAAI,EAAGD,KAAKE,IAAI,IAAqB,IAAf,IAAMd,KACjD,IACL,CAACA,IAEEe,EAAkBlC,EAAAA,QAAQ,KAC/B,GAAIqB,EAAY,OAAOM,GAAQQ,OAAOC,eAAeC,wBAA0B,UAE/E,MAAMC,EAAYT,IAClB,GAAkB,IAAdS,EAAiB,OAAOX,GAAQQ,OAAOC,eAAeG,2BAA6B,UACvF,GAAkB,MAAdD,EAAmB,OAAOX,GAAQQ,OAAOC,eAAeC,wBAA0B,UAKpF,MAAO,OAHGN,KAAKS,MAAM,IAAqBF,EAAY,IAA3B,SACjBP,KAAKS,MAAM,IAAoBF,EAAY,IAA1B,SACjBP,KAAKS,MAAM,IAAqBF,EAAY,IAA3B,OAE1B,CAACjB,EAAYQ,IAEXY,EAAmBX,EAAAA,YAAY,CAACY,EAAkBJ,IACnDA,EAAY,GACRI,EAAU,SAASf,GAAQQ,OAAOC,eAAeO,iCAAmC,SAAShB,GAAQQ,OAAOC,eAAeO,8BAE5HD,EAAU,SAASf,GAAQQ,OAAOC,eAAeO,iCAAmC,SAAShB,GAAQQ,OAAOC,eAAeQ,gCAChI,IAEGC,EAAef,EAAAA,YACpB,CAACY,GAAU,KACV,MAAMJ,EAAYT,IAClB,OAAIR,EAAmB,SAASM,GAAQQ,OAAOC,eAAeO,8BACvDF,EAAiBC,EAASJ,IAElC,CAACjB,EAAYQ,IAGRiB,EAAmBhB,EAAAA,YAAY,CAACY,EAAkBJ,IACnDA,EAAY,IAGTI,EAFWf,GAAQQ,OAAOC,eAAeO,2BAE4BhB,GAAQQ,OAAOC,eAAeQ,0BACxG,IACGG,EAAejB,EAAAA,YACpB,CAACY,GAAU,KACV,MAAMJ,EAAYT,IAClB,OAAIR,EAAmBM,GAAQQ,OAAOC,eAAeO,2BAC9CG,EAAiBJ,EAASJ,IAElC,CAACjB,EAAYQ,IAERmB,EAAK5B,EACX,SAAS6B,IACRC,SAASC,gBAAgBhB,MAAMiB,YAAY,YAAahE,OAAOE,YAAc,KAC9E,CAGA,OAFA2D,IACA7D,OAAOQ,iBAAiB,SAAUqD,GAEjC1C,EAAAA,KAAA,MAAA,CACC8C,UAAU,qKACVlB,MAAO,CAAED,mBAAiB,cACd,eAAc,mBACRb,EAAa,SAAWC,EAAkB,SAAW,YAAWtC,SAAA,CAElFiB,EAAAA,IAAA,MAAA,CAAKoD,UAAU,8EAA6ErE,SAC3FiB,EAAAA,IAAA,MAAA,CAAKoD,UAAU,wBAAuBrE,SACrCiB,EAAAA,IAACqD,SAAM,CAACC,SAAO,QAGT,OAAPP,EACA/C,MAAA,MAAA,CACCoD,UAAU,yHACVlB,MAAO,CACNqB,OAAQ,aAAaT,OACrB/D,SAEDiB,EAAAA,IAAA,MAAA,CACCoD,UAAW,+BAA+BN,OAC1CZ,MAAO,CACNsB,WAAY9B,GAAQQ,OAAOuB,MAAMC,gBAAkB,oBAC9BC,MAAOb,KAC5B/D,SAEAgE,MAGA3B,EACHd,EAAAA,YAAK8C,UAAU,qDAAoDrE,SAAA,CAClEiB,EAAAA,IAAA,MAAA,CAAKoD,UAAU,2GAA0GrE,SACxHiB,EAAAA,IAAA,MAAA,CAAKoD,UAAU,2DAEfrE,KAGFuB,EAAAA,KAAA,MAAA,CACC8C,UAAWQ,EAAG,iFAAkFvC,EAAkB,6BAA+B,8BACjJa,MAAO,CACN2B,UAAW,cAA6B,GAAd,GAAK3C,QAC/B4C,WAAY,0BACZP,OAAQ,aAAaT,OACrB/D,SAAA,CAEDiB,MAAA,MAAA,CAAKoD,UAAW,0CAA0CN,UAC3CZ,MAAO,CACrBD,gBAAiB,GAAGa,WAGrBxC,EAAAA,KAAA,MAAA,CAEC8C,UAAWQ,EAAG,mFAAoFhB,KAC/EV,MAAO,CAC1ByB,MAAOb,KACP/D,SAAA,CAEAiB,MAAC+D,EAAU,CAAC/E,KAAM,KAClBsB,EAAAA,KAAA,IAAA,CACC4B,MAAO,CACNsB,WAAY9B,GAAQQ,OAAOuB,MAAMC,gBAAkB,qBACnD3E,SAAA,CAEA,IACAwC,IAAYyC,EAAAA,aAAaC,iBAC1BjE,EAAAA,sBAMI,OAAP+C,GACA/C,EAAAA,WAAKoD,UAAU,qCAAoCrE,SAClDiB,EAAAA,WACCoD,UAAWQ,EAAG,oEAAqEhB,KACnFV,MAAO,CACNsB,WAAY9B,GAAQQ,OAAOuB,MAAMC,gBAAkB,oBAC9BC,MAAOb,KAC5B/D,SAEAwC,IAAYyC,eAAaE,gBAI5B7C,GAA0B,OAAP0B,IAAgB3B,GACnCpB,EAAAA,IAAA,MAAA,CAAKoD,UAAU,mCAAkCrE,SAChDiB,EAAAA,IAAA,MAAA,CACCoD,UAAW,0BAA0BN,2CACrCZ,MAAO,CACND,gBAAiB,GAAGa,SACpB/D,SAEDiB,EAAAA,IAAA,MAAA,CACCoD,UAAW,cAAcN,uDACzBZ,MAAO,CACN3B,MAAO,GAAGW,KACVe,gBAAiB,GAAGa,eAOd,OAAPC,IAAgB3B,GACfpB,MAAA,MAAA,CAAKoD,UAAU,qCAAoCrE,SACjDuB,OAAA,MAAA,CAAK8C,UAAWQ,EAAG,2BAA4BhB,KAC9CV,MAAO,CACZyB,MAAOb,KACP/D,SAAA,CAEO+C,KAAKS,MAAMrB,GAAM,SAKnB,OAAP6B,IAAgB3B,GAChBpB,MAAA,MAAA,CAAKoD,UAAU,2DAA0DrE,SACxEiB,EAAAA,IAAA,MAAA,CACCoD,UAAWQ,EAAG,2BAA4BhB,KAC1CV,MAAO,CACNsB,WAAY9B,GAAQQ,OAAOuB,MAAMC,gBAAkB,oBAC9BC,MAAOb,KAC5B/D,SAEAwC,IAAYyC,eAAaG,yBAMhC,GC5LA,SAASC,GAAgBC,UACvBA,EAASC,WACTA,EAAUC,YACVA,EAAWC,QACXA,EAAOC,MACPA,EAAKC,SACLA,EAAQC,iBACRA,EAAgBC,wBAChBA,EAAuBC,UACvBA,EAASC,UACTA,EAASC,iBACTA,EAAgBC,cAChBA,EAAaC,iBACbA,EAAgBC,UAChBA,EAASC,SACTA,EAAQ/D,WACRA,IAEA,MAAMvB,MAAEA,GAAU2B,aAAW7C,IAAiB,CAAA,GACxC4C,UAAEA,GAAcC,aAAWC,EAAAA,kBAAoB,CAAA,EAErD,OACEnB,EAAAA,KAAA,MAAA,CAAK8C,UAAU,+DAA6D,cAAa,kBAAkB+B,EAAW,WAAaR,EAAmB,iBAAmBvD,EAAa,WAAa,QAAOrC,SAAA,CACxMiB,cACEoF,QAAS,KACPf,IACAW,KAEF5B,UAAU,wCAAuCrE,SAEjDiB,EAAAA,IAACqF,IAAC,CAACjC,UAAU,kBAGfpD,EAAAA,IAAA,MAAA,CAAKoD,UAAU,iCAAgCrE,SAC5Cc,IAAUnB,GACTsB,EAAAA,IAACsF,EAAM,CAELC,OAAO,EACPC,IAAKV,EACLW,kBAAmB,EACnBR,iBAAkBA,EAClBS,UAAQ,EACRC,iBAAiB,aACjBpB,YAAaA,EACbQ,iBAAkBA,EAClB7C,MAAO,CACL0D,SAAU,QACVC,IAAK,EACLC,OAAQ,EACRC,OAAQ,EACRxF,MAAO,OACPC,OAAQ,OACRwF,UAAW,UAhBRd,KAsBX5E,EAAAA,KAAA,MAAA,CAAK8C,UAAU,qFAAoFrE,SAAA,CAChG8F,GACC7E,EAAAA,IAACiG,EAAAA,eAAc,CACb7C,UAAU,2CACV8C,OAAQC,YACRC,WAAY7E,IAAYyC,EAAAA,aAAaS,OACrC4B,WAAY7B,EACZ8B,cAAY,IAIf7B,EACCzE,EAAAA,IAAA,MAAA,CAAAjB,SACEiB,EAAAA,IAACiG,iBAAc,CACb7C,UAAU,wCACVgD,WAAY7E,IAAYyC,eAAauC,SACrCF,WAAY3B,EACZ4B,cAAY,MAGb3B,EAUD,KATF3E,MAACiG,EAAAA,eAAc,CAAA,cACD,iBACZ7C,UAAW,wEACTkB,EAAa,cAAgB,IAE/B8B,WAAY7E,IAAYyC,eAAawC,WACrCH,WAAYzB,EACZ6B,SAAUnC,OAKhBtE,EAAAA,IAAA,QAAA,CACE0G,GAAG,eACHC,YAAY,YACZC,QAAQ,OACR1E,MAAO,CACL0D,SAAU,WACVG,QAAQ,OAEVc,IAAK,GAAGC,EAAAA,2DAIhB,CC5GA,IAAIC,EAAgD,KAChDC,EAAsB,KACtBC,EAAqB,KCkBzB,IAAIC,EACAC,EAIO,KAmmBX,IAAAC,EAAehH,EAAMC,KAjmBrB,UAA2BgH,gBAAEA,EAAerC,cAAEA,EAAasC,OAAEA,EAAMC,mBAAEA,EAAkBC,kBAAEA,EAAiBC,iBAAEA,EAAgBC,iBAAEA,EAAgBC,YAAEA,IAC/I,MAAMC,MAAEA,EAAKC,WAAEA,EAAUC,OAAEA,EAAMC,WAAEA,GAAeJ,EAC5C7C,EAAYkD,EAAAA,OAAsB,MAClCC,EAAmBD,EAAAA,OAA6B,MAChDE,EAAiBF,EAAAA,OAAsB,OACtCG,EAAgBC,GAAqBlJ,EAAAA,SAAiB,KACtDoF,EAAY+D,GAAiBnJ,EAAAA,UAAS,IACtCyF,EAAkB2D,GAAuBpJ,EAAAA,UAAS,IAClDiG,EAAUoD,GAAerJ,EAAAA,UAAS,IAClCsJ,EAAsBC,GAA2BvJ,EAAAA,UAAS,IAC1DkC,EAAYsH,GAAiBxJ,EAAAA,UAAS,GACvCyJ,EAA4BX,EAAAA,OAA6C,OACxEY,EAAaC,GAAkB3J,EAAAA,SAAS,KACxC4J,EAAiBC,GAAsB7J,EAAAA,SAAmB,KAC1D8J,EAAkBC,GAAuB/J,EAAAA,SAAS,KAClDgK,EAAYC,GAAiBjK,EAAAA,UAAS,IACtCkK,EAAaC,GAAcnK,EAAAA,UAAS,IACpCuF,EAAO6E,GAAYpK,EAAAA,UAAS,IAC5BqK,EAAQC,GAAatK,EAAAA,SAAc,KACnC2F,EAAW4E,GAAgBvK,EAAAA,UAAS,GACrCwK,EAAmB1B,EAAAA,QAAO,IAC1B2B,aAAEA,ID5CK,WACZ,MAAOC,EAAmBC,GAAwB3K,EAAAA,SAAS,IACpD4K,EAAWC,GAAgB7K,EAAAA,SAAS,IACpC8K,EAAUC,GAAe/K,EAAAA,UAAS,GAEnCgL,EAAelC,EAAAA,OAAO,GACtBmC,EAAuBnC,EAAAA,OAAO,GAC9BoC,EAAUpC,EAAAA,QAAO,GAgCvBqC,eAAeC,IAGb,GACoB,oBAAXnL,QACc,oBAAdoL,UAEP,OAAO,EAGT,IACEC,QAAQC,IAAI,kCAIZ,MAAOC,EAASC,EAAUC,SAAqBC,QAAQC,IAAI,CACzDD,QAAAE,UAAAC,KAAA,WAAA,OAAAC,QAAO,mCAAmC,GAC1CC,OAAO,yBACPA,OAAO,oCAGTjE,EAAgByD,QAGVC,EAASQ,WAAW,eACpBR,EAASS,QACfZ,QAAQC,IAAI,oCAKZ,MAAMY,EAAc,CAClBC,QAAS,YACTC,UAAW,OACXC,aAAa,gDAGf,IACExE,QAAuBC,EAAcwE,eACnCxE,EAAcyE,gBAAgBC,UAC9BN,GAEFb,QAAQC,IAAI,0CACb,CAAC,MAAOmB,GACPpB,QAAQqB,KAAK,kDAAmDD,GAGhE5E,QAAuBC,EAAcwE,eACnCxE,EAAcyE,gBAAgBC,UAC9B,CACEL,QAAS,OACTC,UAAW,SAGff,QAAQC,IAAI,qCACb,CAED,OAAO,CACR,CAAC,MAAOqB,GAGP,OAFAtB,QAAQsB,MAAM,0CAA2CA,GACzD/E,EAAuB,MAChB,CACR,CACH,CAEA,SAASgF,EAAaC,GAEpB,SAAKA,GAAwB,IAAhBA,EAAKC,SAC+B,KAA1CD,EAAKE,OAAQC,GAAMA,EAAE,GAAK,IAAKF,MACxC,CA2EA,OA9KAxM,EAAAA,UAAU,KAIR,GAHA2K,EAAQgC,SAAU,GAGdpF,EAiBJ,OAXKD,IACHA,EAAuBuD,KAIzBvD,EAAqBiE,KAAMqB,IACrBA,GAAWjC,EAAQgC,SACrBnC,GAAY,KAIT,KACLG,EAAQgC,SAAU,GAjBlBnC,GAAY,IAmBb,IAyHHxK,EAAAA,UAAU,KACR0K,EAAqBiC,QAAUxC,EAC3BA,EAAoB,GAAKM,EAAakC,QAAU,IAClDrC,EAAcuC,GAAMA,EAAI,GACxBzC,EAAqB,KAEtB,CAACD,IAEJnK,EAAAA,UAAU,KACRyK,EAAakC,QAAUtC,GACtB,CAACA,IAmBG,CACLH,aA1EmBU,MAAOkC,EAAsBzH,KAGhD,IAAKkC,IAAmBoD,EAAQgC,UAAYtH,GAAWsH,SAASI,MAC9D,OAIF,MAAMA,EAAQ1H,EAAUsH,QAAQI,MAChC,KAAIA,EAAMC,WAAa,GAEvB,IACE,MAAMC,QAAc1F,EAAe2F,cACjCH,EACA,CAAEI,gBAAgB,IAGpB,IAAKF,IAAUA,EAAMT,OAAQ,OAE7B,MAGMD,EAHYU,EAAM,GAAGG,UAIxBC,MAAM,IACNC,IAAKC,GAAY,CAChBA,EAAGC,EAAI,GAAKD,EAAGC,EAAI,IAAMD,EAAGC,GAAK,EACjCD,EAAGE,EAAI,GAAKF,EAAGE,EAAI,KAAOF,EAAGE,GAAK,EAClCF,EAAGG,OAAS,IAGa,IAAzBjD,EAAakC,SAAkBL,EAAaC,IAC9CnC,EAAsBuD,GAASA,EAAO,GAGX,IAAzBlD,EAAakC,SAAiBL,EAAaC,IAC7CnC,EAAsBuD,GAASA,EAAO,GAGX,IAAzBlD,EAAakC,SAAeG,GACjC,CAAC,MAAOc,GACP7C,QAAQsB,MAAM,wBAAyBuB,EACxC,GAkCDrD,WACAF,YACA9E,cArBoB,KACpBkF,EAAakC,QAAU,EACvBjC,EAAqBiC,QAAU,EAC/BvC,EAAqB,GACrBE,EAAa,IAkBbuD,aAfmBjD,UACdrD,GAAmBD,IACtBA,EAAuBuD,UACDvD,GACPqD,EAAQgC,SACrBnC,GAAY,KAYpB,CClJ0BsD,GACnBC,GAAYxF,EAAAA,QAAO,GACnByF,GAAiBzF,EAAAA,QAAO,GACxB0F,GAAa1F,EAAAA,OAAO,GACpB2F,GAAsB3F,EAAAA,OAAO,GAG7B4F,GAAgB5F,EAAAA,OAAO,GACvB6F,GAAmB7F,EAAAA,OAAO,GAC1B8F,GAAyB9F,EAAAA,QAAO,GAChC+F,GAAkB/F,EAAAA,QAAO,GACzBgG,GAAqBhG,EAAAA,QAAO,GAC5BiG,GAAqBjG,EAAAA,OAAmB,QACxCkG,GAA6BlG,EAAAA,OAAmB,QAChDmG,GAAQnG,EAAAA,OAAsB,OAC7BoG,GAAgBC,IAAqBnP,EAAAA,SAAmB,KACxDgG,GAAWoJ,IAAgBpP,EAAAA,SAAS,IACpCqP,GAAyBC,IAA8BtP,EAAAA,SAAgCuP,EAAAA,wBACxFC,aAAEA,GAAYC,eAAEA,GAAcC,iBAAEA,GAAgBC,KAAEA,GAAIC,YAAEA,GAAWC,kBAAEA,IAAsBvN,EAAAA,WAAWwN,iBAGtGC,GAAYjH,EAAAA,OAAOV,GACzB7H,EAAAA,UAAU,KACTwP,GAAU7C,QAAU9E,GAClB,CAACA,IAEJ,MAAM4H,GAAYlH,EAAAA,OAAOuB,GACzB9J,EAAAA,UAAU,KACTyP,GAAU9C,QAAU7C,GAClB,CAACA,IAUJ,MAyBM4F,GCzFP,UAAqBC,UAAEA,EAASC,UAAEA,IACjC,MAAMC,EAAWtH,EAAAA,OAA6C,MACxDuH,EAAevH,EAAAA,OAAOqH,GAE5B5P,EAAAA,UAAU,KACT8P,EAAanD,QAAUiD,GACrB,CAACA,IAEJ,MAAMG,EAAO3N,EAAAA,YAAY,KACpByN,EAASlD,UACZqD,aAAaH,EAASlD,SACtBkD,EAASlD,QAAU,OAElB,IAEGsD,EAAM7N,EAAAA,YAAY,KACnByN,EAASlD,SAASqD,aAAaH,EAASlD,SAC5CkD,EAASlD,QAAUuD,WAAW,KAC7BL,EAASlD,QAAU,KACnBmD,EAAanD,WACXgD,IACD,CAACA,IAKJ,OAFA3P,EAAAA,UAAU,IAAM+P,EAAM,CAACA,IAEhB,CAAEI,MAAOF,EAAKG,IAAKH,EAAKF,OAChC,CD8DsBM,CAAY,CAAEV,UA9FH,KA8FwCC,UAzBtCxN,EAAAA,YAAY,KAC7CkO,EAAAA,QAAQC,QAAQ,GAAGnI,0BAAoC,CACtDP,OAAQ2H,GAAU7C,QAClBxE,QACAqI,WAAYrC,GAAcxB,QAC1B1F,GAAIyH,GAAM/B,WAkBT,CAACvE,EAAYD,MAIVvD,GAAY,KACjBoJ,GAAerB,SAAU,EACzB+C,GAAaK,OACbC,aAAavI,GACTyB,EAA0ByD,SAASqD,aAAa9G,EAA0ByD,SAC9E1E,EAAiBwI,EAAAA,gBACjB1I,EAAkB,IAClBoH,MAAmB,GACnBxG,EAAkB,IAClBF,EAAekE,QAAU,KACzB/D,GAAc,GACdC,GAAoB,GACpBC,GAAY,GACZE,GAAwB,GACxBC,GAAc,GACdC,EAA0ByD,QAAU,KACpCvD,EAAe,IACfE,EAAmB,IACnBE,EAAoB,IACpBE,GAAeD,GACfwF,IAAa,GACbyB,EAAAA,cAAcC,YACd/G,GAAW,GACXC,GAAS,GACToE,GAAWtB,QAAU,EACrBuB,GAAoBvB,QAAU,EAC9BwB,GAAcxB,QAAU,EACxByB,GAAiBzB,QAAU,EAC3B0B,GAAuB1B,SAAU,EACjC4B,GAAmB5B,SAAU,EACI,OAA7BnE,EAAiBmE,SACpBnE,EAAiBmE,QAAQoD,OAG1BjG,EAAO8G,QAASC,IACXrI,EAAiBmE,SAASnE,EAAiBmE,QAAQxM,oBAAoB,gBAAiB0Q,KAG7F9G,EAAU,IACVE,EAAiB0C,SAAU,EAC3B3C,GAAa,GACblC,GAAmB,GACfzC,EAAUsH,SAASmE,QACtBlI,GAAc,IAkBVmI,GAAkB3O,EAAAA,YAAY,KACnC8N,WAAW,KACVtH,GAAc,IACZ,MACD,IAEGoI,GAAuB5O,cAC3BiK,IAGA,GAFAtB,QAAQC,IAAI,eAAgBqB,GAExByC,KAA4BE,EAAAA,sBAG/B,OAFAD,GAA2BkC,EAAAA,+BAC3BpC,GAAclB,GAASA,EAAO,GAG/B/E,GAAc,IAEf,CAACkG,KAGIoC,GAAc9O,EAAAA,YAAY,KAC/B6H,EAAiB0C,SAAU,EAC3B+C,GAAaK,OACblG,GAAS,GACTG,GAAa,GACTd,EAA0ByD,SAASqD,aAAa9G,EAA0ByD,SAC1EnE,EAAiBmE,SACpBnE,EAAiBmE,QAAQ3H,QAE1B0L,EAAAA,cAAcC,YACdvB,GAAK+B,cAAcC,aACnBd,EAAAA,QAAQC,QAAQ,GAAGnI,gCAA0C,CAC5DP,OAAQA,EACRM,QACAlB,GAAIyH,GAAM/B,QACV0E,OAAQ,UAETpD,GAAWtB,QAAU,EACrBuB,GAAoBvB,QAAU,EAC9B0B,GAAuB1B,SAAU,EACjC4B,GAAmB5B,SAAU,EACzBnE,EAAiBmE,SACpBnE,EAAiBmE,QAAQoD,OAE1BjG,EAAO8G,QAASC,IACXrI,EAAiBmE,SAASnE,EAAiBmE,QAAQxM,oBAAoB,gBAAiB0Q,KAE7F9G,EAAU,IACViG,aAAavI,IACX,CAACe,EAAkBsB,EAAQ4G,EAAAA,cAAezG,IAGvCqH,GAAsBhR,EAAAA,QAAQ,IAAMiR,EAAAA,iCAAkC,IACtEC,GAAoBC,EAAAA,qBAAqB9C,IACzC+C,GAAqBC,EAAAA,sBAAsBH,IAE3CI,GAAgBxP,EAAAA,YAAYwI,UACjCX,EAAiB0C,SAAU,QACrB+D,EAAAA,cAAcmB,UAAU,GAAGxK,EAAAA,iCACjCwC,GAAS,GACTG,GAAa,GACTd,EAA0ByD,SAASqD,aAAa9G,EAA0ByD,SAC9E9D,GAAoB,IAClB,CAACK,EAA2BwH,EAAAA,gBAEzBoB,GAAe1P,EAAAA,YAAYwI,UAEhC,IAAI0D,GAAgB3B,UAAW+B,GAAM/B,QAArC,CACA2B,GAAgB3B,SAAU,EAC1B,IACC+B,GAAM/B,cAAgByC,GAAK+B,cAAcY,UAEzCzB,EAAAA,QAAQC,QAAQ,GAAGnI,6BAAuC,CACzDP,OAAQ2H,GAAU7C,QAClBxE,QACAlB,GAAIyH,GAAM/B,SAEX,CAAC,MAAON,GACRtB,QAAQC,IAAIqB,EAAO,8BACnBiE,EAAAA,QAAQC,QAAQ,GAAGnI,kCAA4C,CAC9DP,OAAQA,EACRM,QACAkE,MAAO2F,OAAO3F,IAEf,CAAS,QACTiC,GAAgB3B,SAAU,CAC1B,CAnB6C,GAoB5C,CAAC9E,EAAQO,EAAYD,IAEnB8J,GAA0B7P,EAAAA,YAC9B,EAAG8P,WACEA,GAAQA,EAAK3S,KAAO,GAAK0K,EAAiB0C,UAC7ChE,EAAmBgF,GAASA,EAAKwE,OAAOD,KACnClE,GAAerB,SAAWtH,EAAUsH,UACxCqB,GAAerB,SAAU,EACzBzC,GAAa,KACZ0H,KACAQ,wBAAsB,CACrBC,UAAW,GAAGjK,eACdP,OAAQ2H,GAAU7C,QAClBxE,QACAmK,QAAS,sCAERjN,MAIN,CAACuM,GAAeQ,EAAAA,sBAAuBhK,EAAYD,EAAO9C,IAGrDwM,GAAYzP,EAAAA,YAAYwI,UAC7B,MAAM2H,EAAYlJ,EAAgBA,EAAgBmD,OAAS,GACvD+F,GAAatI,EAAiB0C,gBAC3B+D,gBAAcmB,UAAUxK,EAAAA,oBAAsBkL,GAChDtI,EAAiB0C,UACpBlF,EAAiByI,WAAW2B,GAAW,QAGvC,CAACxI,EAAiBY,IAEfuI,GAA0BpQ,EAAAA,YAAY,KACvCoG,GAAoBA,EAAiBmE,SACxCnE,EAAiBmE,QAAQoD,OAE1BlH,GAAoB,GACpB,IACC,GAAIxD,GAAaA,EAAUsH,SAAWtH,EAAUsH,QAAQmE,OAAQ,CAE/D,MAAM2B,EAAapN,EAAUsH,QAAQmE,OAAO4B,iBAAiB,GAC7D,GAAID,EAAY,CACf,MAAME,EAAWF,EAAWG,cAC5BnK,EAAekE,QAAUgG,EAASE,WAAa,IAC/C,CAED,MAAMC,EAA6C,CAClDC,mBAAoB,MAEjBpE,GAAe,KAClBmE,EAAqBE,SAAWrE,GAAe,IAEhDnG,EAAiBmE,QAAU,IAAIsG,cAAc5N,EAAUsH,QAAQmE,OAAQgC,GACvEtK,EAAiBmE,QAAQzM,iBAAiB,gBAAiB+R,IAG3DzJ,EAAiBmE,QAAQzM,iBAAiB,OAAQ,KACjD8I,GAAwB,KAGzBe,EAAU,IAAID,EAAQmI,KACtBzJ,EAAiBmE,QAAQwD,MAAM,KAC/BnH,GAAwB,EACxB,CACD,CAAC,MAAOkK,GACRnI,QAAQC,IAAI,mCAAoCkI,EAChD,GACC,CAAC7N,EAAWsJ,GAAgBsD,GAAyBnI,IAElDqJ,GAAkB/Q,EAAAA,YAAYwI,UAC/B1B,EAA0ByD,SAC7BqD,aAAa9G,EAA0ByD,SAExC6F,KACIvI,EAAiB0C,UACpBzD,EAA0ByD,QAAUuD,WAAWtF,UAC1CX,EAAiB0C,gBACd+D,EAAAA,cAAcmB,UAAU,GAAGxK,EAAAA,iCACjCwB,GAAoB,KAEnB,OAEJC,GAAY,GACRmB,EAAiB0C,eACd+D,EAAAA,cAAcmB,UAAU,GAAGxK,EAAAA,gCAEhC,CAACmL,GAAyBvI,EAAkByG,EAAAA,gBAE/C1Q,EAAAA,UAAU,KACTwO,GAAmB7B,QAAUwG,IAC3B,CAACA,KAEJ,MAAMC,GAAsBhR,EAAAA,YAC3B,EAAG8P,WACF,MAAMmB,EAAYjE,GAAK+B,cAAckC,YACrC,GAAInB,EAAK3S,KAAO,GAAK8T,EAAW,CAC1BhF,GAAuB1B,UAC3B0B,GAAuB1B,SAAU,EACjCyC,GAAK+B,cAAcmC,WAAW1I,MAAOsH,IAmBpC,GAjBAxC,GAAaU,MAKbE,EAAAA,QAAQC,QAAQ,GAAGnI,8BAAwC,CAC1DP,OAAQ2H,GAAU7C,QAClBxE,QACArC,MAAOoM,GAAMpM,OAAS,GACtByN,aAAcrB,GAAMpM,OAASoM,EAAKpM,MAAM0G,OAAS,GACjDgH,OAAQtB,GAAMsB,OACdC,IAAKvB,GAAMuB,IACXC,qBAAsBxB,GAAMuB,MAAQ/E,GAAM/B,QAC1CgH,QAAS1F,GAAWtB,QACpBiH,iBAAkB1F,GAAoBvB,QACtCkH,KAAM,0BAEH3B,GAAQA,EAAKpM,OAASoM,EAAKpM,MAAM0G,OAAS,EAAG,CAChD,MAAM1G,EAAQtC,SAASsQ,cAAc,kBAEjB,IAAhB5B,EAAKsB,QAAmBtB,EAAKuB,MAAQ/E,GAAM/B,SAC9CuB,GAAoBvB,QAAU,EAC1BsB,GAAWtB,QAAU,GACxBsB,GAAWtB,SAAW,EACK,IAAvBsB,GAAWtB,SAAkB4B,GAAmB5B,QAKlB,IAAvBsB,GAAWtB,SAAiB7G,GAAOiO,SAC7C/D,aAAavI,GACbiJ,EAAAA,cAAcmB,UAAUxK,sBAAsB6K,EAAKpM,SANnDyI,GAAmB5B,SAAU,EAC7BqD,aAAavI,GACbiJ,EAAAA,cAAcC,kBACRD,EAAAA,cAAcmB,UAAUxK,sBAAsB6K,EAAKpM,UAO1D4J,GAAaK,OACbvG,EAAoB0I,EAAKpM,OACzBkK,aAAavI,GACb2H,GAAK+B,cAAcC,aACnBd,EAAAA,QAAQC,QAAQ,GAAGnI,gCAA0C,CAC5DP,OAAQA,EACRM,QACAlB,GAAIyH,GAAM/B,QACV0E,OAAQ,mBAEThD,GAAuB1B,SAAU,EACjCuD,WAAW,IAAM1B,GAAmB7B,UAAW,QAGhDuB,GAAoBvB,SAAW,EAC3BuB,GAAoBvB,SAAW,IAClCsB,GAAWtB,QAAU,EACrB4B,GAAmB5B,SAAU,IAE1B7G,GAAOiO,QAAYrM,GAAmBA,GAAiBsM,YAAc9B,EAAKpM,MAOnE4B,GAAiBsM,YAAc9B,EAAKpM,OAASA,GAAOiO,SAC1DrM,GAAmBA,EAAgBuM,qBAAuBvM,EAAgBwM,WAC7ExM,EAAgBuM,oBAAsB,EACtCvD,EAAAA,cAAcmB,UAAUxK,sBAAsB6K,EAAKpM,QAE/C4B,IAAiBA,EAAgBuM,qBAAuB,KAX7DvM,EAAkB,CACjBwM,UAAW,EACXD,oBAAqB,EACrBD,UAAW9B,EAAKpM,OAEjB4K,EAAAA,cAAcmB,UAAUxK,sBAAsB6K,EAAKpM,QAUrD,KAGH,MAAMqO,EAAQ9O,GAAWsH,QAAUtH,EAAUsH,QAAQyH,gBAAkB,KACnED,GACHhG,GAAcxB,SAAW,EAEK,IAA1BwB,GAAcxB,SAAe+C,GAAaS,QAC9Cf,GAAK+B,cAAckD,UAAU,CAC5BC,MAAOH,EACPI,OAAQ/E,GAAU7C,UAIW,IAA1BwB,GAAcxB,SAAiBwB,GAAcxB,QAAU,GAAM,GAChE2D,EAAAA,QAAQC,QAAQ,GAAGnI,oBAA8B,CAChDP,OAAQ2H,GAAU7C,QAClBxE,QACAsL,IAAK/E,GAAM/B,QACX6D,WAAYrC,GAAcxB,YAM5ByB,GAAiBzB,SAAW,EACK,IAA7ByB,GAAiBzB,SAAiByB,GAAiBzB,QAAU,GAAM,GACtE2D,EAAAA,QAAQC,QAAQ,GAAGnI,4BAAsC,CACxDP,OAAQ2H,GAAU7C,QAClBxE,QACAqM,cAAepG,GAAiBzB,UAInC,GAEF,CAACtH,EAAWwC,EAAQuH,GAAMsB,gBAAetI,EAAYD,IAGhDhD,GAA0B/C,EAAAA,YAAYwI,UAC3C3B,GAAc,GACdgG,IAAa,GACbI,OACItB,GAAUpB,UACboB,GAAUpB,SAAU,EACpByF,wBAAsB,CACrBC,UAAW,eACXxK,OAAQ2H,GAAU7C,QAClB6G,OAAQ,UACRrL,WAGFH,EAAiByM,EAAAA,2BACjB7L,GAAc,GACVqB,EAAiB0C,eACd+D,EAAAA,cAAcmB,UAAU,GAAGxK,EAAAA,oCAE9B4C,EAAiB0C,eACd+D,EAAAA,cAAcmB,UAAU,GAAGxK,EAAAA,+CAE9B4C,EAAiB0C,UACpB1D,GAAc,GACdJ,GAAoB,GACpBe,GAAW,GACXI,GAAa,GACb+G,MAED,IACC,GAAI1L,GAAaA,EAAUsH,SAAWtH,EAAUsH,QAAQmE,QAAU7G,EAAiB0C,QAAS,CAE3F,MAAM8F,EAAapN,EAAUsH,QAAQmE,OAAO4B,iBAAiB,GAC7D,GAAID,EAAY,CACf,MAAME,EAAWF,EAAWG,cAC5BnK,EAAekE,QAAUgG,EAASE,WAAa,IAC/C,CAED,MAAMC,EAA6C,CAClDC,mBAAoB,MAEjBpE,GAAe,KAClBmE,EAAqBE,SAAWrE,GAAe,IAE5C1E,EAAiB0C,UACpBnE,EAAiBmE,QAAU,IAAIsG,cAAc5N,EAAUsH,QAAQmE,OAAQgC,IAEpE7I,EAAiB0C,SAAWnE,EAAiBmE,SAChDnE,EAAiBmE,QAAQzM,iBAAiB,gBAAiBkT,IAG5DrJ,EAAU,IAAI0F,GAAU9C,QAASyG,KAC7B5K,EAAiBmE,SAASnE,EAAiBmE,QAAQwD,MAAM,KAC7DnH,GAAwB,GACpBiB,EAAiB0C,UACpBlF,EAAiByI,WAAW2B,GAAW,KAExC,CACD,CAAC,MAAOjE,GACR7C,QAAQC,IAAI,mBAAoB4C,EAChC,GACC,CAACvI,EAAWsJ,GAAgByE,GAAqBvB,GAAW5H,EAAkBoF,KAgFjF,OA9EArP,EAAAA,UAAU,KACTyO,GAA2B9B,QAAUxH,IACnC,CAACA,KAEJnF,EAAAA,UAAU,KACLoI,IACHR,GAAgB,GAChBkK,MAEM,KAIN,MAAM4C,EAAShG,GAAM/B,QACrB+B,GAAM/B,QAAU,KACZ+H,IACHtF,GAAK+B,cAAcC,aACnBd,EAAAA,QAAQC,QAAQ,GAAGnI,gCAA0C,CAC5DP,OAAQ2H,GAAU7C,QAClBxE,QACAlB,GAAIyN,EACJrD,OAAQ,aAGVvH,EAAO8G,QAASC,IACfrI,GAAkBmE,SAASxM,oBAAoB,gBAAiB0Q,OAGhE,CAACzI,IACJpI,EAAAA,UAAU,KACTqJ,EAAgBsL,KAAKxL,IACnB,CAACA,IAEJnJ,EAAAA,UAAU,KACTsJ,EAAmB,KACjB,CAACC,IACJvJ,EAAAA,UAAU,KACT,MAAM4U,EAAuBlM,EAAe8D,QAAU9D,EAAe8D,OAAS,EACzEzD,IAAwB6L,GAAyB1P,IAAoB+E,EAAiB0C,SAAY3H,GAClGwD,GAAoBA,EAAiBmE,UACnCzH,GACJsD,EAAiBmE,QAAQoD,SAM1B,CAAChH,EAAsB7D,EAAkBwD,EAAgB1D,IAE5DhF,EAAAA,UAAU,KACT,MAAM4U,EAAuBlM,EAAe8D,QAAU9D,EAAe8D,OAAS,EAC9E,GAAIzD,GAAwB6L,IAAyB1P,GAChDsD,GAAoBA,EAAiBmE,SAAW1C,EAAiB0C,UAAY3H,IAC3EE,EAAkB,CACtB,MAAM2P,EAAY,IAAIC,KAAKpM,EAAgB,GAAGb,KAAU6J,KAAsB,CAC7EmC,KAAMrC,KAEPlC,OACAH,MAAmB,GACnBpH,EAAkB,IAElBmH,KAAiB2F,EAAWpM,EAAekE,QAC3C,GAGD,CAAC5D,EAAsB7D,EAAkBwD,EAAgB1D,EAAOwD,EAAkByB,IACrFjK,EAAAA,UAAU,KACTyO,GAA2B9B,QAAUxH,IACnC,CAACA,KAEJnF,EAAAA,UAAU,KACT,MAAM+U,EAAYC,EAAAA,+BAA+B1D,GAAqB2D,cACtErG,GAAkBmG,IAChB,CAACzD,KACJtR,EAAAA,UAAU,KACT4E,MACE,IAGFrE,EAAAA,IAACoE,EAAe,CACfY,cAAeA,EACfX,UAAWA,GACXC,WAAYA,EACZE,QAASmM,GACTvH,YAAaA,EACb3E,MAAOA,EACPC,SA3coB2F,UACrBsK,4BAA0B,CACzB7C,UAAW,GAAGjK,WACdD,QACAN,OAAQ2H,GAAU7C,QAClB5L,OAAQuH,EACRD,SACAmL,QAAQ,IAETpE,GAAK+B,cAAcC,aACnBxM,WACMkN,MAicL5M,iBAAkBA,EAClBvD,WAAYA,EACZwD,wBAAyBA,GACzBO,SAAUA,EACVkM,cAAeA,GACfxM,UAAWA,EACXC,UAAWA,EACXP,YAAaiM,GACbzL,iBAAkB0L,GAClBxL,iBAAkBsJ,GAClBrJ,UAAWA,IAGd,GEznBc,SAAU0P,GAActN,OACpCA,EAAMK,YACNA,EAAWJ,mBACXA,EAAkBC,kBAClBA,EAAiBC,iBACjBA,EAAgBC,iBAChBA,IAEA,MAAOxG,EAAO2T,GAAY3V,EAAAA,SAAS,KAC5B4V,EAAmBC,GAAwB7V,EAAAA,SAAS,IACpDiC,EAAW6T,GAAgB9V,EAAAA,SAAwB,OACnDkC,EAAYsH,GAAiBxJ,EAAAA,UAAS,IACtCoC,EAAgB2T,GAAqB/V,EAAAA,SAAS,GAC/CgW,EAAgBlN,EAAAA,OAAiB,KAChCmN,EAAc9N,GAAmBnI,EAAAA,UAAS,GAC3CkW,EAAkBlU,EAAQ4T,EAC1BzT,EAAkB+T,GAAmB,IAAMA,GAAmB,GAYpE3V,EAAAA,UAAU,KACR,GAAsB,oBAAXN,QAA0BA,OAAOkW,uBAAwB,CAClE,MAAMC,EAAqBC,IACzB,GAAmB,OAAfA,EAAMC,KAAe,CACvB,IAAIC,EAAW3T,KAAK4T,IAAIH,EAAMC,MACV,OAAhBD,EAAMI,QACRF,GAAY3T,KAAK8T,IAAKL,EAAMI,MAAQ7T,KAAK+T,GAAM,MAEjDJ,EAAW3T,KAAKC,IAAI,EAAGD,KAAKE,IAAI,IAAKyT,IACrCZ,EAASY,EACV,GAGGK,EAAoBzL,UACxB,MAAM0L,EACJV,uBAEF,GACEU,GAC+C,mBAAxCA,EAAkBD,kBAEzB,IAEqB,kBADMC,EAAkBD,qBAGzC3W,OAAOQ,iBAAiB,oBAAqB2V,EAEhD,CAAC,MAAO3C,GACPnI,QAAQsB,MAAM,mBAAoB6G,EACnC,MAGDxT,OAAOQ,iBAAiB,oBAAqB2V,GAG/C,MAAO,KACDA,GACFnW,OAAOS,oBAAoB,oBAAqB0V,KAKtDrS,SAAStD,iBAAiB,QAASmW,EAAmB,CAAEE,MAAM,GAC/D,GACA,IAEHvW,EAAAA,UAAU,KAMR,GALAyV,EAAc9I,QAAU,IACnB8I,EAAc9I,QAAQU,UACzBsI,GAGEF,EAAc9I,QAAQH,QAAU,EAAG,CACrC,MAAMgK,EACJnU,KAAKC,OAAOmT,EAAc9I,SAAWtK,KAAKE,OAAOkT,EAAc9I,SAE/D6I,EADE5T,GAAmB4U,EAAY,EACd7I,GAAStL,KAAKE,IAAI,IAAKoL,EAAO,IAE9BA,GAAStL,KAAKC,IAAI,EAAGqL,EAAO,IAElD,CAEI/L,GAAkC,OAAdF,IAAsBC,GAC7C8U,KAED,CAACd,EAAiB/T,EAAiBF,EAAWC,IAElD3B,EAAAA,UAAU,KACL6B,GAAkB,KAAqB,OAAdH,IAAuBC,GACnD+U,IAEG7U,EAAiB,IAAoB,OAAdH,GAC1B+U,KAEC,CAAC5U,EAAgBH,EAAWC,IAE9B,MAAM+U,EAAiB,KACrBnB,EAAa,GACb,MAAMoB,EAAQC,YAAY,KACxBrB,EAAc5H,GACC,OAATA,GAAiBA,GAAQ,GAC3BkJ,cAAcF,GACd1N,GAAc,GACP,MAEF0E,EAAO,IAEf,MAGC8I,EAAiB,KACrBlB,EAAa,MACRG,GACHzM,GAAc,IAKlB,OACE1I,EAAAA,IAACuW,EAAW,CACVrV,MAAOkU,EACPjU,UAAWA,EACXC,WAAYA,EACZC,gBAAiBA,EACjBC,eAAgBA,EAAcvC,SAE7BqC,GACCpB,EAAAA,IAACwW,GACCnP,gBAAiBA,EACjBrC,cAvHc,KACpB6P,EAAS,IACTE,EAAqB,GACrBC,EAAa,MACbtM,GAAc,GACduM,EAAkB,GAClB5N,GAAgB,GAChB6N,EAAc9I,QAAU,IAiHlB9E,OAAQA,EACRK,YAAaA,EACbJ,mBAAoBA,EACpBC,kBAAmBA,EACnBC,iBAAkBA,EAClBC,iBAAkBA,KAK5B,CCpJA,SAAS+O,GAAiBC,cAAEA,EAAahV,OAAEA,EAAMiV,OAACA,IACjD,MAAOC,EAAiBC,GAAsB3X,WAAS,CACtDuH,UAAU,EACVsL,QAAS,MAEH+E,EAASC,GAAc7X,EAAAA,UAAS,GACjC4F,EAAYkD,EAAAA,OAAO,MAEnBgP,EAAyBnV,EAAAA,YAAYwI,UAC1C,MAAM4M,QAAmBC,0BACzBL,EAAmBI,GACnBF,GAAW,IACT,IAMH,OAJAtX,EAAAA,UAAU,KACTuX,KACE,IAECF,EACI9W,EAAAA,IAACmX,EAAAA,cAAa,CAACC,IAAKT,EAAQU,WAAW,UAG3CT,GAAiBnQ,SACbzG,EAAAA,IAACsX,EAAAA,MAAK,CAAC5V,OAAQA,EAAQqQ,QAAS6E,GAAiB7E,UAGxD/R,EAAAA,IAACsF,EAAM,CACNC,OAAO,EACPC,IAAKV,EACLW,kBAAmB,EACnBR,iBAAkBA,EAAAA,iBAClBS,YACAnB,YAAa,IAAMmS,KAAgB,GACnC/Q,iBAAiB,aACjBzD,MAAO,CACN0D,SAAU,WACVC,IAAK,EACLC,OAAQ,EACRC,OAAQ,EACRxF,MAAO,OACPC,OAAQ,OACRwF,UAAW,UAIf,CC/CA,SAASuR,GAAYC,KAAEA,EAAIC,QAAEA,EAAOC,iBAAEA,EAAmB,8CACxD,MAAMC,EAAiB3P,EAAAA,OAAgC,MACjD4P,EAAkB5P,EAAAA,OAAsB,MAE9C,IAAI6P,EAAsB,CACzBC,UAAU,EACVC,UAAU,EACVC,YAAY,EACZC,OAAO,EACPC,OAAO,EACPC,aAAc,OACdvR,QAAS,WACTwR,OAAQC,EAAAA,aAkCT,OA/BA5Y,EAAAA,UAAU,KACT,IAAKmY,EAAgBxL,SAAWoL,GAAQG,GAAgBvL,QAAS,CAChE,MAAMkM,EAASC,EAAQZ,EAAevL,QAAS,IAC3CyL,IAEJS,EAAOlN,MAAM,KACZwM,EAAgBxL,QAAUkM,EAE1B,MAAME,EAAiB,IACnBX,EACHY,QAAS,CAAC,CAAE5R,IAAK2Q,EAAMlE,KAAM,2BAG9BgF,EAAOR,SAASU,EAAeV,UAC/BQ,EAAOzR,IAAI2R,EAAeC,SAE1BhB,IAAUa,IAEX,GACC,CAACd,EAAMG,IAEVlY,EAAAA,UAAU,KACT,MAAM6Y,EAASV,EAAgBxL,QAC/B,MAAO,KACFkM,IAAWA,EAAOI,eACrBJ,EAAOK,UACPf,EAAgBxL,QAAU,QAG1B,CAACwL,IAGH5X,MAAA,MAAA,CAAKoD,UAAWsU,EAAgB3Y,SAC/BiB,EAAAA,IAAA,QAAA,CAAOwF,IAAKmS,EAAgBO,OAAK,EAAC9U,UAAU,WAAWwV,aAAW,EAACC,OAASlG,GAAMA,EAAEmG,oBAGvF,CC5CA,SAASC,GAAiBC,eAAEA,EAAcC,OAAEA,EAAMnR,OAAEA,EAAMJ,iBAAEA,EAAgBH,mBAAEA,IAC7E,MAAMhG,UAAEA,GAAcC,aAAWC,EAAAA,kBAAoB,CAAA,EAC/CyX,EAAiBvX,EAAAA,aAChBwX,EAAWC,GAAgBla,EAAAA,UAAS,GAWrCma,EAAkB,KACvBD,GAAcD,IAIf,OACC7Y,OAAAgZ,EAAAA,SAAA,CAAAva,SAAA,CACCuB,EAAAA,KAAA,MAAA,CAAA,cAAiB,qBAAqB8C,UAAU,oGAAoGlB,MAAO,CAAEqX,WAAYL,GAAgBhX,OAAOuB,MAAMxB,iBAAiBlD,SAAA,CACtNiB,MAAA,MAAA,CAAKoD,UAAU,6DACdpD,EAAAA,IAACqD,SAAM,CAACC,SAAO,MAEhBtD,EAAAA,IAAA,MAAA,CAAKoD,UAAU,SAAQrE,SACrBia,GACA1Y,EAAAA,KAAA,MAAA,CAAK8C,UAAU,YAAWrE,SAAA,CACzBiB,EAAAA,IAAA,KAAA,CACCoD,UAAU,cACVlB,MAAO,CACNsB,WAAY0V,GAAgBhX,OAAOsX,SAASC,mBAAqB,wBACjEC,SAAUR,GAAgBhX,OAAOsX,SAASG,iBAAmB,OAC7DhW,MAAOuV,GAAgBhX,OAAOsX,SAASI,cAAgB,OACvDC,WAAYX,GAAgBhX,OAAOsX,SAASM,mBAAqB,UACjE/a,SAEAwC,IAAYyC,EAAAA,aAAa+V,iBAE3B/Z,EAAAA,IAAA,IAAA,CACCkC,MAAO,CACNsB,WAAY0V,GAAgBhX,OAAOuB,MAAMC,gBAAkB,oBAC3DgW,SAAUR,GAAgBhX,OAAOuB,MAAMuW,cAAgB,OACvDrW,MAAOuV,GAAgBhX,OAAOuB,MAAMwW,eAAiB,WACrDlb,SAEAwC,IAAYyC,EAAAA,aAAa8M,UAE3B9Q,EAAAA,IAAA,IAAA,CACCkC,MAAO,CACNsB,WAAY0V,GAAgBhX,OAAOuB,MAAMC,gBAAkB,oBAC3DgW,SAAUR,GAAgBhX,OAAOuB,MAAMuW,cAAgB,OACvDrW,MAAOuV,GAAgBhX,OAAOuB,MAAMwW,eAAiB,WACrDlb,SAEAmb,EAAAA,mBAAmBlB,KAGrBhZ,MAAA,MAAA,CAAKoD,UAAU,iDAAiDgC,QAASiU,EAAiBxS,IAAKsT,EAAAA,0BAA0BrS,GAASsS,IAAI,cAIxIpB,GACAhZ,EAAAA,WAAKoD,UAAU,6BAA4BrE,SAC1CiB,MAACiG,EAAAA,eAAc,CAAA,cAAa,iBAAiBQ,UAAU,EAAOL,WAAY7E,IAAYyC,EAAAA,aAAaqW,WAAYjX,UAAU,eAAeiD,WA3DtH,KAClB4S,EACHA,MAEAvR,IAAmBwI,EAAAA,gBAEpB3I,KAAqB,WAyDnB4R,GACA7Y,EAAAA,KAACga,EAAAA,OAAM,CAAClX,UAAU,gCAAgCmX,QAASlB,EAAiBmB,KAAMrB,EAASpa,SAAA,CAC1FiB,EAAAA,IAAA,MAAA,CAAKoD,UAAU,mBAAkBrE,SAChCiB,MAAA,OAAA,CAAMoD,UAAU,WAAWgC,QAASiU,EAAeta,SAClDiB,EAAAA,IAACqF,EAAAA,EAAC,CAACjC,UAAU,wDAGfpD,EAAAA,IAAA,MAAA,CAAKoD,UAAU,4CAA2CrE,SACzDiB,EAAAA,IAACuX,GAAYC,KAAM1P,EAAS2S,EAAAA,OAAO3S,GAAQ4S,SAAWD,EAAAA,OAAOE,KAAKD,SAAUhD,iBAAiB,oDAMnG,CCnFA,SAASkD,GAAO5G,OACfA,EAAMrM,YACNA,EAAWjG,OACXA,EAAMmZ,uBACNA,EAAsBC,uBACtBA,EAAsBC,WACtBA,EAAUC,UACVA,EAASC,oBACTA,EAAmBpM,KACnBA,IAEA,MAAM/G,OAAEA,EAAMD,WAAEA,EAAUE,WAAEA,EAAUmT,kBAAEA,EAAiBC,SAAEA,EAAQvT,MAAEA,EAAKwT,SAAEA,GAAazT,GAAe,CAAA,EAClG0T,EAAW,CAACC,EAAAA,qBAAsBC,EAAAA,qBAAsBC,EAAAA,0BAA0BC,SAASL,GAAY,KACtGM,EAAYhF,GAAiBxX,EAAAA,UAAS,GAYvCyc,EAAe9Z,EAAAA,YAAYwI,UAChC,IACKgR,UACGxM,GAAM+M,KAAKC,QAAQ,CACxB7H,SACApM,QACAkU,KAAMX,EACNrT,SACAtH,OAAQuH,KAETkT,OApBFlL,UAAQC,QAAQnI,GAAY,GAAI,CAC/BP,OAAQ0M,EACRpM,MAAOA,EACPpH,OAAQuH,EACRgU,YAAab,EACbc,WAAY,IACZlU,OAAQA,GAiBR,CAAC,MAAOgE,GACRtB,QAAQC,IAAIqB,EACZ,GACC,CAACuP,KAEE9Z,UAAEA,GAAcC,aAAWC,EAAAA,kBAAoB,CAAA,EAErDhC,EAAAA,UAAU,MACLob,GAA0BC,IAC7Ba,KAEC,CAACd,EAAwBC,IAO5B,MAAMmB,GAAkBjB,GAAaH,GAA0BC,KAA4BO,EAE3F,OACCrb,EAAAA,IAACkc,EAAAA,KAAI9Y,UAAU,6CAA4CrE,SAC1DuB,EAAAA,KAAA,MAAA,CAAK8C,UAAU,0DAAyDrE,SAAA,CACvEiB,EAAAA,IAACyW,EAAgB,CAACE,OAAQjV,GAAQiV,OAAQD,cAAeA,IACzD1W,EAAAA,IAACmc,SAAM,CACN3B,KAAMkB,EACNnB,QAAS,CAAC6B,EAAGtL,OAKb1N,UAAU,gBACViZ,OAAO,SAAQtd,SAEfiB,EAAAA,IAAA,MAAA,CACCoD,UAAU,mIACVlB,MAAO,CAAEqX,WAAY7X,GAAQQ,OAAOuB,MAAMxB,0BAE1C3B,EAAAA,KAAA,MAAA,CAAK8C,UAAU,8BAA6BrE,SAAA,CAC3CiB,EAAAA,IAACqD,EAAAA,OAAM,CAACiZ,MAAO/a,IAAYyC,EAAAA,aAAauY,0BACxCvc,MAAA,MAAA,CAAKoD,UAAU,0CAAyCrE,SACvDiB,EAAAA,IAAA,QAAA,CAAO4G,QAAQ,OAAOxD,UAAU,sEAAsE8U,OAAK,EAACsE,MAAI,EAACC,UAAQ,EAAC7D,aAAW,EAAA7Z,SACpIiB,EAAAA,IAAA,SAAA,CAAQ6G,IAAKiB,IAAW4U,EAAAA,WAAWC,KAAOC,0BAA0BC,EAAAA,oBAAqBvJ,KAAK,kBAG/F2I,GAAkBjc,EAAAA,IAACiG,EAAAA,eAAc,CAAC7C,UAAU,qBAAqBgD,WAAY7E,IAAYyC,EAAAA,aAAa8Y,MAAOzW,WAAY,KAAK0U,oBAQtI,CC3FO,MAAMgC,EAAgB,KAC5B,MAAMpV,YACLA,EAAWqV,QACXA,EAAOC,QACPA,EAAOjC,UACPA,EAASlT,OACTA,EAAMoV,cACNA,EAAalE,eACbA,EAActR,iBACdA,EAAgBH,mBAChBA,EAAkBuT,uBAClBA,EAAsBD,uBACtBA,EAAsB/D,QACtBA,EAAOF,gBACPA,EAAeuG,cACfA,EAAa3V,kBACbA,EAAiBC,iBACjBA,EAAgBpD,UAChBA,EAAS4W,oBACTA,EAAmBpM,KACnBA,EAAIkM,WACJA,GACGvZ,EAAAA,WAAWwN,EAAAA,eACTkK,EAAiBvX,EAAAA,YAEvB,OAAIsb,EAEF3c,EAAAA,KAAA,MAAA,CAAA,kBAAqB,QAAOvB,SAAA,CAC3BiB,EAAAA,IAACyW,EAAgB,CAAA,GACjBzW,EAAAA,IAACmc,EAAAA,OAAM,CACNE,OAAO,SACP7B,MAAM,EACNpX,UAAU,gBACVmX,QAAS,CAAChF,EAAOzE,gBAMjB9Q,EAAAA,IAAC+Y,EAAgB,CAACC,kBAAeC,OAAQ+D,EAASlV,OAAQA,SAM1DkT,EACIhb,EAAAA,IAAA,MAAA,CAAA,kBAAqB,UAASjB,SAACiB,EAAAA,IAAC4a,EAAM,CAACI,WAAS,EAACD,WAAYA,MAGjEjE,EAEF9W,MAAA,MAAA,CAAA,kBAAqB,UAAUoD,UAAU,sFAAsFlB,MAAO,CAAEqX,WAAYL,GAAgBhX,OAAOuB,MAAMxB,iBAAiBlD,SACjMiB,EAAAA,IAACmX,EAAAA,cAAa,CAACC,IAAK8B,GAAgBvC,OAAQU,WAAW,YAItDT,EAAgBnQ,SACZzG,EAAAA,6BAAqB,gBAAejB,SAACiB,MAACsX,EAAAA,MAAK,CAAA,KAG9C6F,GAAkBnE,GAAmB8B,GAA2BD,GAchEsC,GAAiBrC,GAA0BD,KAA4B7B,EAE1EhZ,MAAA,MAAA,CAAA,kBAAqB,sBACrBA,EAAAA,IAAC4a,GACA5G,OAAQkJ,EACRpC,uBAAwBA,EACxBnT,YAAaA,EACbkT,uBAAwBA,EACxBI,oBAAqBA,EACrBpM,KAAMA,EACNkM,WAAYA,EACZrZ,OAAQwX,MAMV5Y,EAAAA,KAAA,MAAA,CAAA,kBAAqB,cAAavB,SAAA,CACjCiB,EAAAA,IAACyW,EAAgB,CAAA,GACjBzW,EAAAA,IAACmc,EAAAA,OAAM,CACNE,OAAO,SACP7B,MAAI,EACJpX,UAAU,gBACVmX,QAAS,CAAChF,EAAOzE,OAIhB/R,SAEDiB,MAAC+Y,EAAgB,CAChBC,eAAgBA,EAChBC,OAAQ,KACP+D,MACA3Y,KAEDqD,iBAAkBA,EAClBI,OAAQA,EACRP,mBAAoBA,SAjDtBvH,EAAAA,IAAA,MAAA,CAAA,kBAAqB,WAAUjB,SAC/BiB,MAAC4U,EAAa,CACbtN,OAAQ4V,EACRvV,YAAaA,EACbJ,mBAAoBA,EACpBC,kBAAmBA,EACnBC,iBAAkBA,EAClBC,iBAAkBA,OCzDhB0V,EAAqCC,IAC1C,MAAM3b,OACLA,EAAMsb,QACNA,EAAOlO,YACPA,EAAWC,kBACXA,EAAiBuO,cACjBA,EAAaC,YACbA,EAAWC,yBACXA,EAAwBC,yBACxBA,EAAwBC,0BACxBA,EAAyBC,YACzBA,EAAWC,cACXA,EAAa3C,oBACbA,EAAmBF,WACnBA,EAAUkC,QACVA,EAAOjC,UACPA,GACGqC,EACE1V,EAA2B,CAChCC,MAAO,GACPC,WAAY,GACZC,OAAQ,OACRC,WAAY,EACZqT,SAAU,GACVF,kBAAmB,EACnB2C,gBAAiB,GACjBC,UAAW,GACXC,YAAa,QACTV,EAAM1V,aAAe,IAEpBqW,EAAQX,EAAMW,OAAS,IACvBlW,OAAEA,EAAMsT,SAAEA,EAAQvT,WAAEA,EAAUE,WAAEA,EAAUH,MAAEA,EAAKsT,kBAAEA,EAAiB2C,gBAAEA,EAAeE,YAAEA,EAAWD,UAAEA,GAAcnW,EAClHkH,EAAO9O,EAAAA,QAAQ,IAAMke,EAAAA,eAAeD,GAAQ,CAACA,KAC5Cb,EAAevO,GAAoB1P,EAAAA,UAAS,IAC5C2b,EAAwBqD,GAA6Bhf,EAAAA,UAAS,IAC9D4b,EAAwBqD,GAA6Bjf,EAAAA,UAAS,IAC9D0X,EAAiBC,GAAsB3X,WAAS,CACtDuH,UAAU,EACVsL,QAAS,MAEHiH,EAAgBxR,GAAqBtI,EAAAA,SAAS,KAC9Ckf,EAAiB7W,GAAsBrI,EAAAA,UAAS,IAChD4X,EAASC,GAAc7X,EAAAA,UAAS,IAChCmf,EAAW3P,GAAgBxP,EAAAA,UAAS,IACpCge,EAAexV,GAAoBxI,EAAAA,SAAS,KAC5Cof,EAAe7W,GAAoBvI,EAAAA,SAAwBgV,EAAAA,4BAC5DqK,SAAEA,GCzDT,SAAuBF,GACrB,MAAOE,EAAUC,GAAetf,EAAAA,SAAyB,KAClDuf,EAAmBC,GAAwBxf,EAAAA,UAAS,GAErDoW,EAAoBzT,cAAa0T,IACrC,IACE,MAAMoJ,MAAEA,EAAKnJ,KAAEA,EAAIG,MAAEA,GAAUJ,EAC/BiJ,EAAapR,GAAyB,IACjCA,EACH,CACEuR,MAAOA,GAAOC,iBAAc/f,EAC5B2W,KAAMA,GAAMoJ,iBAAc/f,EAC1B8W,MAAOA,GAAOiJ,iBAAc/f,EAC5BggB,WAAW,IAAIC,MAAOC,gBAG3B,CAAC,MAAOjT,GACPtB,QAAQC,IAAIqB,EACb,GACA,IAEGgK,EAAoBjU,EAAAA,YAAYwI,UACpC,MAAM0L,EACJV,uBAEF,QAC+B,IAAtBU,GACwC,mBAAxCA,EAAkBD,kBAEzB,IAEmB,kBADMC,EAAkBD,oBAEvC4I,GAAqB,GAErBlU,QAAQqB,KAAK,wCAEhB,CAAC,MAAOC,GACPtB,QAAQsB,MAAM,kDAAmDA,EAClE,MAGD4S,GAAqB,IAEtB,IAmBH,OAjBAjf,EAAAA,UAAU,KACJ4e,GAAaI,EACftf,OAAOQ,iBAAiB,oBAAqB2V,GAE7CkJ,EAAY,IAEP,KACLrf,OAAOS,oBAAoB,oBAAqB0V,KAEjD,CAAC+I,EAAWI,EAAmBnJ,IAElC7V,EAAAA,UAAU,KACJ4e,GACFvI,KAED,CAACuI,EAAWvI,IAER,CAAEyI,WACX,CDNsBS,CAAcX,GACnCY,uBAEA,MAAM5a,GAAYxC,EAAAA,YAAY,KAC7B6F,EAAiBwI,EAAAA,gBACjB1I,EAAkB,IAClBoH,GAAiB,GACjBrH,GAAmB,IACjB,IAyBG2X,GAA8B,KACnCzX,EAAiB,MACjBC,EAAiB,KAGZyX,GAAYtd,cACjBwI,MAAOsH,IACN,GAAIA,GAA6B,YAArBA,GAAMyN,YAAiD,iBAArBzN,GAAM0N,YAAgD,MAAf1N,GAAM2N,KAS1F,OARAzN,wBAAsB,CACrBC,UAAW,GAAGjK,qCACdP,OAAQ4V,EACRjK,OAAQ,UACRrL,UAEDsW,GAA0B,QAC1BR,IAA4B/L,GAG7BiM,IAAgBjM,GAChBuM,GAA0B,GAC1BgB,KACA,MAAMK,EAAcrC,EACpBiB,GAA0B,GAC1BtM,wBAAsB,CACrBC,UAAW,GAAGjK,iCACdP,OAAQiY,EACRtM,OAAQ,UACRrL,UAEG0W,GACHzM,wBAAsB,CACrBC,UAAW,GAAGjK,yBACdP,OAAQiY,EACRtM,OAAQ,UACRuM,eAAgBtL,EAAAA,0BAA4BoK,EAC5C1W,WAGH,CAACwT,EAAUvT,EAAYqV,IAGlBuC,GAAmCzL,IACxCmK,GAA0B,GAC1B3W,EAAkB,IAClB0W,GAA0B,GAC1BrP,EAAK6Q,YAAYC,wBAAwB,CACxC3L,OAAkBkJ,EAClB0C,UAAW,KACVC,yBAAuB,CACtB/N,UAAW,GAAGjK,cACdP,OAAQ4V,EACR4C,WAAY,WACZxM,KAAM,6BACN1L,WAGFmY,OAAQ,KACPvC,MACAqC,yBAAuB,CACtB/N,UAAW,GAAGjK,cACdP,OAAQ4V,EACR4C,WAAY,OACZxM,KAAM,6BACN1L,WAGF2S,QAAS,KACRkD,MACAoC,yBAAuB,CACtB/N,UAAW,GAAGjK,cACdP,OAAQ4V,EACR4C,WAAY,QACZxM,KAAM,6BACN1L,WAGFoY,QAAU3S,IAnGI,IAACsE,IAoGNtE,EAnGVsQ,IAAc,IAAKhM,EAAMI,QAASmI,EAAAA,mBAAmBvI,KACrDuN,KACAf,GAA0B,GAC1B3W,EAAkBmK,GAClBuM,GAA0B,GAC1BrM,wBAAsB,CACrBC,UAAW,GAAGjK,gCACdP,OAAQ4V,EACRjK,OAAQ,SACRrL,QACAmK,QAASmI,EAAAA,mBAAmBvI,KAEzB2M,GACHzM,wBAAsB,CACrBC,UAAW,GAAGjK,yBACdP,OAAQ4V,EACRjK,OAAQ,SACRuM,eAAgBtL,EAAAA,0BAA4BoK,EAC5C1W,UAkFAiY,yBAAuB,CACtB/N,UAAW,GAAGjK,cACdP,OAAQ4V,EACR4C,WAAY,QACZxM,KAAM,6BACN1L,WAGFuX,UAAYxN,IACXkO,yBAAuB,CACtB/N,UAAW,GAAGjK,cACdP,OAAQ4V,EACR4C,WAAY,UACZxM,KAAM,6BACN1L,UAEDuX,GAAUxN,OAKPhD,GAAiB9M,EAAAA,YACtBwI,MAAO4V,EAAYC,KAClB,MAAMC,EAAeC,QAAQH,EAAKjhB,cAAsBqhB,QAAQ,IAE1DC,EAAWJ,QAAoDA,QAAoBK,EAAAA,YAAYN,GAC/FO,EAAgBC,EAAAA,0BAA0B,CAC/C3Y,SACA4Y,aAAc,GAAGxF,IACjB1a,OAAQ,GAAGuH,IACX4Y,mBAAoB9Y,EACpB+Y,YA9Le,IA+LfC,UAAWzF,EACX0F,aAAc/C,EACdgD,WAAYjD,IAEbjM,wBAAsB,CACrBC,UAAW,GAAGjK,wBACdP,OAAQ4V,EACRtV,QACA+J,KAAMqP,KAAKC,UAAU,IAAIT,EAAe,CAAEL,eAAce,UAAWZ,GAAY,OAEhFhD,MACA,UACOzO,EAAKsS,WAAWC,mBAAmB,CACxCnB,OACAO,gBACAxM,OAAQkJ,EACRtV,gBAEKiH,EAAKsS,WAAWE,cAAc,CACnCC,MAAOzD,EACP0D,UAAW,SACXC,KAAMjD,EACNvK,OAAQkJ,IAET1S,QAAQC,IAAI,+BACZlD,GAAmB,GACnBsK,wBAAsB,CACrBC,UAAW,GAAGjK,iBACdP,OAAQ4V,EACRjK,OAAQ,UACRrL,QACA+J,KAAMqP,KAAKC,UAAUT,KAEtB3O,wBAAsB,CACrBC,UAAW,gBACXxK,OAAQ4V,EACRjK,OAAQ,UACRrL,UAEDH,EAAiByM,EAAAA,2BACjBvE,WAAW,KACVpI,GAAmB,IACjB,IACH,CAAC,MAAOuE,GACR+F,wBAAsB,CACrBC,UAAW,gBACXxK,OAAQ4V,EACRjK,OAAQ,SACRrL,QACAmK,QAASmI,EAAAA,mBAAmBpO,KAE7B+F,wBAAsB,CACrBC,UAAW,GAAGjK,gBACdP,OAAQ4V,EACRjK,OAAQ,SACRrL,QACAmK,QAASmI,EAAAA,mBAAmBpO,GAC5B6F,KAAMqP,KAAKC,UAAUT,KAEtBhZ,EAAkB0S,EAAAA,mBAAmBpO,IACrCvE,GAAmB,GACnBoW,IAAc,IAAI7R,EAAOiG,QAASmI,EAAAA,mBAAmBpO,KACrDoT,IACA,CAAS,QACTxQ,GAAa,GACb6O,KACA,GAEF,CAACL,EAAeqB,EAAUjB,EAAeC,IAkCpCvG,GAAyBnV,EAAAA,YAAYwI,UAC1C,IAAKtC,EAAa,QAAUA,EAAa,OAIxC,YAHA4V,IAAc,CACb5L,QAAS,4DAKX,MAAMkF,QAAmBC,0BACrBD,EAAWxQ,UACdoL,wBAAsB,CACrBC,UAAW,GAAGjK,sBACdP,OAAQ4V,EACRjK,OAAQ,SACRrL,UAEDmP,GAAW,KAQXA,GAAW,GACXlF,wBAAsB,CACrBC,UAAW,GAAGjK,sBACdP,OAAQ4V,EACRjK,OAAQ,UACRrL,WAGFiP,EAAmBI,GACnBzP,EAAkB,IAClBD,GAAmB,IACjB,CAACK,EAAOsV,EAAexV,EAAkBG,IAe5C,OAbApI,EAAAA,UAAU,KACLwd,GAAWjC,GACXnT,GACHmP,MAEC,CAACnP,EAAYE,EAAYkV,EAASjC,IAErCvb,EAAAA,UAAU,KACLwd,GAAWjC,GACXoD,GAAmBvW,GAAcqV,GACpCuC,MAEC,CAACrB,EAAiBvW,EAAYqV,EAAeD,EAASjC,IAExDhb,2BAAiB,iBAAgBjB,SACjCiB,EAAAA,IAACyhB,EAAAA,wBAAuB,CAAA1iB,SACvBiB,MAAC0hB,EAAAA,eAAc,CAAChgB,OAAQA,EAAM3C,SAC7BiB,EAAAA,IAAClB,EAAoB,CAAAC,SACpBiB,MAACgP,EAAAA,cAAc/O,SAAQ,CACtBC,MAAO,CACNyH,cACAqV,UACAW,cACAV,UACAjC,YACA4C,gBACA9V,SACAoV,gBACAlE,iBACAtR,mBACAH,qBACAuT,yBACAD,yBACA/D,UACAF,kBACAuG,gBACAzO,eACAC,kBACAC,mBACAvK,aACAmD,oBACAC,mBACAqH,cACAC,oBACAkM,sBACAF,aACAlM,QACA9P,SAEDiB,MAAC+c,EAAa,CAAA,yCAeiCM,IACpD,MAAOjT,EAASuX,GAAcziB,EAAAA,UAAS,GAKvC,OAJAO,EAAAA,UAAU,KACTkiB,GAAW,IACT,IAEEvX,EAIEpK,MAACod,EAAQ,IAAKC,IAHbrd,EAAAA,IAAA,MAAA,CAAA,cAAiB"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import{jsx as e,jsxs as t,Fragment as a}from"react/jsx-runtime";import{useRef as n,useState as r,useMemo as o,useCallback as c,useEffect as i,useContext as s}from"react";import{v as l,p as d,s as u,a as m,L as h,u as f,H as g,b as p,S as y,m as S,g as b,G as w,d as v,F as x,P as F,c as N,e as I,M as _,f as k,h as E,i as C,j as M,k as D,l as T,n as P,o as R,q as z,r as $,t as j,C as L,w as V,x as O,y as W}from"./Modal-CZgyeNwW.mjs";import{ArrowRight as U}from"lucide-react";import{Drawer as A}from"@mui/material";let B=null,q=!1;const J=async()=>{if("undefined"==typeof window)return null;if(B)return B;if(q)for(;q;)if(await new Promise(e=>setTimeout(e,100)),B)return B;q=!0;try{const[e,t]=await Promise.all([import("@tensorflow/tfjs"),import("./pose-detection.esm-EvS-BndH.mjs")]);await e.setBackend("webgl"),await e.ready();const a=await t.createDetector(t.SupportedModels.BlazePose,{runtime:"tfjs",modelType:"full"}),n=document.createElement("canvas");n.width="number"==typeof l.width?l.width:l.width.ideal??l.width.max??1280,n.height="number"==typeof l.height?l.height:l.height.ideal??l.height.max??720;const r=n.getContext("2d");if(r){r.fillStyle="black",r.fillRect(0,0,n.width,n.height);try{await a.estimatePoses(n)}catch(e){console.warn("Warmup frame failed (non-fatal):",e)}}return B=a,a}catch(e){return console.error("Failed to preload detector:",e),null}finally{q=!1}};function G({faceScanId:e,onValidPose:t,onScanComplete:a,onModelReady:s,onStartRecording:l,shopDomain:h,isError:f,isSuccess:g}){const p=n(null),[y,S]=r(0),[b,w]=r(0),[v,x]=r(!1),[F,N]=r(!1),I=n(!1),_=n(null),k=n(null),E=n(y),C=n(b),M=n([]),D=n(""),T=n(0),P=n(!1),R=n(0),z="undefined"!=typeof navigator&&(/iPad|iPhone|iPod/.test(navigator.userAgent)||"MacIntel"===navigator.platform&&navigator.maxTouchPoints>1),$=o(()=>["Face front","Turn head fully left","Turn head fully right","Smile facing front"],[]),j=o(()=>function(e,t){let a;return(...n)=>{a&&clearTimeout(a),a=setTimeout(()=>e(...n),t)}}(e=>{d.capture(`${h}/face_scan_pose_detection`,e)},4e3),[h]),L=c((t,a,n,r,o,c)=>{const i=Date.now();if(!(i-R.current<4e3)){R.current=i;try{const i=t[0]||[0,0,0],s=t[2]||[0,0,0],l=t[5]||[0,0,0],d=t[9]||[0,0,0],u=t[10]||[0,0,0],m=t.map(e=>e[2]).filter(e=>e>0),h=m.length>0?m.reduce((e,t)=>e+t,0)/m.length:0,f=a.map(e=>e[2]).filter(e=>e>0),g=f.length>0?f.reduce((e,t)=>e+t,0)/f.length:0,p=Math.abs(s[0]-l[0]),y=i[0]-l[0],S=s[0]-i[0];let b="frontal";1===n?b="left":2===n?b="right":3===n&&(b="frontal_smile");const w={faceScanId:e,stage:n,timestamp:(new Date).toISOString(),data:JSON.stringify({headValid:r,bodyInvalid:o,consecutiveValid:c,avgFaceScore:parseFloat(h.toFixed(3)),avgBodyScore:parseFloat(g.toFixed(3)),faceKeypointsDetected:m.length,bodyKeypointsDetected:f.length,eyeDistance:parseFloat(p.toFixed(2)),noseToRightRatio:parseFloat((y/p).toFixed(3)),noseToLeftRatio:parseFloat((S/p).toFixed(3)),noseScore:parseFloat(i[2].toFixed(3)),leftEyeScore:parseFloat(s[2].toFixed(3)),rightEyeScore:parseFloat(l[2].toFixed(3)),leftMouthScore:parseFloat(d[2].toFixed(3)),rightMouthScore:parseFloat(u[2].toFixed(3)),expectedPosition:b})};j(w)}catch(e){console.warn("Failed to track pose detection:",e)}}},[e,j]),V=()=>{d.capture(`${h}/face_scan_detection_started`,{faceScanId:e,stage:E.current,timestamp:(new Date).toISOString(),stageName:$[E.current]}),setTimeout(()=>{N(!0)},1500)};return i(()=>{if(!f&&!g)return(async()=>{if("undefined"!=typeof window)try{console.log("Initializing Face Scan...");const e=await J();e&&(p.current=e,console.log("Face scan model loaded successfully"),x(!0),s?.())}catch(e){console.error("Error initializing face scan:",e)}})(),()=>{(async()=>{try{x(!1)}catch(e){console.error("Error disposing resources:",e)}finally{S(0),w(0),E.current=0,C.current=0,M.current=[]}})()}},[f,g]),i(()=>{E.current=y},[y]),i(()=>{C.current=b},[b]),{faceScanDetector:async(n,r)=>{if(p.current&&n.current&&v&&F&&!(n.current.readyState<2||I.current)){I.current=!0;try{const o=n.current.videoWidth||720,c=n.current.videoHeight||1280;_.current||(_.current=document.createElement("canvas"),_.current.width=o,_.current.height=c);const i=_.current.getContext("2d");i&&i.drawImage(n.current,0,0,o,c);const s=await p.current.estimatePoses(i?_.current:n.current);if(s.length>0){const n=[],o=[];if(s[0].keypoints.forEach((e,t)=>{const a=e.score??0,r=[e.x??-1,e.y??-1,a];t<=10?n.push(r):o.push(r)}),r?.current){const e=r.current.getContext("2d"),{width:t,height:a}=r.current;if(e){e.clearRect(0,0,t,a);const n=Math.min(C.current/2,1),r=.35*a;e.beginPath(),e.strokeStyle="#00ff00",e.lineWidth=6,e.arc(t/2,a/2,r+10,-Math.PI/2,-Math.PI/2+2*n*Math.PI),e.stroke()}}const c=((e,t)=>{if(!t[0]||!t[2]||!t[5]||t[0][2]<.2||t[2][2]<.2||t[5][2]<.2)return!1;const a=.5*(t[10][1]+t[9][1])-.5*(t[5][1]+t[2][1]),n=(.5*(t[10][1]+t[9][1])-t[0][1])/a,r=n>.7||n<.3,o=Math.abs(t[2][1]-t[5][1])<.5*a&&Math.abs(t[9][1]-t[10][1])<.5*a,c=t[2][0]-t[5][0],i=t[0][0]-t[5][0],s=i>.4*c&&i<.6*c,l=i>.85*c,d=t[2][0]-t[0][0]>.85*c;switch(e){case 0:case 3:return!r&&o&&s;case 1:return!r&&o&&l;case 2:return!r&&o&&d;default:return!1}})(E.current,n),i=o.slice(2).filter(e=>e[2]>.7).length<=2;M.current.push(c&&i),M.current.length>10&&M.current.shift();if(M.current.filter(Boolean).length>=2){const e=C.current+1;w(e)}else w(Math.max(0,C.current-1));if(C.current>=2){t?.();const n=E.current+1;w(0),M.current=[],S(n),N(!1),(async()=>{if(await u.playAudio(`${m}face-scan-vos/Sound-effect.mp3`),d.capture(`${h}/face_scan_detection_stage_completed`,{faceScanId:e,completedStage:E.current,nextStage:n,timestamp:(new Date).toISOString(),totalStages:4,stageName:$[E.current]}),1===n&&l&&l(),n>=4)a?.();else{let e=null;switch(n){case 1:e="Left.mp3";break;case 2:e="Right.mp3";break;case 3:e="Smile.mp3"}e&&await u.playAudio(`${m}face-scan-vos/${e}`),V()}})()}L(n,o,E.current,c,i,C.current)}}catch(e){console.error("Pose detection error:",e)}finally{I.current=!1}}},scanStage:y,setScanStage:S,consecutiveValid:b,isModelLoaded:v,resetScan:()=>{d.capture(`${h}/face_scan_reset`,{faceScanId:e,stage:E.current,timestamp:(new Date).toISOString(),stageName:$[E.current],consecutiveValid:C.current}),S(0),w(0),N(!1),E.current=0,C.current=0,M.current=[],D.current="",T.current=0,P.current=!1,k.current&&(speechSynthesis.cancel(),k.current=null)},enableVoiceCommands:()=>{if(P.current=!0,"undefined"!=typeof window&&z&&"speechSynthesis"in window){const e=new SpeechSynthesisUtterance("");e.volume=0,speechSynthesis.speak(e)}},startScanSequence:V,isScanningActive:F}}function H({onRetry:a,loading:n}){const{translate:r}=s(h)||{},o=f();return e("div",{"data-testid":"face-scan-error-screen",className:"fixed common-ui-main top-[0] left-[0] z-[999] flex justify-center items-center w-full h-full",style:{background:o?.style?.base?.backgroundColor},children:t("div",{className:"flex flex-col w-full items-center p-[1rem] rounded-lg ",children:[e(g,{noTitle:!0}),t("div",{className:"flex flex-col items-center w-full",children:[e("h2",{className:"text-xl font-semibold text-gray-800 ",style:{fontFamily:o?.style?.heading?.headingFontFamily||"SeriouslyNostalgic Fn",fontSize:o?.style?.heading?.headingFontSize||"32px",color:o?.style?.heading?.headingColor||"#000",fontWeight:o?.style?.heading?.headingFontWeight||"normal"},children:r?.(p.scanFailed)}),e("p",{className:"mb-[1.5rem]",style:{fontFamily:o?.style?.subheading?.subheadingFontFamily||"'Inter', sans-serif",fontSize:o?.style?.subheading?.subheadingFontSize||"14px",color:o?.style?.subheading?.subheadingColor||"#4b5563",fontWeight:o?.style?.subheading?.subheadingFontWeight||"normal"},children:r?.(p.clickToResetScan)}),e(y,{"data-testid":"face-scan-retry-btn",disabled:n,className:"w-full h-[45px] shadow-[0px_1px_2px_0px_#00000040] text-[16px]",buttonText:r?.(p.resetScan),postfixIcon:e(U,{}),buttonFunc:()=>a?.()})]})]})})}function K({stage:a,videoLoading:n,setVideoLoading:r,videoError:o,setVideoError:c,gender:i,btnConfig:l}){const{translate:d}=s(h)||{},u=f();return-1===a?t("div",{"data-testid":"face-scan-guide",className:"text-center common-ui-main p-[16px] w-full h-full",style:{background:u?.style?.base?.backgroundColor},children:[t("div",{className:"w-full",children:[e(g,{noTitle:!0}),e("h2",{style:{fontFamily:u?.style?.heading?.headingFontFamily||"SeriouslyNostalgic Fn",fontSize:u?.style?.heading?.headingFontSize||"32px",color:u?.style?.heading?.headingColor||"#000",fontWeight:u?.style?.heading?.headingFontWeight||"normal"},children:d?.(p.faceVisible)}),n&&e("div",{className:"mb-4 flex items-center justify-center",style:{fontFamily:u?.style?.subheading?.subheadingFontFamily||"'Inter', sans-serif",fontSize:u?.style?.subheading?.subheadingFontSize||"14px",color:u?.style?.subheading?.subheadingColor||"#4b5563",fontWeight:u?.style?.subheading?.subheadingFontWeight||"normal"},children:d?.(p.loadingVideo)}),o&&e("div",{className:"mb-4 text-red-600",style:{fontFamily:u?.style?.subheading?.subheadingFontFamily||"'Inter', sans-serif",fontSize:u?.style?.subheading?.subheadingFontSize||"14px",color:"#ff0000",fontWeight:u?.style?.subheading?.subheadingFontWeight||"normal"},children:d?.(p.videoLoadFailed)}),!o&&e("div",{className:" w-[100px] mx-auto",children:e("video",{src:i===w.Male?S:b,autoPlay:!0,loop:!0,controls:!1,muted:!0,playsInline:!0,className:"h-full w-full object-contain border-none",onCanPlay:()=>r(!1),onLoadStart:()=>r(!0),onError:()=>c(!0),"aria-label":"Instructional video: Please remove your glasses before starting the scan."})})]}),e("div",{className:"flex justify-center w-full p-[1rem]",children:e(y,{"data-testid":"face-scan-start-btn",disabled:l.isDisabled,className:"!w-[60px] !h-[35px] !py-0 !px-0",buttonText:d?.(l.label),postfixIcon:l?.icon,buttonFunc:l.onClick})})]}):e("div",{className:"text-center common-ui-main p-[16px] w-full h-full",style:{background:u?.style?.base?.backgroundColor},children:t("div",{className:"w-full",children:[e(g,{noTitle:!0}),e("h3",{className:"mb-[0.5rem]",style:{fontFamily:u?.style?.heading?.headingFontFamily||"SeriouslyNostalgic Fn",fontSize:u?.style?.heading?.headingFontSize||"32px",color:u?.style?.heading?.headingColor||"#000",fontWeight:u?.style?.heading?.headingFontWeight||"normal"},children:d?.(v[a])}),t("div",{className:"max-w-[400px] justify-center gap-1 mx-auto flex items-center",children:[e("div",{className:` w-[120px] overflow-hidden ${0===a?"opacity-100":"opacity-0 hidden"} `,children:e("video",{className:"h-full w-full object-contain border-none",muted:!0,loop:!0,autoPlay:!0,playsInline:!0,children:e("source",{src:i===w.Male?x.male.forward:x.female.forward,type:"video/mp4"})})}),e("div",{className:` w-[120px] overflow-hidden ${1===a?"opacity-100":"opacity-0 hidden"} `,children:e("video",{className:"h-full w-full object-contain border-none",muted:!0,loop:!0,autoPlay:!0,playsInline:!0,children:e("source",{src:i===w.Male?x.male.left:x.female.left,type:"video/mp4"})})}),e("div",{className:` w-[120px] overflow-hidden ${2===a?"opacity-100":"opacity-0 hidden"} `,children:e("video",{className:"h-full w-full object-contain border-none",muted:!0,loop:!0,autoPlay:!0,playsInline:!0,children:e("source",{src:i===w.Male?x.male.right:x.female.right,type:"video/mp4"})})}),e("div",{className:` w-[120px] overflow-hidden ${3===a?"opacity-100":"opacity-0 hidden"} `,children:e("video",{className:"h-full w-full object-contain border-none",muted:!0,loop:!0,autoPlay:!0,playsInline:!0,children:e("source",{src:i===w.Male?x.male.smile:x.female.smile,type:"video/mp4"})})})]})]})})}const X=({webcamRef:n,canvasRef:r})=>{const{isError:o,isSuccess:c,showLoader:i,hasError:d,errorType:u,resetScanState:m,showGuideCard:h,scanStage:g,videoLoading:p,setVideoLoading:y,videoError:S,setVideoError:b,gender:w,getButtonText:v,isScanning:x,startScan:k,isModelLoaded:E,onRetry:C}=s(F),M=f();return o?e(H,{onRetry:C}):c?e("div",{className:"fixed z-[9] common-ui-main w-full h-full",style:{background:M?.style?.base?.backgroundColor},children:e(N,{url:M?.loader,loaderType:"black"})}):t(a,{children:[e("audio",{id:"audioElement",crossOrigin:"anonymous",preload:"auto",style:{position:"absolute",zIndex:-99999},src:void 0}),i&&!d&&e("div",{className:"fixed z-[9] common-ui-main w-full h-full",style:{background:M?.style?.base?.backgroundColor},children:e(N,{url:M?.loader,loaderType:"black"})}),e("div",{className:"h-full flex-col common-ui-main relative w-full flex justify-center items-center text-center rounded-t-[20px]",style:{background:M?.style?.base?.backgroundColor},children:e("div",{className:"flex-1 w-full max-w-md overflow-hidden",children:t("div",{className:"w-full h-full",children:[e("video",{ref:n,autoPlay:!0,playsInline:!0,muted:!0,width:l.width.ideal,height:l.height.ideal,className:"w-full h-full object-cover fixed left-0 top-0 z-0",style:{transform:"scaleX(-1)"}}),e("canvas",{ref:r,width:l.width.ideal,height:l.height.ideal,style:{transform:"scaleX(-1)",opacity:"0"}})]})})}),!i&&d&&u===I.Camera&&e(_,{testId:"face-scan-camera-error-screen",config:M}),!i&&d&&u!==I.Camera&&e(H,{loading:i,onRetry:m}),h&&!d&&!i&&e(A,{open:!0,className:"face-scan-small camera-draw",anchor:"bottom",onClose:(e,t)=>{},hideBackdrop:!0,children:e(K,{stage:g,videoLoading:p,setVideoLoading:y,videoError:S,setVideoError:b,gender:w,btnConfig:{label:v(),onClick:x?m:k,isDisabled:i||!E,icon:e(x?U:a,{})}})})]})},Q=t=>{const{onScanSuccess:a,onScanError:s,onRetry:l,onScanStart:d,onCaptureComplete:h,onUploadStart:f,onUploadEnd:g,onMeasurementSocketStart:y,onMeasurementSocketClose:S,config:b,isError:w,isSuccess:v}=t,x=t.userDetails??{email:"",gender:"male",deviceFocalLength:0,shopDomain:"",bodyScanId:"",callbackUrl:null},N=t.token??"",_=n(null),U=n(null),A=n(null),B=n([]),q=n(null),J=n(null),H=n(0),K=n(null),[Q,Y]=r(!1),[Z,ee]=r(!1),[te,ae]=r(!1),[ne,re]=r(!0),[oe,ce]=r(!1),[ie,se]=r(!1),[le,de]=r(!1),[ue,me]=r(null),[he,fe]=r(k()),ge=o(()=>E(N),[N]),{email:pe,gender:ye,deviceFocalLength:Se,shopDomain:be,callbackUrl:we,bodyScanId:ve}=x,[xe,Fe]=r([]),[Ne,Ie]=r(C),_e=o(()=>M(),[]),ke=D(xe),Ee=T(ke);P();const Ce=c(()=>{console.log("Stopping recording..."),cancelAnimationFrame(H.current),K.current&&(K.current.pause(),K.current.srcObject=null,K.current=null),A.current&&"recording"===A.current.state&&A.current.stop(),A.current=null},[]),Me=async()=>{if(h?.(),B.current){if(0===B.current.length)return console.error("No video data recorded"),me(I.Scan),de(!0),void Y(!1);f?.(),Y(!0);const e=new File(B.current,`${he}.${Ee}`,{type:ke}),t=J.current??await V(e),a=e.size,n=(a/1048576).toFixed(2),r=Math.round(a/1e4),o={video_size_mb:parseFloat(n),video_size_bytes:a,video_fps:t||0,blob_count:B.current.length,estimated_duration_seconds:r},c=[{gender:ye},{face_scan_id:he},{focal_length:`${Se}`},{customer_store_url:be},{scan_type:"face_scan"},{body_scan_id:ve}];we&&c.push({callback_url:we}),O({eventName:`${be}/face_scan_meta_data`,faceScanId:he,email:pe,data:JSON.stringify({metaData:c,videoData:o})});const i=Date.now();O({eventName:`${be}/face_scan_upload_start`,faceScanId:he,email:pe,startTime:i});try{const t=await ge.fileUpload.faceScanFileUploader({file:e,arrayMetaData:c,objectKey:he,email:pe,contentType:e.type}),a=Date.now();O({eventName:`${be}/face_scan_upload_complete`,faceScanId:he,email:pe,completionTime:a,uploadDuration:a-i}),g?.(),console.log("✅ Upload successful",t),ge.measurement.handlFaceScaneSocket({onPreopen:()=>{y?.(),W({eventName:`${be}/webSocket`,faceScanID:he,connection:"pre_open",type:"faceScan_recommendation",email:pe})},faceScanId:he,onOpen:()=>{W({eventName:`${be}/webSocket`,faceScanID:he,connection:"open",type:"faceScan_recommendation",email:pe}),console.log("websocket connect open")},onError:e=>{W({eventName:`${be}/webSocket`,faceScanID:he,connection:"error",type:"faceScan_recommendation",email:pe}),Oe(e)},onSuccess:e=>{W({eventName:`${be}/webSocket`,faceScanID:he,connection:"success",type:"faceScan_recommendation",email:pe}),We(e)},onClose:()=>{S?.(),console.log("websocket connect close"),W({eventName:`${be}/webSocket`,faceScanID:he,connection:"close",type:"faceScan_recommendation",email:pe})}})}catch(e){g?.(),Oe(e)}}},De=c(()=>{const e=_.current?.srcObject;if(!e)return;const t=e.getVideoTracks()[0].getSettings(),{width:a=1280,height:n=720}=t,r=document.createElement("canvas"),o=r.getContext("2d");a>n?(r.width=n,r.height=a):(r.width=a,r.height=n);const c=document.createElement("video");c.srcObject=e,c.muted=!0,c.playsInline=!0,c.play(),K.current=c;const i=()=>{a>n?(o?.save(),o?.translate(r.width,0),o?.rotate(Math.PI/2),o?.drawImage(c,0,0,n,a),o?.restore()):o?.drawImage(c,0,0,a,n),H.current=requestAnimationFrame(i)};H.current=requestAnimationFrame(i);const s=t.frameRate??10;J.current=s;const l=r.captureStream(s),d={videoBitsPerSecond:15e5};let u;xe.length>0&&(d.mimeType=xe[0]);try{u=new MediaRecorder(l,d)}catch(e){return console.error("MediaRecorder init failed:",e),me(I.Scan),de(!0),void Y(!1)}B.current=[],u.ondataavailable=e=>{e?.data&&e.data.size>0&&B.current&&B.current.push(e.data)},u.onstop=()=>{console.log("Recording stopped, total blobs:",B?.current?.length),Me()},u.start(100),A.current=u,console.log("Recording started successfully (portrait normalized)")},[xe,Me]),{faceScanDetector:Te,scanStage:Pe,setScanStage:Re,resetScan:ze,isModelLoaded:$e,startScanSequence:je}=G({faceScanId:he,shopDomain:be,isError:w,isSuccess:v,onScanComplete:()=>{Ce()},onModelReady:()=>{ee(!0)},onStartRecording:()=>{console.log("Stage 0 completed - starting recording for stages 1-3"),De()}}),Le=c(()=>{$e&&(d?.(),Re(0),u.playAudio(`${m}face-scan-vos/Face-forward.mp3`),setTimeout(()=>{ae(!0),setTimeout(()=>{je()},200)},200))},[Re,ae,je,$e,d]),Ve=c(()=>{ae(!1),re(!0),Ce(),ze(),l?.(),B.current=[],J.current=null,A.current&&(A.current=null),de(!1),me(null),Re(-1),fe(k()),_.current&&q.current&&(_.current.srcObject=q.current,console.log("Camera stream restored after reset"))},[Re,ae,re,Ce,ze,fe]),Oe=e=>{console.log(e,"ws error"),s?.(e),me(I.Scan),de(!0),Y(!1),re(!1),O({eventName:`${be}/faceScan`,faceScanId:he,status:"failed",email:pe,data:JSON.stringify(e)})},We=e=>{console.log(e,"ws success"),e&&"intermediate"===e?.resultType&&O({eventName:`${be}/faceScan_success/intermediate`,faceScanId:he,status:"success",email:pe,data:JSON.stringify(e)}),a?.(e)};i(()=>{if(!w&&!v)return navigator.mediaDevices.getUserMedia&&navigator.mediaDevices.getUserMedia({video:Ne}).then(e=>{q.current=e,_.current&&(_.current.srcObject=e)}).catch(e=>{if(console.error("Error accessing webcam:",e),Ne===C)return void Ie(R);const t=["NotAllowedError","PermissionDeniedError","NotFoundError","DevicesNotFoundError","NotReadableError","TrackStartError"].includes(e?.name);me(t?I.Camera:I.Scan),de(!0)}),()=>{q.current&&q.current.getTracks().forEach(e=>e.stop())}},[w,v,Ne]),i(()=>{if(!Z||!te)return;const e=setInterval(()=>{Te(_,U)},500);return()=>clearInterval(e)},[Te,Z,te]);return console.log("Model ready:",Z,"Has error:",le,"Is scanning:",te,"showLoader",Q),i(()=>{const e=z(_e,$);Fe(e)},[_e]),i(()=>{Re(-1)},[]),e("div",{"data-face-scan-stage":Pe,children:e(j,{children:e(L,{config:b,children:e(F.Provider,{value:{isError:w,isSuccess:v,showLoader:Q,hasError:le,errorType:ue,resetScanState:Ve,showGuideCard:ne,scanStage:Pe,videoLoading:oe,setVideoLoading:ce,videoError:ie,setVideoError:se,gender:ye,getButtonText:()=>te?p.reset:$e?p.start:p.loading,isScanning:te,startScan:Le,isModelLoaded:$e,onRetry:l},children:e(X,{webcamRef:_,canvasRef:U})})})})})};export{Q as F,J as p};
|
|
3
|
+
//# sourceMappingURL=FaceScan-DQvhrp3K.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FaceScan-DQvhrp3K.mjs","sources":["../src/customHooks/useFaceScan.ts","../src/components/faceScan/FaceScanErrorScreen.tsx","../src/components/faceScan/FaceScanGuide.tsx","../src/components/faceScan/FaceScanStep.tsx","../src/components/faceScan/FaceScan.tsx"],"sourcesContent":["import {\n useState,\n useRef,\n useEffect,\n useCallback,\n useMemo,\n RefObject,\n} from \"react\";\n// 1. Use TYPE-ONLY imports. These are erased at build time and won't crash SSR.\nimport type { PoseDetector } from \"@tensorflow-models/pose-detection\";\nimport posthog from \"../utils/posthog\";\nimport speechService from \"../utils/service/speechService\";\nimport { videoConstraints, voiceOverAssetsPath } from \"../utils/constants\";\n\n/**\n * useFaceScan Hook with PostHog Analytics\n * Refactored for Zero-Config SSR/CSR Compatibility\n */\n\n// Debounce utility\nfunction debounce(fn: (...args: any) => void, delay: number) {\n let timer: ReturnType<typeof setTimeout>;\n return (...args: any) => {\n if (timer) clearTimeout(timer);\n timer = setTimeout(() => fn(...args), delay);\n };\n}\n\n// Global Singleton Cache (Preserves state across re-renders)\nlet preloadedDetector: PoseDetector | null = null;\nlet isPreloading = false;\n\n// 2. Async Preloader with Dynamic Imports\nexport const preloadDetector = async (): Promise<PoseDetector | null> => {\n // Guard: Never run on server\n if (typeof window === \"undefined\") return null;\n if (preloadedDetector) return preloadedDetector;\n if (isPreloading) {\n // Simple wait mechanism if already loading\n while (isPreloading) {\n await new Promise(r => setTimeout(r, 100));\n if (preloadedDetector) return preloadedDetector;\n }\n }\n\n isPreloading = true;\n\n try {\n // DYNAMIC IMPORTS: Only fetch these heavy bundles in the browser\n const [tf, poseDetection] = await Promise.all([\n import(\"@tensorflow/tfjs\"),\n import(\"@tensorflow-models/pose-detection\")\n ]);\n\n // Initialize Backend\n await tf.setBackend(\"webgl\");\n await tf.ready();\n\n const detector = await poseDetection.createDetector(\n poseDetection.SupportedModels.BlazePose,\n {\n runtime: \"tfjs\",\n modelType: \"full\",\n }\n );\n\n // Dummy video warmup (Performance Optimization)\n // This runs the model once on a blank canvas to \"wake up\" the GPU\n // so the first user frame detects instantly.\n const dummyCanvas = document.createElement(\"canvas\");\n dummyCanvas.width = typeof videoConstraints.width === \"number\" ? videoConstraints.width : videoConstraints.width.ideal ?? videoConstraints.width.max ?? 1280;\n dummyCanvas.height = typeof videoConstraints.height === \"number\" ? videoConstraints.height : videoConstraints.height.ideal ?? videoConstraints.height.max ?? 720;\n const ctx = dummyCanvas.getContext(\"2d\");\n if (ctx) {\n ctx.fillStyle = \"black\";\n ctx.fillRect(0, 0, dummyCanvas.width, dummyCanvas.height);\n try {\n await detector.estimatePoses(dummyCanvas);\n } catch (e) {\n console.warn(\"Warmup frame failed (non-fatal):\", e);\n }\n }\n\n preloadedDetector = detector;\n return detector;\n } catch (err) {\n console.error(\"Failed to preload detector:\", err);\n return null;\n } finally {\n isPreloading = false;\n }\n};\n\ntype Point = [number, number, number];\n\nfunction useFaceScan({\n faceScanId,\n onValidPose,\n onScanComplete,\n onModelReady,\n onStartRecording,\n shopDomain,\n isError,\n isSuccess,\n}: {\n faceScanId: string;\n onValidPose?: () => void;\n onScanComplete?: () => void;\n onModelReady?: () => void;\n onStartRecording: () => void;\n shopDomain: string;\n isError?: boolean;\n isSuccess?: boolean;\n}) {\n const detectorRef = useRef<PoseDetector | null>(null);\n const [scanStage, setScanStage] = useState(0);\n const [consecutiveValid, setConsecutiveValid] = useState(0);\n const [isModelLoaded, setIsModelLoaded] = useState(false);\n const [isScanningActive, setIsScanningActive] = useState(false);\n const isInferencingRef = useRef(false);\n // Persistent offscreen canvas for inference input.\n // tf.browser.fromPixels(videoElement) calls gl.texImage2D with the video,\n // which returns black frames on SwiftShader (headless CI) because video decoded\n // frames aren't in SW-composited memory. Drawing to a canvas first makes the\n // pixels CPU-accessible so SwiftShader can read them correctly.\n const inferenceCanvasRef = useRef<HTMLCanvasElement | null>(null);\n \n const utteranceRef = useRef<SpeechSynthesisUtterance | null>(null);\n const scanStageRef = useRef(scanStage);\n const consecutiveValidRef = useRef(consecutiveValid);\n const validityBufferRef = useRef<boolean[]>([]);\n const lastSpokenMessageRef = useRef(\"\");\n const lastSpokenTimeRef = useRef(0);\n const voiceEnabledRef = useRef(false);\n \n const VALIDITY_BUFFER_LENGTH = 10;\n const CONSECUTIVE_VALID_LENGTH = 2;\n const TOTAL_STAGES = 4;\n const POSE_TRACKING_INTERVAL = 4000;\n const lastPoseTrackingTimeRef = useRef(0);\n\n // Safe iOS detection (SSR safe)\n const isIOS = typeof navigator !== 'undefined' && (\n /iPad|iPhone|iPod/.test(navigator.userAgent) ||\n (navigator.platform === \"MacIntel\" && navigator.maxTouchPoints > 1)\n );\n\n const directionMessages = useMemo(\n () => [\n \"Face front\",\n \"Turn head fully left\",\n \"Turn head fully right\",\n \"Smile facing front\",\n ],\n []\n );\n\n const debouncedTrackPoseDetection = useMemo(\n () =>\n debounce((payload) => {\n posthog.capture(`${shopDomain}/face_scan_pose_detection`, payload);\n }, 4000),\n [shopDomain]\n );\n\n const trackPoseDetection = useCallback(\n (\n faceKeypoints: Point[],\n bodyKeypoints: Point[],\n stage: number,\n headValid: boolean,\n bodyInvalid: boolean,\n consecutiveValidCount: number\n ) => {\n const now = Date.now();\n if (now - lastPoseTrackingTimeRef.current < POSE_TRACKING_INTERVAL) {\n return;\n }\n\n lastPoseTrackingTimeRef.current = now;\n\n try {\n const nose = faceKeypoints[0] || [0, 0, 0];\n const leftEye = faceKeypoints[2] || [0, 0, 0];\n const rightEye = faceKeypoints[5] || [0, 0, 0];\n const leftMouth = faceKeypoints[9] || [0, 0, 0];\n const rightMouth = faceKeypoints[10] || [0, 0, 0];\n\n const faceKeypointScores = faceKeypoints\n .map((kp: Point) => kp[2])\n .filter((score: number) => score > 0);\n const avgFaceScore =\n faceKeypointScores.length > 0\n ? faceKeypointScores.reduce((a, b) => a + b, 0) /\n faceKeypointScores.length\n : 0;\n\n const bodyKeypointScores = bodyKeypoints\n .map((kp: Point) => kp[2])\n .filter((score: number) => score > 0);\n const avgBodyScore =\n bodyKeypointScores.length > 0\n ? bodyKeypointScores.reduce((a: number, b: number) => a + b, 0) /\n bodyKeypointScores.length\n : 0;\n\n const eyeDistance = Math.abs(leftEye[0] - rightEye[0]);\n const noseToRight = nose[0] - rightEye[0];\n const noseToLeft = leftEye[0] - nose[0];\n\n let expectedPosition = \"frontal\";\n if (stage === 1) expectedPosition = \"left\";\n else if (stage === 2) expectedPosition = \"right\";\n else if (stage === 3) expectedPosition = \"frontal_smile\";\n\n const payload = {\n faceScanId,\n stage,\n timestamp: new Date().toISOString(),\n data: JSON.stringify({\n headValid,\n bodyInvalid,\n consecutiveValid: consecutiveValidCount,\n avgFaceScore: parseFloat(avgFaceScore.toFixed(3)),\n avgBodyScore: parseFloat(avgBodyScore.toFixed(3)),\n faceKeypointsDetected: faceKeypointScores.length,\n bodyKeypointsDetected: bodyKeypointScores.length,\n eyeDistance: parseFloat(eyeDistance.toFixed(2)),\n noseToRightRatio: parseFloat((noseToRight / eyeDistance).toFixed(3)),\n noseToLeftRatio: parseFloat((noseToLeft / eyeDistance).toFixed(3)),\n noseScore: parseFloat(nose[2].toFixed(3)),\n leftEyeScore: parseFloat(leftEye[2].toFixed(3)),\n rightEyeScore: parseFloat(rightEye[2].toFixed(3)),\n leftMouthScore: parseFloat(leftMouth[2].toFixed(3)),\n rightMouthScore: parseFloat(rightMouth[2].toFixed(3)),\n expectedPosition,\n }),\n };\n debouncedTrackPoseDetection(payload);\n } catch (error) {\n console.warn(\"Failed to track pose detection:\", error);\n }\n },\n [faceScanId, debouncedTrackPoseDetection]\n );\n\n const enableVoiceCommands = () => {\n voiceEnabledRef.current = true;\n if (typeof window !== 'undefined' && isIOS && \"speechSynthesis\" in window) {\n const silentUtterance = new SpeechSynthesisUtterance(\"\");\n silentUtterance.volume = 0;\n speechSynthesis.speak(silentUtterance);\n }\n };\n\n const startScanSequence = () => {\n posthog.capture(`${shopDomain}/face_scan_detection_started`, {\n faceScanId,\n stage: scanStageRef.current,\n timestamp: new Date().toISOString(),\n stageName: directionMessages[scanStageRef.current],\n });\n\n setTimeout(() => {\n setIsScanningActive(true);\n }, 1500);\n };\n\n const isHeadInPosition = (stage: number, keypoints: Point[]) => {\n // ... logic unchanged ...\n const nose = 0;\n const leftEye = 2;\n const rightEye = 5;\n const leftMouth = 9;\n const rightMouth = 10;\n\n const minScore = 0.2;\n if (\n !keypoints[nose] || !keypoints[leftEye] || !keypoints[rightEye] ||\n keypoints[nose][2] < minScore ||\n keypoints[leftEye][2] < minScore ||\n keypoints[rightEye][2] < minScore\n ) {\n return false;\n }\n\n const eyesToMouthVerticalDistance =\n 0.5 * (keypoints[rightMouth][1] + keypoints[leftMouth][1]) -\n 0.5 * (keypoints[rightEye][1] + keypoints[leftEye][1]);\n\n const faceTiltedness =\n (0.5 * (keypoints[rightMouth][1] + keypoints[leftMouth][1]) -\n keypoints[nose][1]) /\n eyesToMouthVerticalDistance;\n\n const faceIsTilted = faceTiltedness > 0.7 || faceTiltedness < 0.3;\n\n const faceIsVertical =\n Math.abs(keypoints[leftEye][1] - keypoints[rightEye][1]) <\n 0.5 * eyesToMouthVerticalDistance &&\n Math.abs(keypoints[leftMouth][1] - keypoints[rightMouth][1]) <\n 0.5 * eyesToMouthVerticalDistance;\n\n const eyeDistance = keypoints[leftEye][0] - keypoints[rightEye][0];\n const noseToRight = keypoints[nose][0] - keypoints[rightEye][0];\n const noseToLeft = keypoints[leftEye][0] - keypoints[nose][0];\n\n const frontalFace =\n noseToRight > 0.4 * eyeDistance && noseToRight < 0.6 * eyeDistance;\n const fullLeft = noseToRight > 0.85 * eyeDistance;\n const fullRight = noseToLeft > 0.85 * eyeDistance;\n\n switch (stage) {\n case 0:\n return !faceIsTilted && faceIsVertical && frontalFace;\n case 1:\n return !faceIsTilted && faceIsVertical && fullLeft;\n case 2:\n return !faceIsTilted && faceIsVertical && fullRight;\n case 3:\n return !faceIsTilted && faceIsVertical && frontalFace;\n default:\n return false;\n }\n };\n\n const faceScanDetector = async (\n webcamRef: RefObject<HTMLVideoElement | null>,\n canvasRef: RefObject<HTMLCanvasElement | null>\n ) => {\n if (\n !detectorRef.current ||\n !webcamRef.current ||\n !isModelLoaded ||\n !isScanningActive\n )\n return;\n\n // Safety: ensure video is actually playing with data\n if (webcamRef.current.readyState < 2) return;\n\n // Prevent concurrent inference calls — important on slow GPUs (SwiftShader/CI)\n // where a single estimatePoses() can take longer than the 500ms interval.\n if (isInferencingRef.current) return;\n isInferencingRef.current = true;\n\n try {\n const vw = webcamRef.current.videoWidth || 720;\n const vh = webcamRef.current.videoHeight || 1280;\n if (!inferenceCanvasRef.current) {\n inferenceCanvasRef.current = document.createElement(\"canvas\");\n inferenceCanvasRef.current.width = vw;\n inferenceCanvasRef.current.height = vh;\n }\n const ictx = inferenceCanvasRef.current.getContext(\"2d\");\n if (ictx) ictx.drawImage(webcamRef.current, 0, 0, vw, vh);\n const poses = await detectorRef.current.estimatePoses(\n ictx ? inferenceCanvasRef.current : webcamRef.current\n );\n if (poses.length > 0) {\n const faceKeypoints: Point[] = [];\n const bodyKeypoints: Point[] = [];\n poses[0].keypoints.forEach((landmark, idx) => {\n const score = landmark.score ?? 0;\n const point: Point = [landmark.x ?? -1, landmark.y ?? -1, score];\n if (idx <= 10) faceKeypoints.push(point);\n else bodyKeypoints.push(point);\n });\n\n // Optional drawing\n if (canvasRef?.current) {\n const ctx = canvasRef.current.getContext(\"2d\");\n const { width, height } = canvasRef.current;\n if (ctx) {\n ctx.clearRect(0, 0, width, height);\n\n const progress = Math.min(\n consecutiveValidRef.current / CONSECUTIVE_VALID_LENGTH,\n 1\n );\n const radius = height * 0.35;\n ctx.beginPath();\n ctx.strokeStyle = \"#00ff00\";\n ctx.lineWidth = 6;\n ctx.arc(\n width / 2,\n height / 2,\n radius + 10,\n -Math.PI / 2,\n -Math.PI / 2 + progress * 2 * Math.PI\n );\n ctx.stroke();\n }\n }\n\n const headValid = isHeadInPosition(scanStageRef.current, faceKeypoints);\n const bodyInvalid =\n bodyKeypoints.slice(2).filter((p) => p[2] > 0.7).length <= 2;\n\n validityBufferRef.current.push(headValid && bodyInvalid);\n if (validityBufferRef.current.length > VALIDITY_BUFFER_LENGTH) {\n validityBufferRef.current.shift();\n }\n\n const validCount = validityBufferRef.current.filter(Boolean).length;\n if (validCount >= 2) {\n const nextValid = consecutiveValidRef.current + 1;\n setConsecutiveValid(nextValid);\n } else {\n setConsecutiveValid(Math.max(0, consecutiveValidRef.current - 1));\n }\n\n if (consecutiveValidRef.current >= CONSECUTIVE_VALID_LENGTH) {\n onValidPose?.();\n\n const nextStage = scanStageRef.current + 1;\n setConsecutiveValid(0);\n validityBufferRef.current = [];\n setScanStage(nextStage);\n setIsScanningActive(false);\n\n (async () => {\n await speechService.playAudio(\n `${voiceOverAssetsPath}face-scan-vos/Sound-effect.mp3`\n );\n posthog.capture(\n `${shopDomain}/face_scan_detection_stage_completed`,\n {\n faceScanId,\n completedStage: scanStageRef.current,\n nextStage,\n timestamp: new Date().toISOString(),\n totalStages: TOTAL_STAGES,\n stageName: directionMessages[scanStageRef.current],\n }\n );\n \n if (nextStage === 1 && onStartRecording) {\n onStartRecording();\n }\n if (nextStage >= TOTAL_STAGES) {\n onScanComplete?.();\n } else {\n let nextSound = null;\n switch (nextStage) {\n case 1: nextSound = \"Left.mp3\"; break;\n case 2: nextSound = \"Right.mp3\"; break;\n case 3: nextSound = \"Smile.mp3\"; break;\n }\n if (nextSound) {\n await speechService.playAudio(\n `${voiceOverAssetsPath}face-scan-vos/${nextSound}`\n );\n }\n startScanSequence();\n }\n })();\n }\n\n trackPoseDetection(\n faceKeypoints,\n bodyKeypoints,\n scanStageRef.current,\n headValid,\n bodyInvalid,\n consecutiveValidRef.current\n );\n }\n } catch (err) {\n console.error(\"Pose detection error:\", err);\n } finally {\n isInferencingRef.current = false;\n }\n };\n\n const initializeModels = async () => {\n // 3. Browser Guard\n if (typeof window === 'undefined') return;\n\n try {\n console.log(\"Initializing Face Scan...\");\n // Calls our safe, dynamic preloader\n const detector = await preloadDetector();\n \n if (detector) {\n detectorRef.current = detector;\n console.log(\"Face scan model loaded successfully\");\n setIsModelLoaded(true);\n onModelReady?.();\n }\n } catch (err) {\n console.error(\"Error initializing face scan:\", err);\n }\n };\n\n const disposeModelAndTf = async () => {\n // Optional: We might NOT want to dispose if we want to keep the cache \n // for re-mounting. But if you must dispose:\n try {\n setIsModelLoaded(false);\n // NOTE: We generally don't dispose the detector if we want to reuse it \n // via the 'preloadedDetector' global variable.\n // If you dispose here, make sure to set 'preloadedDetector = null' too.\n /* if (detectorRef.current) {\n await detectorRef.current.dispose();\n detectorRef.current = null;\n preloadedDetector = null; // Clear global cache if disposing\n }\n */\n } catch (err) {\n console.error(\"Error disposing resources:\", err);\n } finally {\n // Reset state\n setScanStage(0);\n setConsecutiveValid(0);\n scanStageRef.current = 0;\n consecutiveValidRef.current = 0;\n validityBufferRef.current = [];\n }\n };\n\n const resetScan = () => {\n posthog.capture(`${shopDomain}/face_scan_reset`, {\n faceScanId,\n stage: scanStageRef.current,\n timestamp: new Date().toISOString(),\n stageName: directionMessages[scanStageRef.current],\n consecutiveValid: consecutiveValidRef.current,\n });\n\n setScanStage(0);\n setConsecutiveValid(0);\n setIsScanningActive(false);\n scanStageRef.current = 0;\n consecutiveValidRef.current = 0;\n validityBufferRef.current = [];\n lastSpokenMessageRef.current = \"\";\n lastSpokenTimeRef.current = 0;\n voiceEnabledRef.current = false;\n if (utteranceRef.current) {\n speechSynthesis.cancel();\n utteranceRef.current = null;\n }\n };\n\n useEffect(() => {\n if (isError || isSuccess) return;\n initializeModels();\n return () => {\n disposeModelAndTf();\n };\n }, [isError, isSuccess]);\n\n useEffect(() => {\n scanStageRef.current = scanStage;\n }, [scanStage]);\n\n useEffect(() => {\n consecutiveValidRef.current = consecutiveValid;\n }, [consecutiveValid]);\n\n return {\n faceScanDetector,\n scanStage,\n setScanStage,\n consecutiveValid,\n isModelLoaded,\n resetScan,\n enableVoiceCommands,\n startScanSequence,\n isScanningActive,\n };\n}\n\nexport default useFaceScan;","import { ArrowRight } from \"lucide-react\";\nimport Header from \"../Header\";\nimport SpecificButton from \"../../atoms/specificButton/SpecificButton\";\nimport { useContext } from \"react\";\nimport { LanguageKeys } from \"../../utils/languageKeys\";\nimport { LanguageContext } from \"../../utils/context/languageContext\";\nimport { useConfig } from \"../../utils/context/configContext\";\n\nfunction FaceScanErrorScreen({ onRetry, loading }: { onRetry?: () => void; loading?: boolean }) {\n\tconst { translate } = useContext(LanguageContext) || {};\n\tconst config = useConfig();\n\n\treturn (\n\t\t<div data-testid=\"face-scan-error-screen\" className=\"fixed common-ui-main top-[0] left-[0] z-[999] flex justify-center items-center w-full h-full\" style={{ background: config?.style?.base?.backgroundColor }}>\n\t\t\t<div className=\"flex flex-col w-full items-center p-[1rem] rounded-lg \">\n\t\t\t\t<Header noTitle />\n\t\t\t\t<div className=\"flex flex-col items-center w-full\">\n\t\t\t\t\t<h2\n\t\t\t\t\t\tclassName=\"text-xl font-semibold text-gray-800 \"\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\tfontFamily: config?.style?.heading?.headingFontFamily || \"SeriouslyNostalgic Fn\",\n\t\t\t\t\t\t\tfontSize: config?.style?.heading?.headingFontSize || \"32px\",\n\t\t\t\t\t\t\tcolor: config?.style?.heading?.headingColor || \"#000\",\n\t\t\t\t\t\t\tfontWeight: config?.style?.heading?.headingFontWeight || \"normal\",\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t{translate?.(LanguageKeys.scanFailed)}\n\t\t\t\t\t</h2>\n\t\t\t\t\t<p\n\t\t\t\t\t\tclassName=\"mb-[1.5rem]\"\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\tfontFamily: config?.style?.subheading?.subheadingFontFamily || \"'Inter', sans-serif\",\n\t\t\t\t\t\t\tfontSize: config?.style?.subheading?.subheadingFontSize || \"14px\",\n\t\t\t\t\t\t\tcolor: config?.style?.subheading?.subheadingColor || \"#4b5563\",\n\t\t\t\t\t\t\tfontWeight: config?.style?.subheading?.subheadingFontWeight || \"normal\",\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t{translate?.(LanguageKeys.clickToResetScan)}\n\t\t\t\t\t</p>\n\t\t\t\t\t<SpecificButton\n\t\t\t\t\t\tdata-testid=\"face-scan-retry-btn\"\n\t\t\t\t\t\tdisabled={loading}\n\t\t\t\t\t\tclassName=\"w-full h-[45px] shadow-[0px_1px_2px_0px_#00000040] text-[16px]\"\n\t\t\t\t\t\tbuttonText={translate?.(LanguageKeys.resetScan)}\n\t\t\t\t\t\tpostfixIcon={<ArrowRight />}\n\t\t\t\t\t\tbuttonFunc={() => onRetry?.()}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport default FaceScanErrorScreen;\n","import Header from \"../Header\";\nimport { GenderType } from \"../../utils/enums\";\nimport { directionMessages, FACE_SCAN_HEADSHOT, glassesOffVideo, maleGlassesOffVideo } from \"../../utils/constants\";\nimport { LanguageContext } from \"../../utils/context/languageContext\";\nimport { useContext } from \"react\";\nimport SpecificButton from \"../../atoms/specificButton/SpecificButton\";\nimport { LanguageKeys } from \"../../utils/languageKeys\";\nimport { useConfig } from \"../../utils/context/configContext\";\nimport { Dispatch, ReactNode, SetStateAction } from \"react\";\nimport { Gender } from \"../../types/interfaces\";\n\ninterface IButtonConfig {\n\tisDisabled: boolean;\n\ticon?: ReactNode;\n\tonClick: () => void;\n\tlabel: string;\n}\n\ninterface FaceScanGuideProps {\n\tstage: number;\n\tvideoLoading: boolean;\n\tsetVideoLoading: Dispatch<SetStateAction<boolean>>;\n\tvideoError: boolean;\n\tsetVideoError: Dispatch<SetStateAction<boolean>>;\n\tgender: Gender;\n\tbtnConfig: IButtonConfig;\n}\n\nfunction FaceScanGuide({ stage, videoLoading, setVideoLoading, videoError, setVideoError, gender, btnConfig }: FaceScanGuideProps) {\n\tconst { translate } = useContext(LanguageContext) || {};\n\tconst config = useConfig();\n\n\tif (stage === -1) {\n\t\treturn (\n\t\t\t<div data-testid=\"face-scan-guide\" className=\"text-center common-ui-main p-[16px] w-full h-full\" style={{ background: config?.style?.base?.backgroundColor }}>\n\t\t\t\t<div className=\"w-full\">\n\t\t\t\t\t<Header noTitle />\n\t\t\t\t\t<h2\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\tfontFamily: config?.style?.heading?.headingFontFamily || \"SeriouslyNostalgic Fn\",\n\t\t\t\t\t\t\tfontSize: config?.style?.heading?.headingFontSize || \"32px\",\n\t\t\t\t\t\t\tcolor: config?.style?.heading?.headingColor || \"#000\",\n\t\t\t\t\t\t\tfontWeight: config?.style?.heading?.headingFontWeight || \"normal\",\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t{translate?.(LanguageKeys.faceVisible)}\n\t\t\t\t\t</h2>\n\t\t\t\t\t{videoLoading && (\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName=\"mb-4 flex items-center justify-center\"\n\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\tfontFamily: config?.style?.subheading?.subheadingFontFamily || \"'Inter', sans-serif\",\n\t\t\t\t\t\t\t\tfontSize: config?.style?.subheading?.subheadingFontSize || \"14px\",\n\t\t\t\t\t\t\t\tcolor: config?.style?.subheading?.subheadingColor || \"#4b5563\",\n\t\t\t\t\t\t\t\tfontWeight: config?.style?.subheading?.subheadingFontWeight || \"normal\",\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{translate?.(LanguageKeys.loadingVideo)}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t\t{videoError && (\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName=\"mb-4 text-red-600\"\n\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\tfontFamily: config?.style?.subheading?.subheadingFontFamily || \"'Inter', sans-serif\",\n\t\t\t\t\t\t\t\tfontSize: config?.style?.subheading?.subheadingFontSize || \"14px\",\n\t\t\t\t\t\t\t\tcolor: \"#ff0000\",\n\t\t\t\t\t\t\t\tfontWeight: config?.style?.subheading?.subheadingFontWeight || \"normal\",\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{translate?.(LanguageKeys.videoLoadFailed)}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t\t{!videoError && (\n\t\t\t\t\t\t<div className={` w-[100px] mx-auto`}>\n\t\t\t\t\t\t\t<video\n\t\t\t\t\t\t\t\tsrc={gender === GenderType.Male ? maleGlassesOffVideo : glassesOffVideo}\n\t\t\t\t\t\t\t\tautoPlay\n\t\t\t\t\t\t\t\tloop\n\t\t\t\t\t\t\t\tcontrols={false}\n\t\t\t\t\t\t\t\tmuted\n\t\t\t\t\t\t\t\tplaysInline\n\t\t\t\t\t\t\t\tclassName=\"h-full w-full object-contain border-none\"\n\t\t\t\t\t\t\t\tonCanPlay={() => setVideoLoading(false)}\n\t\t\t\t\t\t\t\tonLoadStart={() => setVideoLoading(true)}\n\t\t\t\t\t\t\t\tonError={() => setVideoError(true)}\n\t\t\t\t\t\t\t\taria-label=\"Instructional video: Please remove your glasses before starting the scan.\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t\t<div className=\"flex justify-center w-full p-[1rem]\">\n\t\t\t\t\t<SpecificButton\n\t\t\t\t\t\tdata-testid=\"face-scan-start-btn\"\n\t\t\t\t\t\tdisabled={btnConfig.isDisabled}\n\t\t\t\t\t\tclassName=\"!w-[60px] !h-[35px] !py-0 !px-0\"\n\t\t\t\t\t\tbuttonText={translate?.(btnConfig.label)}\n\t\t\t\t\t\tpostfixIcon={btnConfig?.icon}\n\t\t\t\t\t\tbuttonFunc={btnConfig.onClick}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<div className=\"text-center common-ui-main p-[16px] w-full h-full\" style={{ background: config?.style?.base?.backgroundColor }}>\n\t\t\t<div className=\"w-full\">\n\t\t\t\t<Header noTitle />\n\t\t\t\t<h3\n\t\t\t\t\tclassName=\"mb-[0.5rem]\"\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tfontFamily: config?.style?.heading?.headingFontFamily || \"SeriouslyNostalgic Fn\",\n\t\t\t\t\t\tfontSize: config?.style?.heading?.headingFontSize || \"32px\",\n\t\t\t\t\t\tcolor: config?.style?.heading?.headingColor || \"#000\",\n\t\t\t\t\t\tfontWeight: config?.style?.heading?.headingFontWeight || \"normal\",\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t{translate?.(directionMessages[stage])}\n\t\t\t\t</h3>\n\t\t\t\t<div className=\"max-w-[400px] justify-center gap-1 mx-auto flex items-center\">\n\t\t\t\t\t<div className={` w-[120px] overflow-hidden ${stage === 0 ? \"opacity-100\" : \"opacity-0 hidden\"} `}>\n\t\t\t\t\t\t<video className=\"h-full w-full object-contain border-none\" muted loop autoPlay playsInline>\n\t\t\t\t\t\t\t<source src={gender === GenderType.Male ? FACE_SCAN_HEADSHOT.male.forward : FACE_SCAN_HEADSHOT.female.forward} type=\"video/mp4\" />\n\t\t\t\t\t\t</video>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className={` w-[120px] overflow-hidden ${stage === 1 ? \"opacity-100\" : \"opacity-0 hidden\"} `}>\n\t\t\t\t\t\t<video className=\"h-full w-full object-contain border-none\" muted loop autoPlay playsInline>\n\t\t\t\t\t\t\t<source src={gender === GenderType.Male ? FACE_SCAN_HEADSHOT.male.left : FACE_SCAN_HEADSHOT.female.left} type=\"video/mp4\" />\n\t\t\t\t\t\t</video>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className={` w-[120px] overflow-hidden ${stage === 2 ? \"opacity-100\" : \"opacity-0 hidden\"} `}>\n\t\t\t\t\t\t<video className=\"h-full w-full object-contain border-none\" muted loop autoPlay playsInline>\n\t\t\t\t\t\t\t<source src={gender === GenderType.Male ? FACE_SCAN_HEADSHOT.male.right : FACE_SCAN_HEADSHOT.female.right} type=\"video/mp4\" />\n\t\t\t\t\t\t</video>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className={` w-[120px] overflow-hidden ${stage === 3 ? \"opacity-100\" : \"opacity-0 hidden\"} `}>\n\t\t\t\t\t\t<video className=\"h-full w-full object-contain border-none\" muted loop autoPlay playsInline>\n\t\t\t\t\t\t\t<source src={gender === GenderType.Male ? FACE_SCAN_HEADSHOT.male.smile : FACE_SCAN_HEADSHOT.female.smile} type=\"video/mp4\" />\n\t\t\t\t\t\t</video>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport default FaceScanGuide;\n","import React, { useContext } from \"react\";\nimport ParamsContext from \"../../utils/context/paramsContext\";\nimport FaceScanErrorScreen from \"./FaceScanErrorScreen\";\nimport LoadingScreen from \"../LoadingScreen\";\nimport { videoConstraints } from \"../../utils/constants\";\nimport { Drawer } from \"@mui/material\";\nimport FaceScanGuide from \"./FaceScanGuide\";\nimport Modal from \"../Modal\";\nimport { FaceScanErrorType } from \"../../utils/enums\";\nimport { ArrowRight } from \"lucide-react\";\nimport { useConfig } from \"../../utils/context/configContext\";\ninterface FaceScanStepProps {\n\twebcamRef: React.RefObject<HTMLVideoElement | null>;\n\tcanvasRef: React.RefObject<HTMLCanvasElement | null>;\n}\n\nconst FaceScanStep: React.FC<FaceScanStepProps> = ({ webcamRef, canvasRef }) => {\n\tconst {\n\t\tisError,\n\t\tisSuccess,\n\t\tshowLoader,\n\t\thasError,\n\t\terrorType,\n\t\tresetScanState,\n\t\tshowGuideCard,\n\t\tscanStage,\n\t\tvideoLoading,\n\t\tsetVideoLoading,\n\t\tvideoError,\n\t\tsetVideoError,\n\t\tgender,\n\t\tgetButtonText,\n\t\tisScanning,\n\t\tstartScan,\n\t\tisModelLoaded,\n\t\tonRetry,\n\t} = useContext(ParamsContext);\n\tconst resolvedConfig = useConfig();\n\tif (isError) {\n\t\treturn <FaceScanErrorScreen onRetry={onRetry} />;\n\t}\n\tif (isSuccess) {\n\t\treturn (\n\t\t\t<div className=\"fixed z-[9] common-ui-main w-full h-full\" style={{ background: resolvedConfig?.style?.base?.backgroundColor }}>\n\t\t\t\t<LoadingScreen url={resolvedConfig?.loader} loaderType=\"black\" />\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<>\n\t\t\t<audio id=\"audioElement\" crossOrigin=\"anonymous\" preload=\"auto\" style={{ position: \"absolute\", zIndex: -99999 }} src={undefined} />\n\n\t\t\t{/* Error overlay */}\n\t\t\t{showLoader && !hasError && (\n\t\t\t\t<div className=\"fixed z-[9] common-ui-main w-full h-full\" style={{ background: resolvedConfig?.style?.base?.backgroundColor }}>\n\t\t\t\t\t{/* <Asset genderType={gender} /> */}\n\t\t\t\t\t<LoadingScreen url={resolvedConfig?.loader} loaderType=\"black\" />\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Always show camera view */}\n\t\t\t<div className=\"h-full flex-col common-ui-main relative w-full flex justify-center items-center text-center rounded-t-[20px]\" style={{ background: resolvedConfig?.style?.base?.backgroundColor }}>\n\t\t\t\t<div className=\"flex-1 w-full max-w-md overflow-hidden\">\n\t\t\t\t\t<div className=\"w-full h-full\">\n\t\t\t\t\t\t<video\n\t\t\t\t\t\t\tref={webcamRef}\n\t\t\t\t\t\t\tautoPlay\n\t\t\t\t\t\t\tplaysInline\n\t\t\t\t\t\t\tmuted\n\t\t\t\t\t\t\twidth={videoConstraints.width.ideal}\n\t\t\t\t\t\t\theight={videoConstraints.height.ideal}\n\t\t\t\t\t\t\tclassName=\"w-full h-full object-cover fixed left-0 top-0 z-0\"\n\t\t\t\t\t\t\tstyle={{ transform: \"scaleX(-1)\" }}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<canvas ref={canvasRef} width={videoConstraints.width.ideal} height={videoConstraints.height.ideal} style={{ transform: \"scaleX(-1)\", opacity: \"0\" }} />\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{/* Error overlay */}\n\t\t\t{!showLoader && hasError && errorType === FaceScanErrorType.Camera && (\n\t\t\t\t<Modal testId=\"face-scan-camera-error-screen\" config={resolvedConfig} />\n\t\t\t)}\n\t\t\t{!showLoader && hasError && errorType !== FaceScanErrorType.Camera && (\n\t\t\t\t<FaceScanErrorScreen loading={showLoader} onRetry={resetScanState} />\n\t\t\t)}\n\n\t\t\t{/* Scan guide drawer - only show when scanning */}\n\t\t\t{showGuideCard && !hasError && !showLoader && (\n\t\t\t\t<Drawer\n\t\t\t\t\topen\n\t\t\t\t\tclassName=\"face-scan-small camera-draw\"\n\t\t\t\t\tanchor=\"bottom\"\n\t\t\t\t\tonClose={(event, reason) => {\n\t\t\t\t\t\tif (reason === \"backdropClick\") {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}}\n\t\t\t\t\thideBackdrop\n\t\t\t\t>\n\t\t\t\t\t<FaceScanGuide\n\t\t\t\t\t\tstage={scanStage}\n\t\t\t\t\t\tvideoLoading={videoLoading}\n\t\t\t\t\t\tsetVideoLoading={setVideoLoading}\n\t\t\t\t\t\tvideoError={videoError}\n\t\t\t\t\t\tsetVideoError={setVideoError}\n\t\t\t\t\t\tgender={gender}\n\t\t\t\t\t\tbtnConfig={{\n\t\t\t\t\t\t\tlabel: getButtonText(),\n\t\t\t\t\t\t\tonClick: isScanning ? resetScanState : startScan,\n\t\t\t\t\t\t\tisDisabled: showLoader || !isModelLoaded,\n\t\t\t\t\t\t\ticon: isScanning ? <ArrowRight /> : <></>,\n\t\t\t\t\t\t}}\n\t\t\t\t\t/>\n\t\t\t\t</Drawer>\n\t\t\t)}\n\t\t</>\n\t);\n};\n\nexport default FaceScanStep;\n","\"use client\";\nimport { useEffect, useRef, useState, useCallback, useMemo } from \"react\";\nimport { FaceScanMetaData, FaceScanProps } from \"../../types/interfaces\";\nimport {\n\tgenerateUuid,\n\tgetPreferredMediaRecorderTypes,\n\tgetRecordingExtension,\n\tgetRecordingMimeType,\n\tgetSupportedMediaRecorderTypes,\n\tgetVideoFPS,\n\thandleScanTimeCapture,\n\thandleWebSocketCapture,\n} from \"../../utils/utils\";\nimport { videoConstraintsExact, videoConstraintsFallback, videoTypes, voiceOverAssetsPath } from \"../../utils/constants\";\nimport { getSwanService } from \"../../utils/service/swanService\";\nimport speechService from \"../../utils/service/speechService\";\nimport useFaceScan from \"../../customHooks/useFaceScan\";\nimport usePosthogPageview from \"../../customHooks/usePosthogPageview\";\nimport { LanguageKeys } from \"../../utils/languageKeys\";\nimport { FaceScanErrorType } from \"../../utils/enums\";\nimport LanguageContextProvider from \"../../utils/context/languageContext\";\nimport { ConfigProvider } from \"../../utils/context/configContext\";\nimport FaceScanStep from \"./FaceScanStep\";\nimport ParamsContext from \"../../utils/context/paramsContext\";\n\nexport const FaceScan: React.FC<FaceScanProps> = (props) => {\n\tconst { onScanSuccess, onScanError, onRetry, onScanStart, onCaptureComplete, onUploadStart, onUploadEnd, onMeasurementSocketStart, onMeasurementSocketClose, config, isError, isSuccess } = props;\n\tconst userDetails: FaceScanMetaData = props.userDetails ?? {\n\t\temail: \"\",\n\t\tgender: \"male\",\n\t\tdeviceFocalLength: 0,\n\t\tshopDomain: \"\",\n\t\tbodyScanId: \"\",\n\t\tcallbackUrl: null,\n\t};\n\tconst token = props.token ?? \"\";\n\tconst webcamRef = useRef<HTMLVideoElement | null>(null);\n\tconst canvasRef = useRef<HTMLCanvasElement | null>(null);\n\tconst mediaRecorderRef = useRef<MediaRecorder | null>(null);\n\tconst recordedBlobsRef = useRef<Blob[] | null>([]);\n\tconst streamRef = useRef<MediaStream | null>(null);\n\tconst recordedFpsRef = useRef<number | null>(null);\n\tconst drawFrameRafRef = useRef<number>(0);\n\tconst recordingVideoElRef = useRef<HTMLVideoElement | null>(null);\n\tconst [showLoader, setShowLoader] = useState(false);\n\tconst [modelReady, setModelReady] = useState(false);\n\tconst [isScanning, setIsScanning] = useState(false);\n\tconst [showGuideCard, setShowGuideCard] = useState(true);\n\tconst [videoLoading, setVideoLoading] = useState(false);\n\tconst [videoError, setVideoError] = useState(false);\n\tconst [hasError, setHasError] = useState(false);\n\tconst [errorType, setErrorType] = useState<FaceScanErrorType | null>(null);\n\tconst [faceScanId, setFaceScanId] = useState(generateUuid());\n\tconst swan = useMemo(() => getSwanService(token), [token]);\n\tconst { email, gender, deviceFocalLength, shopDomain, callbackUrl, bodyScanId } = userDetails;\n\tconst [supportedTypes, setSupportedTypes] = useState<string[]>([]);\n\tconst [currentVideoConstraints, setCurrentVideoConstraints] = useState<MediaTrackConstraints>(videoConstraintsExact);\n\tconst preferredVideoTypes = useMemo(() => getPreferredMediaRecorderTypes(), []);\n\tconst recordingMimeType = getRecordingMimeType(supportedTypes);\n\tconst recordingExtension = getRecordingExtension(recordingMimeType);\n\tusePosthogPageview();\n\n\tconst stopRecording = useCallback(() => {\n\t\tconsole.log(\"Stopping recording...\");\n\t\tcancelAnimationFrame(drawFrameRafRef.current);\n\t\tif (recordingVideoElRef.current) {\n\t\t\trecordingVideoElRef.current.pause();\n\t\t\trecordingVideoElRef.current.srcObject = null;\n\t\t\trecordingVideoElRef.current = null;\n\t\t}\n\t\tif (mediaRecorderRef.current && mediaRecorderRef.current.state === \"recording\") {\n\t\t\tmediaRecorderRef.current.stop();\n\t\t}\n\t\tmediaRecorderRef.current = null;\n\t}, []);\n\n\tconst uploadFinalVideo = async () => {\n\t\tonCaptureComplete?.();\n\t\tif (recordedBlobsRef.current) {\n\t\t\tif (recordedBlobsRef.current.length === 0) {\n\t\t\t\tconsole.error(\"No video data recorded\");\n\t\t\t\tsetErrorType(FaceScanErrorType.Scan);\n\t\t\t\tsetHasError(true);\n\t\t\t\tsetShowLoader(false);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tonUploadStart?.();\n\t\t\tsetShowLoader(true);\n\t\t\tconst videoFile = new File(recordedBlobsRef.current, `${faceScanId}.${recordingExtension}`, {\n\t\t\t\ttype: recordingMimeType,\n\t\t\t});\n\t\t\t// Use FPS captured at recording time, fallback to calculation if not available\n\t\t\tconst videoFps = recordedFpsRef.current ?? (await getVideoFPS(videoFile));\n\n\t\t\tconst fileSize = videoFile.size;\n\t\t\tconst fileSizeMB = (fileSize / (1024 * 1024)).toFixed(2);\n\t\t\tconst estimatedDuration = Math.round(fileSize / 10000);\n\t\t\tconst videoData = {\n\t\t\t\tvideo_size_mb: parseFloat(fileSizeMB),\n\t\t\t\tvideo_size_bytes: fileSize,\n\t\t\t\tvideo_fps: videoFps || 0,\n\t\t\t\tblob_count: recordedBlobsRef.current.length,\n\t\t\t\testimated_duration_seconds: estimatedDuration,\n\t\t\t};\n\n\t\t\tconst metaData: any[] = [\n\t\t\t\t{ gender: gender },\n\t\t\t\t{ face_scan_id: faceScanId },\n\t\t\t\t{focal_length: `${deviceFocalLength}`},\n\t\t\t\t{ customer_store_url: shopDomain },\n\t\t\t\t{ scan_type: \"face_scan\" },\n\t\t\t\t{ body_scan_id: bodyScanId }\n\t\t\t];\n\t\t\tif (callbackUrl) {\n\t\t\t\tmetaData.push({ callback_url: callbackUrl });\n\t\t\t}\n\n\t\t\thandleScanTimeCapture({\n\t\t\t\teventName: `${shopDomain}/face_scan_meta_data`,\n\t\t\t\tfaceScanId,\n\t\t\t\temail,\n\t\t\t\tdata: JSON.stringify({ metaData, videoData }),\n\t\t\t});\n\n\t\t\tconst uploadStartTime = Date.now();\n\t\t\thandleScanTimeCapture({\n\t\t\t\teventName: `${shopDomain}/face_scan_upload_start`,\n\t\t\t\tfaceScanId,\n\t\t\t\temail,\n\t\t\t\tstartTime: uploadStartTime,\n\t\t\t});\n\n\t\t\ttry {\n\t\t\t\tconst res = await swan.fileUpload.faceScanFileUploader({\n\t\t\t\t\tfile: videoFile,\n\t\t\t\t\tarrayMetaData: metaData,\n\t\t\t\t\tobjectKey: faceScanId,\n\t\t\t\t\temail,\n\t\t\t\t\tcontentType: videoFile.type,\n\t\t\t\t});\n\n\t\t\t\tconst uploadEndTime = Date.now();\n\t\t\t\tconst uploadDuration = uploadEndTime - uploadStartTime;\n\n\t\t\t\thandleScanTimeCapture({\n\t\t\t\t\teventName: `${shopDomain}/face_scan_upload_complete`,\n\t\t\t\t\tfaceScanId,\n\t\t\t\t\temail,\n\t\t\t\t\tcompletionTime: uploadEndTime,\n\t\t\t\t\tuploadDuration,\n\t\t\t\t});\n\t\t\t\tonUploadEnd?.();\n\t\t\t\tconsole.log(\"✅ Upload successful\", res);\n\t\t\t\tswan.measurement.handlFaceScaneSocket({\n\t\t\t\t\tonPreopen: () => {\n\t\t\t\t\t\tonMeasurementSocketStart?.();\n\t\t\t\t\t\thandleWebSocketCapture({\n\t\t\t\t\t\t\teventName: `${shopDomain}/webSocket`,\n\t\t\t\t\t\t\tfaceScanID: faceScanId,\n\t\t\t\t\t\t\tconnection: \"pre_open\",\n\t\t\t\t\t\t\ttype: \"faceScan_recommendation\",\n\t\t\t\t\t\t\temail,\n\t\t\t\t\t\t});\n\t\t\t\t\t},\n\t\t\t\t\tfaceScanId,\n\t\t\t\t\tonOpen: () => {\n\t\t\t\t\t\thandleWebSocketCapture({\n\t\t\t\t\t\t\teventName: `${shopDomain}/webSocket`,\n\t\t\t\t\t\t\tfaceScanID: faceScanId,\n\t\t\t\t\t\t\tconnection: \"open\",\n\t\t\t\t\t\t\ttype: \"faceScan_recommendation\",\n\t\t\t\t\t\t\temail,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tconsole.log(\"websocket connect open\");\n\t\t\t\t\t},\n\t\t\t\t\tonError: (err) => {\n\t\t\t\t\t\thandleWebSocketCapture({\n\t\t\t\t\t\t\teventName: `${shopDomain}/webSocket`,\n\t\t\t\t\t\t\tfaceScanID: faceScanId,\n\t\t\t\t\t\t\tconnection: \"error\",\n\t\t\t\t\t\t\ttype: \"faceScan_recommendation\",\n\t\t\t\t\t\t\temail,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tonError(err);\n\t\t\t\t\t},\n\t\t\t\t\tonSuccess: (data) => {\n\t\t\t\t\t\thandleWebSocketCapture({\n\t\t\t\t\t\t\teventName: `${shopDomain}/webSocket`,\n\t\t\t\t\t\t\tfaceScanID: faceScanId,\n\t\t\t\t\t\t\tconnection: \"success\",\n\t\t\t\t\t\t\ttype: \"faceScan_recommendation\",\n\t\t\t\t\t\t\temail,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tonSuccess(data);\n\t\t\t\t\t},\n\t\t\t\t\tonClose: () => {\n\t\t\t\t\t\tonMeasurementSocketClose?.();\n\t\t\t\t\t\tconsole.log(\"websocket connect close\");\n\t\t\t\t\t\thandleWebSocketCapture({\n\t\t\t\t\t\t\teventName: `${shopDomain}/webSocket`,\n\t\t\t\t\t\t\tfaceScanID: faceScanId,\n\t\t\t\t\t\t\tconnection: \"close\",\n\t\t\t\t\t\t\ttype: \"faceScan_recommendation\",\n\t\t\t\t\t\t\temail,\n\t\t\t\t\t\t});\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tonUploadEnd?.();\n\t\t\t\tonError(error);\n\t\t\t}\n\t\t}\n\t};\n\n\tconst startRecording = useCallback(() => {\n\t\tconst stream = webcamRef.current?.srcObject as MediaStream | null;\n\t\tif (!stream) return;\n\n\t\t// Create a canvas to normalize orientation\n\t\tconst videoTrack = stream.getVideoTracks()[0];\n\t\tconst settings = videoTrack.getSettings();\n\t\t// Fallback to landscape defaults (1280×720); portrait swap below handles orientation\n\t\tconst { width = 1280, height = 720 } = settings;\n\n\t\tconst canvas = document.createElement(\"canvas\");\n\t\tconst ctx = canvas.getContext(\"2d\");\n\n\t\t// Always force portrait (swap width/height if landscape)\n\t\tif (width > height) {\n\t\t\tcanvas.width = height;\n\t\t\tcanvas.height = width;\n\t\t} else {\n\t\t\tcanvas.width = width;\n\t\t\tcanvas.height = height;\n\t\t}\n\n\t\tconst videoEl = document.createElement(\"video\");\n\t\tvideoEl.srcObject = stream;\n\t\tvideoEl.muted = true;\n\t\tvideoEl.playsInline = true;\n\t\tvideoEl.play();\n\t\trecordingVideoElRef.current = videoEl;\n\n\t\t// Draw video frames onto canvas\n\t\tconst drawFrame = () => {\n\t\t\t// Rotate if camera gives landscape frames\n\t\t\tif (width > height) {\n\t\t\t\tctx?.save();\n\t\t\t\tctx?.translate(canvas.width, 0);\n\t\t\t\tctx?.rotate(Math.PI / 2);\n\t\t\t\tctx?.drawImage(videoEl, 0, 0, height, width);\n\t\t\t\tctx?.restore();\n\t\t\t} else {\n\t\t\t\tctx?.drawImage(videoEl, 0, 0, width, height);\n\t\t\t}\n\t\t\tdrawFrameRafRef.current = requestAnimationFrame(drawFrame);\n\t\t};\n\t\tdrawFrameRafRef.current = requestAnimationFrame(drawFrame);\n\n\t\t// Match recording FPS to actual camera FPS to avoid duplicate frames in the encoded video\n\t\tconst recordingFps = settings.frameRate ?? 10;\n\t\trecordedFpsRef.current = recordingFps; // Store FPS at recording time\n\t\tconst canvasStream = canvas.captureStream(recordingFps);\n\t\tconst mediaRecorderOptions: MediaRecorderOptions = {\n\t\t\tvideoBitsPerSecond: 1_500_000,\n\t\t};\n\t\tif (supportedTypes.length > 0) {\n\t\t\tmediaRecorderOptions.mimeType = supportedTypes[0];\n\t\t}\n\t\tlet mediaRecorder;\n\n\t\ttry {\n\t\t\tmediaRecorder = new MediaRecorder(canvasStream, mediaRecorderOptions);\n\t\t} catch (e) {\n\t\t\tconsole.error(\"MediaRecorder init failed:\", e);\n\t\t\tsetErrorType(FaceScanErrorType.Scan);\n\t\t\tsetHasError(true);\n\t\t\tsetShowLoader(false);\n\t\t\treturn;\n\t\t}\n\n\t\trecordedBlobsRef.current = [];\n\t\tmediaRecorder.ondataavailable = (event) => {\n\t\t\tif (event?.data && event.data.size > 0 && recordedBlobsRef.current) {\n\t\t\t\trecordedBlobsRef.current.push(event.data);\n\t\t\t}\n\t\t};\n\t\tmediaRecorder.onstop = () => {\n\t\t\tconsole.log(\"Recording stopped, total blobs:\", recordedBlobsRef?.current?.length);\n\t\t\tuploadFinalVideo();\n\t\t};\n\n\t\tmediaRecorder.start(100); // 100ms chunks\n\t\tmediaRecorderRef.current = mediaRecorder;\n\t\tconsole.log(\"Recording started successfully (portrait normalized)\");\n\t}, [supportedTypes, uploadFinalVideo]);\n\n\tconst { faceScanDetector, scanStage, setScanStage, resetScan, isModelLoaded, startScanSequence } = useFaceScan({\n\t\tfaceScanId,\n\t\tshopDomain,\n\t\tisError,\n\t\tisSuccess,\n\t\tonScanComplete: () => {\n\t\t\tstopRecording();\n\t\t},\n\t\tonModelReady: () => {\n\t\t\tsetModelReady(true);\n\t\t},\n\t\tonStartRecording: () => {\n\t\t\tconsole.log(\"Stage 0 completed - starting recording for stages 1-3\");\n\t\t\tstartRecording();\n\t\t},\n\t});\n\n\tconst startScan = useCallback(() => {\n\t\tif (!isModelLoaded) return;\n\t\tonScanStart?.();\n\t\tsetScanStage(0);\n\t\tspeechService.playAudio(`${voiceOverAssetsPath}face-scan-vos/Face-forward.mp3`);\n\t\tsetTimeout(() => {\n\t\t\tsetIsScanning(true);\n\t\t\tsetTimeout(() => {\n\t\t\t\tstartScanSequence();\n\t\t\t}, 200);\n\t\t}, 200);\n\t}, [setScanStage, setIsScanning, startScanSequence, isModelLoaded, onScanStart]);\n\n\tconst resetScanState = useCallback(() => {\n\t\tsetIsScanning(false);\n\t\tsetShowGuideCard(true);\n\t\tstopRecording();\n\t\tresetScan();\n\t\tonRetry?.();\n\t\trecordedBlobsRef.current = [];\n\t\trecordedFpsRef.current = null;\n\t\tif (mediaRecorderRef.current) {\n\t\t\tmediaRecorderRef.current = null;\n\t\t}\n\t\tsetHasError(false);\n\t\tsetErrorType(null);\n\t\tsetScanStage(-1);\n\t\tsetFaceScanId(generateUuid());\n\t\tif (webcamRef.current && streamRef.current) {\n\t\t\twebcamRef.current.srcObject = streamRef.current;\n\t\t\tconsole.log(\"Camera stream restored after reset\");\n\t\t}\n\t}, [setScanStage, setIsScanning, setShowGuideCard, stopRecording, resetScan, setFaceScanId]);\n\n\tconst onError = (data: any) => {\n\t\tconsole.log(data, \"ws error\");\n\t\tonScanError?.(data);\n\t\tsetErrorType(FaceScanErrorType.Scan);\n\t\tsetHasError(true);\n\t\tsetShowLoader(false);\n\t\tsetShowGuideCard(false);\n\t\thandleScanTimeCapture({\n\t\t\teventName: `${shopDomain}/faceScan`,\n\t\t\tfaceScanId,\n\t\t\tstatus: \"failed\",\n\t\t\temail,\n\t\t\tdata: JSON.stringify(data),\n\t\t});\n\t};\n\n\tconst onSuccess = (data: any) => {\n\t\tconsole.log(data, \"ws success\");\n\t\tif (data && data?.resultType === \"intermediate\") {\n\t\t\thandleScanTimeCapture({\n\t\t\t\teventName: `${shopDomain}/faceScan_success/intermediate`,\n\t\t\t\tfaceScanId,\n\t\t\t\tstatus: \"success\",\n\t\t\t\temail,\n\t\t\t\tdata: JSON.stringify(data),\n\t\t\t});\n\t\t}\n\t\tonScanSuccess?.(data);\n\t};\n\n\tuseEffect(() => {\n\t\tif (isError || isSuccess) return;\n\t\tif (navigator.mediaDevices.getUserMedia) {\n\t\t\tnavigator.mediaDevices\n\t\t\t\t.getUserMedia({ video: currentVideoConstraints })\n\t\t\t\t.then((stream) => {\n\t\t\t\t\tstreamRef.current = stream;\n\t\t\t\t\tif (webcamRef.current) {\n\t\t\t\t\t\twebcamRef.current.srcObject = stream;\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.catch((err) => {\n\t\t\t\t\tconsole.error(\"Error accessing webcam:\", err);\n\t\t\t\t\tif (currentVideoConstraints === videoConstraintsExact) {\n\t\t\t\t\t\tsetCurrentVideoConstraints(videoConstraintsFallback);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tconst isCameraAccessError = [\"NotAllowedError\", \"PermissionDeniedError\", \"NotFoundError\", \"DevicesNotFoundError\", \"NotReadableError\", \"TrackStartError\"].includes(err?.name);\n\t\t\t\t\tsetErrorType(isCameraAccessError ? FaceScanErrorType.Camera : FaceScanErrorType.Scan);\n\t\t\t\t\tsetHasError(true);\n\t\t\t\t});\n\t\t}\n\t\treturn () => {\n\t\t\tif (streamRef.current) {\n\t\t\t\tstreamRef.current.getTracks().forEach((track) => track.stop());\n\t\t\t}\n\t\t};\n\t}, [isError, isSuccess, currentVideoConstraints]);\n\n\t// Face detection interval - only run when scanning is active\n\tuseEffect(() => {\n\t\tif (!modelReady || !isScanning) return;\n\t\tconst intervalId = setInterval(() => {\n\t\t\tfaceScanDetector(webcamRef, canvasRef);\n\t\t}, 500);\n\n\t\t// eslint-disable-next-line consistent-return\n\t\treturn () => clearInterval(intervalId);\n\t}, [faceScanDetector, modelReady, isScanning]);\n\n\tconst getButtonText = () => {\n\t\tif (isScanning) return LanguageKeys.reset;\n\t\tif (!isModelLoaded) return LanguageKeys.loading;\n\t\treturn LanguageKeys.start;\n\t};\n\n\tconsole.log(\"Model ready:\", modelReady, \"Has error:\", hasError, \"Is scanning:\", isScanning, \"showLoader\", showLoader);\n\tuseEffect(() => {\n\t\tconst supported = getSupportedMediaRecorderTypes(preferredVideoTypes, videoTypes);\n\t\tsetSupportedTypes(supported);\n\t}, [preferredVideoTypes]);\n\tuseEffect(() => {\n\t\tsetScanStage(-1);\n\t}, []);\n\n\treturn (\n\t\t<div data-face-scan-stage={scanStage}>\n\t\t<LanguageContextProvider>\n\t\t\t<ConfigProvider config={config}>\n\t\t\t\t<ParamsContext.Provider\n\t\t\t\t\tvalue={{\n\t\t\t\t\t\tisError,\n\t\t\t\t\t\tisSuccess,\n\t\t\t\t\t\tshowLoader,\n\t\t\t\t\t\thasError,\n\t\t\t\t\t\terrorType,\n\t\t\t\t\t\tresetScanState,\n\t\t\t\t\t\tshowGuideCard,\n\t\t\t\t\t\tscanStage,\n\t\t\t\t\t\tvideoLoading,\n\t\t\t\t\t\tsetVideoLoading,\n\t\t\t\t\t\tvideoError,\n\t\t\t\t\t\tsetVideoError,\n\t\t\t\t\t\tgender,\n\t\t\t\t\t\tgetButtonText,\n\t\t\t\t\t\tisScanning,\n\t\t\t\t\t\tstartScan,\n\t\t\t\t\t\tisModelLoaded,\n\t\t\t\t\t\tonRetry,\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<FaceScanStep webcamRef={webcamRef} canvasRef={canvasRef} />\n\t\t\t\t</ParamsContext.Provider>\n\t\t\t</ConfigProvider>\n\t\t</LanguageContextProvider>\n\t\t</div>\n\t);\n};\n"],"names":["preloadedDetector","isPreloading","preloadDetector","async","window","Promise","r","setTimeout","tf","poseDetection","all","import","setBackend","ready","detector","createDetector","SupportedModels","BlazePose","runtime","modelType","dummyCanvas","document","createElement","width","videoConstraints","ideal","max","height","ctx","getContext","fillStyle","fillRect","estimatePoses","e","console","warn","err","error","useFaceScan","faceScanId","onValidPose","onScanComplete","onModelReady","onStartRecording","shopDomain","isError","isSuccess","detectorRef","useRef","scanStage","setScanStage","useState","consecutiveValid","setConsecutiveValid","isModelLoaded","setIsModelLoaded","isScanningActive","setIsScanningActive","isInferencingRef","inferenceCanvasRef","utteranceRef","scanStageRef","consecutiveValidRef","validityBufferRef","lastSpokenMessageRef","lastSpokenTimeRef","voiceEnabledRef","lastPoseTrackingTimeRef","isIOS","navigator","test","userAgent","platform","maxTouchPoints","directionMessages","useMemo","debouncedTrackPoseDetection","fn","delay","timer","args","clearTimeout","debounce","payload","posthog","capture","trackPoseDetection","useCallback","faceKeypoints","bodyKeypoints","stage","headValid","bodyInvalid","consecutiveValidCount","now","Date","current","nose","leftEye","rightEye","leftMouth","rightMouth","faceKeypointScores","map","kp","filter","score","avgFaceScore","length","reduce","a","b","bodyKeypointScores","avgBodyScore","eyeDistance","Math","abs","noseToRight","noseToLeft","expectedPosition","timestamp","toISOString","data","JSON","stringify","parseFloat","toFixed","faceKeypointsDetected","bodyKeypointsDetected","noseToRightRatio","noseToLeftRatio","noseScore","leftEyeScore","rightEyeScore","leftMouthScore","rightMouthScore","startScanSequence","stageName","useEffect","log","initializeModels","disposeModelAndTf","faceScanDetector","webcamRef","canvasRef","readyState","vw","videoWidth","vh","videoHeight","ictx","drawImage","poses","keypoints","forEach","landmark","idx","point","x","y","push","clearRect","progress","min","radius","beginPath","strokeStyle","lineWidth","arc","PI","stroke","eyesToMouthVerticalDistance","faceTiltedness","faceIsTilted","faceIsVertical","frontalFace","fullLeft","fullRight","isHeadInPosition","slice","p","shift","Boolean","nextValid","nextStage","speechService","playAudio","voiceOverAssetsPath","completedStage","totalStages","nextSound","resetScan","speechSynthesis","cancel","enableVoiceCommands","silentUtterance","SpeechSynthesisUtterance","volume","speak","FaceScanErrorScreen","onRetry","loading","translate","useContext","LanguageContext","config","useConfig","_jsx","className","style","background","base","backgroundColor","children","_jsxs","Header","noTitle","fontFamily","heading","headingFontFamily","fontSize","headingFontSize","color","headingColor","fontWeight","headingFontWeight","LanguageKeys","scanFailed","subheading","subheadingFontFamily","subheadingFontSize","subheadingColor","subheadingFontWeight","clickToResetScan","SpecificButton","disabled","buttonText","postfixIcon","ArrowRight","buttonFunc","FaceScanGuide","videoLoading","setVideoLoading","videoError","setVideoError","gender","btnConfig","faceVisible","loadingVideo","videoLoadFailed","src","GenderType","Male","maleGlassesOffVideo","glassesOffVideo","autoPlay","loop","controls","muted","playsInline","onCanPlay","onLoadStart","onError","isDisabled","label","icon","onClick","FACE_SCAN_HEADSHOT","male","forward","female","type","left","right","smile","FaceScanStep","showLoader","hasError","errorType","resetScanState","showGuideCard","getButtonText","isScanning","startScan","ParamsContext","resolvedConfig","LoadingScreen","url","loader","loaderType","_Fragment","id","crossOrigin","preload","position","zIndex","undefined","ref","transform","opacity","FaceScanErrorType","Camera","Modal","testId","Drawer","open","anchor","onClose","event","reason","hideBackdrop","FaceScan","props","onScanSuccess","onScanError","onScanStart","onCaptureComplete","onUploadStart","onUploadEnd","onMeasurementSocketStart","onMeasurementSocketClose","userDetails","email","deviceFocalLength","bodyScanId","callbackUrl","token","mediaRecorderRef","recordedBlobsRef","streamRef","recordedFpsRef","drawFrameRafRef","recordingVideoElRef","setShowLoader","modelReady","setModelReady","setIsScanning","setShowGuideCard","setHasError","setErrorType","setFaceScanId","generateUuid","swan","getSwanService","supportedTypes","setSupportedTypes","currentVideoConstraints","setCurrentVideoConstraints","videoConstraintsExact","preferredVideoTypes","getPreferredMediaRecorderTypes","recordingMimeType","getRecordingMimeType","recordingExtension","getRecordingExtension","usePosthogPageview","stopRecording","cancelAnimationFrame","pause","srcObject","state","stop","uploadFinalVideo","Scan","videoFile","File","videoFps","getVideoFPS","fileSize","size","fileSizeMB","estimatedDuration","round","videoData","video_size_mb","video_size_bytes","video_fps","blob_count","estimated_duration_seconds","metaData","face_scan_id","focal_length","customer_store_url","scan_type","body_scan_id","callback_url","handleScanTimeCapture","eventName","uploadStartTime","startTime","res","fileUpload","faceScanFileUploader","file","arrayMetaData","objectKey","contentType","uploadEndTime","completionTime","uploadDuration","measurement","handlFaceScaneSocket","onPreopen","handleWebSocketCapture","faceScanID","connection","onOpen","onSuccess","startRecording","stream","settings","getVideoTracks","getSettings","canvas","videoEl","play","drawFrame","save","rotate","restore","requestAnimationFrame","recordingFps","frameRate","canvasStream","captureStream","mediaRecorderOptions","videoBitsPerSecond","mediaRecorder","mimeType","MediaRecorder","ondataavailable","onstop","start","status","resultType","mediaDevices","getUserMedia","video","then","catch","videoConstraintsFallback","isCameraAccessError","includes","name","getTracks","track","intervalId","setInterval","clearInterval","supported","getSupportedMediaRecorderTypes","videoTypes","LanguageContextProvider","ConfigProvider","Provider","value","reset"],"mappings":"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"}
|