From f6f14850e67ddf706587542dd49f47435b399017 Mon Sep 17 00:00:00 2001 From: "James Morris, MS" <96435344+james-a-morris@users.noreply.github.com> Date: Thu, 3 Oct 2024 11:07:18 -0400 Subject: [PATCH] feat(epic): marketing blog page (#59) --- next.config.mjs | 5 + package.json | 11 + .../[slug]/_components/article-content.tsx | 147 ++++++ .../blog/[slug]/_components/breadcrumb.tsx | 16 + .../[slug]/_components/contentful-image.tsx | 49 ++ .../blog/[slug]/_components/divider.tsx | 9 + .../[slug]/_components/iframe-container.tsx | 13 + .../blog/[slug]/_components/meta-info.tsx | 33 ++ .../blog/[slug]/_components/share-link.tsx | 85 +++ src/app/(routes)/blog/[slug]/page.tsx | 134 +++++ .../blog/_components/article-full-card.tsx | 41 ++ .../blog/_components/article-snippet-card.tsx | 31 ++ .../blog/_components/back-to-top-button.tsx | 16 + .../blog/_components/background-banner.tsx | 14 + src/app/(routes)/blog/_components/filter.tsx | 23 + .../(routes)/blog/_components/pagination.tsx | 76 +++ src/app/(routes)/blog/_components/posts.tsx | 15 + src/app/(routes)/blog/page.tsx | 95 ++++ src/app/_amplitude/index.ts | 69 ++- src/app/_assets/blog-background.png | Bin 0 -> 27866 bytes src/app/_components/footer.tsx | 9 +- src/app/_components/header-nav/index.tsx | 12 +- src/app/_components/icons/index.tsx | 3 + src/app/_components/icons/link.tsx | 37 ++ src/app/_components/icons/newspaper.tsx | 38 ++ src/app/_components/icons/search.tsx | 22 + src/app/_components/link.tsx | 2 +- src/app/_components/text.tsx | 8 +- src/app/_constants/amplitude.ts | 5 +- src/app/_constants/environment.ts | 4 + src/app/_constants/links.ts | 6 + src/app/_hooks/useFilter.ts | 47 ++ src/app/_hooks/useSetQueryParams.ts | 70 +++ src/app/_lib/amplitude.ts | 5 +- src/app/_lib/cache.ts | 13 + src/app/_lib/contentful.ts | 141 +++++ tailwind.config.ts | 4 +- yarn.lock | 487 +++++++++++++++++- 38 files changed, 1779 insertions(+), 16 deletions(-) create mode 100644 src/app/(routes)/blog/[slug]/_components/article-content.tsx create mode 100644 src/app/(routes)/blog/[slug]/_components/breadcrumb.tsx create mode 100644 src/app/(routes)/blog/[slug]/_components/contentful-image.tsx create mode 100644 src/app/(routes)/blog/[slug]/_components/divider.tsx create mode 100644 src/app/(routes)/blog/[slug]/_components/iframe-container.tsx create mode 100644 src/app/(routes)/blog/[slug]/_components/meta-info.tsx create mode 100644 src/app/(routes)/blog/[slug]/_components/share-link.tsx create mode 100644 src/app/(routes)/blog/[slug]/page.tsx create mode 100644 src/app/(routes)/blog/_components/article-full-card.tsx create mode 100644 src/app/(routes)/blog/_components/article-snippet-card.tsx create mode 100644 src/app/(routes)/blog/_components/back-to-top-button.tsx create mode 100644 src/app/(routes)/blog/_components/background-banner.tsx create mode 100644 src/app/(routes)/blog/_components/filter.tsx create mode 100644 src/app/(routes)/blog/_components/pagination.tsx create mode 100644 src/app/(routes)/blog/_components/posts.tsx create mode 100644 src/app/(routes)/blog/page.tsx create mode 100644 src/app/_assets/blog-background.png create mode 100644 src/app/_components/icons/link.tsx create mode 100644 src/app/_components/icons/newspaper.tsx create mode 100644 src/app/_components/icons/search.tsx create mode 100644 src/app/_hooks/useFilter.ts create mode 100644 src/app/_hooks/useSetQueryParams.ts create mode 100644 src/app/_lib/cache.ts create mode 100644 src/app/_lib/contentful.ts diff --git a/next.config.mjs b/next.config.mjs index cd0f948..70b3b31 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -7,6 +7,11 @@ const nextConfig = { hostname: "pbs.twimg.com", pathname: "**", }, + { + protocol: "https", + hostname: "images.ctfassets.net", + pathname: "**", + } ], }, }; diff --git a/package.json b/package.json index e041d31..1d767a9 100644 --- a/package.json +++ b/package.json @@ -13,20 +13,31 @@ }, "dependencies": { "@amplitude/analytics-browser": "^2.4.1", + "@contentful/rich-text-plain-text-renderer": "^16.2.8", + "@contentful/rich-text-react-renderer": "^15.22.9", + "@contentful/rich-text-types": "^16.8.3", "@headlessui/react": "^1.7.18", "@next/third-parties": "^14.2.5", "@typeform/embed-react": "^3.17.0", + "contentful": "^10.13.1", "embla-carousel-auto-scroll": "^8.1.5", "embla-carousel-autoplay": "^8.1.5", "embla-carousel-react": "^8.1.5", + "lodash.words": "^4.2.0", + "luxon": "^3.5.0", "next": "14.1.0", "numeral": "^2.0.6", "react": "^18", "react-dom": "^18", + "sharp": "^0.33.5", "tailwind-merge": "^2.2.1", + "tailwind-scrollbar-hide": "^1.1.7", + "use-debounce": "^10.0.3", "zod": "^3.22.4" }, "devDependencies": { + "@types/lodash.words": "^4.2.9", + "@types/luxon": "^3.4.2", "@types/node": "^20", "@types/numeral": "^2.0.5", "@types/react": "^18", diff --git a/src/app/(routes)/blog/[slug]/_components/article-content.tsx b/src/app/(routes)/blog/[slug]/_components/article-content.tsx new file mode 100644 index 0000000..7eb1935 --- /dev/null +++ b/src/app/(routes)/blog/[slug]/_components/article-content.tsx @@ -0,0 +1,147 @@ +import { + documentToReactComponents, + RenderMark, + RenderNode, +} from "@contentful/rich-text-react-renderer"; +import { BLOCKS, Document, INLINES, MARKS } from "@contentful/rich-text-types"; +import Link from "next/link"; +import { isExternal } from "util/types"; +import Divider from "./divider"; +import { IframeContainer } from "./iframe-container"; +import { Text } from "@/app/_components/text"; +import { Asset } from "contentful"; +import ContentfulImage from "./contentful-image"; + +// Map text-format types to custom components + +const markRenderers: RenderMark = { + [MARKS.BOLD]: (text) => {text}, + [MARKS.ITALIC]: (text) => {text}, + [MARKS.UNDERLINE]: (text) => {text}, + [MARKS.CODE]: (text) => {text}, + [MARKS.SUPERSCRIPT]: (text) => {text}, + [MARKS.SUBSCRIPT]: (text) => {text}, +}; + +const nodeRenderers: RenderNode = { + [INLINES.HYPERLINK]: (node, children) => { + const href = node.data.uri as string; + if ( + href.includes("youtube.com/embed") || + href.includes("player.vimeo.com") || + children?.toString().toLowerCase().includes("iframe") // to handle uncommon cases, creator can set the text to "iframe" + ) { + return ( + + + + ); + } + return ( + + {children} + + ); + }, + [BLOCKS.DOCUMENT]: (_, children) => children, + [BLOCKS.PARAGRAPH]: (_, children) => ( + +

{children}

+
+ ), + [BLOCKS.HEADING_1]: (_, children) => ( + +

{children}

+
+ ), + [BLOCKS.HEADING_2]: (_, children) => ( + +

{children}

+
+ ), + [BLOCKS.HEADING_3]: (_, children) => ( + +

{children}

+
+ ), + [BLOCKS.HEADING_4]: (_, children) => ( + +

{children}

+
+ ), + [BLOCKS.HEADING_5]: (_, children) => ( + +
{children}
+
+ ), + [BLOCKS.HEADING_6]: (_, children) => ( + +
{children}
+
+ ), + [BLOCKS.EMBEDDED_RESOURCE]: (_, children) =>
{children}
, + [BLOCKS.UL_LIST]: (_, children) => , + [BLOCKS.OL_LIST]: (_, children) =>
    {children}
, + [BLOCKS.LIST_ITEM]: (_, children) =>
  • {children}
  • , + [BLOCKS.QUOTE]: (_, children) =>
    {children}
    , + [BLOCKS.HR]: () => , + [BLOCKS.TABLE]: (_, children) => ( + + {children} +
    + ), + [BLOCKS.TABLE_ROW]: (_, children) => {children}, + [BLOCKS.TABLE_HEADER_CELL]: (_, children) => {children}, + [BLOCKS.TABLE_CELL]: (_, children) => {children}, + [BLOCKS.EMBEDDED_ASSET]: (node) => { + const data = node.data.target as Asset<"WITHOUT_UNRESOLVABLE_LINKS", string>; + const { file, description, title } = data.fields; + const mimeGroup = file?.contentType.split("/")[0]; // image / video etc + switch (mimeGroup) { + case "image": + return ; + // TODO: test this, make custom component if necessary + case "video": + return ( + + ); + // TODO: add other asset types, handle them + default: + return

    unknown file type

    ; + } + }, +}; + +const options = { + renderNode: nodeRenderers, + renderMark: markRenderers, + preserveWhitespace: true, +}; + +export default function ArticleContent({ content }: { content: Document }) { + return ( +
    + {documentToReactComponents(content, options)} +
    + ); +} diff --git a/src/app/(routes)/blog/[slug]/_components/breadcrumb.tsx b/src/app/(routes)/blog/[slug]/_components/breadcrumb.tsx new file mode 100644 index 0000000..8e80d8e --- /dev/null +++ b/src/app/(routes)/blog/[slug]/_components/breadcrumb.tsx @@ -0,0 +1,16 @@ +import Link from "next/link"; +import { ChevronDownIcon } from "@/app/_components/icons"; + +export default function Breadcrumb({ fullTitle }: { fullTitle: string }) { + // Max title to 40 characters + const title = fullTitle.length > 40 ? fullTitle.slice(0, 40) + "..." : fullTitle; + return ( +
    + + Blog + + +
    {title}
    +
    + ); +} diff --git a/src/app/(routes)/blog/[slug]/_components/contentful-image.tsx b/src/app/(routes)/blog/[slug]/_components/contentful-image.tsx new file mode 100644 index 0000000..9a4eef2 --- /dev/null +++ b/src/app/(routes)/blog/[slug]/_components/contentful-image.tsx @@ -0,0 +1,49 @@ +import { Asset } from "contentful"; +import Image from "next/image"; +import { object } from "zod"; + +export default function ContentfulImage({ + image, + borderless, + displayDescription, + fillDisplay, +}: { + image?: Asset<"WITHOUT_UNRESOLVABLE_LINKS", string>; + borderless?: boolean; + displayDescription?: boolean; + fillDisplay?: boolean; +}) { + if (!image) { + return null; + } + + const { file, description, title } = image.fields; + const url = file?.url; + if (!url) { + return null; + } + const urlWithProtocol = `https:${url}`; + + const classes = borderless ? "" : "rounded-3xl border border-white-translucent"; + + const props = fillDisplay + ? { fill: true, objectFit: "cover" } + : { + height: file.details.image?.height, + width: file.details.image?.width, + }; + + return ( +
    + {description + {description && displayDescription &&

    {description}

    } +
    + ); +} diff --git a/src/app/(routes)/blog/[slug]/_components/divider.tsx b/src/app/(routes)/blog/[slug]/_components/divider.tsx new file mode 100644 index 0000000..6c06851 --- /dev/null +++ b/src/app/(routes)/blog/[slug]/_components/divider.tsx @@ -0,0 +1,9 @@ +import { twMerge } from "@/app/_lib/tw-merge"; + +export default function Divider({ className }: { className?: string }) { + return ( +
    + ); +} diff --git a/src/app/(routes)/blog/[slug]/_components/iframe-container.tsx b/src/app/(routes)/blog/[slug]/_components/iframe-container.tsx new file mode 100644 index 0000000..a1e4489 --- /dev/null +++ b/src/app/(routes)/blog/[slug]/_components/iframe-container.tsx @@ -0,0 +1,13 @@ +import { twMerge } from "@/app/_lib/tw-merge"; + +type Props = { + className?: string; +}; + +export function IframeContainer({ className, children }: React.PropsWithChildren) { + return ( + + {children} + + ); +} diff --git a/src/app/(routes)/blog/[slug]/_components/meta-info.tsx b/src/app/(routes)/blog/[slug]/_components/meta-info.tsx new file mode 100644 index 0000000..1bf1a11 --- /dev/null +++ b/src/app/(routes)/blog/[slug]/_components/meta-info.tsx @@ -0,0 +1,33 @@ +import { Text } from "@/app/_components/text"; +import { getReadingTime } from "@/app/_lib/contentful"; +import { Document } from "@contentful/rich-text-types"; +import { DateTime } from "luxon"; +import { twMerge } from "tailwind-merge"; + +export function MetaInfo({ + isoCreatedDate, + content, + preventCenter, + compact, +}: { + isoCreatedDate: string; + content: Document; + preventCenter?: boolean; + compact?: boolean; +}) { + const dateString = DateTime.fromISO(isoCreatedDate).toFormat("MMM dd, yyyy"); + const minutesToRead = getReadingTime(content); + return ( +
    + {dateString}• + + {minutesToRead} min read + +
    + ); +} diff --git a/src/app/(routes)/blog/[slug]/_components/share-link.tsx b/src/app/(routes)/blog/[slug]/_components/share-link.tsx new file mode 100644 index 0000000..e192702 --- /dev/null +++ b/src/app/(routes)/blog/[slug]/_components/share-link.tsx @@ -0,0 +1,85 @@ +"use client"; + +import { Text } from "@/app/_components/text"; +import { BlogPostType } from "@/app/_lib/contentful"; +import { TwitterIcon, LinkIcon } from "@/app/_components/icons"; +import { useEffect, useState } from "react"; +import { twMerge } from "@/app/_lib/tw-merge"; +import { useRouter } from "next/navigation"; + +function LinkButton({ + onClick, + icon: Icon, +}: { + onClick: () => void; + icon: typeof TwitterIcon; +}) { + const [copyClicked, setCopyClicked] = useState(false); + useEffect(() => { + if (copyClicked) { + setTimeout(() => { + setCopyClicked(false); + }, 1000); + } + }, [copyClicked]); + + const borderColor = copyClicked ? "border-aqua-100" : "border-white-translucent"; + const textColor = copyClicked ? "text-aqua-100" : "text-white"; + return ( + + ); +} + +export default function ShareLink({ + entry, + collapsed, +}: { + entry: BlogPostType; + collapsed?: boolean; +}) { + const url = `https://across.to/blog/${entry.fields.slug}`; + const twitterUrl = `https://twitter.com/intent/tweet?url=${url}`; + + const buttons = [ + { + icon: LinkIcon, + onClick: () => { + navigator.clipboard.writeText(url); + }, + }, + { + icon: TwitterIcon, + onClick: () => { + window.open(twitterUrl, "_blank")?.focus(); + }, + }, + ]; + + return ( +
    + Share this article +
    + {buttons.map((button, index) => ( + + ))} +
    +
    + ); +} diff --git a/src/app/(routes)/blog/[slug]/page.tsx b/src/app/(routes)/blog/[slug]/page.tsx new file mode 100644 index 0000000..8ea6e75 --- /dev/null +++ b/src/app/(routes)/blog/[slug]/page.tsx @@ -0,0 +1,134 @@ +import { + resolvePublishDateToIsoDate, + retrieveContentfulEntry, + retrieveContentfulPublishedSlugs, +} from "@/app/_lib/contentful"; +import { redirect } from "next/navigation"; +import { ReactNode } from "react"; +import { twMerge } from "tailwind-merge"; +import Divider from "./_components/divider"; +import ArticleContent from "./_components/article-content"; +import BackgroundBanner from "../_components/background-banner"; +import Breadcrumb from "./_components/breadcrumb"; +import { MetaInfo } from "./_components/meta-info"; +import BackToTopButton from "../_components/back-to-top-button"; +import ContentfulImage from "./_components/contentful-image"; +import ShareLink from "./_components/share-link"; +import ArticleSnippetCard from "../_components/article-snippet-card"; +import { Metadata } from "next"; +import { documentToPlainTextString } from "@contentful/rich-text-plain-text-renderer"; +import { SITE_BASE_URL } from "@/app/_constants/links"; + +type SpecificBlogPageProps = { params: { slug: string } }; + +export async function generateMetadata({ + params, +}: SpecificBlogPageProps): Promise { + const entry = await retrieveContentfulEntry(params.slug); + if (!entry) { + redirect("/404"); + } + const title = entry.fields.title; + const description = + documentToPlainTextString(entry.fields.content).substring(0, 50) + "..."; + const imageUrl = `https:${entry.fields.featuredImage?.fields.file?.url}`; + + return { + keywords: entry.fields.tag ?? [], + metadataBase: new URL(SITE_BASE_URL), + publisher: "Across Protocol", + alternates: { + canonical: `/blog/${params.slug}`, + }, + title, + description, + icons: { + icon: ["/favicon-32x32.png", "/favicon-16x16.png"], + }, + twitter: { + card: "summary_large_image", + site: "@AcrossProtocol", + title, + images: [imageUrl], + }, + openGraph: { + siteName: "Across Protocol", + title, + description, + images: [imageUrl], + url: `/blog/${params.slug}`, + }, + }; +} + +export async function generateStaticParams() { + // Grab all relevant slugs and pipe them into the SSG function + // so that we can generate static blog pages without needing to + // use SSR at runtime as much as possible. + const { slugsForQuery } = await retrieveContentfulPublishedSlugs(); + return slugsForQuery.map((slug) => ({ + slug, + })); +} + +const SubStack = ({ + children, + className, +}: { + children?: ReactNode; + className?: string; +}) => + children && ( +
    + {children} +
    + ); + +export default async function SpecificBlogPage({ params }: SpecificBlogPageProps) { + const entry = await retrieveContentfulEntry(params.slug); + if (!entry) { + redirect("/404"); + } + const fullTitle = entry.fields.title; + const content = entry.fields.content; + return ( + <> + +
    +
    +
    + +
    +
    + + + + +

    + {fullTitle} +

    + {/** Block for Share Links */} + +
    + +
    + +
    + + + + +
    + {entry.relevantEntries.map((entry) => ( + + ))} +
    +
    + +
    + + ); +} diff --git a/src/app/(routes)/blog/_components/article-full-card.tsx b/src/app/(routes)/blog/_components/article-full-card.tsx new file mode 100644 index 0000000..edd9e13 --- /dev/null +++ b/src/app/(routes)/blog/_components/article-full-card.tsx @@ -0,0 +1,41 @@ +import { + resolvePublishDateToIsoDate, + retrieveContentfulEntry, +} from "@/app/_lib/contentful"; +import ContentfulImage from "../[slug]/_components/contentful-image"; +import { MetaInfo } from "../[slug]/_components/meta-info"; +import { documentToPlainTextString } from "@contentful/rich-text-plain-text-renderer"; +import { Text } from "@/app/_components/text"; +import Link from "next/link"; + +export default async function ArticleFullCard({ slug }: { slug: string }) { + const article = await retrieveContentfulEntry(slug); + if (!article) { + return null; + } + const description = + article.fields.description ?? documentToPlainTextString(article.fields.content); + + return ( + +
    + + + {article.fields.title} + + + {description} + +
    +
    + +
    + + ); +} diff --git a/src/app/(routes)/blog/_components/article-snippet-card.tsx b/src/app/(routes)/blog/_components/article-snippet-card.tsx new file mode 100644 index 0000000..db2501c --- /dev/null +++ b/src/app/(routes)/blog/_components/article-snippet-card.tsx @@ -0,0 +1,31 @@ +import { BlogPostType, resolvePublishDateToIsoDate } from "@/app/_lib/contentful"; +import { Text } from "@/app/_components/text"; +import Link from "next/link"; +import ContentfulImage from "../[slug]/_components/contentful-image"; +import { MetaInfo } from "../[slug]/_components/meta-info"; + +export default function ArticleSnippetCard({ + article, +}: { + article: BlogPostType; + expandedOnDesktop?: boolean; +}) { + return ( + + +
    + + + {article.fields.title} + +
    + + ); +} diff --git a/src/app/(routes)/blog/_components/back-to-top-button.tsx b/src/app/(routes)/blog/_components/back-to-top-button.tsx new file mode 100644 index 0000000..90665c8 --- /dev/null +++ b/src/app/(routes)/blog/_components/back-to-top-button.tsx @@ -0,0 +1,16 @@ +"use client"; + +import { Text } from "@/app/_components/text"; + +export default function BackToTopButton() { + const onClickHandler = () => { + window.scrollTo({ top: 0, behavior: "smooth" }); + }; + return ( +
    + + Back to top + +
    + ); +} diff --git a/src/app/(routes)/blog/_components/background-banner.tsx b/src/app/(routes)/blog/_components/background-banner.tsx new file mode 100644 index 0000000..ee860b7 --- /dev/null +++ b/src/app/(routes)/blog/_components/background-banner.tsx @@ -0,0 +1,14 @@ +import Image from "next/image"; +import backgroundBanner from "@/app/_assets/blog-background.png"; + +export default function BackgroundBanner() { + return ( + background-banner + ); +} diff --git a/src/app/(routes)/blog/_components/filter.tsx b/src/app/(routes)/blog/_components/filter.tsx new file mode 100644 index 0000000..7e0c5f2 --- /dev/null +++ b/src/app/(routes)/blog/_components/filter.tsx @@ -0,0 +1,23 @@ +"use client"; + +import { SearchIcon } from "@/app/_components/icons"; +import { useFilter } from "@/app/_hooks/useFilter"; + +export default function Filter() { + const { text, handleTextChange } = useFilter(); + + return ( +
    +
    + + handleTextChange(e.target.value)} + className="w-80 bg-transparent outline-none" + /> +
    +
    + ); +} diff --git a/src/app/(routes)/blog/_components/pagination.tsx b/src/app/(routes)/blog/_components/pagination.tsx new file mode 100644 index 0000000..4012af6 --- /dev/null +++ b/src/app/(routes)/blog/_components/pagination.tsx @@ -0,0 +1,76 @@ +"use client"; + +import { useFilter } from "@/app/_hooks/useFilter"; +import { twMerge } from "@/app/_lib/tw-merge"; + +export default function Pagination({ + pageLength, + currentPage, + totalCount, +}: { + pageLength: number; + currentPage: number; + totalCount: number; +}) { + const { handlePageChange } = useFilter(); + + const pageChangeHandler = (page: number) => { + handlePageChange(page); + window.scrollTo({ top: 0, behavior: "smooth" }); + }; + + const totalPages = Math.ceil(totalCount / pageLength); + const hasPrevious = currentPage > 1; + const hasNext = currentPage < totalPages; + + return ( +
    + pageChangeHandler(currentPage - 1)} + /> + {Array.from({ length: totalPages }).map((_, index) => ( + pageChangeHandler(index + 1)} + /> + ))} + pageChangeHandler(currentPage + 1)} + /> +
    + ); +} + +const PageButton = ({ + text, + isDisabled, + onClick, + isAqua, +}: { + text: string; + isDisabled: boolean; + isAqua: boolean; + onClick?: () => void; +}) => ( +
    + {text} +
    +); diff --git a/src/app/(routes)/blog/_components/posts.tsx b/src/app/(routes)/blog/_components/posts.tsx new file mode 100644 index 0000000..387c268 --- /dev/null +++ b/src/app/(routes)/blog/_components/posts.tsx @@ -0,0 +1,15 @@ +import { Text } from "@/app/_components/text"; +import ArticleFullCard from "./article-full-card"; + +export async function Posts({ isSearch, slugs }: { isSearch: boolean; slugs: string[] }) { + return ( +
    + + {isSearch ? "Search results" : "Most recent articles"} + + {slugs.map((slug) => ( + + ))} +
    + ); +} diff --git a/src/app/(routes)/blog/page.tsx b/src/app/(routes)/blog/page.tsx new file mode 100644 index 0000000..2a74e91 --- /dev/null +++ b/src/app/(routes)/blog/page.tsx @@ -0,0 +1,95 @@ +import { Text } from "@/app/_components/text"; +import BackToTopButton from "./_components/back-to-top-button"; +import BackgroundBanner from "./_components/background-banner"; +import Filter from "./_components/filter"; + +import { Suspense } from "react"; +import { Posts } from "./_components/posts"; +import { createCacheKey } from "@/app/_lib/cache"; +import { Metadata } from "next"; +import { SITE_BASE_URL } from "@/app/_constants/links"; +import Pagination from "./_components/pagination"; +import { retrieveContentfulPublishedSlugs } from "@/app/_lib/contentful"; + +export type SearchParams = Record; + +type PageProps = { + searchParams: SearchParams; +}; + +export default async function BlogHomePage({ searchParams }: PageProps) { + const key = createCacheKey({ + searchParams, + }); + + const search = searchParams["search"]; + const isSearch = Boolean(!!search); + const page = Number(searchParams["page"]); + + const pageNumber = isNaN(page) || !Number.isInteger(page) || page < 1 ? 1 : page; + const pageLength = 16; + + const { slugsForQuery, totalCount } = await retrieveContentfulPublishedSlugs({ + query: search, + sortByRecent: true, + limit: pageLength, + skip: (pageNumber - 1) * pageLength, + }); + + return ( + <> + +
    + +

    Across Blog

    +
    + + + + Searching... + } + > + + + + +
    + + ); +} + +export async function generateMetadata(): Promise { + const title = "Across Blog"; + const description = + "Explore the latest in cross-chain solutions with the Across blog. Dive into tutorials, updates and announcements that help you leverage our protocol for fast and secure cross-chain transactions."; + + return { + metadataBase: new URL(SITE_BASE_URL), + publisher: "Across Protocol", + alternates: { + canonical: `/blog`, + }, + title, + description, + icons: { + icon: ["/favicon-32x32.png", "/favicon-16x16.png"], + }, + twitter: { + card: "summary_large_image", + site: "@AcrossProtocol", + title, + }, + openGraph: { + siteName: "Across Protocol", + title, + description, + }, + }; +} diff --git a/src/app/_amplitude/index.ts b/src/app/_amplitude/index.ts index 90dc5b6..1c3fbb7 100644 --- a/src/app/_amplitude/index.ts +++ b/src/app/_amplitude/index.ts @@ -177,11 +177,37 @@ export interface IdentifyProperties { WethVolumeUsd?: any; } +export interface BlogSearchProperties { + /** + * | Rule | Value | + * |---|---| + * | Enum Values | splashPage, bridgePage, poolPage, rewardsPage, transactionsPage, stakingPage, referralPage, airdropPage, 404Page, marketingHomePage, marketingBridgePage, marketingAcrossPlusPage, marketingSettlementPage, depositStatusPage, marketingBlogSpecificPage, marketingBlogHomePage | + */ + page: + | "splashPage" + | "bridgePage" + | "poolPage" + | "rewardsPage" + | "transactionsPage" + | "stakingPage" + | "referralPage" + | "airdropPage" + | "404Page" + | "marketingHomePage" + | "marketingBridgePage" + | "marketingAcrossPlusPage" + | "marketingSettlementPage" + | "depositStatusPage" + | "marketingBlogSpecificPage" + | "marketingBlogHomePage"; + search: string; +} + export interface BridgeButtonClickedProperties { /** * | Rule | Value | * |---|---| - * | Enum Values | splashPage, bridgePage, poolPage, rewardsPage, transactionsPage, stakingPage, referralPage, airdropPage, 404Page, marketingHomePage, marketingBridgePage, marketingAcrossPlusPage, marketingSettlementPage, depositStatusPage | + * | Enum Values | splashPage, bridgePage, poolPage, rewardsPage, transactionsPage, stakingPage, referralPage, airdropPage, 404Page, marketingHomePage, marketingBridgePage, marketingAcrossPlusPage, marketingSettlementPage, depositStatusPage, marketingBlogSpecificPage, marketingBlogHomePage | */ page: | "splashPage" @@ -197,7 +223,9 @@ export interface BridgeButtonClickedProperties { | "marketingBridgePage" | "marketingAcrossPlusPage" | "marketingSettlementPage" - | "depositStatusPage"; + | "depositStatusPage" + | "marketingBlogSpecificPage" + | "marketingBlogHomePage"; /** * | Rule | Value | * |---|---| @@ -224,7 +252,7 @@ export interface CtaButtonClickedProperties { /** * | Rule | Value | * |---|---| - * | Enum Values | splashPage, bridgePage, poolPage, rewardsPage, transactionsPage, stakingPage, referralPage, airdropPage, 404Page, marketingHomePage, marketingBridgePage, marketingAcrossPlusPage, marketingSettlementPage, depositStatusPage | + * | Enum Values | splashPage, bridgePage, poolPage, rewardsPage, transactionsPage, stakingPage, referralPage, airdropPage, 404Page, marketingHomePage, marketingBridgePage, marketingAcrossPlusPage, marketingSettlementPage, depositStatusPage, marketingBlogSpecificPage, marketingBlogHomePage | */ page: | "splashPage" @@ -240,7 +268,9 @@ export interface CtaButtonClickedProperties { | "marketingBridgePage" | "marketingAcrossPlusPage" | "marketingSettlementPage" - | "depositStatusPage"; + | "depositStatusPage" + | "marketingBlogSpecificPage" + | "marketingBlogHomePage"; /** * | Rule | Value | * |---|---| @@ -276,7 +306,7 @@ export interface PageViewedProperties { /** * | Rule | Value | * |---|---| - * | Enum Values | splashPage, bridgePage, poolPage, rewardsPage, transactionsPage, stakingPage, referralPage, airdropPage, 404Page, marketingHomePage, marketingBridgePage, marketingAcrossPlusPage, marketingSettlementPage, depositStatusPage | + * | Enum Values | splashPage, bridgePage, poolPage, rewardsPage, transactionsPage, stakingPage, referralPage, airdropPage, 404Page, marketingHomePage, marketingBridgePage, marketingAcrossPlusPage, marketingSettlementPage, depositStatusPage, marketingBlogSpecificPage, marketingBlogHomePage | */ page: | "splashPage" @@ -292,7 +322,9 @@ export interface PageViewedProperties { | "marketingBridgePage" | "marketingAcrossPlusPage" | "marketingSettlementPage" - | "depositStatusPage"; + | "depositStatusPage" + | "marketingBlogSpecificPage" + | "marketingBlogHomePage"; path: string; /** * Address of referee, null if no referral used @@ -320,6 +352,14 @@ export class ApplicationLoaded implements BaseEvent { event_type = "ApplicationLoaded"; } +export class BlogSearch implements BaseEvent { + event_type = "BlogSearch"; + + constructor(public event_properties: BlogSearchProperties) { + this.event_properties = event_properties; + } +} + export class BridgeButtonClicked implements BaseEvent { event_type = "BridgeButtonClicked"; @@ -478,6 +518,23 @@ export class Ampli { return this.track(new ApplicationLoaded(), options); } + /** + * BlogSearch + * + * [View in Tracking Plan](https://data.amplitude.com/risklabs/Risk%20Labs/events/main/latest/BlogSearch) + * + * Event has no description in tracking plan. + * + * @param properties The event's properties (e.g. page) + * @param options Amplitude event options. + */ + blogSearch( + properties: BlogSearchProperties, + options?: EventOptions, + ) { + return this.track(new BlogSearch(properties), options); + } + /** * BridgeButtonClicked * diff --git a/src/app/_assets/blog-background.png b/src/app/_assets/blog-background.png new file mode 100644 index 0000000000000000000000000000000000000000..af79ab3fe438f5007a5c75837a9d6299dc3221c9 GIT binary patch literal 27866 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 Izk2UK0CFQACIA2c literal 0 HcmV?d00001 diff --git a/src/app/_components/footer.tsx b/src/app/_components/footer.tsx index 319b346..582eb94 100644 --- a/src/app/_components/footer.tsx +++ b/src/app/_components/footer.tsx @@ -12,6 +12,7 @@ import { DocumentIcon, DiscourseIcon, GitHubIcon, + NewspaperIcon, } from "./icons"; import { IconBox } from "./icon-box"; import { PRODUCT_LINKS, SOCIAL_LINKS, INFORMATION_LINKS } from "@/app/_constants"; @@ -69,6 +70,12 @@ const socials = [ ]; const information = [ + { + ...INFORMATION_LINKS.blog, + Icon: NewspaperIcon, + iconClassName: "h-5 w-5", + iconContainerClassName: "bg-light-100/[.05]", + }, { ...INFORMATION_LINKS.docs, Icon: DocumentIcon, @@ -123,7 +130,7 @@ function FooterBox(props: {
    {props.items.map((item) => (
    - {props.useExternalLinks ? ( + {props.useExternalLinks && !item.href.startsWith("/") ? ( diff --git a/src/app/_components/header-nav/index.tsx b/src/app/_components/header-nav/index.tsx index a648655..44b387d 100644 --- a/src/app/_components/header-nav/index.tsx +++ b/src/app/_components/header-nav/index.tsx @@ -5,7 +5,7 @@ import { usePathname } from "next/navigation"; import { useState } from "react"; import { twMerge } from "@/app/_lib/tw-merge"; -import { PRODUCT_LINKS, SOCIAL_LINKS } from "@/app/_constants"; +import { INFORMATION_LINKS, PRODUCT_LINKS, SOCIAL_LINKS } from "@/app/_constants"; import { AcrossIcon, @@ -18,6 +18,7 @@ import { TwitterIcon, MediumIcon, DiscourseIcon, + NewspaperIcon, } from "../icons"; import { Button } from "../button"; import { Text } from "../text"; @@ -80,6 +81,14 @@ const communityNavigationItems = [ iconContainerClassName: "bg-light-100/[.05]", containerClassName: "group-hover:bg-light-100/[.05]", }, + { + ...INFORMATION_LINKS.blog, + description: "Across Blog", + Icon: NewspaperIcon, + iconClassName: "h-4 w-4", + iconContainerClassName: "bg-light-100/[.05]", + containerClassName: "group-hover:bg-light-100/[.05]", + }, ]; export function HeaderNav() { @@ -109,6 +118,7 @@ export function HeaderNav() {
    Home + Blog ) { + return ( + + + + + + + + + + + + ); +} diff --git a/src/app/_components/icons/newspaper.tsx b/src/app/_components/icons/newspaper.tsx new file mode 100644 index 0000000..3479249 --- /dev/null +++ b/src/app/_components/icons/newspaper.tsx @@ -0,0 +1,38 @@ +import { SVGProps } from "react"; + +export function NewspaperIcon(props: SVGProps) { + return ( + + + + + + + ); +} diff --git a/src/app/_components/icons/search.tsx b/src/app/_components/icons/search.tsx new file mode 100644 index 0000000..2fa94ad --- /dev/null +++ b/src/app/_components/icons/search.tsx @@ -0,0 +1,22 @@ +import { SVGProps } from "react"; + +export function SearchIcon(props: SVGProps) { + return ( + + + + ); +} diff --git a/src/app/_components/link.tsx b/src/app/_components/link.tsx index be3d43a..2c44251 100644 --- a/src/app/_components/link.tsx +++ b/src/app/_components/link.tsx @@ -17,7 +17,7 @@ type CustomLinkProps = LinkProps & { */ function CustomLink({ href, preserveQueryParams, ...props }: CustomLinkProps) { const params = useSearchParams(); - if (preserveQueryParams && !href.toString().includes("?")) { + if (preserveQueryParams && params.size > 0 && !href.toString().includes("?")) { href = `${href.toString()}?${params.toString()}`; } return ; diff --git a/src/app/_components/text.tsx b/src/app/_components/text.tsx index c48811b..1420ad4 100644 --- a/src/app/_components/text.tsx +++ b/src/app/_components/text.tsx @@ -1,5 +1,5 @@ -import { ComponentProps } from "react"; import { twMerge } from "@/app/_lib/tw-merge"; +import { ComponentProps } from "react"; type TextVariant = keyof typeof textBaseClasses; @@ -9,8 +9,12 @@ type Props = ComponentProps<"div"> & { const textBaseClasses = { body: "text-md", + "cap-case-md": + "text-medium text-medium uppercase lining-nums tabular-nums tracking-wide-4", "cap-case-sm": "text-medium text-xs uppercase lining-nums tabular-nums tracking-wide-4 sm:text-sm", + "cap-case-xs": + "text-medium text-xs uppercase lining-nums tabular-nums tracking-wide-4 xs:text-xs", "cap-case": "text-medium text-xs uppercase lining-nums tabular-nums tracking-wide-4", "heading-1": "text-heading-3 font-lighter lining-nums tabular-nums tracking-tight-5 sm:text-heading-2 md:text-heading-1", @@ -20,6 +24,8 @@ const textBaseClasses = { "text-heading-3 font-lighter lining-nums tabular-nums tracking-tight-5 sm:text-heading-3", "heading-4": "text-heading-4 font-lighter lining-nums tabular-nums tracking-tight-5 sm:text-heading-4", + "heading-5": + "text-heading-5 font-lighter lining-nums tabular-nums tracking-tight-5 sm:text-heading-5", "body-nums": "text-md lining-nums tabular-nums sm:text-lg", "body-nums-sm": "text-sm lining-nums tabular-nums", }; diff --git a/src/app/_constants/amplitude.ts b/src/app/_constants/amplitude.ts index 27f0896..803a316 100644 --- a/src/app/_constants/amplitude.ts +++ b/src/app/_constants/amplitude.ts @@ -3,11 +3,14 @@ export type AMPLITUDE_PAGE = | "marketingHomePage" | "marketingBridgePage" | "marketingAcrossPlusPage" - | "marketingSettlementPage"; + | "marketingSettlementPage" + | "marketingBlogSpecificPage" + | "marketingBlogHomePage"; export const AMPLITUDE_PAGE_LOOKUP: Record = { "/": "marketingHomePage", "/across-bridge": "marketingBridgePage", "/across-plus": "marketingAcrossPlusPage", "/across-settlement": "marketingSettlementPage", + "/blog": "marketingBlogHomePage", }; diff --git a/src/app/_constants/environment.ts b/src/app/_constants/environment.ts index 59fb24d..cf9cc09 100644 --- a/src/app/_constants/environment.ts +++ b/src/app/_constants/environment.ts @@ -5,3 +5,7 @@ export const AMPLITUDE_LOGGING = process.env.NEXT_PUBLIC_AMPLITUDE_DEBUG_LOGGING === "true"; export const GIT_COMMIT_HASH = process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA ?? ""; export const GOOGLE_ANALYTICS_TAG_ID = process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS_ID; + +// Server side environment variables +export const CONTENTFUL_SPACE_ID = process.env.CONTENTFUL_SPACE_ID; +export const CONTENTFUL_ACCESS_TOKEN = process.env.CONTENTFUL_ACCESS_TOKEN; diff --git a/src/app/_constants/links.ts b/src/app/_constants/links.ts index 6eac5d5..825c81f 100644 --- a/src/app/_constants/links.ts +++ b/src/app/_constants/links.ts @@ -48,6 +48,10 @@ export const INFORMATION_LINKS = { label: "Docs", href: "https://docs.across.to/v/v3-developer-docs/introduction/what-is-across", }, + blog: { + label: "Blog", + href: "/blog", + }, }; export const INTEGRATION_LINKS = { @@ -57,4 +61,6 @@ export const INTEGRATION_LINKS = { "https://docs.across.to/v/v3-developer-docs/concepts/intents-architecture-in-across", }; +export const SITE_BASE_URL = "https://across.to" as const; + export const TERMS_OF_SERVICE = "/terms-of-service"; diff --git a/src/app/_hooks/useFilter.ts b/src/app/_hooks/useFilter.ts new file mode 100644 index 0000000..2e4d737 --- /dev/null +++ b/src/app/_hooks/useFilter.ts @@ -0,0 +1,47 @@ +import { useSetQueryParams } from "./useSetQueryParams"; +import { useState, useCallback } from "react"; +import { useDebouncedCallback } from "use-debounce"; +import { ampli } from "../_amplitude"; + +export function useFilter() { + const { params, setParams, removeParams } = useSetQueryParams(["page", "search"]); + const [text, setText] = useState(params.search ?? ""); + + const debouncedSetParam = useDebouncedCallback((value: string) => { + setParams({ + search: value, + page: "1", // Reset page to 1 when searching + }); + if (!!value) { + ampli.blogSearch({ search: value, page: "marketingBlogHomePage" }); + } + }, 300); + + function handleTextChange(value: string) { + setText(value); + debouncedSetParam(value); + } + + function handlePageChange(value: number) { + setParams({ + page: String(value), + }); + } + + const clearAll = useCallback(() => { + setText(""); + removeParams(["page", "search"]); + }, [removeParams]); + + const hasParams = text || params.tag ? true : false; + + return { + text, + handleTextChange, + productParam: params.product, + tag: params.tag, + handlePageChange, + clearAll, + hasParams, + }; +} diff --git a/src/app/_hooks/useSetQueryParams.ts b/src/app/_hooks/useSetQueryParams.ts new file mode 100644 index 0000000..70714fe --- /dev/null +++ b/src/app/_hooks/useSetQueryParams.ts @@ -0,0 +1,70 @@ +"use client"; +import { usePathname, useRouter, useSearchParams } from "next/navigation"; +import { useCallback, useState } from "react"; + +type QueryParams = Record; +type QueryParamKeys = (keyof QueryParams)[]; + +export function useSetQueryParams(paramKeys: QueryParamKeys) { + const router = useRouter(); + const pathname = usePathname(); + const searchParams = useSearchParams(); + + const [paramValues, setParamValues] = useState(() => { + const initialValues: QueryParams = {}; + paramKeys.forEach((key) => { + const fromUrl = searchParams.get(key); + Object.assign(initialValues, { + [key]: fromUrl ? decodeURIComponent(fromUrl) : undefined, + }); + }); + return initialValues; + }); + + const setParams = useCallback( + (params: QueryParams) => { + const newParams = new URLSearchParams(searchParams.toString()); + Object.keys(params).forEach((key) => { + const value = params[key]; + if (value === undefined || value === "") { + newParams.delete(key); + } else { + newParams.set(key, encodeURIComponent(value)); + } + }); + router.push(`${pathname}?${newParams.toString()}`, { + scroll: false, + }); + setParamValues((prev) => ({ ...prev, ...params })); + }, + [pathname, router, searchParams], + ); + + const removeParams = useCallback( + (keys: string[]) => { + const newParams = new URLSearchParams(searchParams.toString()); + keys.forEach((key) => { + newParams.delete(key); + }); + const queryString = newParams.toString(); + const path = queryString ? `${pathname}?${queryString}` : pathname; + router.push(path, { + scroll: false, + }); + setParamValues((prev) => { + const updatedValues = { ...prev }; + keys.forEach((key) => { + delete updatedValues[key]; + }); + return updatedValues; + }); + }, + [pathname, router, searchParams], + ); + + return { + params: paramValues, + setParams, + removeParams, + }; +} diff --git a/src/app/_lib/amplitude.ts b/src/app/_lib/amplitude.ts index 8ab13fe..a5c8238 100644 --- a/src/app/_lib/amplitude.ts +++ b/src/app/_lib/amplitude.ts @@ -39,5 +39,8 @@ export async function initializeAmplitude(setLoaded: (loaded: boolean) => void) } export function pageLookup(pathname: string) { - return AMPLITUDE_PAGE_LOOKUP[pathname] ?? "404Page"; + const isSpecificBlogPage = /^\/blog\/[a-zA-Z0-9-]+$/.test(pathname); + return isSpecificBlogPage + ? "marketingBlogSpecificPage" + : AMPLITUDE_PAGE_LOOKUP[pathname] ?? "404Page"; } diff --git a/src/app/_lib/cache.ts b/src/app/_lib/cache.ts new file mode 100644 index 0000000..548de9d --- /dev/null +++ b/src/app/_lib/cache.ts @@ -0,0 +1,13 @@ +import { SearchParams } from "../(routes)/blog/page"; + +export function createCacheKey(options: { searchParams: SearchParams }) { + const { searchParams } = options; + const newParamString = new URLSearchParams(); + Object.entries(searchParams).forEach(([key, value]) => { + if (typeof value === "string") { + newParamString.set(key, value); + } + }); + + return newParamString.toString(); +} diff --git a/src/app/_lib/contentful.ts b/src/app/_lib/contentful.ts new file mode 100644 index 0000000..7bb3275 --- /dev/null +++ b/src/app/_lib/contentful.ts @@ -0,0 +1,141 @@ +import type { + ChainModifiers, + Entry, + EntryFieldTypes, + EntrySkeletonType, + LocaleCode, +} from "contentful"; +import { createClient } from "contentful"; +import { CONTENTFUL_ACCESS_TOKEN, CONTENTFUL_SPACE_ID } from "../_constants"; +import { documentToPlainTextString } from "@contentful/rich-text-plain-text-renderer"; +import { Document } from "@contentful/rich-text-types"; +import words from "lodash.words"; + +const contentType = "acrossBlogPost"; +const averageReadingSpeed = 238; // words per minute + +type TypeAcrossBlogPostFields = { + title: EntryFieldTypes.Symbol; + slug: EntryFieldTypes.Symbol; + content: EntryFieldTypes.RichText; + tag: EntryFieldTypes.Array; + featuredImage: EntryFieldTypes.AssetLink; + publishDate: EntryFieldTypes.Date; + description: EntryFieldTypes.Symbol; +}; + +type TypeAcrossBlogPostSkeleton = EntrySkeletonType< + TypeAcrossBlogPostFields, + "acrossBlogPost" +>; +export type BlogPostType = Entry< + TypeAcrossBlogPostSkeleton, + "WITHOUT_UNRESOLVABLE_LINKS", + string +>; + +export type BlogPostWithRelevantEntries = BlogPostType & { + relevantEntries: BlogPostType[]; +}; + +function getProductionClient() { + return createClient({ + space: CONTENTFUL_SPACE_ID ?? "", + accessToken: CONTENTFUL_ACCESS_TOKEN ?? "", + }); +} + +export async function retrieveContentfulPublishedSlugs({ + query, + limit, + avoidTags, + includeTags, + sortByRecent, + skip, +}: { + query?: string; + limit?: number; + avoidTags?: string[]; + includeTags?: string[]; + sortByRecent?: boolean; + skip?: number; +} = {}): Promise<{ slugsForQuery: string[]; totalCount: number }> { + const client = getProductionClient(); + const options = { + content_type: contentType, + select: "fields.slug", + "fields.content[exists]": true, + "fields.slug[exists]": true, + ...(limit ? { limit } : {}), + ...(query ? { query: decodeURI(query) } : {}), + ...(avoidTags ? { "fields.tag[nin]": avoidTags.join(",").toLowerCase() } : {}), + ...(includeTags ? { "fields.tag[in]": includeTags.join(",").toLowerCase() } : {}), + ...(sortByRecent ? { order: "-fields.publishDate" } : {}), + ...(skip ? { skip } : {}), + } as const; + const entries = + await client.withoutUnresolvableLinks.getEntries(options); + + return { + slugsForQuery: entries.items.map((item) => item.fields.slug), + totalCount: entries.total, + }; +} + +export async function retrieveContentfulEntry( + entrySlugId: string, + relevantEntryCount = 4, +): Promise { + const client = getProductionClient(); + const options = { + content_type: contentType, + limit: 1, + "fields.slug": entrySlugId, + } as const; + const entries = + await client.withoutUnresolvableLinks.getEntries(options); + const entry = entries.items[0]; + if (!entry) { + return undefined; + } + const relevantEntries = await retrieveRelevantContentfulEntries( + entrySlugId, + entry.fields.tag ?? [], + relevantEntryCount, + ); + return { + ...entry, + relevantEntries, + }; +} + +export async function retrieveRelevantContentfulEntries( + entrySlugId: string, + tags: string[], + limit: number, +): Promise { + const client = getProductionClient(); + const options = { + content_type: contentType, + limit, + "fields.content[exists]": true, // no empty posts + "fields.tag[in]": tags.join(",").toLowerCase(), // get posts with same tags + "fields.slug[nin]": entrySlugId, // don't include current post + "fields.slug[exists]": true, // no empty slugs + "fields.publishDate[exists]": true, // no empty dates + order: "-fields.publishDate", // sorted latest first + } as const; + const entries = + await client.withoutUnresolvableLinks.getEntries(options); + return entries.items; +} + +export function getReadingTime(content: Document): number { + const rawText = documentToPlainTextString(content); + const wordCount = words(rawText).length; + return Math.round(wordCount / averageReadingSpeed); +} + +export function resolvePublishDateToIsoDate(entry: BlogPostType): string { + return entry.fields.publishDate ?? entry.sys.createdAt; +} diff --git a/tailwind.config.ts b/tailwind.config.ts index cc2a4bc..a8d4870 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -9,6 +9,7 @@ const config: Config = { theme: { colors: { transparent: "transparent", + "white-translucent": "#ffffff08", light: { 100: "#fff", 200: "#F0FFFB", @@ -46,6 +47,7 @@ const config: Config = { "heading-2": ["3rem", "3.3rem"], // [48px, 52.8px] "heading-3": ["2rem", "2.2rem"], // [32px, 35.2px] "heading-4": ["1.5rem", "1.65rem"], // [24px, 26.4px] + "heading-5": ["1.25rem", "1.375rem"], // [20px, 22px] }, letterSpacing: { "tight-1": "-0.28rem", // -4.48px @@ -117,6 +119,6 @@ const config: Config = { }, }, }, - plugins: [], + plugins: [require("tailwind-scrollbar-hide")], }; export default config; diff --git a/yarn.lock b/yarn.lock index ee5ff4c..8d37d10 100644 --- a/yarn.lock +++ b/yarn.lock @@ -78,6 +78,40 @@ dependencies: regenerator-runtime "^0.14.0" +"@contentful/content-source-maps@^0.6.0": + version "0.6.1" + resolved "https://registry.yarnpkg.com/@contentful/content-source-maps/-/content-source-maps-0.6.1.tgz#a12828d287bdcd9f31c132f2a230bd1ac3005b77" + integrity sha512-IjsyhakG17OC5xtIa5agVRsnjjxiJf9HZjpDIV6Ix036Pd5YHJrbyyiF4KNEmLlIqe3hQ0kHGWEs9S/HfECmRQ== + dependencies: + "@vercel/stega" "^0.1.2" + json-pointer "^0.6.2" + +"@contentful/rich-text-plain-text-renderer@^16.2.8": + version "16.2.8" + resolved "https://registry.yarnpkg.com/@contentful/rich-text-plain-text-renderer/-/rich-text-plain-text-renderer-16.2.8.tgz#5ba85e7a12ab706eccfeb3de551b0467c4332403" + integrity sha512-5YQPg1rERTTla1XpQqvLYUtmyz7WPZnNX6o66SYQ7kF8FSjK4HKAVJEpzs8qMlyQCKdlzXUVbu9ItrlStORh1g== + dependencies: + "@contentful/rich-text-types" "^16.8.3" + +"@contentful/rich-text-react-renderer@^15.22.9": + version "15.22.9" + resolved "https://registry.yarnpkg.com/@contentful/rich-text-react-renderer/-/rich-text-react-renderer-15.22.9.tgz#aee71e5d0c923038041c5f4000e752864ce56aee" + integrity sha512-ubzuQKaIwB7AeUi1zHQ6EMvpLOMJ9p5LGxZznkcHG1Sn91LNZorQXfMt9K4tSoOR9Cn8KxN6ca8gMNNh5zGbig== + dependencies: + "@contentful/rich-text-types" "^16.8.3" + +"@contentful/rich-text-types@^16.0.2", "@contentful/rich-text-types@^16.8.3": + version "16.8.3" + resolved "https://registry.yarnpkg.com/@contentful/rich-text-types/-/rich-text-types-16.8.3.tgz#f0effea5d0ed23f9abfd992c9b1be8a00f4d63e9" + integrity sha512-vXwXDQMDbqITCWfTkU5R/q+uvXWCc1eYNvdZyjtrs0YDIYr4L7QJ2s1r4ZheIs3iVf3AFucKIHgDSpwCAm2wKA== + +"@emnapi/runtime@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.2.0.tgz#71d018546c3a91f3b51106530edbc056b9f2f2e3" + integrity sha512-bV21/9LQmcQeCPEg3BDFtvwL6cwiTMksYNWQQ4KOxCZikEGalWtenoZ0wCiukJINlGCIi2KXx01g4FoH/LxpzQ== + dependencies: + tslib "^2.4.0" + "@eslint-community/eslint-utils@^4.2.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" @@ -137,6 +171,119 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz#d9fae00a2d5cb40f92cfe64b47ad749fbc38f917" integrity sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw== +"@img/sharp-darwin-arm64@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz#ef5b5a07862805f1e8145a377c8ba6e98813ca08" + integrity sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ== + optionalDependencies: + "@img/sharp-libvips-darwin-arm64" "1.0.4" + +"@img/sharp-darwin-x64@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz#e03d3451cd9e664faa72948cc70a403ea4063d61" + integrity sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q== + optionalDependencies: + "@img/sharp-libvips-darwin-x64" "1.0.4" + +"@img/sharp-libvips-darwin-arm64@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz#447c5026700c01a993c7804eb8af5f6e9868c07f" + integrity sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg== + +"@img/sharp-libvips-darwin-x64@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz#e0456f8f7c623f9dbfbdc77383caa72281d86062" + integrity sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ== + +"@img/sharp-libvips-linux-arm64@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz#979b1c66c9a91f7ff2893556ef267f90ebe51704" + integrity sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA== + +"@img/sharp-libvips-linux-arm@1.0.5": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz#99f922d4e15216ec205dcb6891b721bfd2884197" + integrity sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g== + +"@img/sharp-libvips-linux-s390x@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz#f8a5eb1f374a082f72b3f45e2fb25b8118a8a5ce" + integrity sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA== + +"@img/sharp-libvips-linux-x64@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz#d4c4619cdd157774906e15770ee119931c7ef5e0" + integrity sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw== + +"@img/sharp-libvips-linuxmusl-arm64@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz#166778da0f48dd2bded1fa3033cee6b588f0d5d5" + integrity sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA== + +"@img/sharp-libvips-linuxmusl-x64@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz#93794e4d7720b077fcad3e02982f2f1c246751ff" + integrity sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw== + +"@img/sharp-linux-arm64@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz#edb0697e7a8279c9fc829a60fc35644c4839bb22" + integrity sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA== + optionalDependencies: + "@img/sharp-libvips-linux-arm64" "1.0.4" + +"@img/sharp-linux-arm@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz#422c1a352e7b5832842577dc51602bcd5b6f5eff" + integrity sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ== + optionalDependencies: + "@img/sharp-libvips-linux-arm" "1.0.5" + +"@img/sharp-linux-s390x@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz#f5c077926b48e97e4a04d004dfaf175972059667" + integrity sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q== + optionalDependencies: + "@img/sharp-libvips-linux-s390x" "1.0.4" + +"@img/sharp-linux-x64@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz#d806e0afd71ae6775cc87f0da8f2d03a7c2209cb" + integrity sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA== + optionalDependencies: + "@img/sharp-libvips-linux-x64" "1.0.4" + +"@img/sharp-linuxmusl-arm64@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz#252975b915894fb315af5deea174651e208d3d6b" + integrity sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g== + optionalDependencies: + "@img/sharp-libvips-linuxmusl-arm64" "1.0.4" + +"@img/sharp-linuxmusl-x64@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz#3f4609ac5d8ef8ec7dadee80b560961a60fd4f48" + integrity sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw== + optionalDependencies: + "@img/sharp-libvips-linuxmusl-x64" "1.0.4" + +"@img/sharp-wasm32@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz#6f44f3283069d935bb5ca5813153572f3e6f61a1" + integrity sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg== + dependencies: + "@emnapi/runtime" "^1.2.0" + +"@img/sharp-win32-ia32@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz#1a0c839a40c5351e9885628c85f2e5dfd02b52a9" + integrity sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ== + +"@img/sharp-win32-x64@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz#56f00962ff0c4e0eb93d34a047d29fa995e3e342" + integrity sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg== + "@isaacs/cliui@^8.0.2": version "8.0.2" resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" @@ -313,6 +460,23 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== +"@types/lodash.words@^4.2.9": + version "4.2.9" + resolved "https://registry.yarnpkg.com/@types/lodash.words/-/lodash.words-4.2.9.tgz#06f10d82241371186dd7219b29f708c451f55182" + integrity sha512-t7jYEhzm/JJxYZzU4BNeySF9zXOQwtd5t1pJBcK9ZZf62HUbcFuiLN69G5N9A/QUmE44OsBVrmZcAxbnWm0IXg== + dependencies: + "@types/lodash" "*" + +"@types/lodash@*": + version "4.17.7" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.7.tgz#2f776bcb53adc9e13b2c0dfd493dfcbd7de43612" + integrity sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA== + +"@types/luxon@^3.4.2": + version "3.4.2" + resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.4.2.tgz#e4fc7214a420173cea47739c33cdf10874694db7" + integrity sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA== + "@types/node@^20": version "20.11.16" resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.16.tgz#4411f79411514eb8e2926f036c86c9f0e4ec6708" @@ -402,6 +566,11 @@ resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== +"@vercel/stega@^0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@vercel/stega/-/stega-0.1.2.tgz#0c20c5c9419c4288b1de58a64b5f9f26c763b25f" + integrity sha512-P7mafQXjkrsoyTRppnt0N21udKS9wUmLXHRyP9saLXLHw32j/FgUJ3FscSWgvSqRs4cj7wKZtwqJEvWJ2jbGmA== + acorn-jsx@^5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" @@ -565,6 +734,11 @@ asynciterator.prototype@^1.0.0: dependencies: has-symbols "^1.0.3" +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + autoprefixer@^10.0.1: version "10.4.17" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.17.tgz#35cd5695cbbe82f536a50fa025d561b01fdec8be" @@ -587,6 +761,15 @@ axe-core@=4.7.0: resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.7.0.tgz#34ba5a48a8b564f67e103f0aa5768d76e15bbbbf" integrity sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ== +axios@~1.6.8: + version "1.6.8" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.8.tgz#66d294951f5d988a00e87a0ffb955316a619ea66" + integrity sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + axobject-query@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-3.2.1.tgz#39c378a6e3b06ca679f29138151e45b2b32da62a" @@ -652,6 +835,17 @@ call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.5: get-intrinsic "^1.2.1" set-function-length "^1.1.1" +call-bind@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + set-function-length "^1.2.1" + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -702,11 +896,34 @@ color-convert@^2.0.1: dependencies: color-name "~1.1.4" -color-name@~1.1.4: +color-name@^1.0.0, color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +color-string@^1.9.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" + integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + +color@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/color/-/color-4.2.3.tgz#d781ecb5e57224ee43ea9627560107c0e0c6463a" + integrity sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A== + dependencies: + color-convert "^2.0.1" + color-string "^1.9.0" + +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + commander@^4.0.0: version "4.1.1" resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" @@ -717,6 +934,37 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== +contentful-resolve-response@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/contentful-resolve-response/-/contentful-resolve-response-1.9.0.tgz#7a89e6f332d32b98c6e98af9406ce7aa64707711" + integrity sha512-LtgPx/eREpHXOX82od48zFZbFhXzYw/NfUoYK4Qf1OaKpLzmYPE4cAY4aD+rxVgnMM5JN/mQaPCsofUlJRYEUA== + dependencies: + fast-copy "^2.1.7" + +contentful-sdk-core@^8.1.0: + version "8.3.1" + resolved "https://registry.yarnpkg.com/contentful-sdk-core/-/contentful-sdk-core-8.3.1.tgz#773d59f286d1bd8e50a3ffd9e3f4f410da6fb929" + integrity sha512-HYy4ecFA76ERxz7P0jW7hgDcL8jH+bRckv2QfAwQ4k1yPP9TvxpZwrKnlLM69JOStxVkCXP37HvbjbFnjcoWdg== + dependencies: + fast-copy "^2.1.7" + lodash.isplainobject "^4.0.6" + lodash.isstring "^4.0.1" + p-throttle "^4.1.1" + qs "^6.11.2" + +contentful@^10.13.1: + version "10.13.1" + resolved "https://registry.yarnpkg.com/contentful/-/contentful-10.13.1.tgz#8b14ea44f2816e7a109894145ae6fd161b3a26c6" + integrity sha512-GczP0vSWzZGKllzUOsUnCHTKuW92E9oR44uGrLdA7cpel75gJqSB2KpVBV1KT4oaUQn773MAU/J6tWNqCYadfw== + dependencies: + "@contentful/content-source-maps" "^0.6.0" + "@contentful/rich-text-types" "^16.0.2" + axios "~1.6.8" + contentful-resolve-response "^1.9.0" + contentful-sdk-core "^8.1.0" + json-stringify-safe "^5.0.1" + type-fest "^4.0.0" + cross-spawn@^7.0.0, cross-spawn@^7.0.2: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -769,6 +1017,15 @@ define-data-property@^1.0.1, define-data-property@^1.1.1: gopd "^1.0.1" has-property-descriptors "^1.0.0" +define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" @@ -778,11 +1035,21 @@ define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1: has-property-descriptors "^1.0.0" object-keys "^1.1.1" +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + dequal@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== +detect-libc@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700" + integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw== + didyoumean@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037" @@ -915,6 +1182,18 @@ es-abstract@^1.22.1: unbox-primitive "^1.0.2" which-typed-array "^1.1.13" +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + es-iterator-helpers@^1.0.12, es-iterator-helpers@^1.0.15: version "1.0.15" resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.0.15.tgz#bd81d275ac766431d19305923707c3efd9f1ae40" @@ -1181,6 +1460,11 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== +fast-copy@^2.1.7: + version "2.1.7" + resolved "https://registry.yarnpkg.com/fast-copy/-/fast-copy-2.1.7.tgz#affc9475cb4b555fb488572b2a44231d0c9fa39e" + integrity sha512-ozrGwyuCTAy7YgFCua8rmqmytECYk/JYAMXcswOcm0qvGoE3tPb7ivBeIHTOK2DiapBhDZgacIhzhQIKU5TCfA== + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -1250,6 +1534,11 @@ flatted@^3.2.9: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf" integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== +follow-redirects@^1.15.6: + version "1.15.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== + for-each@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" @@ -1257,6 +1546,11 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" +foreach@^2.0.4: + version "2.0.6" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.6.tgz#87bcc8a1a0e74000ff2bf9802110708cfb02eb6e" + integrity sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg== + foreground-child@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.1.1.tgz#1d173e776d75d2772fed08efe4a0de1ea1b12d0d" @@ -1265,6 +1559,15 @@ foreground-child@^3.1.0: cross-spawn "^7.0.0" signal-exit "^4.0.1" +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + fraction.js@^4.3.7: version "4.3.7" resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7" @@ -1310,6 +1613,17 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@ has-symbols "^1.0.3" hasown "^2.0.0" +get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + get-symbol-description@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" @@ -1422,6 +1736,13 @@ has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.1: dependencies: get-intrinsic "^1.2.2" +has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + has-proto@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" @@ -1495,6 +1816,11 @@ is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: get-intrinsic "^1.2.0" is-typed-array "^1.1.10" +is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" + integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== + is-async-function@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-async-function/-/is-async-function-2.0.0.tgz#8e4418efd3e5d3a6ebb0164c05ef5afb69aa9646" @@ -1714,6 +2040,13 @@ json-buffer@3.0.1: resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== +json-pointer@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/json-pointer/-/json-pointer-0.6.2.tgz#f97bd7550be5e9ea901f8c9264c9d436a22a93cd" + integrity sha512-vLWcKbOaXlO+jvRy4qNd+TI1QUPZzfJj1tpJ3vAXDych5XJf93ftpUKe5pKCrzyIIwgBJcOcCVRUfqQP25afBw== + dependencies: + foreach "^2.0.4" + json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" @@ -1724,6 +2057,11 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== +json-stringify-safe@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== + json5@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" @@ -1790,11 +2128,26 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== + +lodash.isstring@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw== + lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lodash.words@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.words/-/lodash.words-4.2.0.tgz#5ecfeaf8ecf8acaa8e0c8386295f1993c9cf4036" + integrity sha512-mXxqd8Yx9BGPij3lZKFSdOsjOTbL4krbCCp9slEozaN4EMppA2dFmK/f8HeohodprY6W0vOdiQ5WFgPaTI75xQ== + loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -1814,6 +2167,11 @@ lru-cache@^6.0.0: resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.0.tgz#0bd445ca57363465900f4d1f9bd8db343a4d95c3" integrity sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q== +luxon@^3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.5.0.tgz#6b6f65c5cd1d61d1fd19dbf07ee87a50bf4b8e20" + integrity sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ== + merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" @@ -1827,6 +2185,18 @@ micromatch@^4.0.4, micromatch@^4.0.5: braces "^3.0.2" picomatch "^2.3.1" +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + minimatch@9.0.3, minimatch@^9.0.1: version "9.0.3" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" @@ -2031,6 +2401,11 @@ p-locate@^5.0.0: dependencies: p-limit "^3.0.2" +p-throttle@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/p-throttle/-/p-throttle-4.1.1.tgz#80b1fbd358af40a8bfa1667f9dc8b72b714ad692" + integrity sha512-TuU8Ato+pRTPJoDzYD4s7ocJYcNSEZRvlxoq3hcPI2kZDZ49IQ1Wkj7/gDJc3X7XiEAAvRGtDzdXJI0tC3IL1g== + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" @@ -2177,11 +2552,23 @@ prop-types@^15.8.1: object-assign "^4.1.1" react-is "^16.13.1" +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + punycode@^2.1.0: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== +qs@^6.11.2: + version "6.13.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" + integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== + dependencies: + side-channel "^1.0.6" + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -2332,6 +2719,11 @@ semver@^7.5.4: dependencies: lru-cache "^6.0.0" +semver@^7.6.3: + version "7.6.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== + set-function-length@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.0.tgz#2f81dc6c16c7059bda5ab7c82c11f03a515ed8e1" @@ -2343,6 +2735,18 @@ set-function-length@^1.1.1: gopd "^1.0.1" has-property-descriptors "^1.0.1" +set-function-length@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + set-function-name@^2.0.0, set-function-name@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.1.tgz#12ce38b7954310b9f61faa12701620a0c882793a" @@ -2352,6 +2756,35 @@ set-function-name@^2.0.0, set-function-name@^2.0.1: functions-have-names "^1.2.3" has-property-descriptors "^1.0.0" +sharp@^0.33.5: + version "0.33.5" + resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.33.5.tgz#13e0e4130cc309d6a9497596715240b2ec0c594e" + integrity sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw== + dependencies: + color "^4.2.3" + detect-libc "^2.0.3" + semver "^7.6.3" + optionalDependencies: + "@img/sharp-darwin-arm64" "0.33.5" + "@img/sharp-darwin-x64" "0.33.5" + "@img/sharp-libvips-darwin-arm64" "1.0.4" + "@img/sharp-libvips-darwin-x64" "1.0.4" + "@img/sharp-libvips-linux-arm" "1.0.5" + "@img/sharp-libvips-linux-arm64" "1.0.4" + "@img/sharp-libvips-linux-s390x" "1.0.4" + "@img/sharp-libvips-linux-x64" "1.0.4" + "@img/sharp-libvips-linuxmusl-arm64" "1.0.4" + "@img/sharp-libvips-linuxmusl-x64" "1.0.4" + "@img/sharp-linux-arm" "0.33.5" + "@img/sharp-linux-arm64" "0.33.5" + "@img/sharp-linux-s390x" "0.33.5" + "@img/sharp-linux-x64" "0.33.5" + "@img/sharp-linuxmusl-arm64" "0.33.5" + "@img/sharp-linuxmusl-x64" "0.33.5" + "@img/sharp-wasm32" "0.33.5" + "@img/sharp-win32-ia32" "0.33.5" + "@img/sharp-win32-x64" "0.33.5" + shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" @@ -2373,11 +2806,28 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" +side-channel@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" + integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + object-inspect "^1.13.1" + signal-exit@^4.0.1: version "4.1.0" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== +simple-swizzle@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg== + dependencies: + is-arrayish "^0.3.1" + slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" @@ -2393,7 +2843,16 @@ streamsearch@^1.1.0: resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^4.1.0: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -2453,7 +2912,14 @@ string.prototype.trimstart@^1.0.7: define-properties "^1.2.0" es-abstract "^1.22.1" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -2516,6 +2982,11 @@ tailwind-merge@^2.2.1: dependencies: "@babel/runtime" "^7.23.7" +tailwind-scrollbar-hide@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/tailwind-scrollbar-hide/-/tailwind-scrollbar-hide-1.1.7.tgz#90b481fb2e204030e3919427416650c54f56f847" + integrity sha512-X324n9OtpTmOMqEgDUEA/RgLrNfBF/jwJdctaPZDzB3mppxJk7TLIDmOreEDm1Bq4R9LSPu4Epf8VSdovNU+iA== + tailwindcss@^3.3.0: version "3.4.1" resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.1.tgz#f512ca5d1dd4c9503c7d3d28a968f1ad8f5c839d" @@ -2617,6 +3088,11 @@ type-fest@^0.20.2: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== +type-fest@^4.0.0: + version "4.23.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.23.0.tgz#8196561a6b835175473be744f3e41e2dece1496b" + integrity sha512-ZiBujro2ohr5+Z/hZWHESLz3g08BBdrdLMieYFULJO+tWc437sn8kQsWLJoZErY8alNhxre9K4p3GURAG11n+w== + typed-array-buffer@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz#18de3e7ed7974b0a729d3feecb94338d1472cd60" @@ -2691,6 +3167,11 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" +use-debounce@^10.0.3: + version "10.0.3" + resolved "https://registry.yarnpkg.com/use-debounce/-/use-debounce-10.0.3.tgz#636094a37f7aa2bcc77b26b961481a0b571bf7ea" + integrity sha512-DxQSI9ZKso689WM1mjgGU3ozcxU1TJElBJ3X6S4SMzMNcm2lVH0AHmyXB+K7ewjz2BSUKJTDqTcwtSMRfB89dg== + util-deprecate@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"