From 862b94c96c2e2b5f1b19ac009873d4695dc8010c Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Wed, 15 Jan 2025 09:20:23 -0600 Subject: [PATCH 01/44] Refactor optimization env var names --- README.md | 7 ++++--- src/admin/AdminPhotosClient.tsx | 7 +++++-- src/app/p/[photoId]/image/route.tsx | 4 ++-- src/app/p/[photoId]/page.tsx | 4 ++-- src/photo/server.ts | 4 ++-- src/site/SiteChecklistClient.tsx | 15 ++++++++++---- src/site/config.ts | 32 +++++++++++++++++++---------- 7 files changed, 47 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index d1bb34be..0af834b8 100644 --- a/README.md +++ b/README.md @@ -104,9 +104,10 @@ Application behavior can be changed by configuring the following environment var #### Site behavior - `NEXT_PUBLIC_GRID_HOMEPAGE = 1` shows grid layout on homepage - `NEXT_PUBLIC_DEFAULT_THEME = light | dark` sets preferred initial theme (defaults to `system` when not configured) -- `NEXT_PUBLIC_PRO_MODE = 1` enables higher quality image storage (results in increased storage usage) -- `NEXT_PUBLIC_STATICALLY_OPTIMIZE_PAGES = 1` enables static optimization for pages, i.e., renders pages at build time (results in increased project usage)—⚠️ _Experimental_ -- `NEXT_PUBLIC_STATICALLY_OPTIMIZE_OG_IMAGES = 1` enables static optimization for OG images, i.e., renders images at build time (results in increased project usage)—⚠️ _Experimental_ +- `NEXT_PUBLIC_PRESERVE_ORIGINAL_UPLOADS = 1` do not optimize photo uploads before storing (⚠️ results in increased storage usage) +- `NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTOS = 1` enables static optimization for photo pages (p/[photoId]), i.e., renders pages at build time (results in increased project usage) +- `NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_OG_IMAGES = 1` enables static optimization for OG images, i.e., renders images at build time (results in increased project usage) +- `NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_CATEGORIES = 1` enables static optimization for photo categories (tag/[tag], shot-on/[make]/[model], etc.), i.e., renders pages at build time (results in increased project usage) - `NEXT_PUBLIC_MATTE_PHOTOS = 1` constrains the size of each photo, and enables a surrounding border (potentially useful for photos with tall aspect ratios) - `NEXT_PUBLIC_BLUR_DISABLED = 1` prevents image blur data being stored and displayed (potentially useful for limiting Postgres usage) - `NEXT_PUBLIC_GEO_PRIVACY = 1` disables collection/display of location-based data (⚠️ re-compresses uploaded images in order to remove GPS information) diff --git a/src/admin/AdminPhotosClient.tsx b/src/admin/AdminPhotosClient.tsx index 1da58678..c105eb5d 100644 --- a/src/admin/AdminPhotosClient.tsx +++ b/src/admin/AdminPhotosClient.tsx @@ -3,7 +3,10 @@ import PhotoUpload from '@/photo/PhotoUpload'; import { clsx } from 'clsx/lite'; import SiteGrid from '@/components/SiteGrid'; -import { AI_TEXT_GENERATION_ENABLED, PRO_MODE_ENABLED } from '@/site/config'; +import { + AI_TEXT_GENERATION_ENABLED, + PRESERVE_ORIGINAL_UPLOADS, +} from '@/site/config'; import AdminPhotosTable from '@/admin/AdminPhotosTable'; import AdminPhotosTableInfinite from '@/admin/AdminPhotosTableInfinite'; import PathLoaderButton from '@/components/primitives/PathLoaderButton'; @@ -43,7 +46,7 @@ export default function AdminPhotosClient({
Promise<{ photoId: string }[]>) | undefined = undefined; -if (STATICALLY_OPTIMIZED_OG_IMAGES && IS_PRODUCTION) { +if (STATICALLY_OPTIMIZED_PHOTO_OG_IMAGES && IS_PRODUCTION) { generateStaticParams = async () => { const photos = await getPhotoIds({ limit: GENERATE_STATIC_PARAMS_LIMIT }); return photos.map(photoId => ({ photoId })); diff --git a/src/app/p/[photoId]/page.tsx b/src/app/p/[photoId]/page.tsx index decfb273..a84aa724 100644 --- a/src/app/p/[photoId]/page.tsx +++ b/src/app/p/[photoId]/page.tsx @@ -12,7 +12,7 @@ import { } from '@/site/paths'; import PhotoDetailPage from '@/photo/PhotoDetailPage'; import { getPhotosNearIdCached } from '@/photo/cache'; -import { IS_PRODUCTION, STATICALLY_OPTIMIZED_PAGES } from '@/site/config'; +import { IS_PRODUCTION, STATICALLY_OPTIMIZED_PHOTOS } from '@/site/config'; import { getPhotoIds } from '@/photo/db/query'; import { GENERATE_STATIC_PARAMS_LIMIT } from '@/photo/db'; import { cache } from 'react'; @@ -25,7 +25,7 @@ const getPhotosNearIdCachedCached = cache((photoId: string) => export let generateStaticParams: (() => Promise<{ photoId: string }[]>) | undefined = undefined; -if (STATICALLY_OPTIMIZED_PAGES && IS_PRODUCTION) { +if (STATICALLY_OPTIMIZED_PHOTOS && IS_PRODUCTION) { generateStaticParams = async () => { const photos = await getPhotoIds({ limit: GENERATE_STATIC_PARAMS_LIMIT }); return photos.map(photoId => ({ photoId })); diff --git a/src/photo/server.ts b/src/photo/server.ts index d5b9232e..6096da28 100644 --- a/src/photo/server.ts +++ b/src/photo/server.ts @@ -11,7 +11,7 @@ import { ExifData, ExifParserFactory } from 'ts-exif-parser'; import { PhotoFormData } from './form'; import { FilmSimulation } from '@/simulation'; import sharp, { Sharp } from 'sharp'; -import { GEO_PRIVACY_ENABLED, PRO_MODE_ENABLED } from '@/site/config'; +import { GEO_PRIVACY_ENABLED, PRESERVE_ORIGINAL_UPLOADS } from '@/site/config'; const IMAGE_WIDTH_RESIZE = 200; const IMAGE_WIDTH_BLUR = 200; @@ -169,5 +169,5 @@ export const removeGpsData = async (image: ArrayBuffer) => GPSHPositioningError: GPS_NULL_STRING, }, }) - .toFormat('jpeg', { quality: PRO_MODE_ENABLED ? 95 : 80 }) + .toFormat('jpeg', { quality: PRESERVE_ORIGINAL_UPLOADS ? 95 : 80 }) .toBuffer(); diff --git a/src/site/SiteChecklistClient.tsx b/src/site/SiteChecklistClient.tsx index 7cadb46a..d7ed9734 100644 --- a/src/site/SiteChecklistClient.tsx +++ b/src/site/SiteChecklistClient.tsx @@ -54,8 +54,9 @@ export default function SiteChecklistClient({ isProModeEnabled, isGridHomepageEnabled, isStaticallyOptimized, - arePagesStaticallyOptimized, - areOGImagesStaticallyOptimized, + arePhotosStaticallyOptimized, + arePhotoOGImagesStaticallyOptimized, + arePhotoCategoriesStaticallyOptimized, arePhotosMatted, isBlurEnabled, isGeoPrivacyEnabled, @@ -469,15 +470,21 @@ export default function SiteChecklistClient({ Set environment variable to {'"1"'} to enable static optimization, i.e., rendering pages and images at build time: {renderSubStatus( - arePagesStaticallyOptimized ? 'checked' : 'optional', + arePhotosStaticallyOptimized ? 'checked' : 'optional', renderEnvVars(['NEXT_PUBLIC_STATICALLY_OPTIMIZE_PAGES']), 'translate-y-[3.5px]', )} {renderSubStatus( - areOGImagesStaticallyOptimized ? 'checked' : 'optional', + arePhotoOGImagesStaticallyOptimized ? 'checked' : 'optional', renderEnvVars(['NEXT_PUBLIC_STATICALLY_OPTIMIZE_OG_IMAGES']), 'translate-y-[3.5px]', )} + {renderSubStatus( + arePhotoCategoriesStaticallyOptimized ? 'checked' : 'optional', + // eslint-disable-next-line max-len + renderEnvVars(['NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_CATEGORIES']), + 'translate-y-[3.5px]', + )} Date: Wed, 15 Jan 2025 09:41:08 -0600 Subject: [PATCH 02/44] Statically optimize photo categories when configured --- src/app/film/[simulation]/page.tsx | 13 +++++++++++++ src/app/focal/[focal]/page.tsx | 13 +++++++++++++ src/app/shot-on/[make]/[model]/page.tsx | 13 +++++++++++++ src/app/tag/[tag]/page.tsx | 13 +++++++++++++ 4 files changed, 52 insertions(+) diff --git a/src/app/film/[simulation]/page.tsx b/src/app/film/[simulation]/page.tsx index b9332428..0199457c 100644 --- a/src/app/film/[simulation]/page.tsx +++ b/src/app/film/[simulation]/page.tsx @@ -1,13 +1,26 @@ import { INFINITE_SCROLL_GRID_INITIAL } from '@/photo'; +import { getUniqueFilmSimulations } from '@/photo/db/query'; import { FilmSimulation, generateMetaForFilmSimulation } from '@/simulation'; import FilmSimulationOverview from '@/simulation/FilmSimulationOverview'; +import { IS_PRODUCTION } from '@/site/config'; import { getPhotosFilmSimulationDataCached } from '@/simulation/data'; +import { STATICALLY_OPTIMIZED_PHOTO_CATEGORIES } from '@/site/config'; import { Metadata } from 'next/types'; import { cache } from 'react'; const getPhotosFilmSimulationDataCachedCached = cache(getPhotosFilmSimulationDataCached); +export let generateStaticParams: + (() => Promise<{ simulation: FilmSimulation }[]>) | undefined = undefined; + +if (STATICALLY_OPTIMIZED_PHOTO_CATEGORIES && IS_PRODUCTION) { + generateStaticParams = async () => { + const simulations = await getUniqueFilmSimulations(); + return simulations.map(({ simulation }) => ({ simulation })); + }; +} + interface FilmSimulationProps { params: Promise<{ simulation: FilmSimulation }> } diff --git a/src/app/focal/[focal]/page.tsx b/src/app/focal/[focal]/page.tsx index 78747359..ed8d78a6 100644 --- a/src/app/focal/[focal]/page.tsx +++ b/src/app/focal/[focal]/page.tsx @@ -2,6 +2,9 @@ import { generateMetaForFocalLength, getFocalLengthFromString } from '@/focal'; import FocalLengthOverview from '@/focal/FocalLengthOverview'; import { getPhotosFocalLengthDataCached } from '@/focal/data'; import { INFINITE_SCROLL_GRID_INITIAL } from '@/photo'; +import { IS_PRODUCTION } from '@/site/config'; +import { getUniqueFocalLengths } from '@/photo/db/query'; +import { STATICALLY_OPTIMIZED_PHOTO_CATEGORIES } from '@/site/config'; import { PATH_ROOT } from '@/site/paths'; import type { Metadata } from 'next'; import { redirect } from 'next/navigation'; @@ -13,6 +16,16 @@ const getPhotosFocalDataCachedCached = cache((focal: number) => limit: INFINITE_SCROLL_GRID_INITIAL, })); +export let generateStaticParams: + (() => Promise<{ focal: string }[]>) | undefined = undefined; + +if (STATICALLY_OPTIMIZED_PHOTO_CATEGORIES && IS_PRODUCTION) { + generateStaticParams = async () => { + const focalLengths = await getUniqueFocalLengths(); + return focalLengths.map(({ focal }) => ({ focal: focal.toString() })); + }; +} + interface FocalLengthProps { params: Promise<{ focal: string }> } diff --git a/src/app/shot-on/[make]/[model]/page.tsx b/src/app/shot-on/[make]/[model]/page.tsx index 2c26c36f..2a776ee7 100644 --- a/src/app/shot-on/[make]/[model]/page.tsx +++ b/src/app/shot-on/[make]/[model]/page.tsx @@ -5,6 +5,9 @@ import { INFINITE_SCROLL_GRID_INITIAL } from '@/photo'; import { getPhotosCameraDataCached } from '@/camera/data'; import CameraOverview from '@/camera/CameraOverview'; import { cache } from 'react'; +import { STATICALLY_OPTIMIZED_PHOTO_CATEGORIES } from '@/site/config'; +import { IS_PRODUCTION } from '@/site/config'; +import { getUniqueCameras } from '@/photo/db/query'; const getPhotosCameraDataCachedCached = cache(( make: string, @@ -15,6 +18,16 @@ const getPhotosCameraDataCachedCached = cache(( INFINITE_SCROLL_GRID_INITIAL, )); +export let generateStaticParams: + (() => Promise<{ make: string, model: string }[]>) | undefined = undefined; + +if (STATICALLY_OPTIMIZED_PHOTO_CATEGORIES && IS_PRODUCTION) { + generateStaticParams = async () => { + const cameras = await getUniqueCameras(); + return cameras.map(({ camera: { make, model } }) => ({ make, model })); + }; +} + export async function generateMetadata({ params, }: CameraProps): Promise { diff --git a/src/app/tag/[tag]/page.tsx b/src/app/tag/[tag]/page.tsx index f134f6bc..0e19f3a7 100644 --- a/src/app/tag/[tag]/page.tsx +++ b/src/app/tag/[tag]/page.tsx @@ -1,4 +1,7 @@ import { INFINITE_SCROLL_GRID_INITIAL } from '@/photo'; +import { getUniqueTags } from '@/photo/db/query'; +import { IS_PRODUCTION } from '@/site/config'; +import { STATICALLY_OPTIMIZED_PHOTO_CATEGORIES } from '@/site/config'; import { PATH_ROOT } from '@/site/paths'; import { generateMetaForTag } from '@/tag'; import TagOverview from '@/tag/TagOverview'; @@ -10,6 +13,16 @@ import { cache } from 'react'; const getPhotosTagDataCachedCached = cache((tag: string) => getPhotosTagDataCached({ tag, limit: INFINITE_SCROLL_GRID_INITIAL})); +export let generateStaticParams: + (() => Promise<{ tag: string }[]>) | undefined = undefined; + +if (STATICALLY_OPTIMIZED_PHOTO_CATEGORIES && IS_PRODUCTION) { + generateStaticParams = async () => { + const tags = await getUniqueTags(); + return tags.map(({ tag }) => ({ tag })); + }; +} + interface TagProps { params: Promise<{ tag: string }> } From 625767a68b580ba5d8025b7f7c7430baebf88fa6 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Wed, 15 Jan 2025 09:43:42 -0600 Subject: [PATCH 03/44] Fix lint warning --- src/app/p/[photoId]/image/route.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/app/p/[photoId]/image/route.tsx b/src/app/p/[photoId]/image/route.tsx index bc821e91..73bf45ac 100644 --- a/src/app/p/[photoId]/image/route.tsx +++ b/src/app/p/[photoId]/image/route.tsx @@ -4,7 +4,10 @@ import PhotoImageResponse from '@/image-response/PhotoImageResponse'; import { getIBMPlexMonoMedium } from '@/site/font'; import { ImageResponse } from 'next/og'; import { getImageResponseCacheControlHeaders } from '@/image-response/cache'; -import { IS_PRODUCTION, STATICALLY_OPTIMIZED_PHOTO_OG_IMAGES } from '@/site/config'; +import { + IS_PRODUCTION, + STATICALLY_OPTIMIZED_PHOTO_OG_IMAGES, +} from '@/site/config'; import { getPhotoIds } from '@/photo/db/query'; import { GENERATE_STATIC_PARAMS_LIMIT } from '@/photo/db'; import { isNextImageReadyBasedOnPhotos } from '@/photo'; From 9940bdaf0326bb7698091c0ceae6a5555fa3f6e1 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Wed, 15 Jan 2025 09:47:59 -0600 Subject: [PATCH 04/44] Refactor pro mode language --- src/site/SiteChecklistClient.tsx | 12 ++++++------ src/site/config.ts | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/site/SiteChecklistClient.tsx b/src/site/SiteChecklistClient.tsx index d7ed9734..54a50b56 100644 --- a/src/site/SiteChecklistClient.tsx +++ b/src/site/SiteChecklistClient.tsx @@ -51,7 +51,7 @@ export default function SiteChecklistClient({ showFilmSimulations, showExifInfo, defaultTheme, - isProModeEnabled, + areOriginalUploadsPreserved, isGridHomepageEnabled, isStaticallyOptimized, arePhotosStaticallyOptimized, @@ -453,13 +453,13 @@ export default function SiteChecklistClient({ {renderEnvVars(['NEXT_PUBLIC_DEFAULT_THEME'])} - Set environment variable to {'"1"'} to enable - higher quality image storage: - {renderEnvVars(['NEXT_PUBLIC_PRO_MODE'])} + Set environment variable to {'"1"'} to prevent + image uploads being optimized before storing: + {renderEnvVars(['NEXT_PUBLIC_PRESERVE_ORIGINAL_UPLOADS'])} Date: Wed, 15 Jan 2025 18:31:17 -0600 Subject: [PATCH 05/44] Refine config sub-status layout --- src/site/SiteChecklistClient.tsx | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/site/SiteChecklistClient.tsx b/src/site/SiteChecklistClient.tsx index 54a50b56..4324cccd 100644 --- a/src/site/SiteChecklistClient.tsx +++ b/src/site/SiteChecklistClient.tsx @@ -169,6 +169,16 @@ export default function SiteChecklistClient({ {label}
; + + const renderSubStatusWithEnvVar = ( + type: ComponentProps['type'], + variable: string, + ) => + renderSubStatus( + type, + renderEnvVars([variable]), + 'translate-y-[5px]', + ); const renderError = ({ connection, @@ -469,21 +479,17 @@ export default function SiteChecklistClient({ > Set environment variable to {'"1"'} to enable static optimization, i.e., rendering pages and images at build time: - {renderSubStatus( + {renderSubStatusWithEnvVar( arePhotosStaticallyOptimized ? 'checked' : 'optional', - renderEnvVars(['NEXT_PUBLIC_STATICALLY_OPTIMIZE_PAGES']), - 'translate-y-[3.5px]', + 'NEXT_PUBLIC_STATICALLY_OPTIMIZE_PAGES', )} - {renderSubStatus( + {renderSubStatusWithEnvVar( arePhotoOGImagesStaticallyOptimized ? 'checked' : 'optional', - renderEnvVars(['NEXT_PUBLIC_STATICALLY_OPTIMIZE_OG_IMAGES']), - 'translate-y-[3.5px]', + 'NEXT_PUBLIC_STATICALLY_OPTIMIZE_OG_IMAGES', )} - {renderSubStatus( + {renderSubStatusWithEnvVar( arePhotoCategoriesStaticallyOptimized ? 'checked' : 'optional', - // eslint-disable-next-line max-len - renderEnvVars(['NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_CATEGORIES']), - 'translate-y-[3.5px]', + 'NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_CATEGORIES', )} Date: Wed, 15 Jan 2025 18:56:11 -0600 Subject: [PATCH 06/44] Refine static optimization README formatting --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0af834b8..7fbc06a9 100644 --- a/README.md +++ b/README.md @@ -105,9 +105,9 @@ Application behavior can be changed by configuring the following environment var - `NEXT_PUBLIC_GRID_HOMEPAGE = 1` shows grid layout on homepage - `NEXT_PUBLIC_DEFAULT_THEME = light | dark` sets preferred initial theme (defaults to `system` when not configured) - `NEXT_PUBLIC_PRESERVE_ORIGINAL_UPLOADS = 1` do not optimize photo uploads before storing (⚠️ results in increased storage usage) -- `NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTOS = 1` enables static optimization for photo pages (p/[photoId]), i.e., renders pages at build time (results in increased project usage) -- `NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_OG_IMAGES = 1` enables static optimization for OG images, i.e., renders images at build time (results in increased project usage) -- `NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_CATEGORIES = 1` enables static optimization for photo categories (tag/[tag], shot-on/[make]/[model], etc.), i.e., renders pages at build time (results in increased project usage) +- `NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTOS = 1` enables static optimization for photo pages (`p/[photoId]`), i.e., renders pages at build time (⚠️ results in increased project usage) +- `NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_OG_IMAGES = 1` enables static optimization for OG images, i.e., renders images at build time (⚠️ results in increased project usage) +- `NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_CATEGORIES = 1` enables static optimization for photo categories (`tag/[tag]`, `shot-on/[make]/[model]`, etc.), i.e., renders pages at build time (⚠️ results in increased project usage) - `NEXT_PUBLIC_MATTE_PHOTOS = 1` constrains the size of each photo, and enables a surrounding border (potentially useful for photos with tall aspect ratios) - `NEXT_PUBLIC_BLUR_DISABLED = 1` prevents image blur data being stored and displayed (potentially useful for limiting Postgres usage) - `NEXT_PUBLIC_GEO_PRIVACY = 1` disables collection/display of location-based data (⚠️ re-compresses uploaded images in order to remove GPS information) From 10756045a277f1c479b3d484e99c68907c5391eb Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Wed, 15 Jan 2025 19:04:00 -0600 Subject: [PATCH 07/44] Fix variable typos in site checklist --- src/site/SiteChecklistClient.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/site/SiteChecklistClient.tsx b/src/site/SiteChecklistClient.tsx index 4324cccd..f4cda03f 100644 --- a/src/site/SiteChecklistClient.tsx +++ b/src/site/SiteChecklistClient.tsx @@ -481,11 +481,11 @@ export default function SiteChecklistClient({ i.e., rendering pages and images at build time: {renderSubStatusWithEnvVar( arePhotosStaticallyOptimized ? 'checked' : 'optional', - 'NEXT_PUBLIC_STATICALLY_OPTIMIZE_PAGES', + 'NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTOS', )} {renderSubStatusWithEnvVar( arePhotoOGImagesStaticallyOptimized ? 'checked' : 'optional', - 'NEXT_PUBLIC_STATICALLY_OPTIMIZE_OG_IMAGES', + 'NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_OG_IMAGES', )} {renderSubStatusWithEnvVar( arePhotoCategoriesStaticallyOptimized ? 'checked' : 'optional', From 29273d814dba18ee4b8ff6060a1e5e34da670675 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Wed, 15 Jan 2025 19:24:40 -0600 Subject: [PATCH 08/44] Increase grid home page thumbnails from 24 to 48 --- src/photo/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/photo/index.ts b/src/photo/index.ts index 5dee5ae0..08a15418 100644 --- a/src/photo/index.ts +++ b/src/photo/index.ts @@ -27,8 +27,8 @@ export const INFINITE_SCROLL_FEED_MULTIPLE = // INFINITE SCROLL: GRID export const INFINITE_SCROLL_GRID_INITIAL = HIGH_DENSITY_GRID - ? process.env.NODE_ENV === 'development' ? 12 : 24 - : process.env.NODE_ENV === 'development' ? 12 : 24; + ? process.env.NODE_ENV === 'development' ? 12 : 48 + : process.env.NODE_ENV === 'development' ? 12 : 48; export const INFINITE_SCROLL_GRID_MULTIPLE = HIGH_DENSITY_GRID ? process.env.NODE_ENV === 'development' ? 12 : 48 : process.env.NODE_ENV === 'development' ? 12 : 48; From a5291ec5a9882f801f3338824af8edbf2b10c8dc Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Wed, 15 Jan 2025 19:29:27 -0600 Subject: [PATCH 09/44] Tweak photo code comment --- src/photo/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/photo/index.ts b/src/photo/index.ts index 08a15418..460b6632 100644 --- a/src/photo/index.ts +++ b/src/photo/index.ts @@ -33,7 +33,7 @@ export const INFINITE_SCROLL_GRID_MULTIPLE = HIGH_DENSITY_GRID ? process.env.NODE_ENV === 'development' ? 12 : 48 : process.env.NODE_ENV === 'development' ? 12 : 48; -// Thumbnails below /p/[photoId] +// Thumbnails below large photos on pages like /p/[photoId] export const RELATED_GRID_PHOTOS_TO_SHOW = 12; export const DEFAULT_ASPECT_RATIO = 1.5; From cf28267fe01962a2abdf0b8f242eed90a7dbd037 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sat, 18 Jan 2025 22:27:30 -0600 Subject: [PATCH 10/44] Bump dependencies --- package.json | 30 +- pnpm-lock.yaml | 1262 +++++++++++++++++++++++------------------------- 2 files changed, 608 insertions(+), 684 deletions(-) diff --git a/package.json b/package.json index 9816e848..2ed5de60 100644 --- a/package.json +++ b/package.json @@ -9,26 +9,26 @@ "analyze": "ANALYZE=true next build" }, "dependencies": { - "@ai-sdk/openai": "^1.0.18", - "@aws-sdk/client-s3": "3.726.1", - "@aws-sdk/s3-request-presigner": "3.726.1", + "@ai-sdk/openai": "^1.1.0", + "@aws-sdk/client-s3": "3.731.1", + "@aws-sdk/s3-request-presigner": "3.731.1", "@radix-ui/react-dialog": "^1.1.4", "@radix-ui/react-dropdown-menu": "^2.1.4", "@radix-ui/react-visually-hidden": "^1.1.1", "@upstash/ratelimit": "^2.0.5", "@vercel/analytics": "^1.4.1", - "@vercel/blob": "^0.27.0", + "@vercel/blob": "^0.27.1", "@vercel/kv": "^3.0.0", "@vercel/speed-insights": "^1.1.0", - "ai": "^4.0.33", + "ai": "^4.1.0", "camelcase-keys": "^9.1.3", "cmdk": "^1.0.4", "date-fns": "^4.1.0", "date-fns-tz": "^3.2.0", "exifr": "^7.1.3", - "framer-motion": "^11.17.0", + "framer-motion": "^11.18.1", "nanoid": "^5.0.9", - "next": "15.1.4", + "next": "15.1.5", "next-auth": "5.0.0-beta.25", "next-themes": "^0.4.4", "pg": "^8.13.1", @@ -37,30 +37,30 @@ "react-icons": "^5.4.0", "sanitize-html": "^2.14.0", "sharp": "^0.33.5", - "sonner": "^1.7.1", + "sonner": "^1.7.2", "swr": "^2.3.0", "ts-exif-parser": "^0.2.2", "use-debounce": "^10.0.4" }, "devDependencies": { - "@next/bundle-analyzer": "15.1.4", + "@next/bundle-analyzer": "15.1.5", "@tailwindcss/container-queries": "^0.1.1", "@tailwindcss/forms": "^0.5.10", "@testing-library/jest-dom": "^6.6.3", - "@testing-library/react": "^16.1.0", + "@testing-library/react": "^16.2.0", "@types/jest": "^29.5.14", - "@types/node": "^22.10.5", + "@types/node": "^22.10.7", "@types/pg": "^8.11.10", - "@types/react": "19.0.4", - "@types/react-dom": "19.0.2", + "@types/react": "19.0.7", + "@types/react-dom": "19.0.3", "@types/sanitize-html": "^2.13.0", "autoprefixer": "10.4.20", "clsx": "^2.1.1", "eslint": "9.18.0", - "eslint-config-next": "15.1.4", + "eslint-config-next": "15.1.5", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", - "postcss": "8.4.49", + "postcss": "8.5.1", "tailwindcss": "3.4.17", "typescript": "5.7.3" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3042825e..e22d53be 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,47 +9,47 @@ importers: .: dependencies: '@ai-sdk/openai': - specifier: ^1.0.18 - version: 1.0.18(zod@3.23.8) + specifier: ^1.1.0 + version: 1.1.0(zod@3.23.8) '@aws-sdk/client-s3': - specifier: 3.726.1 - version: 3.726.1 + specifier: 3.731.1 + version: 3.731.1 '@aws-sdk/s3-request-presigner': - specifier: 3.726.1 - version: 3.726.1 + specifier: 3.731.1 + version: 3.731.1 '@radix-ui/react-dialog': specifier: ^1.1.4 - version: 1.1.4(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.1.4(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@radix-ui/react-dropdown-menu': specifier: ^2.1.4 - version: 2.1.4(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 2.1.4(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@radix-ui/react-visually-hidden': specifier: ^1.1.1 - version: 1.1.1(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.1.1(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@upstash/ratelimit': specifier: ^2.0.5 version: 2.0.5(@upstash/redis@1.34.3) '@vercel/analytics': specifier: ^1.4.1 - version: 1.4.1(next@15.1.4(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(svelte@4.2.17)(vue@3.4.27(typescript@5.7.3)) + version: 1.4.1(next@15.1.5(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(svelte@4.2.17)(vue@3.4.27(typescript@5.7.3)) '@vercel/blob': - specifier: ^0.27.0 - version: 0.27.0 + specifier: ^0.27.1 + version: 0.27.1 '@vercel/kv': specifier: ^3.0.0 version: 3.0.0 '@vercel/speed-insights': specifier: ^1.1.0 - version: 1.1.0(next@15.1.4(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(svelte@4.2.17)(vue@3.4.27(typescript@5.7.3)) + version: 1.1.0(next@15.1.5(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(svelte@4.2.17)(vue@3.4.27(typescript@5.7.3)) ai: - specifier: ^4.0.33 - version: 4.0.33(react@19.0.0)(zod@3.23.8) + specifier: ^4.1.0 + version: 4.1.0(react@19.0.0)(zod@3.23.8) camelcase-keys: specifier: ^9.1.3 version: 9.1.3 cmdk: specifier: ^1.0.4 - version: 1.0.4(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.0.4(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) date-fns: specifier: ^4.1.0 version: 4.1.0 @@ -60,17 +60,17 @@ importers: specifier: ^7.1.3 version: 7.1.3 framer-motion: - specifier: ^11.17.0 - version: 11.17.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + specifier: ^11.18.1 + version: 11.18.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) nanoid: specifier: ^5.0.9 version: 5.0.9 next: - specifier: 15.1.4 - version: 15.1.4(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + specifier: 15.1.5 + version: 15.1.5(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) next-auth: specifier: 5.0.0-beta.25 - version: 5.0.0-beta.25(next@15.1.4(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) + version: 5.0.0-beta.25(next@15.1.5(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) next-themes: specifier: ^0.4.4 version: 0.4.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0) @@ -93,8 +93,8 @@ importers: specifier: ^0.33.5 version: 0.33.5 sonner: - specifier: ^1.7.1 - version: 1.7.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + specifier: ^1.7.2 + version: 1.7.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0) swr: specifier: ^2.3.0 version: 2.3.0(react@19.0.0) @@ -106,8 +106,8 @@ importers: version: 10.0.4(react@19.0.0) devDependencies: '@next/bundle-analyzer': - specifier: 15.1.4 - version: 15.1.4 + specifier: 15.1.5 + version: 15.1.5 '@tailwindcss/container-queries': specifier: ^0.1.1 version: 0.1.1(tailwindcss@3.4.17) @@ -118,29 +118,29 @@ importers: specifier: ^6.6.3 version: 6.6.3 '@testing-library/react': - specifier: ^16.1.0 - version: 16.1.0(@testing-library/dom@10.1.0)(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + specifier: ^16.2.0 + version: 16.2.0(@testing-library/dom@10.1.0)(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@types/jest': specifier: ^29.5.14 version: 29.5.14 '@types/node': - specifier: ^22.10.5 - version: 22.10.5 + specifier: ^22.10.7 + version: 22.10.7 '@types/pg': specifier: ^8.11.10 version: 8.11.10 '@types/react': - specifier: 19.0.4 - version: 19.0.4 + specifier: 19.0.7 + version: 19.0.7 '@types/react-dom': - specifier: 19.0.2 - version: 19.0.2(@types/react@19.0.4) + specifier: 19.0.3 + version: 19.0.3(@types/react@19.0.7) '@types/sanitize-html': specifier: ^2.13.0 version: 2.13.0 autoprefixer: specifier: 10.4.20 - version: 10.4.20(postcss@8.4.49) + version: 10.4.20(postcss@8.5.1) clsx: specifier: ^2.1.1 version: 2.1.1 @@ -148,17 +148,17 @@ importers: specifier: 9.18.0 version: 9.18.0(jiti@1.21.7) eslint-config-next: - specifier: 15.1.4 - version: 15.1.4(eslint@9.18.0(jiti@1.21.7))(typescript@5.7.3) + specifier: 15.1.5 + version: 15.1.5(eslint@9.18.0(jiti@1.21.7))(typescript@5.7.3) jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@22.10.5) + version: 29.7.0(@types/node@22.10.7) jest-environment-jsdom: specifier: ^29.7.0 version: 29.7.0 postcss: - specifier: 8.4.49 - version: 8.4.49 + specifier: 8.5.1 + version: 8.5.1 tailwindcss: specifier: 3.4.17 version: 3.4.17 @@ -171,14 +171,14 @@ packages: '@adobe/css-tools@4.4.0': resolution: {integrity: sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ==} - '@ai-sdk/openai@1.0.18': - resolution: {integrity: sha512-bienqSVHbUqUcskm2FTIf2X+c481e85EASFfa78YogLqctZQtqPFKJuG5E7i59664Y5G91+LkzIh+1agS13BlA==} + '@ai-sdk/openai@1.1.0': + resolution: {integrity: sha512-D2DaGMK89yYgO32n4Gr7gBJeJGGGS27gdfzYFMRDXlZmKh7VW1WXBp3FXxDwpmt0CgLoVI4qV8lf+gslah+kWw==} engines: {node: '>=18'} peerDependencies: zod: ^3.0.0 - '@ai-sdk/provider-utils@2.0.7': - resolution: {integrity: sha512-4sfPlKEALHPXLmMFcPlYksst3sWBJXmCDZpIBJisRrmwGG6Nn3mq0N1Zu/nZaGcrWZoOY+HT2Wbxla1oTElYHQ==} + '@ai-sdk/provider-utils@2.1.0': + resolution: {integrity: sha512-rBUabNoyB25PBUjaiMSk86fHNSCqTngNZVvXxv8+6mvw47JX5OexW+ZHRsEw8XKTE8+hqvNFVzctaOrRZ2i9Zw==} engines: {node: '>=18'} peerDependencies: zod: ^3.0.0 @@ -190,8 +190,8 @@ packages: resolution: {integrity: sha512-lJi5zwDosvvZER3e/pB8lj1MN3o3S7zJliQq56BRr4e9V3fcRyFtwP0JRxaRS5vHYX3OJ154VezVoQNrk0eaKw==} engines: {node: '>=18'} - '@ai-sdk/react@1.0.9': - resolution: {integrity: sha512-7mtkgVCSzp8J4x3qk5Vtlk1FiZTH7vWIZvIrA6ISbFDy+7mwm45rIDIymzCiofzr3c/Wioy41H2Ki3Nth55bgg==} + '@ai-sdk/react@1.1.0': + resolution: {integrity: sha512-U5lBbLyf1pw79xsk5dgHSkBv9Jta3xzWlOLpxsmHlxh1X94QOH3e1gm+nioQ/JvTuHLm23j2tz3i4MpMdchwXQ==} engines: {node: '>=18'} peerDependencies: react: ^18 || ^19 || ^19.0.0-rc @@ -202,8 +202,8 @@ packages: zod: optional: true - '@ai-sdk/ui-utils@1.0.8': - resolution: {integrity: sha512-7ya/t28oMaFauHxSj4WGQCEV/iicZj9qP+O+tCakMIDq7oDCZMUNBLCQomoWs16CcYY4l0wo1S9hA4PAdFcOvA==} + '@ai-sdk/ui-utils@1.1.0': + resolution: {integrity: sha512-ETXwdHaHwzC7NIehbthDFGwsTFk+gNtRL/lm85nR4WDFvvYQptoM/7wTANs0p0H7zumB3Ep5hKzv0Encu8vSRw==} engines: {node: '>=18'} peerDependencies: zod: ^3.0.0 @@ -256,143 +256,131 @@ packages: '@aws-crypto/util@5.2.0': resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} - '@aws-sdk/client-s3@3.726.1': - resolution: {integrity: sha512-UpOGcob87DiuS2d3fW6vDZg94g57mNiOSkzvR/6GOdvBSlUgk8LLwVzGASB71FdKMl1EGEr4MeD5uKH9JsG+dw==} + '@aws-sdk/client-s3@3.731.1': + resolution: {integrity: sha512-Ab2PA/8Th6JkurCkxnQJZHPE/JnnSsX/XHQzirkQb+JpKOyWMRC/YZUBfAaiwhxqX65RHgklrwil+UbFl4TtAQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/client-sso-oidc@3.726.0': - resolution: {integrity: sha512-5JzTX9jwev7+y2Jkzjz0pd1wobB5JQfPOQF3N2DrJ5Pao0/k6uRYwE4NqB0p0HlGrMTDm7xNq7OSPPIPG575Jw==} - engines: {node: '>=18.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.726.0 - - '@aws-sdk/client-sso@3.726.0': - resolution: {integrity: sha512-NM5pjv2qglEc4XN3nnDqtqGsSGv1k5YTmzDo3W3pObItHmpS8grSeNfX9zSH+aVl0Q8hE4ZIgvTPNZ+GzwVlqg==} + '@aws-sdk/client-sso@3.731.0': + resolution: {integrity: sha512-O4C/UYGgqMsBg21MMApFdgyh8BX568hQhbdoNFmRVTBoSnCZ3w+H4a1wBPX4Gyl0NX+ab6Xxo9rId8HiyPXJ0A==} engines: {node: '>=18.0.0'} - '@aws-sdk/client-sts@3.726.1': - resolution: {integrity: sha512-qh9Q9Vu1hrM/wMBOBIaskwnE4GTFaZu26Q6WHwyWNfj7J8a40vBxpW16c2vYXHLBtwRKM1be8uRLkmDwghpiNw==} + '@aws-sdk/core@3.731.0': + resolution: {integrity: sha512-ithBN1VWASkvAIlozJmenqDvNnFddr/SZXAs58+jCnBHgy3tXLHABZGVNCjetZkHRqNdXEO1kirnoxaFeXMeDA==} engines: {node: '>=18.0.0'} - '@aws-sdk/core@3.723.0': - resolution: {integrity: sha512-UraXNmvqj3vScSsTkjMwQkhei30BhXlW5WxX6JacMKVtl95c7z0qOXquTWeTalYkFfulfdirUhvSZrl+hcyqTw==} + '@aws-sdk/credential-provider-env@3.731.0': + resolution: {integrity: sha512-h0WWZg4QMLgFVyIvQrC43zpVqsUWg1mPM1clpogP43B8+wEhDEQ4qWRzvFs3dQ4cqx/FLyDUZZF4cqgd94z7kw==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-env@3.723.0': - resolution: {integrity: sha512-OuH2yULYUHTVDUotBoP/9AEUIJPn81GQ/YBtZLoo2QyezRJ2QiO/1epVtbJlhNZRwXrToLEDmQGA2QfC8c7pbA==} + '@aws-sdk/credential-provider-http@3.731.0': + resolution: {integrity: sha512-iRtrjtcYaWgbvtu2cvDhIsPWXZGvhy1Hgks4682MEBNTc9AUwlfvDrYz2EEnTtJJyrbOdEHVrYrzqD8qPyVLCg==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-http@3.723.0': - resolution: {integrity: sha512-DTsKC6xo/kz/ZSs1IcdbQMTgiYbpGTGEd83kngFc1bzmw7AmK92DBZKNZpumf8R/UfSpTcj9zzUUmrWz1kD0eQ==} + '@aws-sdk/credential-provider-ini@3.731.1': + resolution: {integrity: sha512-0M0ejuqW8iHNcTH2ZXSY9m+I7Y06qVkj6k3vfQU9XaB//mTUCxxfGfqWAtgfr7Yi73egABTcPc0jyPdcvSW4Kw==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-ini@3.726.0': - resolution: {integrity: sha512-seTtcKL2+gZX6yK1QRPr5mDJIBOatrpoyrO8D5b8plYtV/PDbDW3mtDJSWFHet29G61ZmlNElyXRqQCXn9WX+A==} - engines: {node: '>=18.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.726.0 - - '@aws-sdk/credential-provider-node@3.726.0': - resolution: {integrity: sha512-jjsewBcw/uLi24x8JbnuDjJad4VA9ROCE94uVRbEnGmUEsds75FWOKp3fWZLQlmjLtzsIbJOZLALkZP86liPaw==} + '@aws-sdk/credential-provider-node@3.731.1': + resolution: {integrity: sha512-5c0ZiagMTPmWilXNffeXJCLoCEz97jilHr3QJWwf2GaTay4tzN+Ld71rpdfEenzUR7fuxEWFfVlwQbFOzFNYHg==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-process@3.723.0': - resolution: {integrity: sha512-fgupvUjz1+jeoCBA7GMv0L6xEk92IN6VdF4YcFhsgRHlHvNgm7ayaoKQg7pz2JAAhG/3jPX6fp0ASNy+xOhmPA==} + '@aws-sdk/credential-provider-process@3.731.0': + resolution: {integrity: sha512-6yNMY6q3xHLbs2f2+C6GhvMrjTgtFBiPJJqKaPLsTIhlTRvh4sK8pGm3ITcma0jOxtPDIuoPfBAV8N8XVMBlZg==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-sso@3.726.0': - resolution: {integrity: sha512-WxkN76WeB08j2yw7jUH9yCMPxmT9eBFd9ZA/aACG7yzOIlsz7gvG3P2FQ0tVg25GHM0E4PdU3p/ByTOawzcOAg==} + '@aws-sdk/credential-provider-sso@3.731.1': + resolution: {integrity: sha512-p1tp+rMUf5YNQLr8rVRmDgNtKGYLL0KCdq3K2hwwvFnx9MjReF1sA4lfm3xWsxBQM+j3QN9AvMQqBzDJ+NOSdw==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-web-identity@3.723.0': - resolution: {integrity: sha512-tl7pojbFbr3qLcOE6xWaNCf1zEfZrIdSJtOPeSXfV/thFMMAvIjgf3YN6Zo1a6cxGee8zrV/C8PgOH33n+Ev/A==} - engines: {node: '>=18.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.723.0 - - '@aws-sdk/middleware-bucket-endpoint@3.726.0': - resolution: {integrity: sha512-vpaP80rZqwu0C3ELayIcRIW84/nd1tadeoqllT+N9TDshuEvq4UJ+w47OBHB7RkHFJoc79lXXNYle0fdQdaE/A==} + '@aws-sdk/credential-provider-web-identity@3.731.1': + resolution: {integrity: sha512-+ynAvEGWDR5ZJFxgpwwzhvlQ3WQ7BleWXU6JwpIw3yFrD4eZEn85b8DZC1aEz7C9kb1HSV6B3gpqHqlyS6wj8g==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-expect-continue@3.723.0': - resolution: {integrity: sha512-w/O0EkIzkiqvGu7U8Ke7tue0V0HYM5dZQrz6nVU+R8T2LddWJ+njEIHU4Wh8aHPLQXdZA5NQumv0xLPdEutykw==} + '@aws-sdk/middleware-bucket-endpoint@3.731.0': + resolution: {integrity: sha512-G9vuGW5GWCbzGOwlGFJcOkfxhw1cB6vzcv75QTT0CmciLXK+Cio8O2pqMSTTF2kg9Y6iHVC2BlOtLRkJAVOxVQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-flexible-checksums@3.723.0': - resolution: {integrity: sha512-JY76mrUCLa0FHeMZp8X9+KK6uEuZaRZaQrlgq6zkXX/3udukH0T3YdFC+Y9uw5ddbiwZ5+KwgmlhnPpiXKfP4g==} + '@aws-sdk/middleware-expect-continue@3.731.0': + resolution: {integrity: sha512-oY4nsY/mb5O5eZCzIuWpyvzO45Bi6UBtE48IaJsoyVctagA1l8hB66aczH9M1NHNjvbS4Beu1agwh3Nyb1eJug==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-host-header@3.723.0': - resolution: {integrity: sha512-LLVzLvk299pd7v4jN9yOSaWDZDfH0SnBPb6q+FDPaOCMGBY8kuwQso7e/ozIKSmZHRMGO3IZrflasHM+rI+2YQ==} + '@aws-sdk/middleware-flexible-checksums@3.731.0': + resolution: {integrity: sha512-LMs/rndovYjYSntSYyPE/PIl4iHNiquaU0lpDqpQc9iTgQcNbjdriSUWpibgu1jXlGBpBYCqttNkxmEThbbWxA==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-location-constraint@3.723.0': - resolution: {integrity: sha512-inp9tyrdRWjGOMu1rzli8i2gTo0P4X6L7nNRXNTKfyPNZcBimZ4H0H1B671JofSI5isaklVy5r4pvv2VjjLSHw==} + '@aws-sdk/middleware-host-header@3.731.0': + resolution: {integrity: sha512-ndAJsm5uWPPJRZowLKpB1zuL17qWlWVtCJP4I/ynBkq1PU1DijDXBul2UZaG6Mpvsgms1NXo/h9noHuK7T3v8w==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-logger@3.723.0': - resolution: {integrity: sha512-chASQfDG5NJ8s5smydOEnNK7N0gDMyuPbx7dYYcm1t/PKtnVfvWF+DHCTrRC2Ej76gLJVCVizlAJKM8v8Kg3cg==} + '@aws-sdk/middleware-location-constraint@3.731.0': + resolution: {integrity: sha512-1I2EjAFxrQksrzqdN7YYuY/q2YsjqeX4l7f9VOkdBjiZeDvVIEdM99IT5sISJB/r6CjNrYX5MhqGhE8i1VFchA==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-recursion-detection@3.723.0': - resolution: {integrity: sha512-7usZMtoynT9/jxL/rkuDOFQ0C2mhXl4yCm67Rg7GNTstl67u7w5WN1aIRImMeztaKlw8ExjoTyo6WTs1Kceh7A==} + '@aws-sdk/middleware-logger@3.731.0': + resolution: {integrity: sha512-IIZrOdjbY2vKzPJPrwE7FoFQCIPEL6UqURi8LEaiVyCag4p2fvaTN5pgKuQtGC2+iYd/HHcGT4qn2bAqF5Jmmw==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-sdk-s3@3.723.0': - resolution: {integrity: sha512-wfjOvNJVp8LDWhq4wO5jtSMb8Vgf4tNlR7QTEQfoYc6AGU3WlK5xyUQcpfcpwytEhQTN9u0cJLQpSyXDO+qSCw==} + '@aws-sdk/middleware-recursion-detection@3.731.0': + resolution: {integrity: sha512-y6FLASB1iKWuR5tUipMyo77bt0lEl3OnCrrd2xw/H24avq1HhJjjPR0HHhJE6QKJzF/FYXeV88tcyPSMe32VDw==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-ssec@3.723.0': - resolution: {integrity: sha512-Bs+8RAeSMik6ZYCGSDJzJieGsDDh2fRbh1HQG94T8kpwBXVxMYihm6e9Xp2cyl+w9fyyCnh0IdCKChP/DvrdhA==} + '@aws-sdk/middleware-sdk-s3@3.731.0': + resolution: {integrity: sha512-J9aKyQaVoec5eWTSDfO4h2sKHNP0wTzN15LFcHnkD+e/d0rdmOi7BTkkbJrIaynma9WShIasmrtM3HNi9GiiTA==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-user-agent@3.726.0': - resolution: {integrity: sha512-hZvzuE5S0JmFie1r68K2wQvJbzyxJFdzltj9skgnnwdvLe8F/tz7MqLkm28uV0m4jeHk0LpiBo6eZaPkQiwsZQ==} + '@aws-sdk/middleware-ssec@3.731.0': + resolution: {integrity: sha512-1HP4lxGcQx4awXgxfL8t9faBK7TqEL7zkAZrm/YBbLrT9oQomxZOHKIOHvo5VVh4qmsNBdqnH2XUlSMY71gsww==} engines: {node: '>=18.0.0'} - '@aws-sdk/region-config-resolver@3.723.0': - resolution: {integrity: sha512-tGF/Cvch3uQjZIj34LY2mg8M2Dr4kYG8VU8Yd0dFnB1ybOEOveIK/9ypUo9ycZpB9oO6q01KRe5ijBaxNueUQg==} + '@aws-sdk/middleware-user-agent@3.731.0': + resolution: {integrity: sha512-Ngr2Gz0aec/uduoKaO3srN52SYkEHndYtFzkK/gDUyQwQzi4ha2eIisxPiuHEX6RvXT31V9ouqn/YtVkt0R76A==} engines: {node: '>=18.0.0'} - '@aws-sdk/s3-request-presigner@3.726.1': - resolution: {integrity: sha512-IoM/u1gaZiSHEZkkf+Hn6MvCFUtLJgJysApW6NFbM2GYt4hqGLX5jhbjo5KVxC3wFfAhAwK1deSOM0FriBrKrg==} + '@aws-sdk/nested-clients@3.731.1': + resolution: {integrity: sha512-/L8iVrulnXZl+kgmTn+oxRxNnhcSIbf+r12C06vGUq60w0YMidLvxJZN7vt8H9SnCAGCHqud2MS7ExCEvhc0gA==} engines: {node: '>=18.0.0'} - '@aws-sdk/signature-v4-multi-region@3.723.0': - resolution: {integrity: sha512-lJlVAa5Sl589qO8lwMLVUtnlF1Q7I+6k1Iomv2goY9d1bRl4q2N5Pit2qJVr2AMW0sceQXeh23i2a/CKOqVAdg==} + '@aws-sdk/region-config-resolver@3.731.0': + resolution: {integrity: sha512-XlDpRNkDVHF59f07JmkuAidEv//m3hT6/JL85h0l3+zrpaRWhf8n8lVUyAPNq35ZujK8AcorYM+93u7hdWsliQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/token-providers@3.723.0': - resolution: {integrity: sha512-hniWi1x4JHVwKElANh9afKIMUhAutHVBRD8zo6usr0PAoj+Waf220+1ULS74GXtLXAPCiNXl5Og+PHA7xT8ElQ==} + '@aws-sdk/s3-request-presigner@3.731.1': + resolution: {integrity: sha512-GdG0pXkcTgBpenouB834FoCHyLaivV2rGQn7OEQBiT8SBaTxSackZ6tGlJQAlzZQkiQfE/NePUJU7DczJZZvrg==} engines: {node: '>=18.0.0'} - peerDependencies: - '@aws-sdk/client-sso-oidc': ^3.723.0 - '@aws-sdk/types@3.723.0': - resolution: {integrity: sha512-LmK3kwiMZG1y5g3LGihT9mNkeNOmwEyPk6HGcJqh0wOSV4QpWoKu2epyKE4MLQNUUlz2kOVbVbOrwmI6ZcteuA==} + '@aws-sdk/signature-v4-multi-region@3.731.0': + resolution: {integrity: sha512-1r/b4Os15dR+BCVRRLVQJMF7Krq6xX6IKHxN43kuvODYWz8Nv3XDlaSpeRpAzyJuzW/fTp4JgE+z0+gmJfdEeA==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/token-providers@3.731.1': + resolution: {integrity: sha512-t34GOPwBZsX7zGHjiTXmMHGY3kHM7fLiQ60Jqk0On9P0ASHTDE5U75RgCXboE3u+qEv9wyKyaqMNyMWj9qQlFg==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/types@3.731.0': + resolution: {integrity: sha512-NrdkJg6oOUbXR2r9WvHP408CLyvST8cJfp1/jP9pemtjvjPoh6NukbCtiSFdOOb1eryP02CnqQWItfJC1p2Y/Q==} engines: {node: '>=18.0.0'} '@aws-sdk/util-arn-parser@3.723.0': resolution: {integrity: sha512-ZhEfvUwNliOQROcAk34WJWVYTlTa4694kSVhDSjW6lE1bMataPnIN8A0ycukEzBXmd8ZSoBcQLn6lKGl7XIJ5w==} engines: {node: '>=18.0.0'} - '@aws-sdk/util-endpoints@3.726.0': - resolution: {integrity: sha512-sLd30ASsPMoPn3XBK50oe/bkpJ4N8Bpb7SbhoxcY3Lk+fSASaWxbbXE81nbvCnkxrZCvkPOiDHzJCp1E2im71A==} + '@aws-sdk/util-endpoints@3.731.0': + resolution: {integrity: sha512-riztxTAfncFS9yQWcBJffGgOgLoKSa63ph+rxWJxKl6BHAmWEvHICj1qDcVmnWfIcvJ5cClclY75l9qKaUH7rQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/util-format-url@3.723.0': - resolution: {integrity: sha512-70+xUrdcnencPlCdV9XkRqmgj0vLDb8vm4mcEsgabg5QQ3S80KM0GEuhBAIGMkBWwNQTzCgQy2s7xOUlJPbu+g==} + '@aws-sdk/util-format-url@3.731.0': + resolution: {integrity: sha512-wZHObjnYmiz8wFlUQ4/5dHsT7k0at+GvZM02LgvshcRJLnFjYdrzjelMKuNynd/NNK3gLgTsFTGuIgPpz9r4rA==} engines: {node: '>=18.0.0'} '@aws-sdk/util-locate-window@3.568.0': resolution: {integrity: sha512-3nh4TINkXYr+H41QaPelCceEB2FXP3fxp93YZXB/kqJvX0U9j0N0Uk45gvsjmEPzG8XxkPEeLIfT2I1M7A6Lig==} engines: {node: '>=16.0.0'} - '@aws-sdk/util-user-agent-browser@3.723.0': - resolution: {integrity: sha512-Wh9I6j2jLhNFq6fmXydIpqD1WyQLyTfSxjW9B+PXSnPyk3jtQW8AKQur7p97rO8LAUzVI0bv8kb3ZzDEVbquIg==} + '@aws-sdk/util-user-agent-browser@3.731.0': + resolution: {integrity: sha512-EnYXxTkCNCjTTBjW/pelRPv4Thsi9jepoB6qQjPMA9/ixrZ71BhhQecz9kgqzZLR9BPCwb6hgJ/Yd702jqJ4aQ==} - '@aws-sdk/util-user-agent-node@3.726.0': - resolution: {integrity: sha512-iEj6KX9o6IQf23oziorveRqyzyclWai95oZHDJtYav3fvLJKStwSjygO4xSF7ycHcTYeCHSLO1FFOHgGVs4Viw==} + '@aws-sdk/util-user-agent-node@3.731.0': + resolution: {integrity: sha512-Rze78Ym5Bx7aWMvmZE2iL3JPo2INNCC5N9rLVx98Gg1G0ZaxclVRUvJrh1AojNlOFxU+otkxAe7FA3Foy2iLLQ==} engines: {node: '>=18.0.0'} peerDependencies: aws-crt: '>=1.0.0' @@ -856,59 +844,59 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} - '@next/bundle-analyzer@15.1.4': - resolution: {integrity: sha512-W8X96jOW0U5VjLVAkFr1P37kH2f/Ma9zzwgX2o3Omft92pI0XHpFG8Xa9YUT3NlhRJCe4ZKznr1VxhSrFNA+BA==} + '@next/bundle-analyzer@15.1.5': + resolution: {integrity: sha512-pCYMPgGRwf+FjEwUXFo3QF14VzBSPPsBHSFuXUpq5ifKcY8LbcmoF2xMVVMa2HoYgA1XuqPSAIfLJr4YXNa9xQ==} - '@next/env@15.1.4': - resolution: {integrity: sha512-2fZ5YZjedi5AGaeoaC0B20zGntEHRhi2SdWcu61i48BllODcAmmtj8n7YarSPt4DaTsJaBFdxQAVEVzgmx2Zpw==} + '@next/env@15.1.5': + resolution: {integrity: sha512-jg8ygVq99W3/XXb9Y6UQsritwhjc+qeiO7QrGZRYOfviyr/HcdnhdBQu4gbp2rBIh2ZyBYTBMWbPw3JSCb0GHw==} - '@next/eslint-plugin-next@15.1.4': - resolution: {integrity: sha512-HwlEXwCK3sr6zmVGEvWBjW9tBFs1Oe6hTmTLoFQtpm4As5HCdu8jfSE0XJOp7uhfEGLniIx8yrGxEWwNnY0fmQ==} + '@next/eslint-plugin-next@15.1.5': + resolution: {integrity: sha512-3cCrXBybsqe94UxD6DBQCYCCiP9YohBMgZ5IzzPYHmPzj8oqNlhBii5b6o1HDDaRHdz2pVnSsAROCtrczy8O0g==} - '@next/swc-darwin-arm64@15.1.4': - resolution: {integrity: sha512-wBEMBs+np+R5ozN1F8Y8d/Dycns2COhRnkxRc+rvnbXke5uZBHkUGFgWxfTXn5rx7OLijuUhyfB+gC/ap58dDw==} + '@next/swc-darwin-arm64@15.1.5': + resolution: {integrity: sha512-5ttHGE75Nw9/l5S8zR2xEwR8OHEqcpPym3idIMAZ2yo+Edk0W/Vf46jGqPOZDk+m/SJ+vYZDSuztzhVha8rcdA==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@15.1.4': - resolution: {integrity: sha512-7sgf5rM7Z81V9w48F02Zz6DgEJulavC0jadab4ZsJ+K2sxMNK0/BtF8J8J3CxnsJN3DGcIdC260wEKssKTukUw==} + '@next/swc-darwin-x64@15.1.5': + resolution: {integrity: sha512-8YnZn7vDURUUTInfOcU5l0UWplZGBqUlzvqKKUFceM11SzfNEz7E28E1Arn4/FsOf90b1Nopboy7i7ufc4jXag==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@15.1.4': - resolution: {integrity: sha512-JaZlIMNaJenfd55kjaLWMfok+vWBlcRxqnRoZrhFQrhM1uAehP3R0+Aoe+bZOogqlZvAz53nY/k3ZyuKDtT2zQ==} + '@next/swc-linux-arm64-gnu@15.1.5': + resolution: {integrity: sha512-rDJC4ctlYbK27tCyFUhgIv8o7miHNlpCjb2XXfTLQszwAUOSbcMN9q2y3urSrrRCyGVOd9ZR9a4S45dRh6JF3A==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-arm64-musl@15.1.4': - resolution: {integrity: sha512-7EBBjNoyTO2ipMDgCiORpwwOf5tIueFntKjcN3NK+GAQD7OzFJe84p7a2eQUeWdpzZvhVXuAtIen8QcH71ZCOQ==} + '@next/swc-linux-arm64-musl@15.1.5': + resolution: {integrity: sha512-FG5RApf4Gu+J+pHUQxXPM81oORZrKBYKUaBTylEIQ6Lz17hKVDsLbSXInfXM0giclvXbyiLXjTv42sQMATmZ0A==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-x64-gnu@15.1.4': - resolution: {integrity: sha512-9TGEgOycqZFuADyFqwmK/9g6S0FYZ3tphR4ebcmCwhL8Y12FW8pIBKJvSwV+UBjMkokstGNH+9F8F031JZKpHw==} + '@next/swc-linux-x64-gnu@15.1.5': + resolution: {integrity: sha512-NX2Ar3BCquAOYpnoYNcKz14eH03XuF7SmSlPzTSSU4PJe7+gelAjxo3Y7F2m8+hLT8ZkkqElawBp7SWBdzwqQw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-linux-x64-musl@15.1.4': - resolution: {integrity: sha512-0578bLRVDJOh+LdIoKvgNDz77+Bd85c5JrFgnlbI1SM3WmEQvsjxTA8ATu9Z9FCiIS/AliVAW2DV/BDwpXbtiQ==} + '@next/swc-linux-x64-musl@15.1.5': + resolution: {integrity: sha512-EQgqMiNu3mrV5eQHOIgeuh6GB5UU57tu17iFnLfBEhYfiOfyK+vleYKh2dkRVkV6ayx3eSqbIYgE7J7na4hhcA==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-win32-arm64-msvc@15.1.4': - resolution: {integrity: sha512-JgFCiV4libQavwII+kncMCl30st0JVxpPOtzWcAI2jtum4HjYaclobKhj+JsRu5tFqMtA5CJIa0MvYyuu9xjjQ==} + '@next/swc-win32-arm64-msvc@15.1.5': + resolution: {integrity: sha512-HPULzqR/VqryQZbZME8HJE3jNFmTGcp+uRMHabFbQl63TtDPm+oCXAz3q8XyGv2AoihwNApVlur9Up7rXWRcjg==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-x64-msvc@15.1.4': - resolution: {integrity: sha512-xxsJy9wzq7FR5SqPCUqdgSXiNXrMuidgckBa8nH9HtjjxsilgcN6VgXF6tZ3uEWuVEadotQJI8/9EQ6guTC4Yw==} + '@next/swc-win32-x64-msvc@15.1.5': + resolution: {integrity: sha512-n74fUb/Ka1dZSVYfjwQ+nSJ+ifUff7jGurFcTuJNKZmI62FFOxQXUYit/uZXPTj2cirm1rvGWHG2GhbSol5Ikw==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -1504,8 +1492,8 @@ packages: resolution: {integrity: sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA==} engines: {node: '>=14', npm: '>=6', yarn: '>=1'} - '@testing-library/react@16.1.0': - resolution: {integrity: sha512-Q2ToPvg0KsVL0ohND9A3zLJWcOXXcO8IDu3fj11KhNt0UlCWyFyvnCIBkd12tidB2lkiVRG8VFqdhcqhqnAQtg==} + '@testing-library/react@16.2.0': + resolution: {integrity: sha512-2cSskAvA1QNtKc8Y9VJQRv0tm3hLVgxRGDB+KYhIaPQJ1I+RHbhIXcM+zClKXzMes/wshsMVzf4B9vS4IZpqDQ==} engines: {node: '>=18'} peerDependencies: '@testing-library/dom': ^10.0.0 @@ -1571,19 +1559,19 @@ packages: '@types/json5@0.0.29': resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} - '@types/node@22.10.5': - resolution: {integrity: sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==} + '@types/node@22.10.7': + resolution: {integrity: sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==} '@types/pg@8.11.10': resolution: {integrity: sha512-LczQUW4dbOQzsH2RQ5qoeJ6qJPdrcM/DcMLoqWQkMLMsq83J5lAX3LXjdkWdpscFy67JSOWDnh7Ny/sPFykmkg==} - '@types/react-dom@19.0.2': - resolution: {integrity: sha512-c1s+7TKFaDRRxr1TxccIX2u7sfCnc3RxkVyBIUA2lCpyqCF+QoAwQ/CBg7bsMdVwP120HEH143VQezKtef5nCg==} + '@types/react-dom@19.0.3': + resolution: {integrity: sha512-0Knk+HJiMP/qOZgMyNFamlIjw9OFCsyC2ZbigmEEyXXixgre6IQpm/4V+r3qH4GC1JPvRJKInw+on2rV6YZLeA==} peerDependencies: '@types/react': ^19.0.0 - '@types/react@19.0.4': - resolution: {integrity: sha512-3O4QisJDYr1uTUMZHA2YswiQZRq+Pd8D+GdVFYikTutYsTz+QZgWkAPnP7rx9txoI6EXKcPiluMqWPFV3tT9Wg==} + '@types/react@19.0.7': + resolution: {integrity: sha512-MoFsEJKkAtZCrC1r6CM8U22GzhG7u2Wir8ons/aCKH6MBdD1ibV24zOSSkdZVUKqN5i396zG5VKLYZ3yaUZdLA==} '@types/sanitize-html@2.13.0': resolution: {integrity: sha512-X31WxbvW9TjIhZZNyNBZ/p5ax4ti7qsNDBDEnH4zAgmEh35YnFD1UiS6z9Cd34kKm0LslFW0KPmTQzu/oGtsqQ==} @@ -1688,8 +1676,8 @@ packages: vue-router: optional: true - '@vercel/blob@0.27.0': - resolution: {integrity: sha512-EkYMKRMlHkWRWCnh99adrIT7G6cgv1dWpj/S3Kqykha8cI3EAMSmvnzI1N+41MAZiLMm0CsoGToKgtzGTf5xKQ==} + '@vercel/blob@0.27.1': + resolution: {integrity: sha512-X5EG9W1cZW+Nbt/XdrJJSl5DzCXXn1JRP5nfFwkpFD03nB6uh6BldwX4syElHcciM4Pih8CS7Ri1mtLCJvxSHA==} engines: {node: '>=16.14'} '@vercel/kv@3.0.0': @@ -1778,8 +1766,8 @@ packages: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} - ai@4.0.33: - resolution: {integrity: sha512-mOvhPyVchGZvZuPn8Zj4J+93fZOlaBH1BtunvGmQ/8yFc5hGmid3c0XIdw5UNt3++0sXawKE3j7JUL5ZmiQdKg==} + ai@4.1.0: + resolution: {integrity: sha512-95nI9hBSSAKPrMnpJbaB3yqvh+G8BS4/EtFz3HR0HgEDJpxC0R6JAlB8+B/BXHd/roNGBrS08Z3Zain/6OFSYA==} engines: {node: '>=18'} peerDependencies: react: ^18 || ^19 || ^19.0.0-rc @@ -1977,10 +1965,6 @@ packages: resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} engines: {node: '>=10.16.0'} - bytes@3.1.2: - resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} - engines: {node: '>= 0.8'} - call-bind-apply-helpers@1.0.1: resolution: {integrity: sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==} engines: {node: '>= 0.4'} @@ -2383,8 +2367,8 @@ packages: engines: {node: '>=6.0'} hasBin: true - eslint-config-next@15.1.4: - resolution: {integrity: sha512-u9+7lFmfhKNgGjhQ9tBeyCFsPJyq0SvGioMJBngPC7HXUpR0U+ckEwQR48s7TrRNHra1REm6evGL2ie38agALg==} + eslint-config-next@15.1.5: + resolution: {integrity: sha512-Awm7iUJY8toOR+fU8yTxZnA7/LyOGUGOd6cENCuDfJ3gucHOSmLdOSGJ4u+nlrs8p5qXemua42bZmq+uOzxl6Q==} peerDependencies: eslint: ^7.23.0 || ^8.0.0 || ^9.0.0 typescript: '>=3.3.1' @@ -2591,8 +2575,8 @@ packages: fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} - framer-motion@11.17.0: - resolution: {integrity: sha512-uTNLH9JPMD3ad14WBt3KYRTR+If4tGPLgKTKTIIPaEBMkvazs6EkWNcmCh65qA/tyinOqIbQiuCorXX0qQsNoQ==} + framer-motion@11.18.1: + resolution: {integrity: sha512-EQa8c9lWVOm4zlz14MsBJWr8woq87HsNmsBnQNvcS0hs8uzw6HtGAxZyIU7EGTVpHD1C1n01ufxRyarXcNzpPg==} peerDependencies: '@emotion/is-prop-valid': '*' react: ^18.0.0 || ^19.0.0 @@ -3293,11 +3277,11 @@ packages: resolution: {integrity: sha512-UZ7eQ+h8ywIRAW1hIEl2AqdwzJucU/Kp59+8kkZeSvafXhZjul247BvIJjEVFVeON6d7lM46XX1HXCduKAS8VA==} engines: {node: '>=16 || 14 >=14.17'} - motion-dom@11.16.4: - resolution: {integrity: sha512-2wuCie206pCiP2K23uvwJeci4pMFfyQKpWI0Vy6HrCTDzDCer4TsYtT7IVnuGbDeoIV37UuZiUr6SZMHEc1Vww==} + motion-dom@11.18.1: + resolution: {integrity: sha512-g76KvA001z+atjfxczdRtw/RXOM3OMSdd1f4DL77qCTF/+avrRJiawSG4yDibEQ215sr9kpinSlX2pCTJ9zbhw==} - motion-utils@11.16.0: - resolution: {integrity: sha512-ngdWPjg31rD4WGXFi0eZ00DQQqKKu04QExyv/ymlC+3k+WIgYVFbt6gS5JsFPbJODTF/r8XiE/X+SsoT9c0ocw==} + motion-utils@11.18.1: + resolution: {integrity: sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==} mrmime@2.0.0: resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==} @@ -3312,11 +3296,6 @@ packages: mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - nanoid@3.3.7: - resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - nanoid@3.3.8: resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -3352,8 +3331,8 @@ packages: react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc - next@15.1.4: - resolution: {integrity: sha512-mTaq9dwaSuwwOrcu3ebjDYObekkxRnXpuVL21zotM8qE2W0HBOdVIdg2Li9QjMEZrj73LN96LcWcz62V19FjAg==} + next@15.1.5: + resolution: {integrity: sha512-Cf/TEegnt01hn3Hoywh6N8fvkhbOuChO4wFje24+a86wKOubgVaWkDqxGVgoWlz2Hp9luMJ9zw3epftujdnUOg==} engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} hasBin: true peerDependencies: @@ -3622,8 +3601,8 @@ packages: resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} engines: {node: ^10 || ^12 || >=14} - postcss@8.4.49: - resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==} + postcss@8.5.1: + resolution: {integrity: sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==} engines: {node: ^10 || ^12 || >=14} postgres-array@2.0.0: @@ -3929,8 +3908,8 @@ packages: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} - sonner@1.7.1: - resolution: {integrity: sha512-b6LHBfH32SoVasRFECrdY8p8s7hXPDn3OHUFbZZbiB1ctLS9Gdh6rpX2dVrpQA0kiL5jcRzDDldwwLkSKk3+QQ==} + sonner@1.7.2: + resolution: {integrity: sha512-zMbseqjrOzQD1a93lxahm+qMGxWovdMxBlkTbbnZdNqVLt4j+amF9PQxUCL32WfztOFt9t9ADYkejAL3jF9iNA==} peerDependencies: react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc @@ -4402,13 +4381,13 @@ snapshots: '@adobe/css-tools@4.4.0': {} - '@ai-sdk/openai@1.0.18(zod@3.23.8)': + '@ai-sdk/openai@1.1.0(zod@3.23.8)': dependencies: '@ai-sdk/provider': 1.0.4 - '@ai-sdk/provider-utils': 2.0.7(zod@3.23.8) + '@ai-sdk/provider-utils': 2.1.0(zod@3.23.8) zod: 3.23.8 - '@ai-sdk/provider-utils@2.0.7(zod@3.23.8)': + '@ai-sdk/provider-utils@2.1.0(zod@3.23.8)': dependencies: '@ai-sdk/provider': 1.0.4 eventsource-parser: 3.0.0 @@ -4421,20 +4400,20 @@ snapshots: dependencies: json-schema: 0.4.0 - '@ai-sdk/react@1.0.9(react@19.0.0)(zod@3.23.8)': + '@ai-sdk/react@1.1.0(react@19.0.0)(zod@3.23.8)': dependencies: - '@ai-sdk/provider-utils': 2.0.7(zod@3.23.8) - '@ai-sdk/ui-utils': 1.0.8(zod@3.23.8) + '@ai-sdk/provider-utils': 2.1.0(zod@3.23.8) + '@ai-sdk/ui-utils': 1.1.0(zod@3.23.8) swr: 2.3.0(react@19.0.0) throttleit: 2.1.0 optionalDependencies: react: 19.0.0 zod: 3.23.8 - '@ai-sdk/ui-utils@1.0.8(zod@3.23.8)': + '@ai-sdk/ui-utils@1.1.0(zod@3.23.8)': dependencies: '@ai-sdk/provider': 1.0.4 - '@ai-sdk/provider-utils': 2.0.7(zod@3.23.8) + '@ai-sdk/provider-utils': 2.1.0(zod@3.23.8) zod-to-json-schema: 3.24.1(zod@3.23.8) optionalDependencies: zod: 3.23.8 @@ -4459,20 +4438,20 @@ snapshots: '@aws-crypto/crc32@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.723.0 + '@aws-sdk/types': 3.731.0 tslib: 2.8.1 '@aws-crypto/crc32c@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.723.0 + '@aws-sdk/types': 3.731.0 tslib: 2.8.1 '@aws-crypto/sha1-browser@5.2.0': dependencies: '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.723.0 + '@aws-sdk/types': 3.731.0 '@aws-sdk/util-locate-window': 3.568.0 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 @@ -4482,7 +4461,7 @@ snapshots: '@aws-crypto/sha256-js': 5.2.0 '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.723.0 + '@aws-sdk/types': 3.731.0 '@aws-sdk/util-locate-window': 3.568.0 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 @@ -4490,7 +4469,7 @@ snapshots: '@aws-crypto/sha256-js@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.723.0 + '@aws-sdk/types': 3.731.0 tslib: 2.8.1 '@aws-crypto/supports-web-crypto@5.2.0': @@ -4499,35 +4478,33 @@ snapshots: '@aws-crypto/util@5.2.0': dependencies: - '@aws-sdk/types': 3.723.0 + '@aws-sdk/types': 3.731.0 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 - '@aws-sdk/client-s3@3.726.1': + '@aws-sdk/client-s3@3.731.1': dependencies: '@aws-crypto/sha1-browser': 5.2.0 '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.726.0(@aws-sdk/client-sts@3.726.1) - '@aws-sdk/client-sts': 3.726.1 - '@aws-sdk/core': 3.723.0 - '@aws-sdk/credential-provider-node': 3.726.0(@aws-sdk/client-sso-oidc@3.726.0(@aws-sdk/client-sts@3.726.1))(@aws-sdk/client-sts@3.726.1) - '@aws-sdk/middleware-bucket-endpoint': 3.726.0 - '@aws-sdk/middleware-expect-continue': 3.723.0 - '@aws-sdk/middleware-flexible-checksums': 3.723.0 - '@aws-sdk/middleware-host-header': 3.723.0 - '@aws-sdk/middleware-location-constraint': 3.723.0 - '@aws-sdk/middleware-logger': 3.723.0 - '@aws-sdk/middleware-recursion-detection': 3.723.0 - '@aws-sdk/middleware-sdk-s3': 3.723.0 - '@aws-sdk/middleware-ssec': 3.723.0 - '@aws-sdk/middleware-user-agent': 3.726.0 - '@aws-sdk/region-config-resolver': 3.723.0 - '@aws-sdk/signature-v4-multi-region': 3.723.0 - '@aws-sdk/types': 3.723.0 - '@aws-sdk/util-endpoints': 3.726.0 - '@aws-sdk/util-user-agent-browser': 3.723.0 - '@aws-sdk/util-user-agent-node': 3.726.0 + '@aws-sdk/core': 3.731.0 + '@aws-sdk/credential-provider-node': 3.731.1 + '@aws-sdk/middleware-bucket-endpoint': 3.731.0 + '@aws-sdk/middleware-expect-continue': 3.731.0 + '@aws-sdk/middleware-flexible-checksums': 3.731.0 + '@aws-sdk/middleware-host-header': 3.731.0 + '@aws-sdk/middleware-location-constraint': 3.731.0 + '@aws-sdk/middleware-logger': 3.731.0 + '@aws-sdk/middleware-recursion-detection': 3.731.0 + '@aws-sdk/middleware-sdk-s3': 3.731.0 + '@aws-sdk/middleware-ssec': 3.731.0 + '@aws-sdk/middleware-user-agent': 3.731.0 + '@aws-sdk/region-config-resolver': 3.731.0 + '@aws-sdk/signature-v4-multi-region': 3.731.0 + '@aws-sdk/types': 3.731.0 + '@aws-sdk/util-endpoints': 3.731.0 + '@aws-sdk/util-user-agent-browser': 3.731.0 + '@aws-sdk/util-user-agent-node': 3.731.0 '@aws-sdk/xml-builder': 3.723.0 '@smithy/config-resolver': 4.0.1 '@smithy/core': 3.1.0 @@ -4566,22 +4543,20 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso-oidc@3.726.0(@aws-sdk/client-sts@3.726.1)': + '@aws-sdk/client-sso@3.731.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sts': 3.726.1 - '@aws-sdk/core': 3.723.0 - '@aws-sdk/credential-provider-node': 3.726.0(@aws-sdk/client-sso-oidc@3.726.0(@aws-sdk/client-sts@3.726.1))(@aws-sdk/client-sts@3.726.1) - '@aws-sdk/middleware-host-header': 3.723.0 - '@aws-sdk/middleware-logger': 3.723.0 - '@aws-sdk/middleware-recursion-detection': 3.723.0 - '@aws-sdk/middleware-user-agent': 3.726.0 - '@aws-sdk/region-config-resolver': 3.723.0 - '@aws-sdk/types': 3.723.0 - '@aws-sdk/util-endpoints': 3.726.0 - '@aws-sdk/util-user-agent-browser': 3.723.0 - '@aws-sdk/util-user-agent-node': 3.726.0 + '@aws-sdk/core': 3.731.0 + '@aws-sdk/middleware-host-header': 3.731.0 + '@aws-sdk/middleware-logger': 3.731.0 + '@aws-sdk/middleware-recursion-detection': 3.731.0 + '@aws-sdk/middleware-user-agent': 3.731.0 + '@aws-sdk/region-config-resolver': 3.731.0 + '@aws-sdk/types': 3.731.0 + '@aws-sdk/util-endpoints': 3.731.0 + '@aws-sdk/util-user-agent-browser': 3.731.0 + '@aws-sdk/util-user-agent-node': 3.731.0 '@smithy/config-resolver': 4.0.1 '@smithy/core': 3.1.0 '@smithy/fetch-http-handler': 5.0.1 @@ -4611,97 +4586,9 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso@3.726.0': + '@aws-sdk/core@3.731.0': dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.723.0 - '@aws-sdk/middleware-host-header': 3.723.0 - '@aws-sdk/middleware-logger': 3.723.0 - '@aws-sdk/middleware-recursion-detection': 3.723.0 - '@aws-sdk/middleware-user-agent': 3.726.0 - '@aws-sdk/region-config-resolver': 3.723.0 - '@aws-sdk/types': 3.723.0 - '@aws-sdk/util-endpoints': 3.726.0 - '@aws-sdk/util-user-agent-browser': 3.723.0 - '@aws-sdk/util-user-agent-node': 3.726.0 - '@smithy/config-resolver': 4.0.1 - '@smithy/core': 3.1.0 - '@smithy/fetch-http-handler': 5.0.1 - '@smithy/hash-node': 4.0.1 - '@smithy/invalid-dependency': 4.0.1 - '@smithy/middleware-content-length': 4.0.1 - '@smithy/middleware-endpoint': 4.0.1 - '@smithy/middleware-retry': 4.0.1 - '@smithy/middleware-serde': 4.0.1 - '@smithy/middleware-stack': 4.0.1 - '@smithy/node-config-provider': 4.0.1 - '@smithy/node-http-handler': 4.0.1 - '@smithy/protocol-http': 5.0.1 - '@smithy/smithy-client': 4.1.0 - '@smithy/types': 4.1.0 - '@smithy/url-parser': 4.0.1 - '@smithy/util-base64': 4.0.0 - '@smithy/util-body-length-browser': 4.0.0 - '@smithy/util-body-length-node': 4.0.0 - '@smithy/util-defaults-mode-browser': 4.0.1 - '@smithy/util-defaults-mode-node': 4.0.1 - '@smithy/util-endpoints': 3.0.1 - '@smithy/util-middleware': 4.0.1 - '@smithy/util-retry': 4.0.1 - '@smithy/util-utf8': 4.0.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/client-sts@3.726.1': - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.726.0(@aws-sdk/client-sts@3.726.1) - '@aws-sdk/core': 3.723.0 - '@aws-sdk/credential-provider-node': 3.726.0(@aws-sdk/client-sso-oidc@3.726.0(@aws-sdk/client-sts@3.726.1))(@aws-sdk/client-sts@3.726.1) - '@aws-sdk/middleware-host-header': 3.723.0 - '@aws-sdk/middleware-logger': 3.723.0 - '@aws-sdk/middleware-recursion-detection': 3.723.0 - '@aws-sdk/middleware-user-agent': 3.726.0 - '@aws-sdk/region-config-resolver': 3.723.0 - '@aws-sdk/types': 3.723.0 - '@aws-sdk/util-endpoints': 3.726.0 - '@aws-sdk/util-user-agent-browser': 3.723.0 - '@aws-sdk/util-user-agent-node': 3.726.0 - '@smithy/config-resolver': 4.0.1 - '@smithy/core': 3.1.0 - '@smithy/fetch-http-handler': 5.0.1 - '@smithy/hash-node': 4.0.1 - '@smithy/invalid-dependency': 4.0.1 - '@smithy/middleware-content-length': 4.0.1 - '@smithy/middleware-endpoint': 4.0.1 - '@smithy/middleware-retry': 4.0.1 - '@smithy/middleware-serde': 4.0.1 - '@smithy/middleware-stack': 4.0.1 - '@smithy/node-config-provider': 4.0.1 - '@smithy/node-http-handler': 4.0.1 - '@smithy/protocol-http': 5.0.1 - '@smithy/smithy-client': 4.1.0 - '@smithy/types': 4.1.0 - '@smithy/url-parser': 4.0.1 - '@smithy/util-base64': 4.0.0 - '@smithy/util-body-length-browser': 4.0.0 - '@smithy/util-body-length-node': 4.0.0 - '@smithy/util-defaults-mode-browser': 4.0.1 - '@smithy/util-defaults-mode-node': 4.0.1 - '@smithy/util-endpoints': 3.0.1 - '@smithy/util-middleware': 4.0.1 - '@smithy/util-retry': 4.0.1 - '@smithy/util-utf8': 4.0.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/core@3.723.0': - dependencies: - '@aws-sdk/types': 3.723.0 + '@aws-sdk/types': 3.731.0 '@smithy/core': 3.1.0 '@smithy/node-config-provider': 4.0.1 '@smithy/property-provider': 4.0.1 @@ -4713,18 +4600,18 @@ snapshots: fast-xml-parser: 4.4.1 tslib: 2.8.1 - '@aws-sdk/credential-provider-env@3.723.0': + '@aws-sdk/credential-provider-env@3.731.0': dependencies: - '@aws-sdk/core': 3.723.0 - '@aws-sdk/types': 3.723.0 + '@aws-sdk/core': 3.731.0 + '@aws-sdk/types': 3.731.0 '@smithy/property-provider': 4.0.1 '@smithy/types': 4.1.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-http@3.723.0': + '@aws-sdk/credential-provider-http@3.731.0': dependencies: - '@aws-sdk/core': 3.723.0 - '@aws-sdk/types': 3.723.0 + '@aws-sdk/core': 3.731.0 + '@aws-sdk/types': 3.731.0 '@smithy/fetch-http-handler': 5.0.1 '@smithy/node-http-handler': 4.0.1 '@smithy/property-provider': 4.0.1 @@ -4734,79 +4621,77 @@ snapshots: '@smithy/util-stream': 4.0.1 tslib: 2.8.1 - '@aws-sdk/credential-provider-ini@3.726.0(@aws-sdk/client-sso-oidc@3.726.0(@aws-sdk/client-sts@3.726.1))(@aws-sdk/client-sts@3.726.1)': + '@aws-sdk/credential-provider-ini@3.731.1': dependencies: - '@aws-sdk/client-sts': 3.726.1 - '@aws-sdk/core': 3.723.0 - '@aws-sdk/credential-provider-env': 3.723.0 - '@aws-sdk/credential-provider-http': 3.723.0 - '@aws-sdk/credential-provider-process': 3.723.0 - '@aws-sdk/credential-provider-sso': 3.726.0(@aws-sdk/client-sso-oidc@3.726.0(@aws-sdk/client-sts@3.726.1)) - '@aws-sdk/credential-provider-web-identity': 3.723.0(@aws-sdk/client-sts@3.726.1) - '@aws-sdk/types': 3.723.0 + '@aws-sdk/core': 3.731.0 + '@aws-sdk/credential-provider-env': 3.731.0 + '@aws-sdk/credential-provider-http': 3.731.0 + '@aws-sdk/credential-provider-process': 3.731.0 + '@aws-sdk/credential-provider-sso': 3.731.1 + '@aws-sdk/credential-provider-web-identity': 3.731.1 + '@aws-sdk/nested-clients': 3.731.1 + '@aws-sdk/types': 3.731.0 '@smithy/credential-provider-imds': 4.0.1 '@smithy/property-provider': 4.0.1 '@smithy/shared-ini-file-loader': 4.0.1 '@smithy/types': 4.1.0 tslib: 2.8.1 transitivePeerDependencies: - - '@aws-sdk/client-sso-oidc' - aws-crt - '@aws-sdk/credential-provider-node@3.726.0(@aws-sdk/client-sso-oidc@3.726.0(@aws-sdk/client-sts@3.726.1))(@aws-sdk/client-sts@3.726.1)': + '@aws-sdk/credential-provider-node@3.731.1': dependencies: - '@aws-sdk/credential-provider-env': 3.723.0 - '@aws-sdk/credential-provider-http': 3.723.0 - '@aws-sdk/credential-provider-ini': 3.726.0(@aws-sdk/client-sso-oidc@3.726.0(@aws-sdk/client-sts@3.726.1))(@aws-sdk/client-sts@3.726.1) - '@aws-sdk/credential-provider-process': 3.723.0 - '@aws-sdk/credential-provider-sso': 3.726.0(@aws-sdk/client-sso-oidc@3.726.0(@aws-sdk/client-sts@3.726.1)) - '@aws-sdk/credential-provider-web-identity': 3.723.0(@aws-sdk/client-sts@3.726.1) - '@aws-sdk/types': 3.723.0 + '@aws-sdk/credential-provider-env': 3.731.0 + '@aws-sdk/credential-provider-http': 3.731.0 + '@aws-sdk/credential-provider-ini': 3.731.1 + '@aws-sdk/credential-provider-process': 3.731.0 + '@aws-sdk/credential-provider-sso': 3.731.1 + '@aws-sdk/credential-provider-web-identity': 3.731.1 + '@aws-sdk/types': 3.731.0 '@smithy/credential-provider-imds': 4.0.1 '@smithy/property-provider': 4.0.1 '@smithy/shared-ini-file-loader': 4.0.1 '@smithy/types': 4.1.0 tslib: 2.8.1 transitivePeerDependencies: - - '@aws-sdk/client-sso-oidc' - - '@aws-sdk/client-sts' - aws-crt - '@aws-sdk/credential-provider-process@3.723.0': + '@aws-sdk/credential-provider-process@3.731.0': dependencies: - '@aws-sdk/core': 3.723.0 - '@aws-sdk/types': 3.723.0 + '@aws-sdk/core': 3.731.0 + '@aws-sdk/types': 3.731.0 '@smithy/property-provider': 4.0.1 '@smithy/shared-ini-file-loader': 4.0.1 '@smithy/types': 4.1.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-sso@3.726.0(@aws-sdk/client-sso-oidc@3.726.0(@aws-sdk/client-sts@3.726.1))': + '@aws-sdk/credential-provider-sso@3.731.1': dependencies: - '@aws-sdk/client-sso': 3.726.0 - '@aws-sdk/core': 3.723.0 - '@aws-sdk/token-providers': 3.723.0(@aws-sdk/client-sso-oidc@3.726.0(@aws-sdk/client-sts@3.726.1)) - '@aws-sdk/types': 3.723.0 + '@aws-sdk/client-sso': 3.731.0 + '@aws-sdk/core': 3.731.0 + '@aws-sdk/token-providers': 3.731.1 + '@aws-sdk/types': 3.731.0 '@smithy/property-provider': 4.0.1 '@smithy/shared-ini-file-loader': 4.0.1 '@smithy/types': 4.1.0 tslib: 2.8.1 transitivePeerDependencies: - - '@aws-sdk/client-sso-oidc' - aws-crt - '@aws-sdk/credential-provider-web-identity@3.723.0(@aws-sdk/client-sts@3.726.1)': + '@aws-sdk/credential-provider-web-identity@3.731.1': dependencies: - '@aws-sdk/client-sts': 3.726.1 - '@aws-sdk/core': 3.723.0 - '@aws-sdk/types': 3.723.0 + '@aws-sdk/core': 3.731.0 + '@aws-sdk/nested-clients': 3.731.1 + '@aws-sdk/types': 3.731.0 '@smithy/property-provider': 4.0.1 '@smithy/types': 4.1.0 tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt - '@aws-sdk/middleware-bucket-endpoint@3.726.0': + '@aws-sdk/middleware-bucket-endpoint@3.731.0': dependencies: - '@aws-sdk/types': 3.723.0 + '@aws-sdk/types': 3.731.0 '@aws-sdk/util-arn-parser': 3.723.0 '@smithy/node-config-provider': 4.0.1 '@smithy/protocol-http': 5.0.1 @@ -4814,20 +4699,20 @@ snapshots: '@smithy/util-config-provider': 4.0.0 tslib: 2.8.1 - '@aws-sdk/middleware-expect-continue@3.723.0': + '@aws-sdk/middleware-expect-continue@3.731.0': dependencies: - '@aws-sdk/types': 3.723.0 + '@aws-sdk/types': 3.731.0 '@smithy/protocol-http': 5.0.1 '@smithy/types': 4.1.0 tslib: 2.8.1 - '@aws-sdk/middleware-flexible-checksums@3.723.0': + '@aws-sdk/middleware-flexible-checksums@3.731.0': dependencies: '@aws-crypto/crc32': 5.2.0 '@aws-crypto/crc32c': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/core': 3.723.0 - '@aws-sdk/types': 3.723.0 + '@aws-sdk/core': 3.731.0 + '@aws-sdk/types': 3.731.0 '@smithy/is-array-buffer': 4.0.0 '@smithy/node-config-provider': 4.0.1 '@smithy/protocol-http': 5.0.1 @@ -4837,36 +4722,36 @@ snapshots: '@smithy/util-utf8': 4.0.0 tslib: 2.8.1 - '@aws-sdk/middleware-host-header@3.723.0': + '@aws-sdk/middleware-host-header@3.731.0': dependencies: - '@aws-sdk/types': 3.723.0 + '@aws-sdk/types': 3.731.0 '@smithy/protocol-http': 5.0.1 '@smithy/types': 4.1.0 tslib: 2.8.1 - '@aws-sdk/middleware-location-constraint@3.723.0': + '@aws-sdk/middleware-location-constraint@3.731.0': dependencies: - '@aws-sdk/types': 3.723.0 + '@aws-sdk/types': 3.731.0 '@smithy/types': 4.1.0 tslib: 2.8.1 - '@aws-sdk/middleware-logger@3.723.0': + '@aws-sdk/middleware-logger@3.731.0': dependencies: - '@aws-sdk/types': 3.723.0 + '@aws-sdk/types': 3.731.0 '@smithy/types': 4.1.0 tslib: 2.8.1 - '@aws-sdk/middleware-recursion-detection@3.723.0': + '@aws-sdk/middleware-recursion-detection@3.731.0': dependencies: - '@aws-sdk/types': 3.723.0 + '@aws-sdk/types': 3.731.0 '@smithy/protocol-http': 5.0.1 '@smithy/types': 4.1.0 tslib: 2.8.1 - '@aws-sdk/middleware-sdk-s3@3.723.0': + '@aws-sdk/middleware-sdk-s3@3.731.0': dependencies: - '@aws-sdk/core': 3.723.0 - '@aws-sdk/types': 3.723.0 + '@aws-sdk/core': 3.731.0 + '@aws-sdk/types': 3.731.0 '@aws-sdk/util-arn-parser': 3.723.0 '@smithy/core': 3.1.0 '@smithy/node-config-provider': 4.0.1 @@ -4880,61 +4765,106 @@ snapshots: '@smithy/util-utf8': 4.0.0 tslib: 2.8.1 - '@aws-sdk/middleware-ssec@3.723.0': + '@aws-sdk/middleware-ssec@3.731.0': dependencies: - '@aws-sdk/types': 3.723.0 + '@aws-sdk/types': 3.731.0 '@smithy/types': 4.1.0 tslib: 2.8.1 - '@aws-sdk/middleware-user-agent@3.726.0': + '@aws-sdk/middleware-user-agent@3.731.0': dependencies: - '@aws-sdk/core': 3.723.0 - '@aws-sdk/types': 3.723.0 - '@aws-sdk/util-endpoints': 3.726.0 + '@aws-sdk/core': 3.731.0 + '@aws-sdk/types': 3.731.0 + '@aws-sdk/util-endpoints': 3.731.0 '@smithy/core': 3.1.0 '@smithy/protocol-http': 5.0.1 '@smithy/types': 4.1.0 tslib: 2.8.1 - '@aws-sdk/region-config-resolver@3.723.0': + '@aws-sdk/nested-clients@3.731.1': dependencies: - '@aws-sdk/types': 3.723.0 + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.731.0 + '@aws-sdk/middleware-host-header': 3.731.0 + '@aws-sdk/middleware-logger': 3.731.0 + '@aws-sdk/middleware-recursion-detection': 3.731.0 + '@aws-sdk/middleware-user-agent': 3.731.0 + '@aws-sdk/region-config-resolver': 3.731.0 + '@aws-sdk/types': 3.731.0 + '@aws-sdk/util-endpoints': 3.731.0 + '@aws-sdk/util-user-agent-browser': 3.731.0 + '@aws-sdk/util-user-agent-node': 3.731.0 + '@smithy/config-resolver': 4.0.1 + '@smithy/core': 3.1.0 + '@smithy/fetch-http-handler': 5.0.1 + '@smithy/hash-node': 4.0.1 + '@smithy/invalid-dependency': 4.0.1 + '@smithy/middleware-content-length': 4.0.1 + '@smithy/middleware-endpoint': 4.0.1 + '@smithy/middleware-retry': 4.0.1 + '@smithy/middleware-serde': 4.0.1 + '@smithy/middleware-stack': 4.0.1 + '@smithy/node-config-provider': 4.0.1 + '@smithy/node-http-handler': 4.0.1 + '@smithy/protocol-http': 5.0.1 + '@smithy/smithy-client': 4.1.0 + '@smithy/types': 4.1.0 + '@smithy/url-parser': 4.0.1 + '@smithy/util-base64': 4.0.0 + '@smithy/util-body-length-browser': 4.0.0 + '@smithy/util-body-length-node': 4.0.0 + '@smithy/util-defaults-mode-browser': 4.0.1 + '@smithy/util-defaults-mode-node': 4.0.1 + '@smithy/util-endpoints': 3.0.1 + '@smithy/util-middleware': 4.0.1 + '@smithy/util-retry': 4.0.1 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/region-config-resolver@3.731.0': + dependencies: + '@aws-sdk/types': 3.731.0 '@smithy/node-config-provider': 4.0.1 '@smithy/types': 4.1.0 '@smithy/util-config-provider': 4.0.0 '@smithy/util-middleware': 4.0.1 tslib: 2.8.1 - '@aws-sdk/s3-request-presigner@3.726.1': + '@aws-sdk/s3-request-presigner@3.731.1': dependencies: - '@aws-sdk/signature-v4-multi-region': 3.723.0 - '@aws-sdk/types': 3.723.0 - '@aws-sdk/util-format-url': 3.723.0 + '@aws-sdk/signature-v4-multi-region': 3.731.0 + '@aws-sdk/types': 3.731.0 + '@aws-sdk/util-format-url': 3.731.0 '@smithy/middleware-endpoint': 4.0.1 '@smithy/protocol-http': 5.0.1 '@smithy/smithy-client': 4.1.0 '@smithy/types': 4.1.0 tslib: 2.8.1 - '@aws-sdk/signature-v4-multi-region@3.723.0': + '@aws-sdk/signature-v4-multi-region@3.731.0': dependencies: - '@aws-sdk/middleware-sdk-s3': 3.723.0 - '@aws-sdk/types': 3.723.0 + '@aws-sdk/middleware-sdk-s3': 3.731.0 + '@aws-sdk/types': 3.731.0 '@smithy/protocol-http': 5.0.1 '@smithy/signature-v4': 5.0.1 '@smithy/types': 4.1.0 tslib: 2.8.1 - '@aws-sdk/token-providers@3.723.0(@aws-sdk/client-sso-oidc@3.726.0(@aws-sdk/client-sts@3.726.1))': + '@aws-sdk/token-providers@3.731.1': dependencies: - '@aws-sdk/client-sso-oidc': 3.726.0(@aws-sdk/client-sts@3.726.1) - '@aws-sdk/types': 3.723.0 + '@aws-sdk/nested-clients': 3.731.1 + '@aws-sdk/types': 3.731.0 '@smithy/property-provider': 4.0.1 '@smithy/shared-ini-file-loader': 4.0.1 '@smithy/types': 4.1.0 tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt - '@aws-sdk/types@3.723.0': + '@aws-sdk/types@3.731.0': dependencies: '@smithy/types': 4.1.0 tslib: 2.8.1 @@ -4943,16 +4873,16 @@ snapshots: dependencies: tslib: 2.8.1 - '@aws-sdk/util-endpoints@3.726.0': + '@aws-sdk/util-endpoints@3.731.0': dependencies: - '@aws-sdk/types': 3.723.0 + '@aws-sdk/types': 3.731.0 '@smithy/types': 4.1.0 '@smithy/util-endpoints': 3.0.1 tslib: 2.8.1 - '@aws-sdk/util-format-url@3.723.0': + '@aws-sdk/util-format-url@3.731.0': dependencies: - '@aws-sdk/types': 3.723.0 + '@aws-sdk/types': 3.731.0 '@smithy/querystring-builder': 4.0.1 '@smithy/types': 4.1.0 tslib: 2.8.1 @@ -4961,17 +4891,17 @@ snapshots: dependencies: tslib: 2.8.1 - '@aws-sdk/util-user-agent-browser@3.723.0': + '@aws-sdk/util-user-agent-browser@3.731.0': dependencies: - '@aws-sdk/types': 3.723.0 + '@aws-sdk/types': 3.731.0 '@smithy/types': 4.1.0 bowser: 2.11.0 tslib: 2.8.1 - '@aws-sdk/util-user-agent-node@3.726.0': + '@aws-sdk/util-user-agent-node@3.731.0': dependencies: - '@aws-sdk/middleware-user-agent': 3.726.0 - '@aws-sdk/types': 3.723.0 + '@aws-sdk/middleware-user-agent': 3.731.0 + '@aws-sdk/types': 3.731.0 '@smithy/node-config-provider': 4.0.1 '@smithy/types': 4.1.0 tslib: 2.8.1 @@ -5363,7 +5293,7 @@ snapshots: '@jest/console@29.7.0': dependencies: '@jest/types': 29.6.3 - '@types/node': 22.10.5 + '@types/node': 22.10.7 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 @@ -5376,14 +5306,14 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.10.5 + '@types/node': 22.10.7 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@22.10.5) + jest-config: 29.7.0(@types/node@22.10.7) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -5408,7 +5338,7 @@ snapshots: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.10.5 + '@types/node': 22.10.7 jest-mock: 29.7.0 '@jest/expect-utils@29.7.0': @@ -5426,7 +5356,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 22.10.5 + '@types/node': 22.10.7 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -5448,7 +5378,7 @@ snapshots: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.25 - '@types/node': 22.10.5 + '@types/node': 22.10.7 chalk: 4.1.2 collect-v8-coverage: 1.0.2 exit: 0.1.2 @@ -5518,7 +5448,7 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 22.10.5 + '@types/node': 22.10.7 '@types/yargs': 17.0.32 chalk: 4.1.2 @@ -5539,41 +5469,41 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.4.15 - '@next/bundle-analyzer@15.1.4': + '@next/bundle-analyzer@15.1.5': dependencies: webpack-bundle-analyzer: 4.10.1 transitivePeerDependencies: - bufferutil - utf-8-validate - '@next/env@15.1.4': {} + '@next/env@15.1.5': {} - '@next/eslint-plugin-next@15.1.4': + '@next/eslint-plugin-next@15.1.5': dependencies: fast-glob: 3.3.1 - '@next/swc-darwin-arm64@15.1.4': + '@next/swc-darwin-arm64@15.1.5': optional: true - '@next/swc-darwin-x64@15.1.4': + '@next/swc-darwin-x64@15.1.5': optional: true - '@next/swc-linux-arm64-gnu@15.1.4': + '@next/swc-linux-arm64-gnu@15.1.5': optional: true - '@next/swc-linux-arm64-musl@15.1.4': + '@next/swc-linux-arm64-musl@15.1.5': optional: true - '@next/swc-linux-x64-gnu@15.1.4': + '@next/swc-linux-x64-gnu@15.1.5': optional: true - '@next/swc-linux-x64-musl@15.1.4': + '@next/swc-linux-x64-musl@15.1.5': optional: true - '@next/swc-win32-arm64-msvc@15.1.4': + '@next/swc-win32-arm64-msvc@15.1.5': optional: true - '@next/swc-win32-x64-msvc@15.1.4': + '@next/swc-win32-x64-msvc@15.1.5': optional: true '@nodelib/fs.scandir@2.1.5': @@ -5599,286 +5529,286 @@ snapshots: '@radix-ui/primitive@1.1.1': {} - '@radix-ui/react-arrow@1.1.1(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-arrow@1.1.1(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 19.0.4 - '@types/react-dom': 19.0.2(@types/react@19.0.4) + '@types/react': 19.0.7 + '@types/react-dom': 19.0.3(@types/react@19.0.7) - '@radix-ui/react-collection@1.1.1(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-collection@1.1.1(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.4)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.4)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-slot': 1.1.1(@types/react@19.0.4)(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.7)(react@19.0.0) + '@radix-ui/react-context': 1.1.1(@types/react@19.0.7)(react@19.0.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-slot': 1.1.1(@types/react@19.0.7)(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 19.0.4 - '@types/react-dom': 19.0.2(@types/react@19.0.4) + '@types/react': 19.0.7 + '@types/react-dom': 19.0.3(@types/react@19.0.7) - '@radix-ui/react-compose-refs@1.1.0(@types/react@19.0.4)(react@19.0.0)': + '@radix-ui/react-compose-refs@1.1.0(@types/react@19.0.7)(react@19.0.0)': dependencies: react: 19.0.0 optionalDependencies: - '@types/react': 19.0.4 + '@types/react': 19.0.7 - '@radix-ui/react-compose-refs@1.1.1(@types/react@19.0.4)(react@19.0.0)': + '@radix-ui/react-compose-refs@1.1.1(@types/react@19.0.7)(react@19.0.0)': dependencies: react: 19.0.0 optionalDependencies: - '@types/react': 19.0.4 + '@types/react': 19.0.7 - '@radix-ui/react-context@1.1.1(@types/react@19.0.4)(react@19.0.0)': + '@radix-ui/react-context@1.1.1(@types/react@19.0.7)(react@19.0.0)': dependencies: react: 19.0.0 optionalDependencies: - '@types/react': 19.0.4 + '@types/react': 19.0.7 - '@radix-ui/react-dialog@1.1.4(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-dialog@1.1.4(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.4)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.4)(react@19.0.0) - '@radix-ui/react-dismissable-layer': 1.1.3(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-focus-guards': 1.1.1(@types/react@19.0.4)(react@19.0.0) - '@radix-ui/react-focus-scope': 1.1.1(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@19.0.4)(react@19.0.0) - '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-slot': 1.1.1(@types/react@19.0.4)(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.4)(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.7)(react@19.0.0) + '@radix-ui/react-context': 1.1.1(@types/react@19.0.7)(react@19.0.0) + '@radix-ui/react-dismissable-layer': 1.1.3(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-focus-guards': 1.1.1(@types/react@19.0.7)(react@19.0.0) + '@radix-ui/react-focus-scope': 1.1.1(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-id': 1.1.0(@types/react@19.0.7)(react@19.0.0) + '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-slot': 1.1.1(@types/react@19.0.7)(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.7)(react@19.0.0) aria-hidden: 1.2.4 react: 19.0.0 react-dom: 19.0.0(react@19.0.0) - react-remove-scroll: 2.6.2(@types/react@19.0.4)(react@19.0.0) + react-remove-scroll: 2.6.2(@types/react@19.0.7)(react@19.0.0) optionalDependencies: - '@types/react': 19.0.4 - '@types/react-dom': 19.0.2(@types/react@19.0.4) + '@types/react': 19.0.7 + '@types/react-dom': 19.0.3(@types/react@19.0.7) - '@radix-ui/react-direction@1.1.0(@types/react@19.0.4)(react@19.0.0)': + '@radix-ui/react-direction@1.1.0(@types/react@19.0.7)(react@19.0.0)': dependencies: react: 19.0.0 optionalDependencies: - '@types/react': 19.0.4 + '@types/react': 19.0.7 - '@radix-ui/react-dismissable-layer@1.1.3(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-dismissable-layer@1.1.3(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.4)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.4)(react@19.0.0) - '@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@19.0.4)(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.7)(react@19.0.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.7)(react@19.0.0) + '@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@19.0.7)(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 19.0.4 - '@types/react-dom': 19.0.2(@types/react@19.0.4) + '@types/react': 19.0.7 + '@types/react-dom': 19.0.3(@types/react@19.0.7) - '@radix-ui/react-dropdown-menu@2.1.4(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-dropdown-menu@2.1.4(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.4)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.4)(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@19.0.4)(react@19.0.0) - '@radix-ui/react-menu': 2.1.4(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.4)(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.7)(react@19.0.0) + '@radix-ui/react-context': 1.1.1(@types/react@19.0.7)(react@19.0.0) + '@radix-ui/react-id': 1.1.0(@types/react@19.0.7)(react@19.0.0) + '@radix-ui/react-menu': 2.1.4(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.7)(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 19.0.4 - '@types/react-dom': 19.0.2(@types/react@19.0.4) + '@types/react': 19.0.7 + '@types/react-dom': 19.0.3(@types/react@19.0.7) - '@radix-ui/react-focus-guards@1.1.1(@types/react@19.0.4)(react@19.0.0)': + '@radix-ui/react-focus-guards@1.1.1(@types/react@19.0.7)(react@19.0.0)': dependencies: react: 19.0.0 optionalDependencies: - '@types/react': 19.0.4 + '@types/react': 19.0.7 - '@radix-ui/react-focus-scope@1.1.1(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-focus-scope@1.1.1(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.4)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.4)(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.7)(react@19.0.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.7)(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 19.0.4 - '@types/react-dom': 19.0.2(@types/react@19.0.4) + '@types/react': 19.0.7 + '@types/react-dom': 19.0.3(@types/react@19.0.7) - '@radix-ui/react-id@1.1.0(@types/react@19.0.4)(react@19.0.0)': + '@radix-ui/react-id@1.1.0(@types/react@19.0.7)(react@19.0.0)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.4)(react@19.0.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.7)(react@19.0.0) react: 19.0.0 optionalDependencies: - '@types/react': 19.0.4 + '@types/react': 19.0.7 - '@radix-ui/react-menu@2.1.4(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-menu@2.1.4(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-collection': 1.1.1(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.4)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.4)(react@19.0.0) - '@radix-ui/react-direction': 1.1.0(@types/react@19.0.4)(react@19.0.0) - '@radix-ui/react-dismissable-layer': 1.1.3(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-focus-guards': 1.1.1(@types/react@19.0.4)(react@19.0.0) - '@radix-ui/react-focus-scope': 1.1.1(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@19.0.4)(react@19.0.0) - '@radix-ui/react-popper': 1.2.1(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-roving-focus': 1.1.1(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-slot': 1.1.1(@types/react@19.0.4)(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.4)(react@19.0.0) + '@radix-ui/react-collection': 1.1.1(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.7)(react@19.0.0) + '@radix-ui/react-context': 1.1.1(@types/react@19.0.7)(react@19.0.0) + '@radix-ui/react-direction': 1.1.0(@types/react@19.0.7)(react@19.0.0) + '@radix-ui/react-dismissable-layer': 1.1.3(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-focus-guards': 1.1.1(@types/react@19.0.7)(react@19.0.0) + '@radix-ui/react-focus-scope': 1.1.1(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-id': 1.1.0(@types/react@19.0.7)(react@19.0.0) + '@radix-ui/react-popper': 1.2.1(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-roving-focus': 1.1.1(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-slot': 1.1.1(@types/react@19.0.7)(react@19.0.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.7)(react@19.0.0) aria-hidden: 1.2.4 react: 19.0.0 react-dom: 19.0.0(react@19.0.0) - react-remove-scroll: 2.6.2(@types/react@19.0.4)(react@19.0.0) + react-remove-scroll: 2.6.2(@types/react@19.0.7)(react@19.0.0) optionalDependencies: - '@types/react': 19.0.4 - '@types/react-dom': 19.0.2(@types/react@19.0.4) + '@types/react': 19.0.7 + '@types/react-dom': 19.0.3(@types/react@19.0.7) - '@radix-ui/react-popper@1.2.1(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-popper@1.2.1(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@floating-ui/react-dom': 2.1.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-arrow': 1.1.1(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.4)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.4)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.4)(react@19.0.0) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.4)(react@19.0.0) - '@radix-ui/react-use-rect': 1.1.0(@types/react@19.0.4)(react@19.0.0) - '@radix-ui/react-use-size': 1.1.0(@types/react@19.0.4)(react@19.0.0) + '@radix-ui/react-arrow': 1.1.1(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.7)(react@19.0.0) + '@radix-ui/react-context': 1.1.1(@types/react@19.0.7)(react@19.0.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.7)(react@19.0.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.7)(react@19.0.0) + '@radix-ui/react-use-rect': 1.1.0(@types/react@19.0.7)(react@19.0.0) + '@radix-ui/react-use-size': 1.1.0(@types/react@19.0.7)(react@19.0.0) '@radix-ui/rect': 1.1.0 react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 19.0.4 - '@types/react-dom': 19.0.2(@types/react@19.0.4) + '@types/react': 19.0.7 + '@types/react-dom': 19.0.3(@types/react@19.0.7) - '@radix-ui/react-portal@1.1.3(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-portal@1.1.3(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.4)(react@19.0.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.7)(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 19.0.4 - '@types/react-dom': 19.0.2(@types/react@19.0.4) + '@types/react': 19.0.7 + '@types/react-dom': 19.0.3(@types/react@19.0.7) - '@radix-ui/react-presence@1.1.2(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-presence@1.1.2(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.4)(react@19.0.0) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.4)(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.7)(react@19.0.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.7)(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 19.0.4 - '@types/react-dom': 19.0.2(@types/react@19.0.4) + '@types/react': 19.0.7 + '@types/react-dom': 19.0.3(@types/react@19.0.7) - '@radix-ui/react-primitive@2.0.0(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-primitive@2.0.0(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: - '@radix-ui/react-slot': 1.1.0(@types/react@19.0.4)(react@19.0.0) + '@radix-ui/react-slot': 1.1.0(@types/react@19.0.7)(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 19.0.4 - '@types/react-dom': 19.0.2(@types/react@19.0.4) + '@types/react': 19.0.7 + '@types/react-dom': 19.0.3(@types/react@19.0.7) - '@radix-ui/react-primitive@2.0.1(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-primitive@2.0.1(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: - '@radix-ui/react-slot': 1.1.1(@types/react@19.0.4)(react@19.0.0) + '@radix-ui/react-slot': 1.1.1(@types/react@19.0.7)(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 19.0.4 - '@types/react-dom': 19.0.2(@types/react@19.0.4) + '@types/react': 19.0.7 + '@types/react-dom': 19.0.3(@types/react@19.0.7) - '@radix-ui/react-roving-focus@1.1.1(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-roving-focus@1.1.1(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-collection': 1.1.1(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.4)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.4)(react@19.0.0) - '@radix-ui/react-direction': 1.1.0(@types/react@19.0.4)(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@19.0.4)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.4)(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.4)(react@19.0.0) + '@radix-ui/react-collection': 1.1.1(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.7)(react@19.0.0) + '@radix-ui/react-context': 1.1.1(@types/react@19.0.7)(react@19.0.0) + '@radix-ui/react-direction': 1.1.0(@types/react@19.0.7)(react@19.0.0) + '@radix-ui/react-id': 1.1.0(@types/react@19.0.7)(react@19.0.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.7)(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.7)(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 19.0.4 - '@types/react-dom': 19.0.2(@types/react@19.0.4) + '@types/react': 19.0.7 + '@types/react-dom': 19.0.3(@types/react@19.0.7) - '@radix-ui/react-slot@1.1.0(@types/react@19.0.4)(react@19.0.0)': + '@radix-ui/react-slot@1.1.0(@types/react@19.0.7)(react@19.0.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.0(@types/react@19.0.4)(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.0(@types/react@19.0.7)(react@19.0.0) react: 19.0.0 optionalDependencies: - '@types/react': 19.0.4 + '@types/react': 19.0.7 - '@radix-ui/react-slot@1.1.1(@types/react@19.0.4)(react@19.0.0)': + '@radix-ui/react-slot@1.1.1(@types/react@19.0.7)(react@19.0.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.4)(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.7)(react@19.0.0) react: 19.0.0 optionalDependencies: - '@types/react': 19.0.4 + '@types/react': 19.0.7 - '@radix-ui/react-use-callback-ref@1.1.0(@types/react@19.0.4)(react@19.0.0)': + '@radix-ui/react-use-callback-ref@1.1.0(@types/react@19.0.7)(react@19.0.0)': dependencies: react: 19.0.0 optionalDependencies: - '@types/react': 19.0.4 + '@types/react': 19.0.7 - '@radix-ui/react-use-controllable-state@1.1.0(@types/react@19.0.4)(react@19.0.0)': + '@radix-ui/react-use-controllable-state@1.1.0(@types/react@19.0.7)(react@19.0.0)': dependencies: - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.4)(react@19.0.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.7)(react@19.0.0) react: 19.0.0 optionalDependencies: - '@types/react': 19.0.4 + '@types/react': 19.0.7 - '@radix-ui/react-use-escape-keydown@1.1.0(@types/react@19.0.4)(react@19.0.0)': + '@radix-ui/react-use-escape-keydown@1.1.0(@types/react@19.0.7)(react@19.0.0)': dependencies: - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.4)(react@19.0.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.7)(react@19.0.0) react: 19.0.0 optionalDependencies: - '@types/react': 19.0.4 + '@types/react': 19.0.7 - '@radix-ui/react-use-layout-effect@1.1.0(@types/react@19.0.4)(react@19.0.0)': + '@radix-ui/react-use-layout-effect@1.1.0(@types/react@19.0.7)(react@19.0.0)': dependencies: react: 19.0.0 optionalDependencies: - '@types/react': 19.0.4 + '@types/react': 19.0.7 - '@radix-ui/react-use-rect@1.1.0(@types/react@19.0.4)(react@19.0.0)': + '@radix-ui/react-use-rect@1.1.0(@types/react@19.0.7)(react@19.0.0)': dependencies: '@radix-ui/rect': 1.1.0 react: 19.0.0 optionalDependencies: - '@types/react': 19.0.4 + '@types/react': 19.0.7 - '@radix-ui/react-use-size@1.1.0(@types/react@19.0.4)(react@19.0.0)': + '@radix-ui/react-use-size@1.1.0(@types/react@19.0.7)(react@19.0.0)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.4)(react@19.0.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.7)(react@19.0.0) react: 19.0.0 optionalDependencies: - '@types/react': 19.0.4 + '@types/react': 19.0.7 - '@radix-ui/react-visually-hidden@1.1.1(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-visually-hidden@1.1.1(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 19.0.4 - '@types/react-dom': 19.0.2(@types/react@19.0.4) + '@types/react': 19.0.7 + '@types/react-dom': 19.0.3(@types/react@19.0.7) '@radix-ui/rect@1.1.0': {} @@ -6263,15 +6193,15 @@ snapshots: lodash: 4.17.21 redent: 3.0.0 - '@testing-library/react@16.1.0(@testing-library/dom@10.1.0)(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@testing-library/react@16.2.0(@testing-library/dom@10.1.0)(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@babel/runtime': 7.24.5 '@testing-library/dom': 10.1.0 react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 19.0.4 - '@types/react-dom': 19.0.2(@types/react@19.0.4) + '@types/react': 19.0.7 + '@types/react-dom': 19.0.3(@types/react@19.0.7) '@tootallnate/once@2.0.0': {} @@ -6306,7 +6236,7 @@ snapshots: '@types/graceful-fs@4.1.9': dependencies: - '@types/node': 22.10.5 + '@types/node': 22.10.7 '@types/istanbul-lib-coverage@2.0.6': {} @@ -6325,7 +6255,7 @@ snapshots: '@types/jsdom@20.0.1': dependencies: - '@types/node': 22.10.5 + '@types/node': 22.10.7 '@types/tough-cookie': 4.0.5 parse5: 7.1.2 @@ -6333,21 +6263,21 @@ snapshots: '@types/json5@0.0.29': {} - '@types/node@22.10.5': + '@types/node@22.10.7': dependencies: undici-types: 6.20.0 '@types/pg@8.11.10': dependencies: - '@types/node': 22.10.5 + '@types/node': 22.10.7 pg-protocol: 1.6.1 pg-types: 4.0.2 - '@types/react-dom@19.0.2(@types/react@19.0.4)': + '@types/react-dom@19.0.3(@types/react@19.0.7)': dependencies: - '@types/react': 19.0.4 + '@types/react': 19.0.7 - '@types/react@19.0.4': + '@types/react@19.0.7': dependencies: csstype: 3.1.3 @@ -6459,17 +6389,16 @@ snapshots: dependencies: crypto-js: 4.2.0 - '@vercel/analytics@1.4.1(next@15.1.4(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(svelte@4.2.17)(vue@3.4.27(typescript@5.7.3))': + '@vercel/analytics@1.4.1(next@15.1.5(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(svelte@4.2.17)(vue@3.4.27(typescript@5.7.3))': optionalDependencies: - next: 15.1.4(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + next: 15.1.5(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) react: 19.0.0 svelte: 4.2.17 vue: 3.4.27(typescript@5.7.3) - '@vercel/blob@0.27.0': + '@vercel/blob@0.27.1': dependencies: async-retry: 1.3.3 - bytes: 3.1.2 is-buffer: 2.0.5 is-node-process: 1.2.0 throttleit: 2.1.0 @@ -6479,9 +6408,9 @@ snapshots: dependencies: '@upstash/redis': 1.34.3 - '@vercel/speed-insights@1.1.0(next@15.1.4(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(svelte@4.2.17)(vue@3.4.27(typescript@5.7.3))': + '@vercel/speed-insights@1.1.0(next@15.1.5(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(svelte@4.2.17)(vue@3.4.27(typescript@5.7.3))': optionalDependencies: - next: 15.1.4(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + next: 15.1.5(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) react: 19.0.0 svelte: 4.2.17 vue: 3.4.27(typescript@5.7.3) @@ -6510,7 +6439,7 @@ snapshots: '@vue/shared': 3.4.27 estree-walker: 2.0.2 magic-string: 0.30.10 - postcss: 8.4.49 + postcss: 8.5.1 source-map-js: 1.2.1 optional: true @@ -6571,15 +6500,14 @@ snapshots: transitivePeerDependencies: - supports-color - ai@4.0.33(react@19.0.0)(zod@3.23.8): + ai@4.1.0(react@19.0.0)(zod@3.23.8): dependencies: '@ai-sdk/provider': 1.0.4 - '@ai-sdk/provider-utils': 2.0.7(zod@3.23.8) - '@ai-sdk/react': 1.0.9(react@19.0.0)(zod@3.23.8) - '@ai-sdk/ui-utils': 1.0.8(zod@3.23.8) + '@ai-sdk/provider-utils': 2.1.0(zod@3.23.8) + '@ai-sdk/react': 1.1.0(react@19.0.0)(zod@3.23.8) + '@ai-sdk/ui-utils': 1.1.0(zod@3.23.8) '@opentelemetry/api': 1.9.0 jsondiffpatch: 0.6.0 - zod-to-json-schema: 3.24.1(zod@3.23.8) optionalDependencies: react: 19.0.0 zod: 3.23.8 @@ -6708,14 +6636,14 @@ snapshots: asynckit@0.4.0: {} - autoprefixer@10.4.20(postcss@8.4.49): + autoprefixer@10.4.20(postcss@8.5.1): dependencies: browserslist: 4.23.3 caniuse-lite: 1.0.30001651 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.0.1 - postcss: 8.4.49 + postcss: 8.5.1 postcss-value-parser: 4.2.0 available-typed-arrays@1.0.7: @@ -6825,8 +6753,6 @@ snapshots: dependencies: streamsearch: 1.1.0 - bytes@3.1.2: {} - call-bind-apply-helpers@1.0.1: dependencies: es-errors: 1.3.0 @@ -6909,11 +6835,11 @@ snapshots: clsx@2.1.1: {} - cmdk@1.0.4(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + cmdk@1.0.4(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0): dependencies: - '@radix-ui/react-dialog': 1.1.4(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@19.0.4)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.0(@types/react-dom@19.0.2(@types/react@19.0.4))(@types/react@19.0.4)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-dialog': 1.1.4(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-id': 1.1.0(@types/react@19.0.7)(react@19.0.0) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) use-sync-external-store: 1.2.2(react@19.0.0) @@ -6970,13 +6896,13 @@ snapshots: cookie@0.7.1: {} - create-jest@29.7.0(@types/node@22.10.5): + create-jest@29.7.0(@types/node@22.10.7): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@22.10.5) + jest-config: 29.7.0(@types/node@22.10.7) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -7269,9 +7195,9 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-config-next@15.1.4(eslint@9.18.0(jiti@1.21.7))(typescript@5.7.3): + eslint-config-next@15.1.5(eslint@9.18.0(jiti@1.21.7))(typescript@5.7.3): dependencies: - '@next/eslint-plugin-next': 15.1.4 + '@next/eslint-plugin-next': 15.1.5 '@rushstack/eslint-patch': 1.10.3 '@typescript-eslint/eslint-plugin': 8.19.0(@typescript-eslint/parser@8.19.0(eslint@9.18.0(jiti@1.21.7))(typescript@5.7.3))(eslint@9.18.0(jiti@1.21.7))(typescript@5.7.3) '@typescript-eslint/parser': 8.19.0(eslint@9.18.0(jiti@1.21.7))(typescript@5.7.3) @@ -7582,10 +7508,10 @@ snapshots: fraction.js@4.3.7: {} - framer-motion@11.17.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + framer-motion@11.18.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0): dependencies: - motion-dom: 11.16.4 - motion-utils: 11.16.0 + motion-dom: 11.18.1 + motion-utils: 11.18.1 tslib: 2.8.1 optionalDependencies: react: 19.0.0 @@ -7974,7 +7900,7 @@ snapshots: '@jest/expect': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.10.5 + '@types/node': 22.10.7 chalk: 4.1.2 co: 4.6.0 dedent: 1.5.3 @@ -7994,16 +7920,16 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@29.7.0(@types/node@22.10.5): + jest-cli@29.7.0(@types/node@22.10.7): dependencies: '@jest/core': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@22.10.5) + create-jest: 29.7.0(@types/node@22.10.7) exit: 0.1.2 import-local: 3.1.0 - jest-config: 29.7.0(@types/node@22.10.5) + jest-config: 29.7.0(@types/node@22.10.7) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -8013,7 +7939,7 @@ snapshots: - supports-color - ts-node - jest-config@29.7.0(@types/node@22.10.5): + jest-config@29.7.0(@types/node@22.10.7): dependencies: '@babel/core': 7.24.5 '@jest/test-sequencer': 29.7.0 @@ -8038,7 +7964,7 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 22.10.5 + '@types/node': 22.10.7 transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -8068,7 +7994,7 @@ snapshots: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 '@types/jsdom': 20.0.1 - '@types/node': 22.10.5 + '@types/node': 22.10.7 jest-mock: 29.7.0 jest-util: 29.7.0 jsdom: 20.0.3 @@ -8082,7 +8008,7 @@ snapshots: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.10.5 + '@types/node': 22.10.7 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -8092,7 +8018,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.9 - '@types/node': 22.10.5 + '@types/node': 22.10.7 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -8131,7 +8057,7 @@ snapshots: jest-mock@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 22.10.5 + '@types/node': 22.10.7 jest-util: 29.7.0 jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): @@ -8166,7 +8092,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.10.5 + '@types/node': 22.10.7 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -8194,7 +8120,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.10.5 + '@types/node': 22.10.7 chalk: 4.1.2 cjs-module-lexer: 1.3.1 collect-v8-coverage: 1.0.2 @@ -8240,7 +8166,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 22.10.5 + '@types/node': 22.10.7 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -8259,7 +8185,7 @@ snapshots: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.10.5 + '@types/node': 22.10.7 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -8268,17 +8194,17 @@ snapshots: jest-worker@29.7.0: dependencies: - '@types/node': 22.10.5 + '@types/node': 22.10.7 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 - jest@29.7.0(@types/node@22.10.5): + jest@29.7.0(@types/node@22.10.7): dependencies: '@jest/core': 29.7.0 '@jest/types': 29.6.3 import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@22.10.5) + jest-cli: 29.7.0(@types/node@22.10.7) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -8472,11 +8398,11 @@ snapshots: minipass@7.1.1: {} - motion-dom@11.16.4: + motion-dom@11.18.1: dependencies: - motion-utils: 11.16.0 + motion-utils: 11.18.1 - motion-utils@11.16.0: {} + motion-utils@11.18.1: {} mrmime@2.0.0: {} @@ -8490,18 +8416,16 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 - nanoid@3.3.7: {} - nanoid@3.3.8: {} nanoid@5.0.9: {} natural-compare@1.4.0: {} - next-auth@5.0.0-beta.25(next@15.1.4(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0): + next-auth@5.0.0-beta.25(next@15.1.5(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0): dependencies: '@auth/core': 0.37.2 - next: 15.1.4(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + next: 15.1.5(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) react: 19.0.0 next-themes@0.4.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0): @@ -8509,9 +8433,9 @@ snapshots: react: 19.0.0 react-dom: 19.0.0(react@19.0.0) - next@15.1.4(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + next@15.1.5(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0): dependencies: - '@next/env': 15.1.4 + '@next/env': 15.1.5 '@swc/counter': 0.1.3 '@swc/helpers': 0.5.15 busboy: 1.6.0 @@ -8521,14 +8445,14 @@ snapshots: react-dom: 19.0.0(react@19.0.0) styled-jsx: 5.1.6(@babel/core@7.24.5)(react@19.0.0) optionalDependencies: - '@next/swc-darwin-arm64': 15.1.4 - '@next/swc-darwin-x64': 15.1.4 - '@next/swc-linux-arm64-gnu': 15.1.4 - '@next/swc-linux-arm64-musl': 15.1.4 - '@next/swc-linux-x64-gnu': 15.1.4 - '@next/swc-linux-x64-musl': 15.1.4 - '@next/swc-win32-arm64-msvc': 15.1.4 - '@next/swc-win32-x64-msvc': 15.1.4 + '@next/swc-darwin-arm64': 15.1.5 + '@next/swc-darwin-x64': 15.1.5 + '@next/swc-linux-arm64-gnu': 15.1.5 + '@next/swc-linux-arm64-musl': 15.1.5 + '@next/swc-linux-x64-gnu': 15.1.5 + '@next/swc-linux-x64-musl': 15.1.5 + '@next/swc-win32-arm64-msvc': 15.1.5 + '@next/swc-win32-x64-msvc': 15.1.5 '@opentelemetry/api': 1.9.0 sharp: 0.33.5 transitivePeerDependencies: @@ -8736,28 +8660,28 @@ snapshots: possible-typed-array-names@1.0.0: {} - postcss-import@15.1.0(postcss@8.4.49): + postcss-import@15.1.0(postcss@8.5.1): dependencies: - postcss: 8.4.49 + postcss: 8.5.1 postcss-value-parser: 4.2.0 read-cache: 1.0.0 resolve: 1.22.8 - postcss-js@4.0.1(postcss@8.4.49): + postcss-js@4.0.1(postcss@8.5.1): dependencies: camelcase-css: 2.0.1 - postcss: 8.4.49 + postcss: 8.5.1 - postcss-load-config@4.0.2(postcss@8.4.49): + postcss-load-config@4.0.2(postcss@8.5.1): dependencies: lilconfig: 3.1.3 yaml: 2.4.2 optionalDependencies: - postcss: 8.4.49 + postcss: 8.5.1 - postcss-nested@6.2.0(postcss@8.4.49): + postcss-nested@6.2.0(postcss@8.5.1): dependencies: - postcss: 8.4.49 + postcss: 8.5.1 postcss-selector-parser: 6.1.2 postcss-selector-parser@6.1.2: @@ -8773,9 +8697,9 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - postcss@8.4.49: + postcss@8.5.1: dependencies: - nanoid: 3.3.7 + nanoid: 3.3.8 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -8862,32 +8786,32 @@ snapshots: react-is@18.3.1: {} - react-remove-scroll-bar@2.3.8(@types/react@19.0.4)(react@19.0.0): + react-remove-scroll-bar@2.3.8(@types/react@19.0.7)(react@19.0.0): dependencies: react: 19.0.0 - react-style-singleton: 2.2.3(@types/react@19.0.4)(react@19.0.0) + react-style-singleton: 2.2.3(@types/react@19.0.7)(react@19.0.0) tslib: 2.8.1 optionalDependencies: - '@types/react': 19.0.4 + '@types/react': 19.0.7 - react-remove-scroll@2.6.2(@types/react@19.0.4)(react@19.0.0): + react-remove-scroll@2.6.2(@types/react@19.0.7)(react@19.0.0): dependencies: react: 19.0.0 - react-remove-scroll-bar: 2.3.8(@types/react@19.0.4)(react@19.0.0) - react-style-singleton: 2.2.3(@types/react@19.0.4)(react@19.0.0) + react-remove-scroll-bar: 2.3.8(@types/react@19.0.7)(react@19.0.0) + react-style-singleton: 2.2.3(@types/react@19.0.7)(react@19.0.0) tslib: 2.8.1 - use-callback-ref: 1.3.3(@types/react@19.0.4)(react@19.0.0) - use-sidecar: 1.1.2(@types/react@19.0.4)(react@19.0.0) + use-callback-ref: 1.3.3(@types/react@19.0.7)(react@19.0.0) + use-sidecar: 1.1.2(@types/react@19.0.7)(react@19.0.0) optionalDependencies: - '@types/react': 19.0.4 + '@types/react': 19.0.7 - react-style-singleton@2.2.3(@types/react@19.0.4)(react@19.0.0): + react-style-singleton@2.2.3(@types/react@19.0.7)(react@19.0.0): dependencies: get-nonce: 1.0.1 react: 19.0.0 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.0.4 + '@types/react': 19.0.7 react@19.0.0: {} @@ -8983,7 +8907,7 @@ snapshots: htmlparser2: 8.0.2 is-plain-object: 5.0.0 parse-srcset: 1.0.2 - postcss: 8.4.49 + postcss: 8.5.1 sax@1.2.4: {} @@ -9095,7 +9019,7 @@ snapshots: slash@3.0.0: {} - sonner@1.7.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + sonner@1.7.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0): dependencies: react: 19.0.0 react-dom: 19.0.0(react@19.0.0) @@ -9280,11 +9204,11 @@ snapshots: normalize-path: 3.0.0 object-hash: 3.0.0 picocolors: 1.1.1 - postcss: 8.4.49 - postcss-import: 15.1.0(postcss@8.4.49) - postcss-js: 4.0.1(postcss@8.4.49) - postcss-load-config: 4.0.2(postcss@8.4.49) - postcss-nested: 6.2.0(postcss@8.4.49) + postcss: 8.5.1 + postcss-import: 15.1.0(postcss@8.5.1) + postcss-js: 4.0.1(postcss@8.5.1) + postcss-load-config: 4.0.2(postcss@8.5.1) + postcss-nested: 6.2.0(postcss@8.5.1) postcss-selector-parser: 6.1.2 resolve: 1.22.8 sucrase: 3.35.0 @@ -9430,24 +9354,24 @@ snapshots: querystringify: 2.2.0 requires-port: 1.0.0 - use-callback-ref@1.3.3(@types/react@19.0.4)(react@19.0.0): + use-callback-ref@1.3.3(@types/react@19.0.7)(react@19.0.0): dependencies: react: 19.0.0 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.0.4 + '@types/react': 19.0.7 use-debounce@10.0.4(react@19.0.0): dependencies: react: 19.0.0 - use-sidecar@1.1.2(@types/react@19.0.4)(react@19.0.0): + use-sidecar@1.1.2(@types/react@19.0.7)(react@19.0.0): dependencies: detect-node-es: 1.1.0 react: 19.0.0 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.0.4 + '@types/react': 19.0.7 use-sync-external-store@1.2.2(react@19.0.0): dependencies: From 3875c1ab1e29d4e21239678677a3159836791083 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sat, 18 Jan 2025 22:28:53 -0600 Subject: [PATCH 11/44] Move next.config to TypeScript --- next.config.js => next.config.ts | 36 +++++++++++++++++++------------- 1 file changed, 22 insertions(+), 14 deletions(-) rename next.config.js => next.config.ts (51%) diff --git a/next.config.js b/next.config.ts similarity index 51% rename from next.config.js rename to next.config.ts index 893b3ae7..62a91b49 100644 --- a/next.config.js +++ b/next.config.ts @@ -1,3 +1,7 @@ +/* eslint-disable @typescript-eslint/no-require-imports */ +import type { NextConfig } from 'next'; +import { RemotePattern } from 'next/dist/shared/lib/image-config'; + const VERCEL_BLOB_STORE_ID = process.env.BLOB_READ_WRITE_TOKEN?.match( /^vercel_blob_rw_([a-z0-9]+)_[a-z0-9]+$/i, )?.[1].toLowerCase(); @@ -16,23 +20,27 @@ const HOSTNAME_AWS_S3 = ? `${process.env.NEXT_PUBLIC_AWS_S3_BUCKET}.s3.${process.env.NEXT_PUBLIC_AWS_S3_REGION}.amazonaws.com` : undefined; -const createRemotePattern = (hostname) => hostname - ? { - protocol: 'https', - hostname, - port: '', - pathname: '/**', - } - : []; +const generateRemotePattern = (hostname: string) => + ({ protocol: 'https', hostname, port: '', pathname: '/**' } as const); -/** @type {import('next').NextConfig} */ -const nextConfig = { +const generateRemotePatterns = () => { + const remotePatterns: RemotePattern[] = []; + if (HOSTNAME_VERCEL_BLOB) { + remotePatterns.push(generateRemotePattern(HOSTNAME_VERCEL_BLOB)); + } + if (HOSTNAME_CLOUDFLARE_R2) { + remotePatterns.push(generateRemotePattern(HOSTNAME_CLOUDFLARE_R2)); + } + if (HOSTNAME_AWS_S3) { + remotePatterns.push(generateRemotePattern(HOSTNAME_AWS_S3)); + } + return remotePatterns; +}; + +const nextConfig: NextConfig = { images: { imageSizes: [200], - remotePatterns: [] - .concat(createRemotePattern(HOSTNAME_VERCEL_BLOB)) - .concat(createRemotePattern(HOSTNAME_CLOUDFLARE_R2)) - .concat(createRemotePattern(HOSTNAME_AWS_S3)), + remotePatterns: generateRemotePatterns(), minimumCacheTTL: 31536000, }, }; From 18b33389b516c61b95633c1b720a9ddce5a200a6 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sat, 18 Jan 2025 22:41:49 -0600 Subject: [PATCH 12/44] Dismiss share modals when clicking links --- src/share/ShareModal.tsx | 3 +++ src/utility/useOnPathChange.ts | 14 ++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 src/utility/useOnPathChange.ts diff --git a/src/share/ShareModal.tsx b/src/share/ShareModal.tsx index 67ee3953..97d42c92 100644 --- a/src/share/ShareModal.tsx +++ b/src/share/ShareModal.tsx @@ -11,6 +11,7 @@ import { PiXLogo } from 'react-icons/pi'; import { SHOW_SOCIAL } from '@/site/config'; import { generateXPostText } from '@/utility/social'; import { useAppState } from '@/state/AppState'; +import useOnPathChange from '@/utility/useOnPathChange'; export default function ShareModal({ title, @@ -44,6 +45,8 @@ export default function ShareModal({ {icon}
; + useOnPathChange(() => setShareModalProps?.(undefined)); + return ( setShareModalProps?.(undefined)}>
diff --git a/src/utility/useOnPathChange.ts b/src/utility/useOnPathChange.ts new file mode 100644 index 00000000..34aa6875 --- /dev/null +++ b/src/utility/useOnPathChange.ts @@ -0,0 +1,14 @@ +import { usePathname } from 'next/navigation'; +import { useEffect, useRef } from 'react'; + +export default function useOnPathChange(onPathChange: () => void) { + const path = usePathname(); + + const initialPath = useRef(path); + + useEffect(() => { + if (initialPath.current !== path) { + onPathChange(); + } + }, [path, onPathChange]); +} From 19a7c59c9a56a939554f5ed3f2e8ce1e765be987 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sun, 19 Jan 2025 12:38:02 -0600 Subject: [PATCH 13/44] Create link component with loader status --- src/app/admin/photos/page.tsx | 3 + src/components/LinkWithStatus.tsx | 111 ++++++++++++++++++++++++++++++ src/components/SwitcherItem.tsx | 25 ++++--- src/photo/PhotoEscapeHandler.tsx | 2 +- 4 files changed, 131 insertions(+), 10 deletions(-) create mode 100644 src/components/LinkWithStatus.tsx diff --git a/src/app/admin/photos/page.tsx b/src/app/admin/photos/page.tsx index d8ef3d11..280a8e4b 100644 --- a/src/app/admin/photos/page.tsx +++ b/src/app/admin/photos/page.tsx @@ -6,6 +6,7 @@ import AdminPhotosClient from '@/admin/AdminPhotosClient'; import { revalidatePath } from 'next/cache'; import { cookies } from 'next/headers'; import { TIMEZONE_COOKIE_NAME } from '@/utility/timezone'; +import sleep from '@/utility/sleep'; export const maxDuration = 60; @@ -15,6 +16,8 @@ const INFINITE_SCROLL_INITIAL_ADMIN_PHOTOS = 25; const INFINITE_SCROLL_MULTIPLE_ADMIN_PHOTOS = 50; export default async function AdminPhotosPage() { + await sleep(3000); + const timezone = (await cookies()).get(TIMEZONE_COOKIE_NAME)?.value; const [ diff --git a/src/components/LinkWithStatus.tsx b/src/components/LinkWithStatus.tsx new file mode 100644 index 00000000..7ea67ca9 --- /dev/null +++ b/src/components/LinkWithStatus.tsx @@ -0,0 +1,111 @@ +'use client'; + +import { + ComponentProps, + ReactNode, + useCallback, + useEffect, + useRef, + useState, +} from 'react'; +import Link from 'next/link'; +import { usePathname } from 'next/navigation'; +import clsx from 'clsx/lite'; + +// Avoid showing spinner for too short a time +const FLICKER_THRESHOLD = 400; +// Clear loading status after 10 seconds of inactivity +const MAX_LOADING_DURATION = 10_000; + +export type LinkWithStatusProps = ComponentProps & { + loader?: ReactNode +} + +export default function LinkWithStatus({ + loader, + href, + className, + onClick, + children, + ...props +}: LinkWithStatusProps) { + const path = usePathname(); + + const [isLoading, setIsLoading] = useState(false); + + const isLoadingStartTime = useRef(undefined); + + const startLoadingTimeout = useRef(undefined); + const stopLoadingTimeout = useRef(undefined); + const maxLoadingTimeout = useRef(undefined); + + const clearTimeouts = useCallback(() => + [startLoadingTimeout, stopLoadingTimeout, maxLoadingTimeout] + .forEach(timeout => { + if (timeout.current) { clearTimeout(timeout.current); } + }), + []); + + const isVisitingLinkHref = path === href; + + useEffect(() => { + if (isVisitingLinkHref) { + clearTimeouts(); + const loadingDuration = isLoadingStartTime.current + ? Date.now() - isLoadingStartTime.current + : 0; + if (loadingDuration < FLICKER_THRESHOLD) { + stopLoadingTimeout.current = setTimeout( + () => { setIsLoading(false); }, + FLICKER_THRESHOLD - loadingDuration, + ); + } else { + setIsLoading(false); + } + } + }, [isVisitingLinkHref, clearTimeouts]); + + // Clear timeouts when unmounting + useEffect(() => () => clearTimeouts(), [clearTimeouts]); + + return { + const isOpeningNewTab = e.metaKey || e.ctrlKey; + if (!isVisitingLinkHref && !isOpeningNewTab) { + startLoadingTimeout.current = setTimeout( + () => { + isLoadingStartTime.current = Date.now(); + setIsLoading(true); + }, + FLICKER_THRESHOLD, + ); + maxLoadingTimeout.current = setTimeout( + () => { setIsLoading(false); }, + MAX_LOADING_DURATION, + ); + } + onClick?.(e); + }} + > + + {children} + + {isLoading && loader && + {loader} + } + ; +} diff --git a/src/components/SwitcherItem.tsx b/src/components/SwitcherItem.tsx index 496db457..d6160bda 100644 --- a/src/components/SwitcherItem.tsx +++ b/src/components/SwitcherItem.tsx @@ -1,7 +1,8 @@ -import Link from 'next/link'; import { clsx } from 'clsx/lite'; import { SHOULD_PREFETCH_ALL_LINKS } from '@/site/config'; -import { JSX } from 'react'; +import { JSX, ReactNode } from 'react'; +import LinkWithStatus from './LinkWithStatus'; +import Spinner from './Spinner'; export default function SwitcherItem({ icon, @@ -36,17 +37,23 @@ export default function SwitcherItem({ : 'hover:text-gray-700 dark:hover:text-gray-400', ); - const renderIcon = () => noPadding - ? icon + const renderContent = (content: ReactNode) => noPadding + ? content :
- {icon} + {content}
; return ( href - ? - {renderIcon()} - - :
{renderIcon()}
+ ? , + }}> + {renderContent(icon)} + + :
{renderContent(icon)}
); }; diff --git a/src/photo/PhotoEscapeHandler.tsx b/src/photo/PhotoEscapeHandler.tsx index 3500ce00..b8da15c9 100644 --- a/src/photo/PhotoEscapeHandler.tsx +++ b/src/photo/PhotoEscapeHandler.tsx @@ -19,7 +19,7 @@ export default function PhotoEscapeHandler() { useEffect(() => { if (shouldRespondToKeyboardCommands) { const onKeyUp = (e: KeyboardEvent) => { - if (e.key.toUpperCase() === 'ESCAPE' && escapePath) { + if (e.key?.toUpperCase() === 'ESCAPE' && escapePath) { router.push(escapePath, { scroll: false }); }; }; From 2b75958f3e259d17f927011474dc1f2d293485be Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sun, 19 Jan 2025 12:42:57 -0600 Subject: [PATCH 14/44] Remove debug timeout from /admin/photos --- src/app/admin/photos/page.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/app/admin/photos/page.tsx b/src/app/admin/photos/page.tsx index 280a8e4b..d8ef3d11 100644 --- a/src/app/admin/photos/page.tsx +++ b/src/app/admin/photos/page.tsx @@ -6,7 +6,6 @@ import AdminPhotosClient from '@/admin/AdminPhotosClient'; import { revalidatePath } from 'next/cache'; import { cookies } from 'next/headers'; import { TIMEZONE_COOKIE_NAME } from '@/utility/timezone'; -import sleep from '@/utility/sleep'; export const maxDuration = 60; @@ -16,8 +15,6 @@ const INFINITE_SCROLL_INITIAL_ADMIN_PHOTOS = 25; const INFINITE_SCROLL_MULTIPLE_ADMIN_PHOTOS = 50; export default async function AdminPhotosPage() { - await sleep(3000); - const timezone = (await cookies()).get(TIMEZONE_COOKIE_NAME)?.value; const [ From a96abdb6f00cb18d7bd1f5cec101f6117aea22ca Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sun, 19 Jan 2025 12:49:05 -0600 Subject: [PATCH 15/44] Reorder LinkWithStatus classes --- src/components/LinkWithStatus.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/components/LinkWithStatus.tsx b/src/components/LinkWithStatus.tsx index 7ea67ca9..fe049330 100644 --- a/src/components/LinkWithStatus.tsx +++ b/src/components/LinkWithStatus.tsx @@ -71,10 +71,7 @@ export default function LinkWithStatus({ return { const isOpeningNewTab = e.metaKey || e.ctrlKey; if (!isVisitingLinkHref && !isOpeningNewTab) { From 00b058c8126c7c6611706eecfc75e26711190bf5 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sun, 19 Jan 2025 15:09:24 -0600 Subject: [PATCH 16/44] Add loading status to admin sub-menu --- src/admin/AdminNavClient.tsx | 24 ++++++++++++++---------- src/components/LinkWithStatus.tsx | 29 ++++++++++++++++++++--------- src/components/SwitcherItem.tsx | 2 +- 3 files changed, 35 insertions(+), 20 deletions(-) diff --git a/src/admin/AdminNavClient.tsx b/src/admin/AdminNavClient.tsx index 98c08c64..eea8d51c 100644 --- a/src/admin/AdminNavClient.tsx +++ b/src/admin/AdminNavClient.tsx @@ -1,7 +1,9 @@ 'use client'; +import LinkWithStatus from '@/components/LinkWithStatus'; import Note from '@/components/Note'; import SiteGrid from '@/components/SiteGrid'; +import Spinner from '@/components/Spinner'; import { PATH_ADMIN_CONFIGURATION, checkPathPrefix, @@ -11,7 +13,6 @@ import { import { useAppState } from '@/state/AppState'; import { clsx } from 'clsx/lite'; import { differenceInMinutes } from 'date-fns'; -import Link from 'next/link'; import { usePathname } from 'next/navigation'; import { useEffect, useMemo, useState } from 'react'; import { BiCog } from 'react-icons/bi'; @@ -60,40 +61,43 @@ export default function AdminNavClient({ contentMain={
{items.map(({ label, href, count }) => - {label} {count > 0 && ({count})} - )} + )}
- } > - +
{shouldShowBanner && }> diff --git a/src/components/LinkWithStatus.tsx b/src/components/LinkWithStatus.tsx index fe049330..8b0876a3 100644 --- a/src/components/LinkWithStatus.tsx +++ b/src/components/LinkWithStatus.tsx @@ -14,15 +14,19 @@ import clsx from 'clsx/lite'; // Avoid showing spinner for too short a time const FLICKER_THRESHOLD = 400; -// Clear loading status after 10 seconds of inactivity -const MAX_LOADING_DURATION = 10_000; +// Clear loading status after long duration of inactivity +const MAX_LOADING_DURATION = 15_000; export type LinkWithStatusProps = ComponentProps & { - loader?: ReactNode + loadingElement?: ReactNode + loadingClassName?: string + contentClassName?: string } export default function LinkWithStatus({ - loader, + loadingElement, + loadingClassName, + contentClassName, href, className, onClick, @@ -71,7 +75,11 @@ export default function LinkWithStatus({ return { const isOpeningNewTab = e.metaKey || e.ctrlKey; if (!isVisitingLinkHref && !isOpeningNewTab) { @@ -92,17 +100,20 @@ export default function LinkWithStatus({ > {children} - {isLoading && loader && - {loader} + {loadingElement} } ; } diff --git a/src/components/SwitcherItem.tsx b/src/components/SwitcherItem.tsx index d6160bda..eb1b70be 100644 --- a/src/components/SwitcherItem.tsx +++ b/src/components/SwitcherItem.tsx @@ -50,7 +50,7 @@ export default function SwitcherItem({ href, className, prefetch, - loader: , + loadingElement: , }}> {renderContent(icon)} From 0d46158277ea7b77c55b25cb45fd78738d35b1b8 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sun, 19 Jan 2025 15:11:59 -0600 Subject: [PATCH 17/44] Tweak admin nav styles --- src/admin/AdminNavClient.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/admin/AdminNavClient.tsx b/src/admin/AdminNavClient.tsx index eea8d51c..240ab4e1 100644 --- a/src/admin/AdminNavClient.tsx +++ b/src/admin/AdminNavClient.tsx @@ -65,7 +65,7 @@ export default function AdminNavClient({ 'border-b border-gray-200 dark:border-gray-800', )}>
{items.map(({ label, href, count }) => @@ -77,7 +77,7 @@ export default function AdminNavClient({ 'px-1 py-0.5 rounded-md', )} loadingClassName="bg-dim" - contentClassName="flex gap-1" + contentClassName="flex gap-0.5" prefetch={false} > {label} From 87591a5c89c5852520a922ad4e2222fe074d7a6b Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sun, 19 Jan 2025 22:48:32 -0600 Subject: [PATCH 18/44] Add inner scroll for large sidebars on /grid --- src/photo/PhotoGridPage.tsx | 22 ++++++++++++++-------- src/photo/PhotoGridSidebar.tsx | 4 ++-- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/photo/PhotoGridPage.tsx b/src/photo/PhotoGridPage.tsx index 22640fcc..a69eba06 100644 --- a/src/photo/PhotoGridPage.tsx +++ b/src/photo/PhotoGridPage.tsx @@ -9,6 +9,7 @@ import PhotoGridSidebar from './PhotoGridSidebar'; import PhotoGridContainer from './PhotoGridContainer'; import { useEffect } from 'react'; import { useAppState } from '@/state/AppState'; +import clsx from 'clsx/lite'; export default function PhotoGridPage({ photos, @@ -35,14 +36,19 @@ export default function PhotoGridPage({ cacheKey={`page-${PATH_GRID}`} photos={photos} count={photosCount} - sidebar={
- -
} + sidebar={ +
+ +
} canSelect /> ); diff --git a/src/photo/PhotoGridSidebar.tsx b/src/photo/PhotoGridSidebar.tsx index af3a803d..ba93d5ff 100644 --- a/src/photo/PhotoGridSidebar.tsx +++ b/src/photo/PhotoGridSidebar.tsx @@ -44,7 +44,7 @@ export default function PhotoGridSidebar({ , [tags, hiddenPhotosCount]); return ( - <> +
{SITE_ABOUT && } - +
); } From 232ddde90940662fdddb197a4f1158185182e7a1 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sun, 19 Jan 2025 22:49:58 -0600 Subject: [PATCH 19/44] Remove 'experimental' from static checklist section --- src/site/SiteChecklistClient.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/site/SiteChecklistClient.tsx b/src/site/SiteChecklistClient.tsx index f4cda03f..6850ed12 100644 --- a/src/site/SiteChecklistClient.tsx +++ b/src/site/SiteChecklistClient.tsx @@ -475,10 +475,9 @@ export default function SiteChecklistClient({ title="Static optimization" status={isStaticallyOptimized} optional - experimental > Set environment variable to {'"1"'} to enable static optimization, - i.e., rendering pages and images at build time: + i.e., render pages and images at build time: {renderSubStatusWithEnvVar( arePhotosStaticallyOptimized ? 'checked' : 'optional', 'NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTOS', From 8ef80a9b16089723f9a70aabad3800aadd4131bd Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sun, 19 Jan 2025 23:18:01 -0600 Subject: [PATCH 20/44] Create performance configuration section --- README.md | 9 ++++-- src/site/SiteChecklistClient.tsx | 53 ++++++++++++++++++-------------- src/site/config.ts | 29 +++++++++-------- 3 files changed, 52 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 7fbc06a9..b66bbbb8 100644 --- a/README.md +++ b/README.md @@ -101,13 +101,16 @@ Application behavior can be changed by configuring the following environment var - `NEXT_PUBLIC_SITE_DESCRIPTION` (seen in nav, beneath title) - `NEXT_PUBLIC_SITE_ABOUT` (seen in grid sidebar—accepts rich formatting tags: ``, ``, ``, ``, ``, `
`) +#### Site performance +> ⚠️ Enabling these will result in increased project usage +- `NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTOS = 1` enables static optimization for photo pages (`p/[photoId]`), i.e., renders pages at build time +- `NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_OG_IMAGES = 1` enables static optimization for OG images, i.e., renders images at build time +- `NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_CATEGORIES = 1` enables static optimization for photo categories (`tag/[tag]`, `shot-on/[make]/[model]`, etc.), i.e., renders pages at build time + #### Site behavior - `NEXT_PUBLIC_GRID_HOMEPAGE = 1` shows grid layout on homepage - `NEXT_PUBLIC_DEFAULT_THEME = light | dark` sets preferred initial theme (defaults to `system` when not configured) - `NEXT_PUBLIC_PRESERVE_ORIGINAL_UPLOADS = 1` do not optimize photo uploads before storing (⚠️ results in increased storage usage) -- `NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTOS = 1` enables static optimization for photo pages (`p/[photoId]`), i.e., renders pages at build time (⚠️ results in increased project usage) -- `NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_OG_IMAGES = 1` enables static optimization for OG images, i.e., renders images at build time (⚠️ results in increased project usage) -- `NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_CATEGORIES = 1` enables static optimization for photo categories (`tag/[tag]`, `shot-on/[make]/[model]`, etc.), i.e., renders pages at build time (⚠️ results in increased project usage) - `NEXT_PUBLIC_MATTE_PHOTOS = 1` constrains the size of each photo, and enables a surrounding border (potentially useful for photos with tall aspect ratios) - `NEXT_PUBLIC_BLUR_DISABLED = 1` prevents image blur data being stored and displayed (potentially useful for limiting Postgres usage) - `NEXT_PUBLIC_GEO_PRIVACY = 1` disables collection/display of location-based data (⚠️ re-compresses uploaded images in order to remove GPS information) diff --git a/src/site/SiteChecklistClient.tsx b/src/site/SiteChecklistClient.tsx index 6850ed12..238bda37 100644 --- a/src/site/SiteChecklistClient.tsx +++ b/src/site/SiteChecklistClient.tsx @@ -26,6 +26,7 @@ import { testConnectionsAction } from '@/admin/actions'; import ErrorNote from '@/components/ErrorNote'; import Spinner from '@/components/Spinner'; import WarningNote from '@/components/WarningNote'; +import { RiSpeedMiniLine } from 'react-icons/ri'; export default function SiteChecklistClient({ // Config checklist @@ -50,13 +51,13 @@ export default function SiteChecklistClient({ showSocial, showFilmSimulations, showExifInfo, - defaultTheme, - areOriginalUploadsPreserved, - isGridHomepageEnabled, isStaticallyOptimized, arePhotosStaticallyOptimized, arePhotoOGImagesStaticallyOptimized, arePhotoCategoriesStaticallyOptimized, + isGridHomepageEnabled, + defaultTheme, + areOriginalUploadsPreserved, arePhotosMatted, isBlurEnabled, isGeoPrivacyEnabled, @@ -436,6 +437,32 @@ export default function SiteChecklistClient({ {renderEnvVars(['AI_TEXT_AUTO_GENERATED_FIELDS'])} + } + optional + > + + Set environment variable to {'"1"'} to enable static optimization, + i.e., render pages and images at build time: + {renderSubStatusWithEnvVar( + arePhotosStaticallyOptimized ? 'checked' : 'optional', + 'NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTOS', + )} + {renderSubStatusWithEnvVar( + arePhotoOGImagesStaticallyOptimized ? 'checked' : 'optional', + 'NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_OG_IMAGES', + )} + {renderSubStatusWithEnvVar( + arePhotoCategoriesStaticallyOptimized ? 'checked' : 'optional', + 'NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_CATEGORIES', + )} + + } @@ -471,26 +498,6 @@ export default function SiteChecklistClient({ image uploads being optimized before storing: {renderEnvVars(['NEXT_PUBLIC_PRESERVE_ORIGINAL_UPLOADS'])} - - Set environment variable to {'"1"'} to enable static optimization, - i.e., render pages and images at build time: - {renderSubStatusWithEnvVar( - arePhotosStaticallyOptimized ? 'checked' : 'optional', - 'NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTOS', - )} - {renderSubStatusWithEnvVar( - arePhotoOGImagesStaticallyOptimized ? 'checked' : 'optional', - 'NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_OG_IMAGES', - )} - {renderSubStatusWithEnvVar( - arePhotoCategoriesStaticallyOptimized ? 'checked' : 'optional', - 'NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_CATEGORIES', - )} - Date: Sun, 19 Jan 2025 23:19:40 -0600 Subject: [PATCH 21/44] Update performance README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b66bbbb8..adc37d22 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ Application behavior can be changed by configuring the following environment var - `NEXT_PUBLIC_SITE_ABOUT` (seen in grid sidebar—accepts rich formatting tags: ``, ``, ``, ``, ``, `
`) #### Site performance -> ⚠️ Enabling these will result in increased project usage +> ⚠️ Enabling may result in increased project usage - `NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTOS = 1` enables static optimization for photo pages (`p/[photoId]`), i.e., renders pages at build time - `NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_OG_IMAGES = 1` enables static optimization for OG images, i.e., renders images at build time - `NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_CATEGORIES = 1` enables static optimization for photo categories (`tag/[tag]`, `shot-on/[make]/[model]`, etc.), i.e., renders pages at build time From 4f50cad0ea2e75988e997bbd5a020ef6f41059b2 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Mon, 20 Jan 2025 10:36:53 -0600 Subject: [PATCH 22/44] Fix toast positioning on mobile --- src/site/sonner.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/site/sonner.css b/src/site/sonner.css index 1ef6f492..26412d6f 100644 --- a/src/site/sonner.css +++ b/src/site/sonner.css @@ -4,6 +4,8 @@ [data-sonner-toaster] { position: fixed; --mobile-offset: 12px !important; + --mobile-offset-left: var(--mobile-offset) !important; + --mobile-offset-right: var(--mobile-offset) !important; right: var(--mobile-offset); left: var(--mobile-offset); width: 100% !important; From 73a2f84489b27c34d0c11ae9753c0f00b8a1fd06 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Mon, 20 Jan 2025 12:21:37 -0600 Subject: [PATCH 23/44] Move original upload configuration into 'performance' --- README.md | 2 +- src/site/SiteChecklistClient.tsx | 25 +++++++++++++------------ src/site/config.ts | 10 +++++----- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index adc37d22..167b26af 100644 --- a/README.md +++ b/README.md @@ -106,11 +106,11 @@ Application behavior can be changed by configuring the following environment var - `NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTOS = 1` enables static optimization for photo pages (`p/[photoId]`), i.e., renders pages at build time - `NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_OG_IMAGES = 1` enables static optimization for OG images, i.e., renders images at build time - `NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_CATEGORIES = 1` enables static optimization for photo categories (`tag/[tag]`, `shot-on/[make]/[model]`, etc.), i.e., renders pages at build time +- `NEXT_PUBLIC_PRESERVE_ORIGINAL_UPLOADS = 1` do not compress photo uploads before storing #### Site behavior - `NEXT_PUBLIC_GRID_HOMEPAGE = 1` shows grid layout on homepage - `NEXT_PUBLIC_DEFAULT_THEME = light | dark` sets preferred initial theme (defaults to `system` when not configured) -- `NEXT_PUBLIC_PRESERVE_ORIGINAL_UPLOADS = 1` do not optimize photo uploads before storing (⚠️ results in increased storage usage) - `NEXT_PUBLIC_MATTE_PHOTOS = 1` constrains the size of each photo, and enables a surrounding border (potentially useful for photos with tall aspect ratios) - `NEXT_PUBLIC_BLUR_DISABLED = 1` prevents image blur data being stored and displayed (potentially useful for limiting Postgres usage) - `NEXT_PUBLIC_GEO_PRIVACY = 1` disables collection/display of location-based data (⚠️ re-compresses uploaded images in order to remove GPS information) diff --git a/src/site/SiteChecklistClient.tsx b/src/site/SiteChecklistClient.tsx index 238bda37..752db7df 100644 --- a/src/site/SiteChecklistClient.tsx +++ b/src/site/SiteChecklistClient.tsx @@ -55,9 +55,9 @@ export default function SiteChecklistClient({ arePhotosStaticallyOptimized, arePhotoOGImagesStaticallyOptimized, arePhotoCategoriesStaticallyOptimized, + areOriginalUploadsPreserved, isGridHomepageEnabled, defaultTheme, - areOriginalUploadsPreserved, arePhotosMatted, isBlurEnabled, isGeoPrivacyEnabled, @@ -447,8 +447,9 @@ export default function SiteChecklistClient({ status={isStaticallyOptimized} optional > - Set environment variable to {'"1"'} to enable static optimization, - i.e., render pages and images at build time: + Set environment variable to {'"1"'} to make site more responsive + by enabling static optimization + (i.e., rendering pages and images at build time): {renderSubStatusWithEnvVar( arePhotosStaticallyOptimized ? 'checked' : 'optional', 'NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTOS', @@ -462,6 +463,15 @@ export default function SiteChecklistClient({ 'NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_CATEGORIES', )}
+ + Set environment variable to {'"1"'} to prevent + image uploads being compressed before storing: + {renderEnvVars(['NEXT_PUBLIC_PRESERVE_ORIGINAL_UPLOADS'])} +
- - Set environment variable to {'"1"'} to prevent - image uploads being optimized before storing: - {renderEnvVars(['NEXT_PUBLIC_PRESERVE_ORIGINAL_UPLOADS'])} - Date: Mon, 20 Jan 2025 12:29:46 -0600 Subject: [PATCH 24/44] Upgrade to framer-motion 12 --- package.json | 2 +- pnpm-lock.yaml | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 2ed5de60..bd3d3f92 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "date-fns": "^4.1.0", "date-fns-tz": "^3.2.0", "exifr": "^7.1.3", - "framer-motion": "^11.18.1", + "framer-motion": "^12.0.0", "nanoid": "^5.0.9", "next": "15.1.5", "next-auth": "5.0.0-beta.25", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e22d53be..e4c6637b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -60,8 +60,8 @@ importers: specifier: ^7.1.3 version: 7.1.3 framer-motion: - specifier: ^11.18.1 - version: 11.18.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + specifier: ^12.0.0 + version: 12.0.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0) nanoid: specifier: ^5.0.9 version: 5.0.9 @@ -2575,8 +2575,8 @@ packages: fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} - framer-motion@11.18.1: - resolution: {integrity: sha512-EQa8c9lWVOm4zlz14MsBJWr8woq87HsNmsBnQNvcS0hs8uzw6HtGAxZyIU7EGTVpHD1C1n01ufxRyarXcNzpPg==} + framer-motion@12.0.0: + resolution: {integrity: sha512-S3V6UlZUa6km3TWJS5wH5hJs0RBgvLo2MYWINA2RwG+T/xGGKweJwEn38AtlDCjq9k70QFk7Op67jm8TAOb4qQ==} peerDependencies: '@emotion/is-prop-valid': '*' react: ^18.0.0 || ^19.0.0 @@ -3277,11 +3277,11 @@ packages: resolution: {integrity: sha512-UZ7eQ+h8ywIRAW1hIEl2AqdwzJucU/Kp59+8kkZeSvafXhZjul247BvIJjEVFVeON6d7lM46XX1HXCduKAS8VA==} engines: {node: '>=16 || 14 >=14.17'} - motion-dom@11.18.1: - resolution: {integrity: sha512-g76KvA001z+atjfxczdRtw/RXOM3OMSdd1f4DL77qCTF/+avrRJiawSG4yDibEQ215sr9kpinSlX2pCTJ9zbhw==} + motion-dom@12.0.0: + resolution: {integrity: sha512-CvYd15OeIR6kHgMdonCc1ihsaUG4MYh/wrkz8gZ3hBX/uamyZCXN9S9qJoYF03GqfTt7thTV/dxnHYX4+55vDg==} - motion-utils@11.18.1: - resolution: {integrity: sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==} + motion-utils@12.0.0: + resolution: {integrity: sha512-MNFiBKbbqnmvOjkPyOKgHUp3Q6oiokLkI1bEwm5QA28cxMZrv0CbbBGDNmhF6DIXsi1pCQBSs0dX8xjeER1tmA==} mrmime@2.0.0: resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==} @@ -7508,10 +7508,10 @@ snapshots: fraction.js@4.3.7: {} - framer-motion@11.18.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + framer-motion@12.0.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0): dependencies: - motion-dom: 11.18.1 - motion-utils: 11.18.1 + motion-dom: 12.0.0 + motion-utils: 12.0.0 tslib: 2.8.1 optionalDependencies: react: 19.0.0 @@ -8398,11 +8398,11 @@ snapshots: minipass@7.1.1: {} - motion-dom@11.18.1: + motion-dom@12.0.0: dependencies: - motion-utils: 11.18.1 + motion-utils: 12.0.0 - motion-utils@11.18.1: {} + motion-utils@12.0.0: {} mrmime@2.0.0: {} From e1055e0c79d97c8326520688d179a404564412c0 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Mon, 20 Jan 2025 12:58:59 -0600 Subject: [PATCH 25/44] Prevent multiple link spinners showing --- src/components/LinkWithStatus.tsx | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/components/LinkWithStatus.tsx b/src/components/LinkWithStatus.tsx index 8b0876a3..5655454d 100644 --- a/src/components/LinkWithStatus.tsx +++ b/src/components/LinkWithStatus.tsx @@ -14,7 +14,7 @@ import clsx from 'clsx/lite'; // Avoid showing spinner for too short a time const FLICKER_THRESHOLD = 400; -// Clear loading status after long duration of inactivity +// Clear loading status after long duration const MAX_LOADING_DURATION = 15_000; export type LinkWithStatusProps = ComponentProps & { @@ -35,6 +35,7 @@ export default function LinkWithStatus({ }: LinkWithStatusProps) { const path = usePathname(); + const [pathWhenClicked, setPathWhenClicked] = useState(); const [isLoading, setIsLoading] = useState(false); const isLoadingStartTime = useRef(undefined); @@ -43,31 +44,40 @@ export default function LinkWithStatus({ const stopLoadingTimeout = useRef(undefined); const maxLoadingTimeout = useRef(undefined); - const clearTimeouts = useCallback(() => + const clearTimeouts = useCallback(() => { [startLoadingTimeout, stopLoadingTimeout, maxLoadingTimeout] .forEach(timeout => { if (timeout.current) { clearTimeout(timeout.current); } - }), - []); + }); + }, []); + + const stopLoading = useCallback(() => { + setIsLoading(false); + setPathWhenClicked(undefined); + }, []); const isVisitingLinkHref = path === href; + const shouldCancelLoading = + (pathWhenClicked && pathWhenClicked !== path) || + isVisitingLinkHref; + useEffect(() => { - if (isVisitingLinkHref) { + if (shouldCancelLoading) { clearTimeouts(); const loadingDuration = isLoadingStartTime.current ? Date.now() - isLoadingStartTime.current : 0; if (loadingDuration < FLICKER_THRESHOLD) { stopLoadingTimeout.current = setTimeout( - () => { setIsLoading(false); }, + stopLoading, FLICKER_THRESHOLD - loadingDuration, ); } else { - setIsLoading(false); + stopLoading(); } } - }, [isVisitingLinkHref, clearTimeouts]); + }, [shouldCancelLoading, clearTimeouts, stopLoading]); // Clear timeouts when unmounting useEffect(() => () => clearTimeouts(), [clearTimeouts]); @@ -83,6 +93,7 @@ export default function LinkWithStatus({ onClick={e => { const isOpeningNewTab = e.metaKey || e.ctrlKey; if (!isVisitingLinkHref && !isOpeningNewTab) { + setPathWhenClicked(path); startLoadingTimeout.current = setTimeout( () => { isLoadingStartTime.current = Date.now(); @@ -91,7 +102,7 @@ export default function LinkWithStatus({ FLICKER_THRESHOLD, ); maxLoadingTimeout.current = setTimeout( - () => { setIsLoading(false); }, + stopLoading, MAX_LOADING_DURATION, ); } From c7576b43ac2171e399cafbf4d7905ebb7ee8be7a Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Mon, 20 Jan 2025 13:11:27 -0600 Subject: [PATCH 26/44] Introduce loading status to thumbnails --- src/components/LinkWithStatus.tsx | 15 ++++++++++++--- src/photo/PhotoMedium.tsx | 6 +++--- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/components/LinkWithStatus.tsx b/src/components/LinkWithStatus.tsx index 5655454d..6b00bf2c 100644 --- a/src/components/LinkWithStatus.tsx +++ b/src/components/LinkWithStatus.tsx @@ -17,10 +17,15 @@ const FLICKER_THRESHOLD = 400; // Clear loading status after long duration const MAX_LOADING_DURATION = 15_000; -export type LinkWithStatusProps = ComponentProps & { +export type LinkWithStatusProps = Omit< + ComponentProps, 'children' +> & { loadingElement?: ReactNode loadingClassName?: string contentClassName?: string + children: ReactNode | ((props: { + isLoading: boolean + }) => ReactNode) } export default function LinkWithStatus({ @@ -44,6 +49,8 @@ export default function LinkWithStatus({ const stopLoadingTimeout = useRef(undefined); const maxLoadingTimeout = useRef(undefined); + const isControlled = typeof children === 'function'; + const clearTimeouts = useCallback(() => { [startLoadingTimeout, stopLoadingTimeout, maxLoadingTimeout] .forEach(timeout => { @@ -114,11 +121,13 @@ export default function LinkWithStatus({ contentClassName, loadingElement ? isLoading ? 'opacity-0' : 'opacity-100' - : loadingClassName + : (loadingClassName || isControlled) ? 'opacity-100' : isLoading ? 'opacity-50' : 'opacity-100', )}> - {children} + {typeof children === 'function' + ? children({ isLoading }) + : children} {isLoading && loadingElement && - + ); }; From 8518bd216cd291bb5471d5fb9767554ffb67f511 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Mon, 20 Jan 2025 13:33:12 -0600 Subject: [PATCH 27/44] Add spinner to loading photo thumbs --- src/photo/PhotoMedium.tsx | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/photo/PhotoMedium.tsx b/src/photo/PhotoMedium.tsx index 085bba98..cd6feb0a 100644 --- a/src/photo/PhotoMedium.tsx +++ b/src/photo/PhotoMedium.tsx @@ -13,6 +13,7 @@ import { SHOULD_PREFETCH_ALL_LINKS } from '@/site/config'; import { useRef } from 'react'; import useOnVisible from '@/utility/useOnVisible'; import LinkWithStatus from '@/components/LinkWithStatus'; +import Spinner from '@/components/Spinner'; export default function PhotoMedium({ photo, @@ -48,16 +49,27 @@ export default function PhotoMedium({ )} prefetch={prefetch} > - + {({ isLoading }) => +
+ {isLoading && +
+ +
} + +
} ); }; From 271aeb0bb41ad85dae5ba82d8739ce41bff9f040 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Mon, 20 Jan 2025 14:02:03 -0600 Subject: [PATCH 28/44] Refine photo thumb loading animation --- eslint.config.mjs | 1 + next.config.ts | 1 - src/photo/PhotoMedium.tsx | 3 ++- tailwind.config.js | 6 ++++++ 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 9a536467..47a47718 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -14,6 +14,7 @@ const eslintConfig = [ rules: { '@next/next/no-img-element': 'off', '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-require-imports': 'off', 'no-unused-expressions': ['warn'], '@typescript-eslint/no-unused-vars': [ 'warn', { diff --git a/next.config.ts b/next.config.ts index 62a91b49..c41d7549 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-require-imports */ import type { NextConfig } from 'next'; import { RemotePattern } from 'next/dist/shared/lib/image-config'; diff --git a/src/photo/PhotoMedium.tsx b/src/photo/PhotoMedium.tsx index cd6feb0a..e67e744d 100644 --- a/src/photo/PhotoMedium.tsx +++ b/src/photo/PhotoMedium.tsx @@ -54,7 +54,8 @@ export default function PhotoMedium({ {isLoading &&
diff --git a/tailwind.config.js b/tailwind.config.js index f86a24d3..a728d01d 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -28,12 +28,18 @@ module.exports = { animation: { 'rotate-pulse': 'rotate-pulse 0.75s linear infinite normal both running', + 'fade-in': + 'fade-in 0.5s linear', 'hover-drift': 'hover-drift 8s linear infinite', 'hover-wobble': 'hover-wobble 6s linear infinite normal both running', }, keyframes: { + 'fade-in': { + '0%': { opacity: '0' }, + '100%': { opacity: '1' }, + }, 'rotate-pulse': { '0%': { transform: 'rotate(0deg) scale(1)' }, '50%': { transform: 'rotate(180deg) scale(0.8)' }, From 33a950f73c63d0b8ba2e6863eea396c8d51a3ea3 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Mon, 20 Jan 2025 14:40:33 -0600 Subject: [PATCH 29/44] Improve error logging for photo uploads --- src/app/admin/uploads/[uploadPath]/page.tsx | 32 ++++++---- src/photo/server.ts | 71 ++++++++++++--------- 2 files changed, 62 insertions(+), 41 deletions(-) diff --git a/src/app/admin/uploads/[uploadPath]/page.tsx b/src/app/admin/uploads/[uploadPath]/page.tsx index dd9a1a2f..2481ddbb 100644 --- a/src/app/admin/uploads/[uploadPath]/page.tsx +++ b/src/app/admin/uploads/[uploadPath]/page.tsx @@ -8,6 +8,7 @@ import { AI_TEXT_GENERATION_ENABLED, BLUR_ENABLED, } from '@/site/config'; +import ErrorNote from '@/components/ErrorNote'; export const maxDuration = 60; @@ -23,16 +24,19 @@ export default async function UploadPage({ params }: Params) { photoFormExif, imageResizedBase64: imageThumbnailBase64, shouldStripGpsData, + error, } = await extractImageDataFromBlobPath(uploadPath, { includeInitialPhotoFields: true, generateBlurData: BLUR_ENABLED, generateResizedImage: AI_TEXT_GENERATION_ENABLED, }); - if ( + const isDataMissing = !photoFormExif || - (AI_TEXT_GENERATION_ENABLED && !imageThumbnailBase64) - ) { + (AI_TEXT_GENERATION_ENABLED && !imageThumbnailBase64); + + if (isDataMissing && !error) { + // Only redirect if there's no error to report redirect(PATH_ADMIN); } @@ -43,14 +47,18 @@ export default async function UploadPage({ params }: Params) { const textFieldsToAutoGenerate = AI_TEXT_AUTO_GENERATED_FIELDS; return ( - + !isDataMissing + ? + : + {error ?? 'Unknown error'} + ); }; diff --git a/src/photo/server.ts b/src/photo/server.ts index 6096da28..3708395a 100644 --- a/src/photo/server.ts +++ b/src/photo/server.ts @@ -29,6 +29,7 @@ export const extractImageDataFromBlobPath = async ( imageResizedBase64?: string shouldStripGpsData?: boolean fileBytes?: ArrayBuffer + error?: string }> => { const { includeInitialPhotoFields, @@ -42,49 +43,60 @@ export const extractImageDataFromBlobPath = async ( const extension = getExtensionFromStorageUrl(url); - const fileBytes = blobPath - ? await fetch(url, { cache: 'no-store' }).then(res => res.arrayBuffer()) - : undefined; - let exifData: ExifData | undefined; let filmSimulation: FilmSimulation | undefined; let blurData: string | undefined; let imageResizedBase64: string | undefined; let shouldStripGpsData = false; + let error: string | undefined; - if (fileBytes) { - const parser = ExifParserFactory.create(Buffer.from(fileBytes)); + const fileBytes = blobPath + ? await fetch(url, { cache: 'no-store' }).then(res => res.arrayBuffer()) + .catch(e => { + error = `Error fetching image from ${url}: "${e.message}"`; + return undefined; + }) + : undefined; - // Data for form - parser.enableBinaryFields(false); - exifData = parser.parse(); + try { + if (fileBytes) { + const parser = ExifParserFactory.create(Buffer.from(fileBytes)); - // Capture film simulation for Fujifilm cameras - if (isExifForFujifilm(exifData)) { - // Parse exif data again with binary fields - // in order to access MakerNote tag - parser.enableBinaryFields(true); - const exifDataBinary = parser.parse(); - const makerNote = exifDataBinary.tags?.MakerNote; - if (Buffer.isBuffer(makerNote)) { - filmSimulation = getFujifilmSimulationFromMakerNote(makerNote); + // Data for form + parser.enableBinaryFields(false); + exifData = parser.parse(); + + // Capture film simulation for Fujifilm cameras + if (isExifForFujifilm(exifData)) { + // Parse exif data again with binary fields + // in order to access MakerNote tag + parser.enableBinaryFields(true); + const exifDataBinary = parser.parse(); + const makerNote = exifDataBinary.tags?.MakerNote; + if (Buffer.isBuffer(makerNote)) { + filmSimulation = getFujifilmSimulationFromMakerNote(makerNote); + } } - } - if (generateBlurData) { - blurData = await blurImage(fileBytes); - } + if (generateBlurData) { + blurData = await blurImage(fileBytes); + } - if (generateResizedImage) { - imageResizedBase64 = await resizeImage(fileBytes); - } + if (generateResizedImage) { + imageResizedBase64 = await resizeImage(fileBytes); + } - shouldStripGpsData = GEO_PRIVACY_ENABLED && ( - Boolean(exifData.tags?.GPSLatitude) || - Boolean(exifData.tags?.GPSLongitude) - ); + shouldStripGpsData = GEO_PRIVACY_ENABLED && ( + Boolean(exifData.tags?.GPSLatitude) || + Boolean(exifData.tags?.GPSLongitude) + ); + } + } catch (e) { + error = `Error extracting image data from ${url}: "${e}"`; } + if (error) { console.log(error); } + return { blobId, ...exifData && { @@ -102,6 +114,7 @@ export const extractImageDataFromBlobPath = async ( imageResizedBase64, shouldStripGpsData, fileBytes, + error, }; }; From 7c8cb57834a93aced0a7297cf0aad6b7e8a7e617 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Tue, 21 Jan 2025 22:54:56 -0600 Subject: [PATCH 30/44] Bump dependencies --- package.json | 6 +++--- pnpm-lock.yaml | 38 +++++++++++++++++++------------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/package.json b/package.json index bd3d3f92..1bb53b08 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,8 @@ }, "dependencies": { "@ai-sdk/openai": "^1.1.0", - "@aws-sdk/client-s3": "3.731.1", - "@aws-sdk/s3-request-presigner": "3.731.1", + "@aws-sdk/client-s3": "3.732.0", + "@aws-sdk/s3-request-presigner": "3.732.0", "@radix-ui/react-dialog": "^1.1.4", "@radix-ui/react-dropdown-menu": "^2.1.4", "@radix-ui/react-visually-hidden": "^1.1.1", @@ -26,7 +26,7 @@ "date-fns": "^4.1.0", "date-fns-tz": "^3.2.0", "exifr": "^7.1.3", - "framer-motion": "^12.0.0", + "framer-motion": "^12.0.1", "nanoid": "^5.0.9", "next": "15.1.5", "next-auth": "5.0.0-beta.25", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e4c6637b..61123822 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,11 +12,11 @@ importers: specifier: ^1.1.0 version: 1.1.0(zod@3.23.8) '@aws-sdk/client-s3': - specifier: 3.731.1 - version: 3.731.1 + specifier: 3.732.0 + version: 3.732.0 '@aws-sdk/s3-request-presigner': - specifier: 3.731.1 - version: 3.731.1 + specifier: 3.732.0 + version: 3.732.0 '@radix-ui/react-dialog': specifier: ^1.1.4 version: 1.1.4(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) @@ -60,8 +60,8 @@ importers: specifier: ^7.1.3 version: 7.1.3 framer-motion: - specifier: ^12.0.0 - version: 12.0.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + specifier: ^12.0.1 + version: 12.0.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) nanoid: specifier: ^5.0.9 version: 5.0.9 @@ -256,8 +256,8 @@ packages: '@aws-crypto/util@5.2.0': resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} - '@aws-sdk/client-s3@3.731.1': - resolution: {integrity: sha512-Ab2PA/8Th6JkurCkxnQJZHPE/JnnSsX/XHQzirkQb+JpKOyWMRC/YZUBfAaiwhxqX65RHgklrwil+UbFl4TtAQ==} + '@aws-sdk/client-s3@3.732.0': + resolution: {integrity: sha512-ITPcG40qdiLXZRvNQ8V3u4yB16afcdIabdRBN6Blba31rk0MfeBGWwah0+lLSTFo1ZIrIQvBl6PAQ7mO0mkKLg==} engines: {node: '>=18.0.0'} '@aws-sdk/client-sso@3.731.0': @@ -304,8 +304,8 @@ packages: resolution: {integrity: sha512-oY4nsY/mb5O5eZCzIuWpyvzO45Bi6UBtE48IaJsoyVctagA1l8hB66aczH9M1NHNjvbS4Beu1agwh3Nyb1eJug==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-flexible-checksums@3.731.0': - resolution: {integrity: sha512-LMs/rndovYjYSntSYyPE/PIl4iHNiquaU0lpDqpQc9iTgQcNbjdriSUWpibgu1jXlGBpBYCqttNkxmEThbbWxA==} + '@aws-sdk/middleware-flexible-checksums@3.732.0': + resolution: {integrity: sha512-MIbF6cYWg5noRO1kRZNC0vewj6qzSYL/BGNlPxRQcqb6egUiGrhPEi8Y/qqweB7MlBHsqSO5YNPwH/Py8ToSVw==} engines: {node: '>=18.0.0'} '@aws-sdk/middleware-host-header@3.731.0': @@ -344,8 +344,8 @@ packages: resolution: {integrity: sha512-XlDpRNkDVHF59f07JmkuAidEv//m3hT6/JL85h0l3+zrpaRWhf8n8lVUyAPNq35ZujK8AcorYM+93u7hdWsliQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/s3-request-presigner@3.731.1': - resolution: {integrity: sha512-GdG0pXkcTgBpenouB834FoCHyLaivV2rGQn7OEQBiT8SBaTxSackZ6tGlJQAlzZQkiQfE/NePUJU7DczJZZvrg==} + '@aws-sdk/s3-request-presigner@3.732.0': + resolution: {integrity: sha512-aIzl8UDZp1fNS6haLKmyHcLXg1vWhu9Yimz/9W1xElGB3XZc0LsTlp57yVTmx9ROYo3kAh+Z6RhF73bESTOmjA==} engines: {node: '>=18.0.0'} '@aws-sdk/signature-v4-multi-region@3.731.0': @@ -2575,8 +2575,8 @@ packages: fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} - framer-motion@12.0.0: - resolution: {integrity: sha512-S3V6UlZUa6km3TWJS5wH5hJs0RBgvLo2MYWINA2RwG+T/xGGKweJwEn38AtlDCjq9k70QFk7Op67jm8TAOb4qQ==} + framer-motion@12.0.1: + resolution: {integrity: sha512-u6p0Qc4cY/AEQAtrC7qiYlXla39qnWoI4JXY7OCNBDXwJ5yRBD8HU+RhaOqqziw2m/b0BDh32f44W94+wXonMQ==} peerDependencies: '@emotion/is-prop-valid': '*' react: ^18.0.0 || ^19.0.0 @@ -4482,7 +4482,7 @@ snapshots: '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 - '@aws-sdk/client-s3@3.731.1': + '@aws-sdk/client-s3@3.732.0': dependencies: '@aws-crypto/sha1-browser': 5.2.0 '@aws-crypto/sha256-browser': 5.2.0 @@ -4491,7 +4491,7 @@ snapshots: '@aws-sdk/credential-provider-node': 3.731.1 '@aws-sdk/middleware-bucket-endpoint': 3.731.0 '@aws-sdk/middleware-expect-continue': 3.731.0 - '@aws-sdk/middleware-flexible-checksums': 3.731.0 + '@aws-sdk/middleware-flexible-checksums': 3.732.0 '@aws-sdk/middleware-host-header': 3.731.0 '@aws-sdk/middleware-location-constraint': 3.731.0 '@aws-sdk/middleware-logger': 3.731.0 @@ -4706,7 +4706,7 @@ snapshots: '@smithy/types': 4.1.0 tslib: 2.8.1 - '@aws-sdk/middleware-flexible-checksums@3.731.0': + '@aws-sdk/middleware-flexible-checksums@3.732.0': dependencies: '@aws-crypto/crc32': 5.2.0 '@aws-crypto/crc32c': 5.2.0 @@ -4833,7 +4833,7 @@ snapshots: '@smithy/util-middleware': 4.0.1 tslib: 2.8.1 - '@aws-sdk/s3-request-presigner@3.731.1': + '@aws-sdk/s3-request-presigner@3.732.0': dependencies: '@aws-sdk/signature-v4-multi-region': 3.731.0 '@aws-sdk/types': 3.731.0 @@ -7508,7 +7508,7 @@ snapshots: fraction.js@4.3.7: {} - framer-motion@12.0.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + framer-motion@12.0.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0): dependencies: motion-dom: 12.0.0 motion-utils: 12.0.0 From b65136faa34586bdf0e3679d92c4106a006f1aeb Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Tue, 21 Jan 2025 22:56:17 -0600 Subject: [PATCH 31/44] Fix README typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 167b26af..ff3509ae 100644 --- a/README.md +++ b/README.md @@ -106,7 +106,7 @@ Application behavior can be changed by configuring the following environment var - `NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTOS = 1` enables static optimization for photo pages (`p/[photoId]`), i.e., renders pages at build time - `NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_OG_IMAGES = 1` enables static optimization for OG images, i.e., renders images at build time - `NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_CATEGORIES = 1` enables static optimization for photo categories (`tag/[tag]`, `shot-on/[make]/[model]`, etc.), i.e., renders pages at build time -- `NEXT_PUBLIC_PRESERVE_ORIGINAL_UPLOADS = 1` do not compress photo uploads before storing +- `NEXT_PUBLIC_PRESERVE_ORIGINAL_UPLOADS = 1` prevents photo uploads being compressed before storing #### Site behavior - `NEXT_PUBLIC_GRID_HOMEPAGE = 1` shows grid layout on homepage From ad11ce32b017311566d644b0e735ec21927d0353 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Tue, 21 Jan 2025 23:24:36 -0600 Subject: [PATCH 32/44] Split link status/loader into two components --- src/admin/AdminNavClient.tsx | 9 +++++---- src/components/LinkWithLoader.tsx | 31 +++++++++++++++++++++++++++++++ src/components/LinkWithStatus.tsx | 31 +++++++------------------------ src/components/SwitcherItem.tsx | 20 ++++++++++---------- 4 files changed, 53 insertions(+), 38 deletions(-) create mode 100644 src/components/LinkWithLoader.tsx diff --git a/src/admin/AdminNavClient.tsx b/src/admin/AdminNavClient.tsx index 240ab4e1..3acc5e8d 100644 --- a/src/admin/AdminNavClient.tsx +++ b/src/admin/AdminNavClient.tsx @@ -1,5 +1,6 @@ 'use client'; +import LinkWithLoader from '@/components/LinkWithLoader'; import LinkWithStatus from '@/components/LinkWithStatus'; import Note from '@/components/Note'; import SiteGrid from '@/components/SiteGrid'; @@ -73,11 +74,11 @@ export default function AdminNavClient({ key={label} href={href} className={clsx( + 'flex gap-0.5', checkPathPrefix(pathname, href) ? 'font-bold' : 'text-dim', 'px-1 py-0.5 rounded-md', )} loadingClassName="bg-dim" - contentClassName="flex gap-0.5" prefetch={false} > {label} @@ -85,19 +86,19 @@ export default function AdminNavClient({ ({count})} )}
- } + loader={} > - +
{shouldShowBanner && }> diff --git a/src/components/LinkWithLoader.tsx b/src/components/LinkWithLoader.tsx new file mode 100644 index 00000000..af01c50b --- /dev/null +++ b/src/components/LinkWithLoader.tsx @@ -0,0 +1,31 @@ +import { ComponentProps, ReactNode } from 'react'; +import LinkWithStatus from './LinkWithStatus'; +import clsx from 'clsx/lite'; +import Link from 'next/link'; + +export default function LinkWithLoader({ + loader, + children, + ...props +}: ComponentProps & { + loader: ReactNode +}) { + return ( + + {({ isLoading }) => <> + + {children} + + {isLoading && + {loader} + } + } + + ); +} diff --git a/src/components/LinkWithStatus.tsx b/src/components/LinkWithStatus.tsx index 6b00bf2c..a2ddf0a9 100644 --- a/src/components/LinkWithStatus.tsx +++ b/src/components/LinkWithStatus.tsx @@ -20,18 +20,14 @@ const MAX_LOADING_DURATION = 15_000; export type LinkWithStatusProps = Omit< ComponentProps, 'children' > & { - loadingElement?: ReactNode loadingClassName?: string - contentClassName?: string children: ReactNode | ((props: { isLoading: boolean }) => ReactNode) } export default function LinkWithStatus({ - loadingElement, loadingClassName, - contentClassName, href, className, onClick, @@ -93,7 +89,10 @@ export default function LinkWithStatus({ {...props } href={href} className={clsx( - 'relative transition-colors', + 'relative flex transition-[colors,opacity]', + (loadingClassName || isControlled) + ? 'opacity-100' + : isLoading ? 'opacity-50' : 'opacity-100', className, isLoading && loadingClassName, )} @@ -116,24 +115,8 @@ export default function LinkWithStatus({ onClick?.(e); }} > - - {typeof children === 'function' - ? children({ isLoading }) - : children} - - {isLoading && loadingElement && - {loadingElement} - } + {typeof children === 'function' + ? children({ isLoading }) + : children} ; } diff --git a/src/components/SwitcherItem.tsx b/src/components/SwitcherItem.tsx index eb1b70be..87c01783 100644 --- a/src/components/SwitcherItem.tsx +++ b/src/components/SwitcherItem.tsx @@ -1,8 +1,8 @@ import { clsx } from 'clsx/lite'; import { SHOULD_PREFETCH_ALL_LINKS } from '@/site/config'; -import { JSX, ReactNode } from 'react'; -import LinkWithStatus from './LinkWithStatus'; +import { JSX } from 'react'; import Spinner from './Spinner'; +import LinkWithLoader from './LinkWithLoader'; export default function SwitcherItem({ icon, @@ -37,23 +37,23 @@ export default function SwitcherItem({ : 'hover:text-gray-700 dark:hover:text-gray-400', ); - const renderContent = (content: ReactNode) => noPadding - ? content + const renderIcon = () => noPadding + ? icon :
- {content} + {icon}
; return ( href - ? , + loader: , }}> - {renderContent(icon)} - - :
{renderContent(icon)}
+ {renderIcon()} + + :
{renderIcon()}
); }; From d6e5aa012e26994774361543217060a21a18677e Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Wed, 22 Jan 2025 18:05:33 -0600 Subject: [PATCH 33/44] Validate date time fields when adding/editing photos --- __tests__/date.test.ts | 44 +++++++++++++++++++-------- src/components/FieldSetWithStatus.tsx | 2 +- src/photo/form/index.ts | 12 ++++++-- src/utility/date.ts | 41 +++++++++++++++++++------ 4 files changed, 74 insertions(+), 25 deletions(-) diff --git a/__tests__/date.test.ts b/__tests__/date.test.ts index 6257779e..a6ebadeb 100644 --- a/__tests__/date.test.ts +++ b/__tests__/date.test.ts @@ -1,6 +1,9 @@ +/* eslint-disable max-len */ import { convertTimestampToNaivePostgresString, convertTimestampWithOffsetToPostgresString, + validatePostgresDateString, + validateNaivePostgresDateString, } from '../src/utility/date'; describe('Date utility', () => { @@ -29,19 +32,34 @@ describe('Date utility', () => { expect(convertTimestampToNaivePostgresString(timestamp)) .toBe('2023-12-02 16:38:36'); }); + it('Malformed date string', () => { + const timestamp = '2024/01a/01 Z'; + expect(convertTimestampWithOffsetToPostgresString(timestamp)) + .toBe(convertTimestampWithOffsetToPostgresString( + new Date().toISOString(), + )); + }); + it('Empty string', () => { + const timestamp = ' '; + expect(convertTimestampWithOffsetToPostgresString(timestamp)) + .toBe(convertTimestampWithOffsetToPostgresString( + new Date().toISOString(), + )); + }); }); - it('Malformed date string', () => { - const timestamp = '2024/01a/01 Z'; - expect(convertTimestampWithOffsetToPostgresString(timestamp)) - .toBe(convertTimestampWithOffsetToPostgresString( - new Date().toISOString(), - )); - }); - it('Empty string', () => { - const timestamp = ' '; - expect(convertTimestampWithOffsetToPostgresString(timestamp)) - .toBe(convertTimestampWithOffsetToPostgresString( - new Date().toISOString(), - )); + describe('validates date strings', () => { + it('Correct', () => { + expect(validatePostgresDateString('2025-01-03T21:00:44.000Z')).toBe(true); + expect(validateNaivePostgresDateString('2025-01-03 16:00:44')).toBe(true); + }); + it('Incorrect', () => { + expect(validatePostgresDateString('2024-01-01')).toBe(false); + expect(validatePostgresDateString('2025-01-03 16:00:44')).toBe(false); + expect(validateNaivePostgresDateString('2024-01-01')).toBe(false); + expect(validatePostgresDateString('2025-01-03T21:00:44.000')).toBe(false); + expect(validateNaivePostgresDateString('2025-01-03T16:00:44')).toBe(false); + expect(validatePostgresDateString('2025-01-03T21:00:44.000ZZ')).toBe(false); + expect(validateNaivePostgresDateString('2025-01-03 16:00:44Z')).toBe(false); + }); }); }); diff --git a/src/components/FieldSetWithStatus.tsx b/src/components/FieldSetWithStatus.tsx index 5b0eda42..305ba493 100644 --- a/src/components/FieldSetWithStatus.tsx +++ b/src/components/FieldSetWithStatus.tsx @@ -58,7 +58,7 @@ export default function FieldSetWithStatus({ {!hideLabel && label &&