✨ Implement Pexels videos option to media popover (#1636)
Closes #1575 Note: Need to create a new environment variable named `NEXT_PUBLIC_PEXELS_API_KEY` to store the API Key obtained from Pexels! https://github.com/user-attachments/assets/4250f799-0bd7-48e9-b9a8-4bc188ad7704 --------- Co-authored-by: Baptiste Arnaud <baptiste.arnaud95@gmail.com> Co-authored-by: younesbenallal <younes.benallal.06@gmail.com>
This commit is contained in:
@ -82,6 +82,7 @@
|
|||||||
"nprogress": "0.2.0",
|
"nprogress": "0.2.0",
|
||||||
"openai": "4.47.1",
|
"openai": "4.47.1",
|
||||||
"papaparse": "5.4.1",
|
"papaparse": "5.4.1",
|
||||||
|
"pexels": "^1.4.0",
|
||||||
"prettier": "2.8.8",
|
"prettier": "2.8.8",
|
||||||
"qs": "6.11.2",
|
"qs": "6.11.2",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
@ -113,6 +114,7 @@
|
|||||||
"@typebot.io/schemas": "workspace:*",
|
"@typebot.io/schemas": "workspace:*",
|
||||||
"@typebot.io/telemetry": "workspace:*",
|
"@typebot.io/telemetry": "workspace:*",
|
||||||
"@typebot.io/tsconfig": "workspace:*",
|
"@typebot.io/tsconfig": "workspace:*",
|
||||||
|
"@typebot.io/variables": "workspace:*",
|
||||||
"@types/canvas-confetti": "1.6.0",
|
"@types/canvas-confetti": "1.6.0",
|
||||||
"@types/jsonwebtoken": "9.0.2",
|
"@types/jsonwebtoken": "9.0.2",
|
||||||
"@types/micro-cors": "0.1.3",
|
"@types/micro-cors": "0.1.3",
|
||||||
@ -131,7 +133,6 @@
|
|||||||
"next-runtime-env": "1.6.2",
|
"next-runtime-env": "1.6.2",
|
||||||
"superjson": "1.12.4",
|
"superjson": "1.12.4",
|
||||||
"typescript": "5.4.5",
|
"typescript": "5.4.5",
|
||||||
"zod": "3.22.4",
|
"zod": "3.22.4"
|
||||||
"@typebot.io/variables": "workspace:*"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ export const GiphyPicker = ({ onSubmit }: GiphySearchFormProps) => {
|
|||||||
placeholder="Search..."
|
placeholder="Search..."
|
||||||
onChange={setInputValue}
|
onChange={setInputValue}
|
||||||
withVariableButton={false}
|
withVariableButton={false}
|
||||||
|
width="full"
|
||||||
/>
|
/>
|
||||||
<GiphyLogo w="100px" />
|
<GiphyLogo w="100px" />
|
||||||
</Flex>
|
</Flex>
|
||||||
|
254
apps/builder/src/components/VideoUploadContent/PexelsPicker.tsx
Normal file
254
apps/builder/src/components/VideoUploadContent/PexelsPicker.tsx
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
|
import {
|
||||||
|
Alert,
|
||||||
|
AlertIcon,
|
||||||
|
Box,
|
||||||
|
Flex,
|
||||||
|
Grid,
|
||||||
|
GridItem,
|
||||||
|
HStack,
|
||||||
|
Image,
|
||||||
|
Link,
|
||||||
|
Spinner,
|
||||||
|
Stack,
|
||||||
|
Text,
|
||||||
|
} from '@chakra-ui/react'
|
||||||
|
import { isDefined } from '@typebot.io/lib'
|
||||||
|
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||||
|
import { createClient, Video, ErrorResponse, Videos } from 'pexels'
|
||||||
|
import { TextInput } from '../inputs'
|
||||||
|
import { TextLink } from '../TextLink'
|
||||||
|
import { env } from '@typebot.io/env'
|
||||||
|
import { PexelsLogo } from '../logos/PexelsLogo'
|
||||||
|
|
||||||
|
const client = createClient(env.NEXT_PUBLIC_PEXELS_API_KEY ?? 'dummy')
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
videoSize: 'large' | 'medium' | 'small'
|
||||||
|
onVideoSelect: (videoUrl: string) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PexelsPicker = ({ videoSize, onVideoSelect }: Props) => {
|
||||||
|
const [isFetching, setIsFetching] = useState(false)
|
||||||
|
const [videos, setVideos] = useState<Video[]>([])
|
||||||
|
const [error, setError] = useState<string | null>(null)
|
||||||
|
const [searchQuery, setSearchQuery] = useState('')
|
||||||
|
const scrollContainer = useRef<HTMLDivElement>(null)
|
||||||
|
const bottomAnchor = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
|
const [nextPage, setNextPage] = useState(0)
|
||||||
|
|
||||||
|
const fetchNewVideos = useCallback(async (query: string, page: number) => {
|
||||||
|
if (query === '') getInitialVideos()
|
||||||
|
if (query.length <= 2) {
|
||||||
|
setNextPage(0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setError(null)
|
||||||
|
setIsFetching(true)
|
||||||
|
try {
|
||||||
|
const result = await client.videos.search({
|
||||||
|
query,
|
||||||
|
per_page: 24,
|
||||||
|
size: videoSize,
|
||||||
|
page,
|
||||||
|
})
|
||||||
|
if ((result as ErrorResponse).error)
|
||||||
|
setError((result as ErrorResponse).error)
|
||||||
|
if (isDefined((result as Videos).videos)) {
|
||||||
|
if (page === 0) setVideos((result as Videos).videos)
|
||||||
|
else
|
||||||
|
setVideos((videos) => [
|
||||||
|
...videos,
|
||||||
|
...((result as Videos)?.videos ?? []),
|
||||||
|
])
|
||||||
|
setNextPage((page) => page + 1)
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
if (err && typeof err === 'object' && 'message' in err)
|
||||||
|
setError(err.message as string)
|
||||||
|
setError('Something went wrong')
|
||||||
|
}
|
||||||
|
setIsFetching(false)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!bottomAnchor.current) return
|
||||||
|
const observer = new IntersectionObserver(
|
||||||
|
(entities: IntersectionObserverEntry[]) => {
|
||||||
|
const target = entities[0]
|
||||||
|
if (target.isIntersecting) fetchNewVideos(searchQuery, nextPage + 1)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
root: scrollContainer.current,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if (bottomAnchor.current && nextPage > 0)
|
||||||
|
observer.observe(bottomAnchor.current)
|
||||||
|
return () => {
|
||||||
|
observer.disconnect()
|
||||||
|
}
|
||||||
|
}, [fetchNewVideos, nextPage, searchQuery])
|
||||||
|
|
||||||
|
const getInitialVideos = async () => {
|
||||||
|
setError(null)
|
||||||
|
setIsFetching(true)
|
||||||
|
client.videos
|
||||||
|
.popular({
|
||||||
|
per_page: 24,
|
||||||
|
size: videoSize,
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
if ((res as ErrorResponse).error) {
|
||||||
|
setError((res as ErrorResponse).error)
|
||||||
|
}
|
||||||
|
setVideos((res as Videos).videos)
|
||||||
|
setIsFetching(false)
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
if (err && typeof err === 'object' && 'message' in err)
|
||||||
|
setError(err.message as string)
|
||||||
|
setError('Something went wrong')
|
||||||
|
setIsFetching(false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectVideo = (video: Video) => {
|
||||||
|
const videoUrl = video.video_files[0].link
|
||||||
|
if (isDefined(videoUrl)) onVideoSelect(videoUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!env.NEXT_PUBLIC_PEXELS_API_KEY) return
|
||||||
|
getInitialVideos()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
if (!env.NEXT_PUBLIC_PEXELS_API_KEY)
|
||||||
|
return <Text>NEXT_PUBLIC_PEXELS_API_KEY is missing in environment</Text>
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack spacing={4} pt="2">
|
||||||
|
<HStack align="center">
|
||||||
|
<TextInput
|
||||||
|
autoFocus
|
||||||
|
placeholder="Search..."
|
||||||
|
onChange={(query) => {
|
||||||
|
setSearchQuery(query)
|
||||||
|
fetchNewVideos(query, 0)
|
||||||
|
}}
|
||||||
|
withVariableButton={false}
|
||||||
|
debounceTimeout={500}
|
||||||
|
forceDebounce
|
||||||
|
width="full"
|
||||||
|
/>
|
||||||
|
<Link isExternal href={`https://www.pexels.com`}>
|
||||||
|
<PexelsLogo width="100px" height="40px" />
|
||||||
|
</Link>
|
||||||
|
</HStack>
|
||||||
|
{isDefined(error) && (
|
||||||
|
<Alert status="error">
|
||||||
|
<AlertIcon />
|
||||||
|
{error}
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
<Stack overflowY="auto" maxH="400px" ref={scrollContainer}>
|
||||||
|
{videos.length > 0 && (
|
||||||
|
<Grid templateColumns="repeat(3, 1fr)" columnGap={2} rowGap={3}>
|
||||||
|
{videos.map((video, index) => (
|
||||||
|
<GridItem
|
||||||
|
as={Stack}
|
||||||
|
key={video.id}
|
||||||
|
boxSize="100%"
|
||||||
|
spacing="0"
|
||||||
|
ref={index === videos.length - 1 ? bottomAnchor : undefined}
|
||||||
|
>
|
||||||
|
<PexelsVideo video={video} onClick={() => selectVideo(video)} />
|
||||||
|
</GridItem>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
|
{isFetching && (
|
||||||
|
<Flex justifyContent="center" py="4">
|
||||||
|
<Spinner />
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
type PexelsVideoProps = {
|
||||||
|
video: Video
|
||||||
|
onClick: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const PexelsVideo = ({ video, onClick }: PexelsVideoProps) => {
|
||||||
|
const { user, url, video_pictures } = video
|
||||||
|
const [isImageHovered, setIsImageHovered] = useState(false)
|
||||||
|
const [thumbnailImage, setThumbnailImage] = useState(
|
||||||
|
video_pictures[0].picture
|
||||||
|
)
|
||||||
|
const [imageIndex, setImageIndex] = useState(1)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let interval: NodeJS.Timer
|
||||||
|
|
||||||
|
if (isImageHovered && video_pictures.length > 0) {
|
||||||
|
interval = setInterval(() => {
|
||||||
|
setImageIndex((prevIndex) => (prevIndex + 1) % video_pictures.length)
|
||||||
|
setThumbnailImage(video_pictures[imageIndex].picture)
|
||||||
|
}, 200)
|
||||||
|
} else {
|
||||||
|
setThumbnailImage(video_pictures[0].picture)
|
||||||
|
setImageIndex(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (interval) {
|
||||||
|
clearInterval(interval)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [isImageHovered, imageIndex, video_pictures])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
pos="relative"
|
||||||
|
onMouseEnter={() => setIsImageHovered(true)}
|
||||||
|
onMouseLeave={() => setIsImageHovered(false)}
|
||||||
|
h="full"
|
||||||
|
>
|
||||||
|
{
|
||||||
|
<Image
|
||||||
|
objectFit="cover"
|
||||||
|
src={thumbnailImage}
|
||||||
|
alt={`Pexels Video ${video.id}`}
|
||||||
|
onClick={onClick}
|
||||||
|
rounded="md"
|
||||||
|
h="100%"
|
||||||
|
aspectRatio={4 / 3}
|
||||||
|
cursor="pointer"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
<Box
|
||||||
|
pos="absolute"
|
||||||
|
bottom={0}
|
||||||
|
left={0}
|
||||||
|
bgColor="rgba(0,0,0,.5)"
|
||||||
|
px="2"
|
||||||
|
rounded="md"
|
||||||
|
opacity={isImageHovered ? 1 : 0}
|
||||||
|
transition="opacity .2s ease-in-out"
|
||||||
|
>
|
||||||
|
<TextLink
|
||||||
|
fontSize="xs"
|
||||||
|
isExternal
|
||||||
|
href={url}
|
||||||
|
noOfLines={1}
|
||||||
|
color="white"
|
||||||
|
>
|
||||||
|
{user.name}
|
||||||
|
</TextLink>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,110 @@
|
|||||||
|
import { Stack, Text } from '@chakra-ui/react'
|
||||||
|
import { useTranslate } from '@tolgee/react'
|
||||||
|
import { VideoBubbleBlock } from '@typebot.io/schemas'
|
||||||
|
import { TextInput } from '@/components/inputs'
|
||||||
|
import { defaultVideoBubbleContent } from '@typebot.io/schemas/features/blocks/bubbles/video/constants'
|
||||||
|
import { SwitchWithLabel } from '../inputs/SwitchWithLabel'
|
||||||
|
|
||||||
|
export const VideoLinkEmbedContent = ({
|
||||||
|
content,
|
||||||
|
updateUrl,
|
||||||
|
onSubmit,
|
||||||
|
}: {
|
||||||
|
content?: VideoBubbleBlock['content']
|
||||||
|
updateUrl: (url: string) => void
|
||||||
|
onSubmit: (content: VideoBubbleBlock['content']) => void
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslate()
|
||||||
|
|
||||||
|
const updateAspectRatio = (aspectRatio?: string) => {
|
||||||
|
return onSubmit({
|
||||||
|
...content,
|
||||||
|
aspectRatio,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateMaxWidth = (maxWidth?: string) => {
|
||||||
|
return onSubmit({
|
||||||
|
...content,
|
||||||
|
maxWidth,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateAutoPlay = (isAutoplayEnabled: boolean) => {
|
||||||
|
return onSubmit({ ...content, isAutoplayEnabled })
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateControlsDisplay = (areControlsDisplayed: boolean) => {
|
||||||
|
if (areControlsDisplayed === false) {
|
||||||
|
// Make sure autoplay is enabled when video controls are disabled
|
||||||
|
return onSubmit({
|
||||||
|
...content,
|
||||||
|
isAutoplayEnabled: true,
|
||||||
|
areControlsDisplayed,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return onSubmit({ ...content, areControlsDisplayed })
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Stack py="2">
|
||||||
|
<TextInput
|
||||||
|
placeholder={t('video.urlInput.placeholder')}
|
||||||
|
defaultValue={content?.url ?? ''}
|
||||||
|
onChange={updateUrl}
|
||||||
|
/>
|
||||||
|
<Text fontSize="xs" color="gray.400" textAlign="center">
|
||||||
|
{t('video.urlInput.helperText')}
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
{content?.url && (
|
||||||
|
<Stack>
|
||||||
|
<TextInput
|
||||||
|
label={t('video.aspectRatioInput.label')}
|
||||||
|
moreInfoTooltip={t('video.aspectRatioInput.moreInfoTooltip')}
|
||||||
|
defaultValue={
|
||||||
|
content?.aspectRatio ?? defaultVideoBubbleContent.aspectRatio
|
||||||
|
}
|
||||||
|
onChange={updateAspectRatio}
|
||||||
|
direction="row"
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={t('video.maxWidthInput.label')}
|
||||||
|
moreInfoTooltip={t('video.maxWidthInput.moreInfoTooltip')}
|
||||||
|
defaultValue={
|
||||||
|
content?.maxWidth ?? defaultVideoBubbleContent.maxWidth
|
||||||
|
}
|
||||||
|
onChange={updateMaxWidth}
|
||||||
|
direction="row"
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
{content?.url && content?.type === 'url' && (
|
||||||
|
<Stack>
|
||||||
|
<SwitchWithLabel
|
||||||
|
label={'Display controls'}
|
||||||
|
initialValue={
|
||||||
|
content?.areControlsDisplayed ??
|
||||||
|
defaultVideoBubbleContent.areControlsDisplayed
|
||||||
|
}
|
||||||
|
onCheckChange={updateControlsDisplay}
|
||||||
|
/>
|
||||||
|
<SwitchWithLabel
|
||||||
|
label={t('editor.blocks.bubbles.audio.settings.autoplay.label')}
|
||||||
|
initialValue={
|
||||||
|
content?.isAutoplayEnabled ??
|
||||||
|
defaultVideoBubbleContent.isAutoplayEnabled
|
||||||
|
}
|
||||||
|
isChecked={
|
||||||
|
content?.isAutoplayEnabled ??
|
||||||
|
defaultVideoBubbleContent.isAutoplayEnabled
|
||||||
|
}
|
||||||
|
isDisabled={content?.areControlsDisplayed === false}
|
||||||
|
onCheckChange={() => updateAutoPlay(!content.isAutoplayEnabled)}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import { IconProps, Icon } from '@chakra-ui/react'
|
import { IconProps, Icon, useColorModeValue } from '@chakra-ui/react'
|
||||||
|
|
||||||
export const GiphyLogo = (props: IconProps) => (
|
export const GiphyLogo = (props: IconProps) => (
|
||||||
<Icon viewBox="0 0 163.79999999999998 35" {...props}>
|
<Icon viewBox="0 0 163.79999999999998 35" {...props}>
|
||||||
@ -15,7 +15,7 @@ export const GiphyLogo = (props: IconProps) => (
|
|||||||
<path d="M16 0v4h-4" fill="#999131" />
|
<path d="M16 0v4h-4" fill="#999131" />
|
||||||
<path
|
<path
|
||||||
d="M59.1 12c-2-1.9-4.4-2.4-6.2-2.4-4.4 0-7.3 2.6-7.3 8 0 3.5 1.8 7.8 7.3 7.8 1.4 0 3.7-.3 5.2-1.4v-3.5h-6.9v-6h13.3v12.1c-1.7 3.5-6.4 5.3-11.7 5.3-10.7 0-14.8-7.2-14.8-14.3S42.7 3.2 52.9 3.2c3.8 0 7.1.8 10.7 4.4zm9.1 19.2V4h7.6v27.2zm20.1-7.4v7.3h-7.7V4h13.2c7.3 0 10.9 4.6 10.9 9.9 0 5.6-3.6 9.9-10.9 9.9zm0-6.5h5.5c2.1 0 3.2-1.6 3.2-3.3 0-1.8-1.1-3.4-3.2-3.4h-5.5zM125 31.2V20.9h-9.8v10.3h-7.7V4h7.7v10.3h9.8V4h7.6v27.2zm24.2-17.9l5.9-9.3h8.7v.3l-10.8 16v10.8h-7.7V20.3L135 4.3V4h8.7z"
|
d="M59.1 12c-2-1.9-4.4-2.4-6.2-2.4-4.4 0-7.3 2.6-7.3 8 0 3.5 1.8 7.8 7.3 7.8 1.4 0 3.7-.3 5.2-1.4v-3.5h-6.9v-6h13.3v12.1c-1.7 3.5-6.4 5.3-11.7 5.3-10.7 0-14.8-7.2-14.8-14.3S42.7 3.2 52.9 3.2c3.8 0 7.1.8 10.7 4.4zm9.1 19.2V4h7.6v27.2zm20.1-7.4v7.3h-7.7V4h13.2c7.3 0 10.9 4.6 10.9 9.9 0 5.6-3.6 9.9-10.9 9.9zm0-6.5h5.5c2.1 0 3.2-1.6 3.2-3.3 0-1.8-1.1-3.4-3.2-3.4h-5.5zM125 31.2V20.9h-9.8v10.3h-7.7V4h7.7v10.3h9.8V4h7.6v27.2zm24.2-17.9l5.9-9.3h8.7v.3l-10.8 16v10.8h-7.7V20.3L135 4.3V4h8.7z"
|
||||||
fill="#000"
|
fill={useColorModeValue('#000', '#fff')}
|
||||||
fillRule="nonzero"
|
fillRule="nonzero"
|
||||||
/>
|
/>
|
||||||
</g>
|
</g>
|
||||||
|
25
apps/builder/src/components/logos/PexelsLogo.tsx
Normal file
25
apps/builder/src/components/logos/PexelsLogo.tsx
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { IconProps, Icon, useColorModeValue } from '@chakra-ui/react'
|
||||||
|
|
||||||
|
export const PexelsLogo = (props: IconProps) => (
|
||||||
|
<Icon viewBox="0 0 130.318 50" {...props}>
|
||||||
|
<g transform="translate(-3894 2762)">
|
||||||
|
<rect
|
||||||
|
width="50"
|
||||||
|
height="50"
|
||||||
|
rx="8"
|
||||||
|
transform="translate(3894 -2762)"
|
||||||
|
fill="#07a081"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M32.671,44.73h7.091V37.935H41.9a5.657,5.657,0,1,0,0-11.314H32.671Zm10.763,3.622H29V23H41.9a9.271,9.271,0,0,1,1.53,18.435Z"
|
||||||
|
transform="translate(3880 -2773)"
|
||||||
|
fill="#fff"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M1.694,0h2.6V-6.16H7.656a6.579,6.579,0,0,0,2.915-.616,4.639,4.639,0,0,0,1.969-1.76,5.1,5.1,0,0,0,.7-2.728,5.146,5.146,0,0,0-.7-2.75,4.639,4.639,0,0,0-1.969-1.76,6.579,6.579,0,0,0-2.915-.616H1.694Zm2.6-8.47v-5.61H7.722a3.03,3.03,0,0,1,2.134.748,2.641,2.641,0,0,1,.814,2.046A2.684,2.684,0,0,1,9.856-9.24a2.978,2.978,0,0,1-2.134.77ZM20.372.264a5.925,5.925,0,0,0,3.179-.836,4.64,4.64,0,0,0,1.9-2.112l-2.024-.99a3.73,3.73,0,0,1-1.2,1.243,3.29,3.29,0,0,1-1.837.5A3.458,3.458,0,0,1,18-2.827a3.433,3.433,0,0,1-1.1-2.409H25.74a3.34,3.34,0,0,0,.088-.572q.022-.308.022-.594a6.154,6.154,0,0,0-.671-2.849,5.361,5.361,0,0,0-1.936-2.112,5.61,5.61,0,0,0-3.069-.8,5.7,5.7,0,0,0-3,.8,5.773,5.773,0,0,0-2.1,2.2,6.476,6.476,0,0,0-.77,3.179A6.482,6.482,0,0,0,15.081-2.8,5.9,5.9,0,0,0,17.226-.561,5.958,5.958,0,0,0,20.372.264Zm-.2-10.34a3,3,0,0,1,2.112.792,2.9,2.9,0,0,1,.924,2.068H16.94a3.313,3.313,0,0,1,1.122-2.112A3.208,3.208,0,0,1,20.174-10.076ZM26.422,0h2.926l2.706-3.982L34.738,0h2.926L33.506-5.962l4.18-5.94H34.76L32.054-7.964,29.348-11.9H26.422l4.158,5.94ZM44.088.264a5.925,5.925,0,0,0,3.179-.836,4.64,4.64,0,0,0,1.9-2.112l-2.024-.99a3.73,3.73,0,0,1-1.2,1.243,3.29,3.29,0,0,1-1.837.5,3.458,3.458,0,0,1-2.4-.891,3.433,3.433,0,0,1-1.1-2.409h8.844a3.34,3.34,0,0,0,.088-.572q.022-.308.022-.594A6.154,6.154,0,0,0,48.9-9.251a5.361,5.361,0,0,0-1.936-2.112,5.61,5.61,0,0,0-3.069-.8,5.7,5.7,0,0,0-3,.8,5.773,5.773,0,0,0-2.1,2.2,6.476,6.476,0,0,0-.77,3.179A6.482,6.482,0,0,0,38.8-2.8,5.9,5.9,0,0,0,40.942-.561,5.958,5.958,0,0,0,44.088.264Zm-.2-10.34A3,3,0,0,1,46-9.284a2.9,2.9,0,0,1,.924,2.068h-6.27a3.313,3.313,0,0,1,1.122-2.112A3.208,3.208,0,0,1,43.89-10.076ZM51.546,0h2.486V-16.654H51.546ZM60.9.264a5.6,5.6,0,0,0,2.321-.451,3.635,3.635,0,0,0,1.551-1.254,3.21,3.21,0,0,0,.55-1.859,3.088,3.088,0,0,0-.792-2.123A4.635,4.635,0,0,0,62.26-6.732L60.324-7.3a4.436,4.436,0,0,1-1.034-.484,1.023,1.023,0,0,1-.484-.924,1.212,1.212,0,0,1,.484-1.012,2.068,2.068,0,0,1,1.3-.374,3.005,3.005,0,0,1,1.705.506A2.944,2.944,0,0,1,63.4-8.228l1.914-.9a4.344,4.344,0,0,0-1.8-2.233,5.337,5.337,0,0,0-2.9-.8,5.1,5.1,0,0,0-2.178.451,3.7,3.7,0,0,0-1.518,1.243,3.2,3.2,0,0,0-.55,1.87,3.1,3.1,0,0,0,.759,2.09,4.624,4.624,0,0,0,2.3,1.32l1.87.528a3.923,3.923,0,0,1,1.078.473,1.057,1.057,0,0,1,.506.957,1.259,1.259,0,0,1-.55,1.078,2.391,2.391,0,0,1-1.43.4,3.2,3.2,0,0,1-1.881-.594A4.049,4.049,0,0,1,57.684-3.96l-1.914.9a4.774,4.774,0,0,0,1.925,2.42A5.7,5.7,0,0,0,60.9.264Z"
|
||||||
|
transform="translate(3959 -2728)"
|
||||||
|
fill={useColorModeValue('#000', '#fff')}
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</Icon>
|
||||||
|
)
|
@ -1,18 +1,46 @@
|
|||||||
import { Stack, Text } from '@chakra-ui/react'
|
import { Button, HStack, Stack } from '@chakra-ui/react'
|
||||||
import { VideoBubbleBlock } from '@typebot.io/schemas'
|
import { VideoBubbleBlock } from '@typebot.io/schemas'
|
||||||
import { TextInput } from '@/components/inputs'
|
|
||||||
import { useTranslate } from '@tolgee/react'
|
|
||||||
import { parseVideoUrl } from '@typebot.io/schemas/features/blocks/bubbles/video/helpers'
|
import { parseVideoUrl } from '@typebot.io/schemas/features/blocks/bubbles/video/helpers'
|
||||||
import { defaultVideoBubbleContent } from '@typebot.io/schemas/features/blocks/bubbles/video/constants'
|
import { useState } from 'react'
|
||||||
import { SwitchWithLabel } from '@/components/inputs/SwitchWithLabel'
|
import { PexelsPicker } from '@/components/VideoUploadContent/PexelsPicker'
|
||||||
|
import { VideoLinkEmbedContent } from '@/components/VideoUploadContent/VideoLinkEmbedContent'
|
||||||
|
|
||||||
|
type Tabs = 'link' | 'pexels'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
content?: VideoBubbleBlock['content']
|
content?: VideoBubbleBlock['content']
|
||||||
onSubmit: (content: VideoBubbleBlock['content']) => void
|
onSubmit: (content: VideoBubbleBlock['content']) => void
|
||||||
}
|
initialTab?: Tabs
|
||||||
|
} & (
|
||||||
|
| {
|
||||||
|
includedTabs?: Tabs[]
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
excludedTabs?: Tabs[]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const defaultDisplayedTabs: Tabs[] = ['link', 'pexels']
|
||||||
|
|
||||||
|
export const VideoUploadContent = ({
|
||||||
|
content,
|
||||||
|
onSubmit,
|
||||||
|
initialTab,
|
||||||
|
...props
|
||||||
|
}: Props) => {
|
||||||
|
const includedTabs =
|
||||||
|
'includedTabs' in props
|
||||||
|
? props.includedTabs ?? defaultDisplayedTabs
|
||||||
|
: defaultDisplayedTabs
|
||||||
|
const excludedTabs = 'excludedTabs' in props ? props.excludedTabs ?? [] : []
|
||||||
|
const displayedTabs = defaultDisplayedTabs.filter(
|
||||||
|
(tab) => !excludedTabs.includes(tab) && includedTabs.includes(tab)
|
||||||
|
)
|
||||||
|
|
||||||
|
const [currentTab, setCurrentTab] = useState<Tabs>(
|
||||||
|
initialTab ?? displayedTabs[0]
|
||||||
|
)
|
||||||
|
|
||||||
export const VideoUploadContent = ({ content, onSubmit }: Props) => {
|
|
||||||
const { t } = useTranslate()
|
|
||||||
const updateUrl = (url: string) => {
|
const updateUrl = (url: string) => {
|
||||||
const {
|
const {
|
||||||
type,
|
type,
|
||||||
@ -20,6 +48,10 @@ export const VideoUploadContent = ({ content, onSubmit }: Props) => {
|
|||||||
id,
|
id,
|
||||||
videoSizeSuggestion,
|
videoSizeSuggestion,
|
||||||
} = parseVideoUrl(url)
|
} = parseVideoUrl(url)
|
||||||
|
if (currentTab !== 'link') {
|
||||||
|
// Allow user to update video settings after selection
|
||||||
|
setCurrentTab('link')
|
||||||
|
}
|
||||||
return onSubmit({
|
return onSubmit({
|
||||||
...content,
|
...content,
|
||||||
type,
|
type,
|
||||||
@ -30,94 +62,40 @@ export const VideoUploadContent = ({ content, onSubmit }: Props) => {
|
|||||||
: {}),
|
: {}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const updateAspectRatio = (aspectRatio?: string) => {
|
|
||||||
return onSubmit({
|
|
||||||
...content,
|
|
||||||
aspectRatio,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateMaxWidth = (maxWidth?: string) => {
|
|
||||||
return onSubmit({
|
|
||||||
...content,
|
|
||||||
maxWidth,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateAutoPlay = (isAutoplayEnabled: boolean) => {
|
|
||||||
return onSubmit({ ...content, isAutoplayEnabled })
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateControlsDisplay = (areControlsDisplayed: boolean) => {
|
|
||||||
if (areControlsDisplayed === false) {
|
|
||||||
// Make sure autoplay is enabled when video controls are disabled
|
|
||||||
return onSubmit({
|
|
||||||
...content,
|
|
||||||
isAutoplayEnabled: true,
|
|
||||||
areControlsDisplayed,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return onSubmit({ ...content, areControlsDisplayed })
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack p="2" spacing={4}>
|
<Stack>
|
||||||
<Stack>
|
<HStack>
|
||||||
<TextInput
|
{displayedTabs.includes('link') && (
|
||||||
placeholder={t('video.urlInput.placeholder')}
|
<Button
|
||||||
defaultValue={content?.url ?? ''}
|
variant={currentTab === 'link' ? 'solid' : 'ghost'}
|
||||||
onChange={updateUrl}
|
onClick={() => setCurrentTab('link')}
|
||||||
|
size="sm"
|
||||||
|
>
|
||||||
|
Link
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{displayedTabs.includes('pexels') && (
|
||||||
|
<Button
|
||||||
|
variant={currentTab === 'pexels' ? 'solid' : 'ghost'}
|
||||||
|
onClick={() => setCurrentTab('pexels')}
|
||||||
|
size="sm"
|
||||||
|
>
|
||||||
|
Pexels
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</HStack>
|
||||||
|
|
||||||
|
{/* Body content to be displayed below conditionally based on currentTab */}
|
||||||
|
{currentTab === 'link' && (
|
||||||
|
<VideoLinkEmbedContent
|
||||||
|
content={content}
|
||||||
|
updateUrl={updateUrl}
|
||||||
|
onSubmit={onSubmit}
|
||||||
/>
|
/>
|
||||||
<Text fontSize="xs" color="gray.400" textAlign="center">
|
|
||||||
{t('video.urlInput.helperText')}
|
|
||||||
</Text>
|
|
||||||
</Stack>
|
|
||||||
{content?.url && (
|
|
||||||
<Stack>
|
|
||||||
<TextInput
|
|
||||||
label={t('video.aspectRatioInput.label')}
|
|
||||||
moreInfoTooltip={t('video.aspectRatioInput.moreInfoTooltip')}
|
|
||||||
defaultValue={
|
|
||||||
content?.aspectRatio ?? defaultVideoBubbleContent.aspectRatio
|
|
||||||
}
|
|
||||||
onChange={updateAspectRatio}
|
|
||||||
direction="row"
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
label={t('video.maxWidthInput.label')}
|
|
||||||
moreInfoTooltip={t('video.maxWidthInput.moreInfoTooltip')}
|
|
||||||
defaultValue={
|
|
||||||
content?.maxWidth ?? defaultVideoBubbleContent.maxWidth
|
|
||||||
}
|
|
||||||
onChange={updateMaxWidth}
|
|
||||||
direction="row"
|
|
||||||
/>
|
|
||||||
</Stack>
|
|
||||||
)}
|
)}
|
||||||
{content?.url && content?.type === 'url' && (
|
{currentTab === 'pexels' && (
|
||||||
<Stack>
|
<PexelsPicker videoSize="medium" onVideoSelect={updateUrl} />
|
||||||
<SwitchWithLabel
|
|
||||||
label={'Display controls'}
|
|
||||||
initialValue={
|
|
||||||
content?.areControlsDisplayed ??
|
|
||||||
defaultVideoBubbleContent.areControlsDisplayed
|
|
||||||
}
|
|
||||||
onCheckChange={updateControlsDisplay}
|
|
||||||
/>
|
|
||||||
<SwitchWithLabel
|
|
||||||
label={t('editor.blocks.bubbles.audio.settings.autoplay.label')}
|
|
||||||
initialValue={
|
|
||||||
content?.isAutoplayEnabled ??
|
|
||||||
defaultVideoBubbleContent.isAutoplayEnabled
|
|
||||||
}
|
|
||||||
isChecked={
|
|
||||||
content?.isAutoplayEnabled ??
|
|
||||||
defaultVideoBubbleContent.isAutoplayEnabled
|
|
||||||
}
|
|
||||||
isDisabled={content?.areControlsDisplayed === false}
|
|
||||||
onCheckChange={() => updateAutoPlay(!content.isAutoplayEnabled)}
|
|
||||||
/>
|
|
||||||
</Stack>
|
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
)
|
)
|
||||||
|
@ -235,6 +235,14 @@ Used to search for images. You can create an Unsplash app [here](https://unsplas
|
|||||||
| NEXT_PUBLIC_UNSPLASH_APP_NAME | | Unsplash App name |
|
| NEXT_PUBLIC_UNSPLASH_APP_NAME | | Unsplash App name |
|
||||||
| NEXT_PUBLIC_UNSPLASH_ACCESS_KEY | | Unsplash API key |
|
| NEXT_PUBLIC_UNSPLASH_ACCESS_KEY | | Unsplash API key |
|
||||||
|
|
||||||
|
## Pexels (video picker)
|
||||||
|
|
||||||
|
Used to search for videos. You can create a Pexels app [here](https://www.pexels.com/api/key/)
|
||||||
|
|
||||||
|
| Parameter | Default | Description |
|
||||||
|
| -------------------------- | ------- | -------------- |
|
||||||
|
| NEXT_PUBLIC_PEXELS_API_KEY | | Pexels API key |
|
||||||
|
|
||||||
## Tolgee (i18n contribution dev tool)
|
## Tolgee (i18n contribution dev tool)
|
||||||
|
|
||||||
<Note>
|
<Note>
|
||||||
|
13
packages/env/env.ts
vendored
13
packages/env/env.ts
vendored
@ -322,6 +322,17 @@ const unsplashEnv = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const pexelsEnv = {
|
||||||
|
client: {
|
||||||
|
NEXT_PUBLIC_PEXELS_API_KEY: z.string().min(1).optional(),
|
||||||
|
},
|
||||||
|
runtimeEnv: {
|
||||||
|
NEXT_PUBLIC_PEXELS_API_KEY: getRuntimeVariable(
|
||||||
|
'NEXT_PUBLIC_PEXELS_API_KEY'
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
const whatsAppEnv = {
|
const whatsAppEnv = {
|
||||||
server: {
|
server: {
|
||||||
META_SYSTEM_USER_TOKEN: z.string().min(1).optional(),
|
META_SYSTEM_USER_TOKEN: z.string().min(1).optional(),
|
||||||
@ -440,6 +451,7 @@ export const env = createEnv({
|
|||||||
...giphyEnv.client,
|
...giphyEnv.client,
|
||||||
...vercelEnv.client,
|
...vercelEnv.client,
|
||||||
...unsplashEnv.client,
|
...unsplashEnv.client,
|
||||||
|
...pexelsEnv.client,
|
||||||
...sentryEnv.client,
|
...sentryEnv.client,
|
||||||
...posthogEnv.client,
|
...posthogEnv.client,
|
||||||
...tolgeeEnv.client,
|
...tolgeeEnv.client,
|
||||||
@ -452,6 +464,7 @@ export const env = createEnv({
|
|||||||
...giphyEnv.runtimeEnv,
|
...giphyEnv.runtimeEnv,
|
||||||
...vercelEnv.runtimeEnv,
|
...vercelEnv.runtimeEnv,
|
||||||
...unsplashEnv.runtimeEnv,
|
...unsplashEnv.runtimeEnv,
|
||||||
|
...pexelsEnv.runtimeEnv,
|
||||||
...sentryEnv.runtimeEnv,
|
...sentryEnv.runtimeEnv,
|
||||||
...posthogEnv.runtimeEnv,
|
...posthogEnv.runtimeEnv,
|
||||||
...tolgeeEnv.runtimeEnv,
|
...tolgeeEnv.runtimeEnv,
|
||||||
|
23
pnpm-lock.yaml
generated
23
pnpm-lock.yaml
generated
@ -238,6 +238,9 @@ importers:
|
|||||||
papaparse:
|
papaparse:
|
||||||
specifier: 5.4.1
|
specifier: 5.4.1
|
||||||
version: 5.4.1
|
version: 5.4.1
|
||||||
|
pexels:
|
||||||
|
specifier: ^1.4.0
|
||||||
|
version: 1.4.0
|
||||||
prettier:
|
prettier:
|
||||||
specifier: 2.8.8
|
specifier: 2.8.8
|
||||||
version: 2.8.8
|
version: 2.8.8
|
||||||
@ -969,7 +972,7 @@ importers:
|
|||||||
version: 2.8.8
|
version: 2.8.8
|
||||||
ts-jest:
|
ts-jest:
|
||||||
specifier: 29.0.5
|
specifier: 29.0.5
|
||||||
version: 29.0.5(@babel/core@7.22.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.22.9))(jest@29.4.1(@types/node@20.4.2)(babel-plugin-macros@3.1.0))(typescript@5.4.5)
|
version: 29.0.5(@babel/core@7.22.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.22.9))(esbuild@0.15.18)(jest@29.4.1(@types/node@20.4.2)(babel-plugin-macros@3.1.0))(typescript@5.4.5)
|
||||||
tsup:
|
tsup:
|
||||||
specifier: 6.5.0
|
specifier: 6.5.0
|
||||||
version: 6.5.0(@swc/core@1.3.101)(postcss@8.4.35)(typescript@5.4.5)
|
version: 6.5.0(@swc/core@1.3.101)(postcss@8.4.35)(typescript@5.4.5)
|
||||||
@ -4083,6 +4086,7 @@ packages:
|
|||||||
'@humanwhocodes/config-array@0.11.14':
|
'@humanwhocodes/config-array@0.11.14':
|
||||||
resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==}
|
resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==}
|
||||||
engines: {node: '>=10.10.0'}
|
engines: {node: '>=10.10.0'}
|
||||||
|
deprecated: Use @eslint/config-array instead
|
||||||
|
|
||||||
'@humanwhocodes/module-importer@1.0.1':
|
'@humanwhocodes/module-importer@1.0.1':
|
||||||
resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
|
resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
|
||||||
@ -4090,6 +4094,7 @@ packages:
|
|||||||
|
|
||||||
'@humanwhocodes/object-schema@2.0.2':
|
'@humanwhocodes/object-schema@2.0.2':
|
||||||
resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==}
|
resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==}
|
||||||
|
deprecated: Use @eslint/object-schema instead
|
||||||
|
|
||||||
'@img/sharp-darwin-arm64@0.33.2':
|
'@img/sharp-darwin-arm64@0.33.2':
|
||||||
resolution: {integrity: sha512-itHBs1rPmsmGF9p4qRe++CzCgd+kFYktnsoR1sbIAfsRMrJZau0Tt1AH9KVnufc2/tU02Gf6Ibujx+15qRE03w==}
|
resolution: {integrity: sha512-itHBs1rPmsmGF9p4qRe++CzCgd+kFYktnsoR1sbIAfsRMrJZau0Tt1AH9KVnufc2/tU02Gf6Ibujx+15qRE03w==}
|
||||||
@ -8489,6 +8494,7 @@ packages:
|
|||||||
|
|
||||||
glob@7.1.7:
|
glob@7.1.7:
|
||||||
resolution: {integrity: sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==}
|
resolution: {integrity: sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==}
|
||||||
|
deprecated: Glob versions prior to v9 are no longer supported
|
||||||
|
|
||||||
glob@7.2.3:
|
glob@7.2.3:
|
||||||
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
|
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
|
||||||
@ -8497,6 +8503,7 @@ packages:
|
|||||||
glob@8.1.0:
|
glob@8.1.0:
|
||||||
resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==}
|
resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
deprecated: Glob versions prior to v9 are no longer supported
|
||||||
|
|
||||||
globals@11.12.0:
|
globals@11.12.0:
|
||||||
resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
|
resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
|
||||||
@ -8789,6 +8796,7 @@ packages:
|
|||||||
|
|
||||||
inflight@1.0.6:
|
inflight@1.0.6:
|
||||||
resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
|
resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
|
||||||
|
deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
|
||||||
|
|
||||||
inherits@2.0.4:
|
inherits@2.0.4:
|
||||||
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
|
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
|
||||||
@ -10607,6 +10615,9 @@ packages:
|
|||||||
periscopic@3.1.0:
|
periscopic@3.1.0:
|
||||||
resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==}
|
resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==}
|
||||||
|
|
||||||
|
pexels@1.4.0:
|
||||||
|
resolution: {integrity: sha512-akpLySokCtw9JHGx7yMavOIAHGVP5721rLUONR/cFKjWkLjUXsHrJ5jndMKss9mx7AEMZRXs7loxEb+vLJf6kA==}
|
||||||
|
|
||||||
picocolors@1.0.0:
|
picocolors@1.0.0:
|
||||||
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
|
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
|
||||||
|
|
||||||
@ -11417,6 +11428,7 @@ packages:
|
|||||||
|
|
||||||
rimraf@3.0.2:
|
rimraf@3.0.2:
|
||||||
resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
|
resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
|
||||||
|
deprecated: Rimraf versions prior to v4 are no longer supported
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
rollup-plugin-postcss@4.0.2:
|
rollup-plugin-postcss@4.0.2:
|
||||||
@ -24186,6 +24198,12 @@ snapshots:
|
|||||||
estree-walker: 3.0.3
|
estree-walker: 3.0.3
|
||||||
is-reference: 3.0.2
|
is-reference: 3.0.2
|
||||||
|
|
||||||
|
pexels@1.4.0:
|
||||||
|
dependencies:
|
||||||
|
isomorphic-fetch: 3.0.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- encoding
|
||||||
|
|
||||||
picocolors@1.0.0: {}
|
picocolors@1.0.0: {}
|
||||||
|
|
||||||
picomatch@2.3.1: {}
|
picomatch@2.3.1: {}
|
||||||
@ -26026,7 +26044,7 @@ snapshots:
|
|||||||
|
|
||||||
ts-interface-checker@0.1.13: {}
|
ts-interface-checker@0.1.13: {}
|
||||||
|
|
||||||
ts-jest@29.0.5(@babel/core@7.22.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.22.9))(jest@29.4.1(@types/node@20.4.2)(babel-plugin-macros@3.1.0))(typescript@5.4.5):
|
ts-jest@29.0.5(@babel/core@7.22.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.22.9))(esbuild@0.15.18)(jest@29.4.1(@types/node@20.4.2)(babel-plugin-macros@3.1.0))(typescript@5.4.5):
|
||||||
dependencies:
|
dependencies:
|
||||||
bs-logger: 0.2.6
|
bs-logger: 0.2.6
|
||||||
fast-json-stable-stringify: 2.1.0
|
fast-json-stable-stringify: 2.1.0
|
||||||
@ -26042,6 +26060,7 @@ snapshots:
|
|||||||
'@babel/core': 7.22.9
|
'@babel/core': 7.22.9
|
||||||
'@jest/types': 29.6.3
|
'@jest/types': 29.6.3
|
||||||
babel-jest: 29.7.0(@babel/core@7.22.9)
|
babel-jest: 29.7.0(@babel/core@7.22.9)
|
||||||
|
esbuild: 0.15.18
|
||||||
|
|
||||||
tsconfck@3.0.3(typescript@5.4.5):
|
tsconfck@3.0.3(typescript@5.4.5):
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
|
Reference in New Issue
Block a user