From 1f43e5b5e57bd7fed9dd320b62aa26c1f73cc096 Mon Sep 17 00:00:00 2001 From: Abul Date: Thu, 20 Nov 2025 19:40:23 +0530 Subject: [PATCH 01/14] Add file viewing and improve download handling --- app/api/files/[id]/download/route.ts | 6 ++- app/api/files/[id]/view/route.ts | 50 ++++++++++++++++++++++++ components/modals/ViewFileModal.tsx | 28 +++++++++++--- lib/services/azure-storage.ts | 58 ++++++++++++++++++++++++---- 4 files changed, 129 insertions(+), 13 deletions(-) create mode 100644 app/api/files/[id]/view/route.ts diff --git a/app/api/files/[id]/download/route.ts b/app/api/files/[id]/download/route.ts index 9ec1913..fb460da 100644 --- a/app/api/files/[id]/download/route.ts +++ b/app/api/files/[id]/download/route.ts @@ -40,7 +40,11 @@ export async function GET( return createErrorResponse(ERROR_MESSAGES.FILE_NOT_FOUND, 404); } - const downloadUrl = await generateDownloadUrl(user!.id, file.fileName); + const downloadUrl = await generateDownloadUrl( + user!.id, + file.fileName, + file.originalFileName + ); return createSuccessResponse({ downloadUrl }); } catch (error) { diff --git a/app/api/files/[id]/view/route.ts b/app/api/files/[id]/view/route.ts new file mode 100644 index 0000000..2357589 --- /dev/null +++ b/app/api/files/[id]/view/route.ts @@ -0,0 +1,50 @@ +import { NextRequest } from "next/server"; +import { connectToDatabase } from "@/lib/services/mongodb"; +import { generateViewUrl } from "@/lib/services/azure-storage"; +import { + getAuthenticatedUser, + validateRequest, + createErrorResponse, + createSuccessResponse, +} from "@/lib/api/api-helpers"; +import { fileIdSchema } from "@/lib/utils/validations"; +import { COLLECTIONS, ERROR_MESSAGES } from "@/lib/config/constants"; +import { ObjectId } from "mongodb"; +import type { FileDocument } from "@/types"; + +export async function GET( + request: NextRequest, + { params }: { params: Promise<{ id: string }> } +) { + try { + const { error, user } = await getAuthenticatedUser(request); + if (error) { + return error; + } + + const { id } = await params; + + const idValidation = await validateRequest(fileIdSchema, { id }); + if (!idValidation.success) { + return createErrorResponse(idValidation.error, 400); + } + + const { db } = await connectToDatabase(); + + const file = await db.collection(COLLECTIONS.FILES).findOne({ + _id: new ObjectId(id), + userId: user!.id, + }); + + if (!file) { + return createErrorResponse(ERROR_MESSAGES.FILE_NOT_FOUND, 404); + } + + const viewUrl = await generateViewUrl(user!.id, file.fileName); + + return createSuccessResponse({ viewUrl }); + } catch (error) { + console.error("View error:", error); + return createErrorResponse("Failed to generate view URL", 500); + } +} diff --git a/components/modals/ViewFileModal.tsx b/components/modals/ViewFileModal.tsx index 8a4b3ff..ffab5de 100644 --- a/components/modals/ViewFileModal.tsx +++ b/components/modals/ViewFileModal.tsx @@ -32,10 +32,10 @@ export function ViewFileModal({ file, isOpen, onClose }: ViewFileModalProps) { setLoading(true); try { - const response = await fetch(`/api/files/${file._id}/download`); + const response = await fetch(`/api/files/${file._id}/view`); if (response.ok) { const data = await response.json(); - setFileUrl(data.downloadUrl); + setFileUrl(data.viewUrl); } else { toast.error("Failed to load file"); } @@ -55,9 +55,27 @@ export function ViewFileModal({ file, isOpen, onClose }: ViewFileModalProps) { } }, [file, isOpen, fetchFileUrl]); - const handleDownload = () => { - if (fileUrl) { - window.open(fileUrl, "_blank"); + const handleDownload = async () => { + if (!file) { + return; + } + + try { + const response = await fetch(`/api/files/${file._id}/download`); + if (response.ok) { + const data = await response.json(); + const link = document.createElement("a"); + link.href = data.downloadUrl; + link.download = file.originalFileName; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + } else { + toast.error("Failed to download file"); + } + } catch (error) { + console.error("Download error:", error); + toast.error("Failed to download file"); } }; diff --git a/lib/services/azure-storage.ts b/lib/services/azure-storage.ts index 255e964..606b91d 100644 --- a/lib/services/azure-storage.ts +++ b/lib/services/azure-storage.ts @@ -21,10 +21,7 @@ const containerName = process.env.AZURE_STORAGE_CONTAINER_NAME || "user-files"; export async function getContainerClient(): Promise { const containerClient = blobServiceClient.getContainerClient(containerName); - // Ensure container exists - await containerClient.createIfNotExists({ - access: "blob", - }); + await containerClient.createIfNotExists(); return containerClient; } @@ -59,15 +56,62 @@ export async function deleteFileFromBlob( await blockBlobClient.deleteIfExists(); } +export async function generateViewUrl( + userId: string, + fileName: string, + expiryHours: number = 2 +): Promise { + const containerClient = await getContainerClient(); + const blobName = `${userId}/${fileName}`; + const blockBlobClient = containerClient.getBlockBlobClient(blobName); + + const sharedKeyCredential = getSharedKeyCredential(); + + const startsOn = new Date(); + const expiresOn = new Date(startsOn.getTime() + expiryHours * 60 * 60 * 1000); + + const sasToken = generateBlobSASQueryParameters( + { + containerName: containerClient.containerName, + blobName, + permissions: BlobSASPermissions.parse("r"), + startsOn, + expiresOn, + }, + sharedKeyCredential + ).toString(); + + return `${blockBlobClient.url}?${sasToken}`; +} + export async function generateDownloadUrl( userId: string, - fileName: string + fileName: string, + originalFileName: string, + expiryMinutes: number = 10 ): Promise { const containerClient = await getContainerClient(); const blobName = `${userId}/${fileName}`; const blockBlobClient = containerClient.getBlockBlobClient(blobName); - return blockBlobClient.url; + const sharedKeyCredential = getSharedKeyCredential(); + + const startsOn = new Date(); + const expiresOn = new Date(startsOn.getTime() + expiryMinutes * 60 * 1000); + + const sasToken = generateBlobSASQueryParameters( + { + containerName: containerClient.containerName, + blobName, + permissions: BlobSASPermissions.parse("r"), + startsOn, + expiresOn, + contentDisposition: `attachment; filename="${originalFileName}"`, + }, + sharedKeyCredential + ).toString(); + + return `${blockBlobClient.url}?${sasToken}`; } function getSharedKeyCredential(): StorageSharedKeyCredential { @@ -102,7 +146,7 @@ export async function generatePresignedUploadUrl( const sasToken = generateBlobSASQueryParameters( { - containerName, + containerName: containerClient.containerName, blobName, permissions: BlobSASPermissions.parse("w"), startsOn, From 92efbfb4cac7211aa9d1308fd3fb12adbf5dbe4e Mon Sep 17 00:00:00 2001 From: Abul Date: Fri, 21 Nov 2025 22:00:47 +0530 Subject: [PATCH 02/14] Implement activity logging and add activity dashboard --- app/api/activity/route.ts | 32 ++++++ app/api/files/[id]/download/route.ts | 8 ++ app/api/files/[id]/restore/route.ts | 8 ++ app/api/files/[id]/route.ts | 51 +++++++-- app/api/files/[id]/view/route.ts | 8 ++ app/api/files/recent/route.ts | 73 ++++++++++++ app/api/upload/complete/route.ts | 8 ++ app/dashboard/activity/page.tsx | 90 +++++++++++++++ app/dashboard/recent/page.tsx | 41 +++++++ components/activity/ActivityList.tsx | 153 +++++++++++++++++++++++++ components/layout/SidebarComponent.tsx | 14 +++ lib/config/constants.ts | 1 + lib/services/activity-logger.ts | 37 ++++++ lib/utils/utils.ts | 33 ++++++ types/index.ts | 21 ++++ 15 files changed, 566 insertions(+), 12 deletions(-) create mode 100644 app/api/activity/route.ts create mode 100644 app/api/files/recent/route.ts create mode 100644 app/dashboard/activity/page.tsx create mode 100644 app/dashboard/recent/page.tsx create mode 100644 components/activity/ActivityList.tsx create mode 100644 lib/services/activity-logger.ts diff --git a/app/api/activity/route.ts b/app/api/activity/route.ts new file mode 100644 index 0000000..61502c1 --- /dev/null +++ b/app/api/activity/route.ts @@ -0,0 +1,32 @@ +import { NextRequest } from "next/server"; +import { connectToDatabase } from "@/lib/services/mongodb"; +import { + getAuthenticatedUser, + createErrorResponse, + createSuccessResponse, +} from "@/lib/api/api-helpers"; +import { COLLECTIONS, ERROR_MESSAGES } from "@/lib/config/constants"; +import type { ActivityLog } from "@/types"; + +export async function GET(request: NextRequest) { + try { + const { error, user } = await getAuthenticatedUser(request); + if (error) { + return error; + } + + const { db } = await connectToDatabase(); + + const activities = await db + .collection(COLLECTIONS.ACTIVITIES) + .find({ userId: user!.id }) + .sort({ timestamp: -1 }) + .limit(100) + .toArray(); + + return createSuccessResponse({ activities }); + } catch (error) { + console.error("Activity fetch error:", error); + return createErrorResponse(ERROR_MESSAGES.INTERNAL_ERROR, 500); + } +} diff --git a/app/api/files/[id]/download/route.ts b/app/api/files/[id]/download/route.ts index fb460da..90ca38a 100644 --- a/app/api/files/[id]/download/route.ts +++ b/app/api/files/[id]/download/route.ts @@ -1,6 +1,7 @@ import { NextRequest } from "next/server"; import { connectToDatabase } from "@/lib/services/mongodb"; import { generateDownloadUrl } from "@/lib/services/azure-storage"; +import { logActivity } from "@/lib/services/activity-logger"; import { getAuthenticatedUser, validateRequest, @@ -46,6 +47,13 @@ export async function GET( file.originalFileName ); + await logActivity({ + userId: user!.id, + fileId: file._id!, + action: "download", + fileName: file.originalFileName, + }); + return createSuccessResponse({ downloadUrl }); } catch (error) { console.error("Download error:", error); diff --git a/app/api/files/[id]/restore/route.ts b/app/api/files/[id]/restore/route.ts index 2db08c6..b67f695 100644 --- a/app/api/files/[id]/restore/route.ts +++ b/app/api/files/[id]/restore/route.ts @@ -1,6 +1,7 @@ import { NextRequest } from "next/server"; import { connectToDatabase } from "@/lib/services/mongodb"; import { validateCSRFToken, createCSRFError } from "@/lib/auth/csrf-middleware"; +import { logActivity } from "@/lib/services/activity-logger"; import { getAuthenticatedUser, validateRequest, @@ -52,6 +53,13 @@ export async function PATCH( } ); + await logActivity({ + userId: user!.id, + fileId: file._id!, + action: "restore", + fileName: file.originalFileName, + }); + return createSuccessResponse({ success: true, message: "File restored successfully", diff --git a/app/api/files/[id]/route.ts b/app/api/files/[id]/route.ts index d890f8f..d77763b 100644 --- a/app/api/files/[id]/route.ts +++ b/app/api/files/[id]/route.ts @@ -1,6 +1,7 @@ import { NextRequest } from "next/server"; import { connectToDatabase } from "@/lib/services/mongodb"; import { validateCSRFToken, createCSRFError } from "@/lib/auth/csrf-middleware"; +import { logActivity } from "@/lib/services/activity-logger"; import { getAuthenticatedUser, validateRequest, @@ -46,22 +47,41 @@ export async function PATCH( const { db } = await connectToDatabase(); - const result = await db - .collection(COLLECTIONS.FILES) - .updateOne( - { - _id: new ObjectId(id), - userId: user!.id, - }, - { - $set: validation.data, - } - ); + const file = await db.collection(COLLECTIONS.FILES).findOne({ + _id: new ObjectId(id), + userId: user!.id, + }); - if (result.matchedCount === 0) { + if (!file) { return createErrorResponse(ERROR_MESSAGES.FILE_NOT_FOUND, 404); } + await db.collection(COLLECTIONS.FILES).updateOne( + { + _id: new ObjectId(id), + userId: user!.id, + }, + { + $set: validation.data, + } + ); + + if ( + validation.data.originalFileName && + validation.data.originalFileName !== file.originalFileName + ) { + await logActivity({ + userId: user!.id, + fileId: file._id!, + action: "rename", + fileName: validation.data.originalFileName, + metadata: { + oldName: file.originalFileName, + newName: validation.data.originalFileName, + }, + }); + } + return createSuccessResponse({ success: true, ...validation.data }); } catch (error) { console.error("File update error:", error); @@ -109,6 +129,13 @@ export async function DELETE( } ); + await logActivity({ + userId: user!.id, + fileId: file._id!, + action: "delete", + fileName: file.originalFileName, + }); + return createSuccessResponse({ success: true, message: "File moved to trash", diff --git a/app/api/files/[id]/view/route.ts b/app/api/files/[id]/view/route.ts index 2357589..22a2c92 100644 --- a/app/api/files/[id]/view/route.ts +++ b/app/api/files/[id]/view/route.ts @@ -1,6 +1,7 @@ import { NextRequest } from "next/server"; import { connectToDatabase } from "@/lib/services/mongodb"; import { generateViewUrl } from "@/lib/services/azure-storage"; +import { logActivity } from "@/lib/services/activity-logger"; import { getAuthenticatedUser, validateRequest, @@ -42,6 +43,13 @@ export async function GET( const viewUrl = await generateViewUrl(user!.id, file.fileName); + await logActivity({ + userId: user!.id, + fileId: file._id!, + action: "view", + fileName: file.originalFileName, + }); + return createSuccessResponse({ viewUrl }); } catch (error) { console.error("View error:", error); diff --git a/app/api/files/recent/route.ts b/app/api/files/recent/route.ts new file mode 100644 index 0000000..262703c --- /dev/null +++ b/app/api/files/recent/route.ts @@ -0,0 +1,73 @@ +import { NextRequest } from "next/server"; +import { connectToDatabase } from "@/lib/services/mongodb"; +import { + getAuthenticatedUser, + createErrorResponse, + createSuccessResponse, +} from "@/lib/api/api-helpers"; +import { COLLECTIONS, ERROR_MESSAGES } from "@/lib/config/constants"; +import type { FileDocument } from "@/types"; + +export async function GET(request: NextRequest) { + try { + const { error, user } = await getAuthenticatedUser(request); + if (error) { + return error; + } + + const { db } = await connectToDatabase(); + + const recentFiles = await db + .collection(COLLECTIONS.ACTIVITIES) + .aggregate([ + { + $match: { + userId: user!.id, + action: { $in: ["upload", "view", "rename"] }, + }, + }, + { + $sort: { timestamp: -1 }, + }, + { + $group: { + _id: "$fileId", + lastActivity: { $first: "$timestamp" }, + }, + }, + { + $sort: { lastActivity: -1 }, + }, + { + $lookup: { + from: COLLECTIONS.FILES, + localField: "_id", + foreignField: "_id", + as: "file", + }, + }, + { + $unwind: "$file", + }, + { + $match: { + "file.deletedAt": { $exists: false }, + }, + }, + { + $replaceRoot: { newRoot: "$file" }, + }, + { + $limit: 20, + }, + ]) + .toArray(); + + const files = recentFiles as FileDocument[]; + + return createSuccessResponse({ files }); + } catch (error) { + console.error("Recent files fetch error:", error); + return createErrorResponse(ERROR_MESSAGES.INTERNAL_ERROR, 500); + } +} diff --git a/app/api/upload/complete/route.ts b/app/api/upload/complete/route.ts index 8ec9052..1b53baa 100644 --- a/app/api/upload/complete/route.ts +++ b/app/api/upload/complete/route.ts @@ -5,6 +5,7 @@ import { verifyBlobExists, getBlobProperties, } from "@/lib/services/azure-storage"; +import { logActivity } from "@/lib/services/activity-logger"; import { getAuthenticatedUser, validateRequest, @@ -109,6 +110,13 @@ export async function POST(request: NextRequest) { } ); + await logActivity({ + userId: user!.id, + fileId: result.insertedId, + action: "upload", + fileName: originalFileName, + }); + return createSuccessResponse({ success: true, file: { ...fileDocument, _id: result.insertedId }, diff --git a/app/dashboard/activity/page.tsx b/app/dashboard/activity/page.tsx new file mode 100644 index 0000000..737a6f3 --- /dev/null +++ b/app/dashboard/activity/page.tsx @@ -0,0 +1,90 @@ +"use client"; + +import { useState, useEffect, useCallback } from "react"; +import { useRequireAuth } from "@/hooks/auth/useRequireAuth"; +import { LoadingScreen } from "@/components/common/LoadingScreen"; +import { DashboardHeader } from "@/components/layout/DashboardHeader"; +import { Sidebar } from "@/components/layout/SidebarComponent"; +import { ActivityList } from "@/components/activity/ActivityList"; +import type { ActivityLog } from "@/types"; + +export default function ActivityPage() { + const { session, status, isAuthenticated } = useRequireAuth(); + const [activities, setActivities] = useState([]); + const [loading, setLoading] = useState(true); + const [isMobileSidebarOpen, setIsMobileSidebarOpen] = useState(false); + + const fetchActivities = useCallback(async () => { + try { + const response = await fetch("/api/activity"); + if (response.ok) { + const data = await response.json(); + setActivities(data.activities); + } + } catch (error) { + console.error("Failed to fetch activities:", error); + } finally { + setLoading(false); + } + }, []); + + useEffect(() => { + if (status === "loading" || !isAuthenticated) { + return; + } + + fetchActivities(); + }, [status, isAuthenticated, fetchActivities]); + + if (status === "loading") { + return ; + } + + if (!session) { + return null; + } + + return ( +
+ setIsMobileSidebarOpen(true)} /> + +
+ {/* Desktop Sidebar */} +
+ +
+ + {/* Mobile Sidebar Overlay */} + {isMobileSidebarOpen && ( +
setIsMobileSidebarOpen(false)} + > +
e.stopPropagation()} + > + +
+
+ )} + +
+
+
+
+

+ Activity +

+

+ Your recent file activities +

+
+ +
+
+
+
+
+ ); +} diff --git a/app/dashboard/recent/page.tsx b/app/dashboard/recent/page.tsx new file mode 100644 index 0000000..94ba4d2 --- /dev/null +++ b/app/dashboard/recent/page.tsx @@ -0,0 +1,41 @@ +"use client"; + +import { DashboardLayout } from "@/components/layout/DashboardLayout"; +import { LoadingScreen } from "@/components/common/LoadingScreen"; +import { useDashboardPage } from "@/hooks/dashboard/useDashboardPage"; + +export default function RecentPage() { + const { + status, + session, + files, + loading, + isUploadModalOpen, + setIsUploadModalOpen, + handleUploadSuccess, + handleFileAction, + handleNewClick, + } = useDashboardPage({ endpoint: "/api/files/recent" }); + + if (status === "loading") { + return ; + } + + if (!session) { + return null; + } + + return ( + setIsUploadModalOpen(false)} + onUploadSuccess={handleUploadSuccess} + onFileAction={handleFileAction} + onNewClick={handleNewClick} + loadingMessage="Loading recent files..." + /> + ); +} diff --git a/components/activity/ActivityList.tsx b/components/activity/ActivityList.tsx new file mode 100644 index 0000000..9ff6ae4 --- /dev/null +++ b/components/activity/ActivityList.tsx @@ -0,0 +1,153 @@ +import { + Upload, + Eye, + Download, + FileEdit, + Trash2, + RotateCcw, + Clock, +} from "lucide-react"; +import { formatRelativeTime } from "@/lib/utils/utils"; +import type { ActivityLog, ActivityAction } from "@/types"; + +interface ActivityListProps { + activities: ActivityLog[]; + loading: boolean; +} + +function getActivityIcon(action: ActivityAction) { + switch (action) { + case "upload": + return Upload; + case "view": + return Eye; + case "download": + return Download; + case "rename": + return FileEdit; + case "delete": + return Trash2; + case "restore": + return RotateCcw; + } +} + +function getActivityColor(action: ActivityAction) { + switch (action) { + case "upload": + return "bg-green-500/10 text-green-600 dark:text-green-400"; + case "view": + return "bg-blue-500/10 text-blue-600 dark:text-blue-400"; + case "download": + return "bg-purple-500/10 text-purple-600 dark:text-purple-400"; + case "rename": + return "bg-yellow-500/10 text-yellow-600 dark:text-yellow-400"; + case "delete": + return "bg-red-500/10 text-red-600 dark:text-red-400"; + case "restore": + return "bg-cyan-500/10 text-cyan-600 dark:text-cyan-400"; + } +} + +function getActivityText(activity: ActivityLog) { + switch (activity.action) { + case "upload": + return ( + <> + Uploaded {activity.fileName} + + ); + case "view": + return ( + <> + Viewed {activity.fileName} + + ); + case "download": + return ( + <> + Downloaded {activity.fileName} + + ); + case "rename": + return ( + <> + Renamed{" "} + {activity.metadata?.oldName} to{" "} + {activity.metadata?.newName} + + ); + case "delete": + return ( + <> + Deleted {activity.fileName} + + ); + case "restore": + return ( + <> + Restored {activity.fileName} + + ); + } +} + +export function ActivityList({ activities, loading }: ActivityListProps) { + if (loading) { + return ( +
+
+
+
+
+ ); + } + + if (activities.length === 0) { + return ( +
+
+ +

+ No activity yet +

+

+ Your file activities will appear here +

+
+
+ ); + } + + return ( +
+
+ {activities.map(activity => { + const Icon = getActivityIcon(activity.action); + const colorClass = getActivityColor(activity.action); + + return ( +
+
+ +
+
+

+ {getActivityText(activity)} +

+

+ {formatRelativeTime(activity.timestamp)} +

+
+
+ ); + })} +
+
+ ); +} diff --git a/components/layout/SidebarComponent.tsx b/components/layout/SidebarComponent.tsx index fd656b6..82818a9 100644 --- a/components/layout/SidebarComponent.tsx +++ b/components/layout/SidebarComponent.tsx @@ -21,6 +21,7 @@ import { Plus, Settings, HelpCircle, + Activity, } from "lucide-react"; import { useEffect } from "react"; import { useStorage } from "@/components/providers/StorageContext"; @@ -43,6 +44,9 @@ export function Sidebar({ className, onNewClick }: SidebarProps) { if (pathname === "/dashboard/recent") { return "recent"; } + if (pathname === "/dashboard/activity") { + return "activity"; + } if (pathname === "/dashboard/trash") { return "trash"; } @@ -69,6 +73,10 @@ export function Sidebar({ className, onNewClick }: SidebarProps) { router.push("/dashboard/starred"); break; case "recent": + router.push("/dashboard/recent"); + break; + case "activity": + router.push("/dashboard/activity"); break; case "trash": router.push("/dashboard/trash"); @@ -95,6 +103,12 @@ export function Sidebar({ className, onNewClick }: SidebarProps) { icon: Star, count: null, }, + { + id: "activity", + label: "Activity", + icon: Activity, + count: null, + }, { id: "trash", label: "Trash", diff --git a/lib/config/constants.ts b/lib/config/constants.ts index 3ed8f13..dbe4531 100644 --- a/lib/config/constants.ts +++ b/lib/config/constants.ts @@ -1,6 +1,7 @@ export const COLLECTIONS = { USERS: "users", FILES: "files", + ACTIVITIES: "activities", } as const; export const ERROR_MESSAGES = { diff --git a/lib/services/activity-logger.ts b/lib/services/activity-logger.ts new file mode 100644 index 0000000..891ad19 --- /dev/null +++ b/lib/services/activity-logger.ts @@ -0,0 +1,37 @@ +import { ObjectId } from "mongodb"; +import { connectToDatabase } from "./mongodb"; +import { ActivityAction, ActivityLog } from "@/types"; +import { COLLECTIONS } from "@/lib/config/constants"; + +interface LogActivityParams { + userId: string; + fileId: string | ObjectId; + action: ActivityAction; + fileName: string; + metadata?: { + oldName?: string; + newName?: string; + }; +} + +export async function logActivity(params: LogActivityParams): Promise { + try { + const { db } = await connectToDatabase(); + + const activityLog: ActivityLog = { + userId: params.userId, + fileId: + typeof params.fileId === "string" + ? new ObjectId(params.fileId) + : params.fileId, + action: params.action, + fileName: params.fileName, + metadata: params.metadata, + timestamp: new Date(), + }; + + await db.collection(COLLECTIONS.ACTIVITIES).insertOne(activityLog); + } catch (error) { + console.error("Failed to log activity:", error); + } +} diff --git a/lib/utils/utils.ts b/lib/utils/utils.ts index d997cfb..ed73c81 100644 --- a/lib/utils/utils.ts +++ b/lib/utils/utils.ts @@ -39,3 +39,36 @@ export function formatTimeRemaining(seconds: number): string { const remainingSeconds = Math.round(seconds % 60); return `${minutes}m ${remainingSeconds}s`; } + +export function formatRelativeTime(date: Date | string): string { + const now = new Date(); + const past = new Date(date); + const seconds = Math.floor((now.getTime() - past.getTime()) / 1000); + + if (seconds < 60) { + return "just now"; + } + + const minutes = Math.floor(seconds / 60); + if (minutes < 60) { + return `${minutes} ${minutes === 1 ? "minute" : "minutes"} ago`; + } + + const hours = Math.floor(minutes / 60); + if (hours < 24) { + return `${hours} ${hours === 1 ? "hour" : "hours"} ago`; + } + + const days = Math.floor(hours / 24); + if (days < 30) { + return `${days} ${days === 1 ? "day" : "days"} ago`; + } + + const months = Math.floor(days / 30); + if (months < 12) { + return `${months} ${months === 1 ? "month" : "months"} ago`; + } + + const years = Math.floor(months / 12); + return `${years} ${years === 1 ? "year" : "years"} ago`; +} diff --git a/types/index.ts b/types/index.ts index e584be3..54fbbc6 100644 --- a/types/index.ts +++ b/types/index.ts @@ -26,6 +26,27 @@ export interface FileDocument { deletedAt?: Date; // When the file was moved to trash (soft delete) } +export type ActivityAction = + | "upload" + | "view" + | "download" + | "rename" + | "delete" + | "restore"; + +export interface ActivityLog { + _id?: ObjectId; + userId: string; + fileId: ObjectId; + action: ActivityAction; + fileName: string; + metadata?: { + oldName?: string; + newName?: string; + }; + timestamp: Date; +} + // File upload response type export interface FileUploadResponse { success: boolean; From 681c05d1a8a2195a0d0e2273eb241ae25beb1c3e Mon Sep 17 00:00:00 2001 From: Abul Date: Fri, 21 Nov 2025 23:01:20 +0530 Subject: [PATCH 03/14] Implement pagination for activity and file retrieval --- app/api/activity/route.ts | 35 +++++-- app/api/files/recent/route.ts | 104 +++++++++++--------- app/api/files/route.ts | 40 ++++++-- app/api/files/starred/route.ts | 42 +++++++-- app/dashboard/activity/page.tsx | 78 ++++++++++++--- app/dashboard/page.tsx | 10 +- app/dashboard/recent/page.tsx | 10 +- app/dashboard/starred/page.tsx | 10 +- app/dashboard/trash/page.tsx | 10 +- components/common/PaginationControls.tsx | 115 +++++++++++++++++++++++ components/layout/DashboardLayout.tsx | 41 ++++++-- hooks/dashboard/useDashboardPage.ts | 83 ++++++++++++---- 12 files changed, 470 insertions(+), 108 deletions(-) create mode 100644 components/common/PaginationControls.tsx diff --git a/app/api/activity/route.ts b/app/api/activity/route.ts index 61502c1..418564c 100644 --- a/app/api/activity/route.ts +++ b/app/api/activity/route.ts @@ -15,16 +15,37 @@ export async function GET(request: NextRequest) { return error; } + const { searchParams } = new URL(request.url); + const page = parseInt(searchParams.get("page") || "1", 10); + const limit = parseInt(searchParams.get("limit") || "7", 10); + const skip = (page - 1) * limit; + const { db } = await connectToDatabase(); - const activities = await db - .collection(COLLECTIONS.ACTIVITIES) - .find({ userId: user!.id }) - .sort({ timestamp: -1 }) - .limit(100) - .toArray(); + const query = { userId: user!.id }; + + const [activities, totalCount] = await Promise.all([ + db + .collection(COLLECTIONS.ACTIVITIES) + .find(query) + .sort({ timestamp: -1 }) + .skip(skip) + .limit(limit) + .toArray(), + db.collection(COLLECTIONS.ACTIVITIES).countDocuments(query), + ]); + + const totalPages = Math.ceil(totalCount / limit); - return createSuccessResponse({ activities }); + return createSuccessResponse({ + activities, + pagination: { + currentPage: page, + totalPages, + totalCount, + limit, + }, + }); } catch (error) { console.error("Activity fetch error:", error); return createErrorResponse(ERROR_MESSAGES.INTERNAL_ERROR, 500); diff --git a/app/api/files/recent/route.ts b/app/api/files/recent/route.ts index 262703c..2151415 100644 --- a/app/api/files/recent/route.ts +++ b/app/api/files/recent/route.ts @@ -15,57 +15,77 @@ export async function GET(request: NextRequest) { return error; } + const { searchParams } = new URL(request.url); + const page = parseInt(searchParams.get("page") || "1", 10); + const limit = parseInt(searchParams.get("limit") || "12", 10); + const skip = (page - 1) * limit; + const { db } = await connectToDatabase(); - const recentFiles = await db - .collection(COLLECTIONS.ACTIVITIES) - .aggregate([ - { - $match: { - userId: user!.id, - action: { $in: ["upload", "view", "rename"] }, - }, - }, - { - $sort: { timestamp: -1 }, - }, - { - $group: { - _id: "$fileId", - lastActivity: { $first: "$timestamp" }, - }, - }, - { - $sort: { lastActivity: -1 }, + const basePipeline = [ + { + $match: { + userId: user!.id, + action: { $in: ["upload", "view", "rename"] }, }, - { - $lookup: { - from: COLLECTIONS.FILES, - localField: "_id", - foreignField: "_id", - as: "file", - }, + }, + { + $sort: { timestamp: -1 }, + }, + { + $group: { + _id: "$fileId", + lastActivity: { $first: "$timestamp" }, }, - { - $unwind: "$file", + }, + { + $sort: { lastActivity: -1 }, + }, + { + $lookup: { + from: COLLECTIONS.FILES, + localField: "_id", + foreignField: "_id", + as: "file", }, - { - $match: { - "file.deletedAt": { $exists: false }, - }, + }, + { + $unwind: "$file", + }, + { + $match: { + "file.deletedAt": { $exists: false }, }, - { - $replaceRoot: { newRoot: "$file" }, - }, - { - $limit: 20, - }, - ]) - .toArray(); + }, + { + $replaceRoot: { newRoot: "$file" }, + }, + ]; + + const [recentFiles, countResult] = await Promise.all([ + db + .collection(COLLECTIONS.ACTIVITIES) + .aggregate([...basePipeline, { $skip: skip }, { $limit: limit }]) + .toArray(), + db + .collection(COLLECTIONS.ACTIVITIES) + .aggregate([...basePipeline, { $count: "total" }]) + .toArray(), + ]); const files = recentFiles as FileDocument[]; + const totalCount = countResult[0]?.total || 0; + const totalPages = Math.ceil(totalCount / limit); - return createSuccessResponse({ files }); + return createSuccessResponse({ + files, + pagination: { + currentPage: page, + totalPages, + totalCount, + limit, + }, + }); } catch (error) { console.error("Recent files fetch error:", error); return createErrorResponse(ERROR_MESSAGES.INTERNAL_ERROR, 500); diff --git a/app/api/files/route.ts b/app/api/files/route.ts index 664d57c..41ee516 100644 --- a/app/api/files/route.ts +++ b/app/api/files/route.ts @@ -15,18 +15,40 @@ export async function GET(request: NextRequest) { return error; } + const { searchParams } = new URL(request.url); + const page = parseInt(searchParams.get("page") || "1", 10); + const limit = parseInt(searchParams.get("limit") || "18", 10); + const skip = (page - 1) * limit; + const { db } = await connectToDatabase(); - const files = await db - .collection(COLLECTIONS.FILES) - .find({ - userId: user!.id, - deletedAt: { $exists: false }, - }) - .sort({ uploadedAt: -1 }) - .toArray(); + const query = { + userId: user!.id, + deletedAt: { $exists: false }, + }; + + const [files, totalCount] = await Promise.all([ + db + .collection(COLLECTIONS.FILES) + .find(query) + .sort({ uploadedAt: -1 }) + .skip(skip) + .limit(limit) + .toArray(), + db.collection(COLLECTIONS.FILES).countDocuments(query), + ]); + + const totalPages = Math.ceil(totalCount / limit); - return createSuccessResponse({ files }); + return createSuccessResponse({ + files, + pagination: { + currentPage: page, + totalPages, + totalCount, + limit, + }, + }); } catch (error) { console.error("Files fetch error:", error); return createErrorResponse(ERROR_MESSAGES.INTERNAL_ERROR, 500); diff --git a/app/api/files/starred/route.ts b/app/api/files/starred/route.ts index fb2e430..5adf97c 100644 --- a/app/api/files/starred/route.ts +++ b/app/api/files/starred/route.ts @@ -15,19 +15,41 @@ export async function GET(request: NextRequest) { return error; } + const { searchParams } = new URL(request.url); + const page = parseInt(searchParams.get("page") || "1", 10); + const limit = parseInt(searchParams.get("limit") || "12", 10); + const skip = (page - 1) * limit; + const { db } = await connectToDatabase(); - const files = await db - .collection(COLLECTIONS.FILES) - .find({ - userId: user!.id, - starred: true, - deletedAt: { $exists: false }, - }) - .sort({ uploadedAt: -1 }) - .toArray(); + const query = { + userId: user!.id, + starred: true, + deletedAt: { $exists: false }, + }; + + const [files, totalCount] = await Promise.all([ + db + .collection(COLLECTIONS.FILES) + .find(query) + .sort({ uploadedAt: -1 }) + .skip(skip) + .limit(limit) + .toArray(), + db.collection(COLLECTIONS.FILES).countDocuments(query), + ]); + + const totalPages = Math.ceil(totalCount / limit); - return createSuccessResponse({ files }); + return createSuccessResponse({ + files, + pagination: { + currentPage: page, + totalPages, + totalCount, + limit, + }, + }); } catch (error) { console.error("Starred files fetch error:", error); return createErrorResponse(ERROR_MESSAGES.INTERNAL_ERROR, 500); diff --git a/app/dashboard/activity/page.tsx b/app/dashboard/activity/page.tsx index 737a6f3..d8a90d2 100644 --- a/app/dashboard/activity/page.tsx +++ b/app/dashboard/activity/page.tsx @@ -6,36 +6,76 @@ import { LoadingScreen } from "@/components/common/LoadingScreen"; import { DashboardHeader } from "@/components/layout/DashboardHeader"; import { Sidebar } from "@/components/layout/SidebarComponent"; import { ActivityList } from "@/components/activity/ActivityList"; +import { Button } from "@/components/ui/button"; import type { ActivityLog } from "@/types"; +interface PaginationMetadata { + currentPage: number; + totalPages: number; + totalCount: number; + limit: number; +} + export default function ActivityPage() { const { session, status, isAuthenticated } = useRequireAuth(); const [activities, setActivities] = useState([]); const [loading, setLoading] = useState(true); + const [loadingMore, setLoadingMore] = useState(false); const [isMobileSidebarOpen, setIsMobileSidebarOpen] = useState(false); + const [currentPage, setCurrentPage] = useState(1); + const [pagination, setPagination] = useState({ + currentPage: 1, + totalPages: 1, + totalCount: 0, + limit: 7, + }); - const fetchActivities = useCallback(async () => { - try { - const response = await fetch("/api/activity"); - if (response.ok) { - const data = await response.json(); - setActivities(data.activities); + const fetchActivities = useCallback( + async (page: number = 1, append: boolean = false) => { + if (append) { + setLoadingMore(true); + } else { + setLoading(true); } - } catch (error) { - console.error("Failed to fetch activities:", error); - } finally { - setLoading(false); - } - }, []); + try { + const response = await fetch(`/api/activity?page=${page}&limit=7`); + if (response.ok) { + const data = await response.json(); + if (append) { + setActivities(prev => [...prev, ...data.activities]); + } else { + setActivities(data.activities); + } + if (data.pagination) { + setPagination(data.pagination); + } + } + } catch (error) { + console.error("Failed to fetch activities:", error); + } finally { + setLoading(false); + setLoadingMore(false); + } + }, + [] + ); useEffect(() => { if (status === "loading" || !isAuthenticated) { return; } - fetchActivities(); + fetchActivities(1, false); }, [status, isAuthenticated, fetchActivities]); + const loadMore = () => { + const nextPage = currentPage + 1; + setCurrentPage(nextPage); + fetchActivities(nextPage, true); + }; + + const hasMore = currentPage < pagination.totalPages; + if (status === "loading") { return ; } @@ -81,6 +121,18 @@ export default function ActivityPage() {

+ {!loading && hasMore && ( +
+ +
+ )} diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx index 76022de..d9ded4c 100644 --- a/app/dashboard/page.tsx +++ b/app/dashboard/page.tsx @@ -15,7 +15,11 @@ export default function DashboardPage() { handleUploadSuccess, handleFileAction, handleNewClick, - } = useDashboardPage({ endpoint: "/api/files" }); + pagination, + goToNextPage, + goToPreviousPage, + goToPage, + } = useDashboardPage({ endpoint: "/api/files", pageSize: 18 }); if (status === "loading") { return ; @@ -36,6 +40,10 @@ export default function DashboardPage() { onFileAction={handleFileAction} onNewClick={handleNewClick} loadingMessage="Loading files..." + pagination={pagination} + onNextPage={goToNextPage} + onPreviousPage={goToPreviousPage} + onGoToPage={goToPage} /> ); } diff --git a/app/dashboard/recent/page.tsx b/app/dashboard/recent/page.tsx index 94ba4d2..f90a005 100644 --- a/app/dashboard/recent/page.tsx +++ b/app/dashboard/recent/page.tsx @@ -15,7 +15,11 @@ export default function RecentPage() { handleUploadSuccess, handleFileAction, handleNewClick, - } = useDashboardPage({ endpoint: "/api/files/recent" }); + pagination, + goToNextPage, + goToPreviousPage, + goToPage, + } = useDashboardPage({ endpoint: "/api/files/recent", pageSize: 12 }); if (status === "loading") { return ; @@ -36,6 +40,10 @@ export default function RecentPage() { onFileAction={handleFileAction} onNewClick={handleNewClick} loadingMessage="Loading recent files..." + pagination={pagination} + onNextPage={goToNextPage} + onPreviousPage={goToPreviousPage} + onGoToPage={goToPage} /> ); } diff --git a/app/dashboard/starred/page.tsx b/app/dashboard/starred/page.tsx index 6c31686..eedcf5f 100644 --- a/app/dashboard/starred/page.tsx +++ b/app/dashboard/starred/page.tsx @@ -15,7 +15,11 @@ export default function StarredPage() { handleUploadSuccess, handleFileAction, handleNewClick, - } = useDashboardPage({ endpoint: "/api/files/starred" }); + pagination, + goToNextPage, + goToPreviousPage, + goToPage, + } = useDashboardPage({ endpoint: "/api/files/starred", pageSize: 12 }); if (status === "loading") { return ; @@ -36,6 +40,10 @@ export default function StarredPage() { onFileAction={handleFileAction} onNewClick={handleNewClick} loadingMessage="Loading starred files..." + pagination={pagination} + onNextPage={goToNextPage} + onPreviousPage={goToPreviousPage} + onGoToPage={goToPage} /> ); } diff --git a/app/dashboard/trash/page.tsx b/app/dashboard/trash/page.tsx index 45be539..241bb57 100644 --- a/app/dashboard/trash/page.tsx +++ b/app/dashboard/trash/page.tsx @@ -15,7 +15,11 @@ export default function TrashPage() { handleUploadSuccess, handleFileAction, handleNewClick, - } = useDashboardPage({ endpoint: "/api/files/trash" }); + pagination, + goToNextPage, + goToPreviousPage, + goToPage, + } = useDashboardPage({ endpoint: "/api/files/trash", pageSize: 25 }); if (status === "loading") { return ; @@ -36,6 +40,10 @@ export default function TrashPage() { onFileAction={handleFileAction} onNewClick={handleNewClick} loadingMessage="Loading trash files..." + pagination={pagination} + onNextPage={goToNextPage} + onPreviousPage={goToPreviousPage} + onGoToPage={goToPage} /> ); } diff --git a/components/common/PaginationControls.tsx b/components/common/PaginationControls.tsx new file mode 100644 index 0000000..a4f322e --- /dev/null +++ b/components/common/PaginationControls.tsx @@ -0,0 +1,115 @@ +import { Button } from "@/components/ui/button"; +import { ChevronLeft, ChevronRight } from "lucide-react"; + +interface PaginationControlsProps { + currentPage: number; + totalPages: number; + totalCount: number; + pageSize: number; + onPreviousPage: () => void; + onNextPage: () => void; + onGoToPage: (page: number) => void; +} + +export function PaginationControls({ + currentPage, + totalPages, + totalCount, + pageSize, + onPreviousPage, + onNextPage, + onGoToPage, +}: PaginationControlsProps) { + if (totalCount === 0) { + return null; + } + + const startItem = (currentPage - 1) * pageSize + 1; + const endItem = Math.min(currentPage * pageSize, totalCount); + + const getPageNumbers = () => { + const pages: (number | string)[] = []; + const maxVisiblePages = 5; + + if (totalPages <= maxVisiblePages) { + for (let i = 1; i <= totalPages; i++) { + pages.push(i); + } + } else { + if (currentPage <= 3) { + pages.push(1, 2, 3, 4, "...", totalPages); + } else if (currentPage >= totalPages - 2) { + pages.push( + 1, + "...", + totalPages - 3, + totalPages - 2, + totalPages - 1, + totalPages + ); + } else { + pages.push( + 1, + "...", + currentPage - 1, + currentPage, + currentPage + 1, + "...", + totalPages + ); + } + } + + return pages; + }; + + return ( +
+
+ Showing {startItem}-{endItem} of {totalCount} +
+ +
+ + +
+ {getPageNumbers().map((page, index) => + page === "..." ? ( + + ... + + ) : ( + + ) + )} +
+ + +
+
+ ); +} diff --git a/components/layout/DashboardLayout.tsx b/components/layout/DashboardLayout.tsx index b67f9cc..f463ab7 100644 --- a/components/layout/DashboardLayout.tsx +++ b/components/layout/DashboardLayout.tsx @@ -5,8 +5,16 @@ import { DashboardHeader } from "@/components/layout/DashboardHeader"; import { Sidebar } from "@/components/layout/SidebarComponent"; import { FileDisplay } from "@/components/files/FileDisplay"; import { UploadModal } from "@/components/modals/UploadModal"; +import { PaginationControls } from "@/components/common/PaginationControls"; import type { FileDocument } from "@/types"; +interface PaginationMetadata { + currentPage: number; + totalPages: number; + totalCount: number; + limit: number; +} + interface DashboardLayoutProps { files: FileDocument[]; loading: boolean; @@ -17,6 +25,10 @@ interface DashboardLayoutProps { onFileAction: () => void; onNewClick: () => void; loadingMessage?: string; + pagination?: PaginationMetadata; + onNextPage?: () => void; + onPreviousPage?: () => void; + onGoToPage?: (page: number) => void; } export function DashboardLayout({ @@ -29,6 +41,10 @@ export function DashboardLayout({ onFileAction, onNewClick, loadingMessage = "Loading files...", + pagination, + onNextPage, + onPreviousPage, + onGoToPage, }: DashboardLayoutProps) { const [isMobileSidebarOpen, setIsMobileSidebarOpen] = useState(false); @@ -71,12 +87,25 @@ export function DashboardLayout({

{loadingMessage}

) : ( - + <> + + {pagination && onNextPage && onPreviousPage && onGoToPage && ( + + )} + )} diff --git a/hooks/dashboard/useDashboardPage.ts b/hooks/dashboard/useDashboardPage.ts index 86b6d27..3253bc1 100644 --- a/hooks/dashboard/useDashboardPage.ts +++ b/hooks/dashboard/useDashboardPage.ts @@ -5,44 +5,71 @@ import type { FileDocument } from "@/types"; interface UseDashboardPageOptions { endpoint: string; + pageSize: number; } -export function useDashboardPage({ endpoint }: UseDashboardPageOptions) { +interface PaginationMetadata { + currentPage: number; + totalPages: number; + totalCount: number; + limit: number; +} + +export function useDashboardPage({ + endpoint, + pageSize, +}: UseDashboardPageOptions) { const { session, status, isAuthenticated } = useRequireAuth(); const { refreshStorage } = useStorage(); const [files, setFiles] = useState([]); const [loading, setLoading] = useState(true); const [isUploadModalOpen, setIsUploadModalOpen] = useState(false); + const [currentPage, setCurrentPage] = useState(1); + const [pagination, setPagination] = useState({ + currentPage: 1, + totalPages: 1, + totalCount: 0, + limit: pageSize, + }); - const fetchFiles = useCallback(async () => { - try { - const response = await fetch(endpoint); - if (response.ok) { - const data = await response.json(); - setFiles(data.files); + const fetchFiles = useCallback( + async (page: number = currentPage) => { + setLoading(true); + try { + const url = `${endpoint}?page=${page}&limit=${pageSize}`; + const response = await fetch(url); + if (response.ok) { + const data = await response.json(); + setFiles(data.files); + if (data.pagination) { + setPagination(data.pagination); + } + } + } catch (error) { + console.error("Failed to fetch files:", error); + } finally { + setLoading(false); } - } catch (error) { - console.error("Failed to fetch files:", error); - } finally { - setLoading(false); - } - }, [endpoint]); + }, + [endpoint, pageSize, currentPage] + ); useEffect(() => { if (status === "loading" || !isAuthenticated) { return; } - fetchFiles(); - }, [status, isAuthenticated, fetchFiles]); + fetchFiles(currentPage); + }, [status, isAuthenticated, currentPage, fetchFiles]); const handleUploadSuccess = async () => { - await fetchFiles(); + setCurrentPage(1); + await fetchFiles(1); await refreshStorage(); }; const handleFileAction = async () => { - await fetchFiles(); + await fetchFiles(currentPage); await refreshStorage(); }; @@ -50,6 +77,24 @@ export function useDashboardPage({ endpoint }: UseDashboardPageOptions) { setIsUploadModalOpen(true); }; + const goToNextPage = () => { + if (currentPage < pagination.totalPages) { + setCurrentPage(prev => prev + 1); + } + }; + + const goToPreviousPage = () => { + if (currentPage > 1) { + setCurrentPage(prev => prev - 1); + } + }; + + const goToPage = (page: number) => { + if (page >= 1 && page <= pagination.totalPages) { + setCurrentPage(page); + } + }; + return { session, status, @@ -60,5 +105,9 @@ export function useDashboardPage({ endpoint }: UseDashboardPageOptions) { handleUploadSuccess, handleFileAction, handleNewClick, + pagination, + goToNextPage, + goToPreviousPage, + goToPage, }; } From 616fefb2be0d36dce2429a721364794c415a5e77 Mon Sep 17 00:00:00 2001 From: Abul Date: Tue, 9 Dec 2025 00:32:59 +0530 Subject: [PATCH 04/14] update --- package-lock.json | 2561 +++++++++++++++++++++++++++++++-------------- package.json | 4 +- 2 files changed, 1776 insertions(+), 789 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1f6d6e6..3c33d12 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,7 @@ "csrf": "^3.1.0", "lucide-react": "^0.542.0", "mongodb": "^6.19.0", - "next": "15.5.2", + "next": "16.0.7", "next-auth": "^4.24.11", "next-themes": "^0.4.6", "react": "19.1.0", @@ -37,7 +37,7 @@ "@types/react": "^19", "@types/react-dom": "^19", "eslint": "^9", - "eslint-config-next": "15.5.2", + "eslint-config-next": "16.0.7", "eslint-config-prettier": "^10.1.8", "prettier": "^3.6.2", "tailwindcss": "^4", @@ -71,13 +71,13 @@ } }, "node_modules/@azure/core-auth": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.10.0.tgz", - "integrity": "sha512-88Djs5vBvGbHQHf5ZZcaoNHo6Y8BKZkt3cw2iuJIQzLEgH4Ox6Tm4hjFhbqOxyYsgIG/eJbFEHpxRIfEEWv5Ow==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.10.1.tgz", + "integrity": "sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg==", "license": "MIT", "dependencies": { - "@azure/abort-controller": "^2.0.0", - "@azure/core-util": "^1.11.0", + "@azure/abort-controller": "^2.1.2", + "@azure/core-util": "^1.13.0", "tslib": "^2.6.2" }, "engines": { @@ -85,17 +85,17 @@ } }, "node_modules/@azure/core-client": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.10.0.tgz", - "integrity": "sha512-O4aP3CLFNodg8eTHXECaH3B3CjicfzkxVtnrfLkOq0XNP7TIECGfHpK/C6vADZkWP75wzmdBnsIA8ksuJMk18g==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.10.1.tgz", + "integrity": "sha512-Nh5PhEOeY6PrnxNPsEHRr9eimxLwgLlpmguQaHKBinFYA/RU9+kOYVOQqOrTsCL+KSxrLLl1gD8Dk5BFW/7l/w==", "license": "MIT", "dependencies": { - "@azure/abort-controller": "^2.0.0", - "@azure/core-auth": "^1.4.0", - "@azure/core-rest-pipeline": "^1.20.0", - "@azure/core-tracing": "^1.0.0", - "@azure/core-util": "^1.6.1", - "@azure/logger": "^1.0.0", + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.10.0", + "@azure/core-rest-pipeline": "^1.22.0", + "@azure/core-tracing": "^1.3.0", + "@azure/core-util": "^1.13.0", + "@azure/logger": "^1.3.0", "tslib": "^2.6.2" }, "engines": { @@ -103,17 +103,17 @@ } }, "node_modules/@azure/core-http-compat": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-2.3.0.tgz", - "integrity": "sha512-qLQujmUypBBG0gxHd0j6/Jdmul6ttl24c8WGiLXIk7IHXdBlfoBqW27hyz3Xn6xbfdyVSarl1Ttbk0AwnZBYCw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-2.3.1.tgz", + "integrity": "sha512-az9BkXND3/d5VgdRRQVkiJb2gOmDU8Qcq4GvjtBmDICNiQ9udFmDk4ZpSB5Qq1OmtDJGlQAfBaS4palFsazQ5g==", "license": "MIT", "dependencies": { - "@azure/abort-controller": "^2.0.0", - "@azure/core-client": "^1.3.0", - "@azure/core-rest-pipeline": "^1.20.0" + "@azure/abort-controller": "^2.1.2", + "@azure/core-client": "^1.10.0", + "@azure/core-rest-pipeline": "^1.22.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@azure/core-lro": { @@ -144,16 +144,16 @@ } }, "node_modules/@azure/core-rest-pipeline": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.22.0.tgz", - "integrity": "sha512-OKHmb3/Kpm06HypvB3g6Q3zJuvyXcpxDpCS1PnU8OV6AJgSFaee/covXBcPbWc6XDDxtEPlbi3EMQ6nUiPaQtw==", + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.22.2.tgz", + "integrity": "sha512-MzHym+wOi8CLUlKCQu12de0nwcq9k9Kuv43j4Wa++CsCpJwps2eeBQwD2Bu8snkxTtDKDx4GwjuR9E8yC8LNrg==", "license": "MIT", "dependencies": { - "@azure/abort-controller": "^2.0.0", - "@azure/core-auth": "^1.8.0", - "@azure/core-tracing": "^1.0.1", - "@azure/core-util": "^1.11.0", - "@azure/logger": "^1.0.0", + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.10.0", + "@azure/core-tracing": "^1.3.0", + "@azure/core-util": "^1.13.0", + "@azure/logger": "^1.3.0", "@typespec/ts-http-runtime": "^0.3.0", "tslib": "^2.6.2" }, @@ -162,9 +162,9 @@ } }, "node_modules/@azure/core-tracing": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.3.0.tgz", - "integrity": "sha512-+XvmZLLWPe67WXNZo9Oc9CrPj/Tm8QnHR92fFAFdnbzwNdCH1h+7UdpaQgRSBsMY+oW1kHXNUZQLdZ1gHX3ROw==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.3.1.tgz", + "integrity": "sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ==", "license": "MIT", "dependencies": { "tslib": "^2.6.2" @@ -174,12 +174,12 @@ } }, "node_modules/@azure/core-util": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.13.0.tgz", - "integrity": "sha512-o0psW8QWQ58fq3i24Q1K2XfS/jYTxr7O1HRcyUE9bV9NttLU+kYOH82Ixj8DGlMTOWgxm1Sss2QAfKK5UkSPxw==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.13.1.tgz", + "integrity": "sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A==", "license": "MIT", "dependencies": { - "@azure/abort-controller": "^2.0.0", + "@azure/abort-controller": "^2.1.2", "@typespec/ts-http-runtime": "^0.3.0", "tslib": "^2.6.2" }, @@ -214,9 +214,9 @@ } }, "node_modules/@azure/storage-blob": { - "version": "12.28.0", - "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.28.0.tgz", - "integrity": "sha512-VhQHITXXO03SURhDiGuHhvc/k/sD2WvJUS7hqhiVNbErVCuQoLtWql7r97fleBlIRKHJaa9R7DpBjfE0pfLYcA==", + "version": "12.29.1", + "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.29.1.tgz", + "integrity": "sha512-7ktyY0rfTM0vo7HvtK6E3UvYnI9qfd6Oz6z/+92VhGRveWng3kJwMKeUpqmW/NmwcDNbxHpSlldG+vsUnRFnBg==", "license": "MIT", "dependencies": { "@azure/abort-controller": "^2.1.2", @@ -230,7 +230,7 @@ "@azure/core-util": "^1.11.0", "@azure/core-xml": "^1.4.5", "@azure/logger": "^1.1.4", - "@azure/storage-common": "^12.0.0-beta.2", + "@azure/storage-common": "^12.1.1", "events": "^3.0.0", "tslib": "^2.8.1" }, @@ -239,9 +239,9 @@ } }, "node_modules/@azure/storage-common": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@azure/storage-common/-/storage-common-12.0.0.tgz", - "integrity": "sha512-QyEWXgi4kdRo0wc1rHum9/KnaWZKCdQGZK1BjU4fFL6Jtedp7KLbQihgTTVxldFy1z1ZPtuDPx8mQ5l3huPPbA==", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@azure/storage-common/-/storage-common-12.1.1.tgz", + "integrity": "sha512-eIOH1pqFwI6UmVNnDQvmFeSg0XppuzDLFeUNO/Xht7ODAzRLgGDh7h550pSxoA+lPDxBl1+D2m/KG3jWzCUjTg==", "license": "MIT", "dependencies": { "@azure/abort-controller": "^2.1.2", @@ -258,19 +258,259 @@ "node": ">=20.0.0" } }, - "node_modules/@babel/runtime": { + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", + "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", + "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.3.tgz", - "integrity": "sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.5" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.5", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, "engines": { "node": ">=6.9.0" } }, "node_modules/@emnapi/core": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.5.0.tgz", - "integrity": "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.1.tgz", + "integrity": "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==", "dev": true, "license": "MIT", "optional": true, @@ -280,9 +520,9 @@ } }, "node_modules/@emnapi/runtime": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.5.0.tgz", - "integrity": "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", + "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", "license": "MIT", "optional": true, "dependencies": { @@ -301,9 +541,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.8.0.tgz", - "integrity": "sha512-MJQFqrZgcW0UNYLGOuQpey/oTN59vyWwplvCGZztn1cKz9agZPPYpJB7h2OMmuu7VLqkvEjN8feFZJmxNF9D+Q==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", "dev": true, "license": "MIT", "dependencies": { @@ -333,9 +573,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, "license": "MIT", "engines": { @@ -343,13 +583,13 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", - "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.6", + "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.2" }, @@ -358,19 +598,22 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", - "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", "dev": true, "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/core": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", - "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -381,9 +624,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", - "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", + "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", "dev": true, "license": "MIT", "dependencies": { @@ -393,7 +636,7 @@ "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", + "js-yaml": "^4.1.1", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" }, @@ -405,9 +648,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.34.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.34.0.tgz", - "integrity": "sha512-EoyvqQnBNsV1CWaEJ559rxXL4c8V92gxirbawSmVUOWXlsRxxQXl6LmCpdUblgxgSkDIqKnhzba2SjRTI/A5Rw==", + "version": "9.39.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz", + "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==", "dev": true, "license": "MIT", "engines": { @@ -418,9 +661,9 @@ } }, "node_modules/@eslint/object-schema": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", - "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -428,13 +671,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", - "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.15.2", + "@eslint/core": "^0.17.0", "levn": "^0.4.1" }, "engines": { @@ -531,10 +774,20 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.3.tgz", - "integrity": "sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", "cpu": [ "arm64" ], @@ -550,13 +803,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.0" + "@img/sharp-libvips-darwin-arm64": "1.2.4" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.3.tgz", - "integrity": "sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", "cpu": [ "x64" ], @@ -572,13 +825,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.0" + "@img/sharp-libvips-darwin-x64": "1.2.4" } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.0.tgz", - "integrity": "sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", "cpu": [ "arm64" ], @@ -592,9 +845,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.0.tgz", - "integrity": "sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", "cpu": [ "x64" ], @@ -608,9 +861,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.0.tgz", - "integrity": "sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", "cpu": [ "arm" ], @@ -624,9 +877,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.0.tgz", - "integrity": "sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", "cpu": [ "arm64" ], @@ -640,9 +893,9 @@ } }, "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.0.tgz", - "integrity": "sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", "cpu": [ "ppc64" ], @@ -655,10 +908,26 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.0.tgz", - "integrity": "sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", "cpu": [ "s390x" ], @@ -672,9 +941,9 @@ } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.0.tgz", - "integrity": "sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", "cpu": [ "x64" ], @@ -688,9 +957,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.0.tgz", - "integrity": "sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", "cpu": [ "arm64" ], @@ -704,9 +973,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.0.tgz", - "integrity": "sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", "cpu": [ "x64" ], @@ -720,9 +989,9 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.3.tgz", - "integrity": "sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", "cpu": [ "arm" ], @@ -738,13 +1007,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.0" + "@img/sharp-libvips-linux-arm": "1.2.4" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.3.tgz", - "integrity": "sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", "cpu": [ "arm64" ], @@ -760,13 +1029,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.0" + "@img/sharp-libvips-linux-arm64": "1.2.4" } }, "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.3.tgz", - "integrity": "sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", "cpu": [ "ppc64" ], @@ -782,13 +1051,35 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.0" + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.3.tgz", - "integrity": "sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", "cpu": [ "s390x" ], @@ -804,13 +1095,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.0" + "@img/sharp-libvips-linux-s390x": "1.2.4" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.3.tgz", - "integrity": "sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", "cpu": [ "x64" ], @@ -826,13 +1117,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.0" + "@img/sharp-libvips-linux-x64": "1.2.4" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.3.tgz", - "integrity": "sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", "cpu": [ "arm64" ], @@ -848,13 +1139,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.0" + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.3.tgz", - "integrity": "sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", "cpu": [ "x64" ], @@ -870,20 +1161,20 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.0" + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.3.tgz", - "integrity": "sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", "cpu": [ "wasm32" ], "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.4.4" + "@emnapi/runtime": "^1.7.0" }, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" @@ -893,9 +1184,9 @@ } }, "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.3.tgz", - "integrity": "sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", "cpu": [ "arm64" ], @@ -912,9 +1203,9 @@ } }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.3.tgz", - "integrity": "sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", "cpu": [ "ia32" ], @@ -931,9 +1222,9 @@ } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.3.tgz", - "integrity": "sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", "cpu": [ "x64" ], @@ -949,19 +1240,6 @@ "url": "https://opencollective.com/libvips" } }, - "node_modules/@isaacs/fs-minipass": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", - "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.4" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -1002,9 +1280,9 @@ "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.30", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", - "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dev": true, "license": "MIT", "dependencies": { @@ -1013,9 +1291,9 @@ } }, "node_modules/@mongodb-js/saslprep": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.3.0.tgz", - "integrity": "sha512-zlayKCsIjYb7/IdfqxorK5+xUMyi4vOKcFy10wKJYc63NSdKI8mNME+uJqfatkPmOSMMUiojrL58IePKBm3gvQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.3.2.tgz", + "integrity": "sha512-QgA5AySqB27cGTXBFmnpifAi7HxoGUeezwo6p9dI03MuDB6Pp33zgclqVb6oVK3j6I9Vesg0+oojW2XxB59SGg==", "license": "MIT", "dependencies": { "sparse-bitfield": "^3.0.3" @@ -1035,15 +1313,15 @@ } }, "node_modules/@next/env": { - "version": "15.5.2", - "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.2.tgz", - "integrity": "sha512-Qe06ew4zt12LeO6N7j8/nULSOe3fMXE4dM6xgpBQNvdzyK1sv5y4oAP3bq4LamrvGCZtmRYnW8URFCeX5nFgGg==", + "version": "16.0.7", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.0.7.tgz", + "integrity": "sha512-gpaNgUh5nftFKRkRQGnVi5dpcYSKGcZZkQffZ172OrG/XkrnS7UBTQ648YY+8ME92cC4IojpI2LqTC8sTDhAaw==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { - "version": "15.5.2", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.5.2.tgz", - "integrity": "sha512-lkLrRVxcftuOsJNhWatf1P2hNVfh98k/omQHrCEPPriUypR6RcS13IvLdIrEvkm9AH2Nu2YpR5vLqBuy6twH3Q==", + "version": "16.0.7", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.0.7.tgz", + "integrity": "sha512-hFrTNZcMEG+k7qxVxZJq3F32Kms130FAhG8lvw2zkKBgAcNOJIxlljNiCjGygvBshvaGBdf88q2CqWtnqezDHA==", "dev": true, "license": "MIT", "dependencies": { @@ -1051,9 +1329,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "15.5.2", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.2.tgz", - "integrity": "sha512-8bGt577BXGSd4iqFygmzIfTYizHb0LGWqH+qgIF/2EDxS5JsSdERJKA8WgwDyNBZgTIIA4D8qUtoQHmxIIquoQ==", + "version": "16.0.7", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.0.7.tgz", + "integrity": "sha512-LlDtCYOEj/rfSnEn/Idi+j1QKHxY9BJFmxx7108A6D8K0SB+bNgfYQATPk/4LqOl4C0Wo3LACg2ie6s7xqMpJg==", "cpu": [ "arm64" ], @@ -1067,9 +1345,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "15.5.2", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.2.tgz", - "integrity": "sha512-2DjnmR6JHK4X+dgTXt5/sOCu/7yPtqpYt8s8hLkHFK3MGkka2snTv3yRMdHvuRtJVkPwCGsvBSwmoQCHatauFQ==", + "version": "16.0.7", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.0.7.tgz", + "integrity": "sha512-rtZ7BhnVvO1ICf3QzfW9H3aPz7GhBrnSIMZyr4Qy6boXF0b5E3QLs+cvJmg3PsTCG2M1PBoC+DANUi4wCOKXpA==", "cpu": [ "x64" ], @@ -1083,9 +1361,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "15.5.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.2.tgz", - "integrity": "sha512-3j7SWDBS2Wov/L9q0mFJtEvQ5miIqfO4l7d2m9Mo06ddsgUK8gWfHGgbjdFlCp2Ek7MmMQZSxpGFqcC8zGh2AA==", + "version": "16.0.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.0.7.tgz", + "integrity": "sha512-mloD5WcPIeIeeZqAIP5c2kdaTa6StwP4/2EGy1mUw8HiexSHGK/jcM7lFuS3u3i2zn+xH9+wXJs6njO7VrAqww==", "cpu": [ "arm64" ], @@ -1099,9 +1377,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "15.5.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.2.tgz", - "integrity": "sha512-s6N8k8dF9YGc5T01UPQ08yxsK6fUow5gG1/axWc1HVVBYQBgOjca4oUZF7s4p+kwhkB1bDSGR8QznWrFZ/Rt5g==", + "version": "16.0.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.0.7.tgz", + "integrity": "sha512-+ksWNrZrthisXuo9gd1XnjHRowCbMtl/YgMpbRvFeDEqEBd523YHPWpBuDjomod88U8Xliw5DHhekBC3EOOd9g==", "cpu": [ "arm64" ], @@ -1115,9 +1393,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "15.5.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.2.tgz", - "integrity": "sha512-o1RV/KOODQh6dM6ZRJGZbc+MOAHww33Vbs5JC9Mp1gDk8cpEO+cYC/l7rweiEalkSm5/1WGa4zY7xrNwObN4+Q==", + "version": "16.0.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.0.7.tgz", + "integrity": "sha512-4WtJU5cRDxpEE44Ana2Xro1284hnyVpBb62lIpU5k85D8xXxatT+rXxBgPkc7C1XwkZMWpK5rXLXTh9PFipWsA==", "cpu": [ "x64" ], @@ -1131,9 +1409,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "15.5.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.2.tgz", - "integrity": "sha512-/VUnh7w8RElYZ0IV83nUcP/J4KJ6LLYliiBIri3p3aW2giF+PAVgZb6mk8jbQSB3WlTai8gEmCAr7kptFa1H6g==", + "version": "16.0.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.0.7.tgz", + "integrity": "sha512-HYlhqIP6kBPXalW2dbMTSuB4+8fe+j9juyxwfMwCe9kQPPeiyFn7NMjNfoFOfJ2eXkeQsoUGXg+O2SE3m4Qg2w==", "cpu": [ "x64" ], @@ -1147,9 +1425,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "15.5.2", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.2.tgz", - "integrity": "sha512-sMPyTvRcNKXseNQ/7qRfVRLa0VhR0esmQ29DD6pqvG71+JdVnESJaHPA8t7bc67KD5spP3+DOCNLhqlEI2ZgQg==", + "version": "16.0.7", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.0.7.tgz", + "integrity": "sha512-EviG+43iOoBRZg9deGauXExjRphhuYmIOJ12b9sAPy0eQ6iwcPxfED2asb/s2/yiLYOdm37kPaiZu8uXSYPs0Q==", "cpu": [ "arm64" ], @@ -1163,9 +1441,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "15.5.2", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.2.tgz", - "integrity": "sha512-W5VvyZHnxG/2ukhZF/9Ikdra5fdNftxI6ybeVKYvBPDtyx7x4jPPSNduUkfH5fo3zG0JQ0bPxgy41af2JX5D4Q==", + "version": "16.0.7", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.0.7.tgz", + "integrity": "sha512-gniPjy55zp5Eg0896qSrf3yB1dw4F/3s8VK1ephdsZZ129j2n6e1WqCbE2YgcKhW9hPB9TVZENugquWJD5x0ug==", "cpu": [ "x64" ], @@ -1264,14 +1542,55 @@ } } }, + "node_modules/@radix-ui/react-arrow/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-arrow/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-avatar": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.1.10.tgz", - "integrity": "sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog==", + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.1.11.tgz", + "integrity": "sha512-0Qk603AHGV28BOBO34p7IgD5m+V5Sg/YovfayABkoDDBM5d3NCx0Mp4gGrjzLGes1jV5eNOE1r3itqOR33VC6Q==", "license": "MIT", "dependencies": { - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-context": "1.1.3", + "@radix-ui/react-primitive": "2.1.4", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-is-hydrated": "0.1.0", "@radix-ui/react-use-layout-effect": "1.1.1" @@ -1317,10 +1636,10 @@ } } }, - "node_modules/@radix-ui/react-compose-refs": { + "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-context": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", - "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -1332,10 +1651,66 @@ } } }, - "node_modules/@radix-ui/react-context": { + "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-compose-refs": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", - "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.3.tgz", + "integrity": "sha512-ieIFACdMpYfMEjF0rEf5KLvfVyIkOz6PDGyNnP+u+4xQ6jny3VCgA4OgXOwNx2aUkxn8zx9fiVcM8CfFYv9Lxw==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -1383,6 +1758,62 @@ } } }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-direction": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", @@ -1425,6 +1856,47 @@ } } }, + "node_modules/@radix-ui/react-dismissable-layer/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-dropdown-menu": { "version": "2.1.16", "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.16.tgz", @@ -1454,6 +1926,62 @@ } } }, + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-focus-guards": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", @@ -1494,6 +2022,47 @@ } } }, + "node_modules/@radix-ui/react-focus-scope/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-id": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", @@ -1552,6 +2121,62 @@ } } }, + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-popper": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz", @@ -1584,6 +2209,62 @@ } } }, + "node_modules/@radix-ui/react-popper/node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-portal": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", @@ -1608,6 +2289,47 @@ } } }, + "node_modules/@radix-ui/react-portal/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-presence": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", @@ -1633,12 +2355,12 @@ } }, "node_modules/@radix-ui/react-primitive": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", - "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz", + "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==", "license": "MIT", "dependencies": { - "@radix-ui/react-slot": "1.2.3" + "@radix-ui/react-slot": "1.2.4" }, "peerDependencies": { "@types/react": "*", @@ -1656,13 +2378,13 @@ } }, "node_modules/@radix-ui/react-progress": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.7.tgz", - "integrity": "sha512-vPdg/tF6YC/ynuBIJlk1mm7Le0VgW6ub6J2UWnTQ7/D23KXcPI1qy+0vBkgKgd38RCMJavBXpB83HPNFMTb0Fg==", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.8.tgz", + "integrity": "sha512-+gISHcSPUJ7ktBy9RnTqbdKW78bcGke3t6taawyZ71pio1JewwGSJizycs7rLhGTvMJYCQB1DBK4KQsxs7U8dA==", "license": "MIT", "dependencies": { - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.3" + "@radix-ui/react-context": "1.1.3", + "@radix-ui/react-primitive": "2.1.4" }, "peerDependencies": { "@types/react": "*", @@ -1710,7 +2432,45 @@ } } }, - "node_modules/@radix-ui/react-slot": { + "node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/react-slot": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", @@ -1728,6 +2488,24 @@ } } }, + "node_modules/@radix-ui/react-slot": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz", + "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-tooltip": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.8.tgz", @@ -1762,6 +2540,62 @@ } } }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-use-callback-ref": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", @@ -1924,6 +2758,47 @@ } } }, + "node_modules/@radix-ui/react-visually-hidden/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-visually-hidden/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/rect": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz", @@ -1937,13 +2812,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@rushstack/eslint-patch": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.12.0.tgz", - "integrity": "sha512-5EwMtOqvJMMa3HbmxLlF74e+3/HhwBTMcvt3nqVJgGCozO6hzIPOBlwm8mGVNR9SN2IJpxSnlxczyDjcn7qIyw==", - "dev": true, - "license": "MIT" - }, "node_modules/@swc/helpers": { "version": "0.5.15", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", @@ -1954,54 +2822,49 @@ } }, "node_modules/@tailwindcss/node": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.13.tgz", - "integrity": "sha512-eq3ouolC1oEFOAvOMOBAmfCIqZBJuvWvvYWh5h5iOYfe1HFC6+GZ6EIL0JdM3/niGRJmnrOc+8gl9/HGUaaptw==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.17.tgz", + "integrity": "sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg==", "dev": true, "license": "MIT", "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", - "jiti": "^2.5.1", - "lightningcss": "1.30.1", - "magic-string": "^0.30.18", + "jiti": "^2.6.1", + "lightningcss": "1.30.2", + "magic-string": "^0.30.21", "source-map-js": "^1.2.1", - "tailwindcss": "4.1.13" + "tailwindcss": "4.1.17" } }, "node_modules/@tailwindcss/oxide": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.13.tgz", - "integrity": "sha512-CPgsM1IpGRa880sMbYmG1s4xhAy3xEt1QULgTJGQmZUeNgXFR7s1YxYygmJyBGtou4SyEosGAGEeYqY7R53bIA==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.17.tgz", + "integrity": "sha512-F0F7d01fmkQhsTjXezGBLdrl1KresJTcI3DB8EkScCldyKp3Msz4hub4uyYaVnk88BAS1g5DQjjF6F5qczheLA==", "dev": true, - "hasInstallScript": true, "license": "MIT", - "dependencies": { - "detect-libc": "^2.0.4", - "tar": "^7.4.3" - }, "engines": { "node": ">= 10" }, "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.1.13", - "@tailwindcss/oxide-darwin-arm64": "4.1.13", - "@tailwindcss/oxide-darwin-x64": "4.1.13", - "@tailwindcss/oxide-freebsd-x64": "4.1.13", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.13", - "@tailwindcss/oxide-linux-arm64-gnu": "4.1.13", - "@tailwindcss/oxide-linux-arm64-musl": "4.1.13", - "@tailwindcss/oxide-linux-x64-gnu": "4.1.13", - "@tailwindcss/oxide-linux-x64-musl": "4.1.13", - "@tailwindcss/oxide-wasm32-wasi": "4.1.13", - "@tailwindcss/oxide-win32-arm64-msvc": "4.1.13", - "@tailwindcss/oxide-win32-x64-msvc": "4.1.13" + "@tailwindcss/oxide-android-arm64": "4.1.17", + "@tailwindcss/oxide-darwin-arm64": "4.1.17", + "@tailwindcss/oxide-darwin-x64": "4.1.17", + "@tailwindcss/oxide-freebsd-x64": "4.1.17", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.17", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.17", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.17", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.17", + "@tailwindcss/oxide-linux-x64-musl": "4.1.17", + "@tailwindcss/oxide-wasm32-wasi": "4.1.17", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.17", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.17" } }, "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.13.tgz", - "integrity": "sha512-BrpTrVYyejbgGo57yc8ieE+D6VT9GOgnNdmh5Sac6+t0m+v+sKQevpFVpwX3pBrM2qKrQwJ0c5eDbtjouY/+ew==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.17.tgz", + "integrity": "sha512-BMqpkJHgOZ5z78qqiGE6ZIRExyaHyuxjgrJ6eBO5+hfrfGkuya0lYfw8fRHG77gdTjWkNWEEm+qeG2cDMxArLQ==", "cpu": [ "arm64" ], @@ -2016,9 +2879,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.13.tgz", - "integrity": "sha512-YP+Jksc4U0KHcu76UhRDHq9bx4qtBftp9ShK/7UGfq0wpaP96YVnnjFnj3ZFrUAjc5iECzODl/Ts0AN7ZPOANQ==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.17.tgz", + "integrity": "sha512-EquyumkQweUBNk1zGEU/wfZo2qkp/nQKRZM8bUYO0J+Lums5+wl2CcG1f9BgAjn/u9pJzdYddHWBiFXJTcxmOg==", "cpu": [ "arm64" ], @@ -2033,9 +2896,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.13.tgz", - "integrity": "sha512-aAJ3bbwrn/PQHDxCto9sxwQfT30PzyYJFG0u/BWZGeVXi5Hx6uuUOQEI2Fa43qvmUjTRQNZnGqe9t0Zntexeuw==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.17.tgz", + "integrity": "sha512-gdhEPLzke2Pog8s12oADwYu0IAw04Y2tlmgVzIN0+046ytcgx8uZmCzEg4VcQh+AHKiS7xaL8kGo/QTiNEGRog==", "cpu": [ "x64" ], @@ -2050,9 +2913,9 @@ } }, "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.13.tgz", - "integrity": "sha512-Wt8KvASHwSXhKE/dJLCCWcTSVmBj3xhVhp/aF3RpAhGeZ3sVo7+NTfgiN8Vey/Fi8prRClDs6/f0KXPDTZE6nQ==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.17.tgz", + "integrity": "sha512-hxGS81KskMxML9DXsaXT1H0DyA+ZBIbyG/sSAjWNe2EDl7TkPOBI42GBV3u38itzGUOmFfCzk1iAjDXds8Oh0g==", "cpu": [ "x64" ], @@ -2067,9 +2930,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.13.tgz", - "integrity": "sha512-mbVbcAsW3Gkm2MGwA93eLtWrwajz91aXZCNSkGTx/R5eb6KpKD5q8Ueckkh9YNboU8RH7jiv+ol/I7ZyQ9H7Bw==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.17.tgz", + "integrity": "sha512-k7jWk5E3ldAdw0cNglhjSgv501u7yrMf8oeZ0cElhxU6Y2o7f8yqelOp3fhf7evjIS6ujTI3U8pKUXV2I4iXHQ==", "cpu": [ "arm" ], @@ -2084,9 +2947,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.13.tgz", - "integrity": "sha512-wdtfkmpXiwej/yoAkrCP2DNzRXCALq9NVLgLELgLim1QpSfhQM5+ZxQQF8fkOiEpuNoKLp4nKZ6RC4kmeFH0HQ==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.17.tgz", + "integrity": "sha512-HVDOm/mxK6+TbARwdW17WrgDYEGzmoYayrCgmLEw7FxTPLcp/glBisuyWkFz/jb7ZfiAXAXUACfyItn+nTgsdQ==", "cpu": [ "arm64" ], @@ -2101,9 +2964,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.13.tgz", - "integrity": "sha512-hZQrmtLdhyqzXHB7mkXfq0IYbxegaqTmfa1p9MBj72WPoDD3oNOh1Lnxf6xZLY9C3OV6qiCYkO1i/LrzEdW2mg==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.17.tgz", + "integrity": "sha512-HvZLfGr42i5anKtIeQzxdkw/wPqIbpeZqe7vd3V9vI3RQxe3xU1fLjss0TjyhxWcBaipk7NYwSrwTwK1hJARMg==", "cpu": [ "arm64" ], @@ -2118,9 +2981,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.13.tgz", - "integrity": "sha512-uaZTYWxSXyMWDJZNY1Ul7XkJTCBRFZ5Fo6wtjrgBKzZLoJNrG+WderJwAjPzuNZOnmdrVg260DKwXCFtJ/hWRQ==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.17.tgz", + "integrity": "sha512-M3XZuORCGB7VPOEDH+nzpJ21XPvK5PyjlkSFkFziNHGLc5d6g3di2McAAblmaSUNl8IOmzYwLx9NsE7bplNkwQ==", "cpu": [ "x64" ], @@ -2135,9 +2998,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.13.tgz", - "integrity": "sha512-oXiPj5mi4Hdn50v5RdnuuIms0PVPI/EG4fxAfFiIKQh5TgQgX7oSuDWntHW7WNIi/yVLAiS+CRGW4RkoGSSgVQ==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.17.tgz", + "integrity": "sha512-k7f+pf9eXLEey4pBlw+8dgfJHY4PZ5qOUFDyNf7SI6lHjQ9Zt7+NcscjpwdCEbYi6FI5c2KDTDWyf2iHcCSyyQ==", "cpu": [ "x64" ], @@ -2152,9 +3015,9 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.13.tgz", - "integrity": "sha512-+LC2nNtPovtrDwBc/nqnIKYh/W2+R69FA0hgoeOn64BdCX522u19ryLh3Vf3F8W49XBcMIxSe665kwy21FkhvA==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.17.tgz", + "integrity": "sha512-cEytGqSSoy7zK4JRWiTCx43FsKP/zGr0CsuMawhH67ONlH+T79VteQeJQRO/X7L0juEUA8ZyuYikcRBf0vsxhg==", "bundleDependencies": [ "@napi-rs/wasm-runtime", "@emnapi/core", @@ -2170,21 +3033,21 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.4.5", - "@emnapi/runtime": "^1.4.5", - "@emnapi/wasi-threads": "^1.0.4", - "@napi-rs/wasm-runtime": "^0.2.12", - "@tybys/wasm-util": "^0.10.0", - "tslib": "^2.8.0" + "@emnapi/core": "^1.6.0", + "@emnapi/runtime": "^1.6.0", + "@emnapi/wasi-threads": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.0.7", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.4.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.13.tgz", - "integrity": "sha512-dziTNeQXtoQ2KBXmrjCxsuPk3F3CQ/yb7ZNZNA+UkNTeiTGgfeh+gH5Pi7mRncVgcPD2xgHvkFCh/MhZWSgyQg==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.17.tgz", + "integrity": "sha512-JU5AHr7gKbZlOGvMdb4722/0aYbU+tN6lv1kONx0JK2cGsh7g148zVWLM0IKR3NeKLv+L90chBVYcJ8uJWbC9A==", "cpu": [ "arm64" ], @@ -2199,9 +3062,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.13.tgz", - "integrity": "sha512-3+LKesjXydTkHk5zXX01b5KMzLV1xl2mcktBJkje7rhFUpUlYJy7IMOLqjIRQncLTa1WZZiFY/foAeB5nmaiTw==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.17.tgz", + "integrity": "sha512-SKWM4waLuqx0IH+FMDUw6R66Hu4OuTALFgnleKbqhgGU30DY20NORZMZUKgLRjQXNN2TLzKvh48QXTig4h4bGw==", "cpu": [ "x64" ], @@ -2216,23 +3079,23 @@ } }, "node_modules/@tailwindcss/postcss": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.13.tgz", - "integrity": "sha512-HLgx6YSFKJT7rJqh9oJs/TkBFhxuMOfUKSBEPYwV+t78POOBsdQ7crhZLzwcH3T0UyUuOzU/GK5pk5eKr3wCiQ==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.17.tgz", + "integrity": "sha512-+nKl9N9mN5uJ+M7dBOOCzINw94MPstNR/GtIhz1fpZysxL/4a+No64jCBD6CPN+bIHWFx3KWuu8XJRrj/572Dw==", "dev": true, "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", - "@tailwindcss/node": "4.1.13", - "@tailwindcss/oxide": "4.1.13", + "@tailwindcss/node": "4.1.17", + "@tailwindcss/oxide": "4.1.17", "postcss": "^8.4.41", - "tailwindcss": "4.1.13" + "tailwindcss": "4.1.17" } }, "node_modules/@tybys/wasm-util": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.0.tgz", - "integrity": "sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", "dev": true, "license": "MIT", "optional": true, @@ -2269,9 +3132,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.19.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.13.tgz", - "integrity": "sha512-yCAeZl7a0DxgNVteXFHt9+uyFbqXGy/ShC4BlcHkoE0AfGXYv/BUiplV72DjMYXHDBXFjhvr6DD1NiRVfB4j8g==", + "version": "20.19.25", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.25.tgz", + "integrity": "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2279,23 +3142,23 @@ } }, "node_modules/@types/react": { - "version": "19.1.12", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.12.tgz", - "integrity": "sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w==", + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz", + "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", "devOptional": true, "license": "MIT", "dependencies": { - "csstype": "^3.0.2" + "csstype": "^3.2.2" } }, "node_modules/@types/react-dom": { - "version": "19.1.9", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.9.tgz", - "integrity": "sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==", + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "devOptional": true, "license": "MIT", "peerDependencies": { - "@types/react": "^19.0.0" + "@types/react": "^19.2.0" } }, "node_modules/@types/webidl-conversions": { @@ -2314,18 +3177,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.42.0.tgz", - "integrity": "sha512-Aq2dPqsQkxHOLfb2OPv43RnIvfj05nw8v/6n3B2NABIPpHnjQnaLo9QGMTvml+tv4korl/Cjfrb/BYhoL8UUTQ==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.49.0.tgz", + "integrity": "sha512-JXij0vzIaTtCwu6SxTh8qBc66kmf1xs7pI4UOiMDFVct6q86G0Zs7KRcEoJgY3Cav3x5Tq0MF5jwgpgLqgKG3A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.42.0", - "@typescript-eslint/type-utils": "8.42.0", - "@typescript-eslint/utils": "8.42.0", - "@typescript-eslint/visitor-keys": "8.42.0", - "graphemer": "^1.4.0", + "@typescript-eslint/scope-manager": "8.49.0", + "@typescript-eslint/type-utils": "8.49.0", + "@typescript-eslint/utils": "8.49.0", + "@typescript-eslint/visitor-keys": "8.49.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" @@ -2338,7 +3200,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.42.0", + "@typescript-eslint/parser": "^8.49.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } @@ -2354,16 +3216,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.42.0.tgz", - "integrity": "sha512-r1XG74QgShUgXph1BYseJ+KZd17bKQib/yF3SR+demvytiRXrwd12Blnz5eYGm8tXaeRdd4x88MlfwldHoudGg==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.49.0.tgz", + "integrity": "sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.42.0", - "@typescript-eslint/types": "8.42.0", - "@typescript-eslint/typescript-estree": "8.42.0", - "@typescript-eslint/visitor-keys": "8.42.0", + "@typescript-eslint/scope-manager": "8.49.0", + "@typescript-eslint/types": "8.49.0", + "@typescript-eslint/typescript-estree": "8.49.0", + "@typescript-eslint/visitor-keys": "8.49.0", "debug": "^4.3.4" }, "engines": { @@ -2379,14 +3241,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.42.0.tgz", - "integrity": "sha512-vfVpLHAhbPjilrabtOSNcUDmBboQNrJUiNAGoImkZKnMjs2TIcWG33s4Ds0wY3/50aZmTMqJa6PiwkwezaAklg==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.49.0.tgz", + "integrity": "sha512-/wJN0/DKkmRUMXjZUXYZpD1NEQzQAAn9QWfGwo+Ai8gnzqH7tvqS7oNVdTjKqOcPyVIdZdyCMoqN66Ia789e7g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.42.0", - "@typescript-eslint/types": "^8.42.0", + "@typescript-eslint/tsconfig-utils": "^8.49.0", + "@typescript-eslint/types": "^8.49.0", "debug": "^4.3.4" }, "engines": { @@ -2401,14 +3263,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.42.0.tgz", - "integrity": "sha512-51+x9o78NBAVgQzOPd17DkNTnIzJ8T/O2dmMBLoK9qbY0Gm52XJcdJcCl18ExBMiHo6jPMErUQWUv5RLE51zJw==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.49.0.tgz", + "integrity": "sha512-npgS3zi+/30KSOkXNs0LQXtsg9ekZ8OISAOLGWA/ZOEn0ZH74Ginfl7foziV8DT+D98WfQ5Kopwqb/PZOaIJGg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.42.0", - "@typescript-eslint/visitor-keys": "8.42.0" + "@typescript-eslint/types": "8.49.0", + "@typescript-eslint/visitor-keys": "8.49.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2419,9 +3281,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.42.0.tgz", - "integrity": "sha512-kHeFUOdwAJfUmYKjR3CLgZSglGHjbNTi1H8sTYRYV2xX6eNz4RyJ2LIgsDLKf8Yi0/GL1WZAC/DgZBeBft8QAQ==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.49.0.tgz", + "integrity": "sha512-8prixNi1/6nawsRYxet4YOhnbW+W9FK/bQPxsGB1D3ZrDzbJ5FXw5XmzxZv82X3B+ZccuSxo/X8q9nQ+mFecWA==", "dev": true, "license": "MIT", "engines": { @@ -2436,15 +3298,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.42.0.tgz", - "integrity": "sha512-9KChw92sbPTYVFw3JLRH1ockhyR3zqqn9lQXol3/YbI6jVxzWoGcT3AsAW0mu1MY0gYtsXnUGV/AKpkAj5tVlQ==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.49.0.tgz", + "integrity": "sha512-KTExJfQ+svY8I10P4HdxKzWsvtVnsuCifU5MvXrRwoP2KOlNZ9ADNEWWsQTJgMxLzS5VLQKDjkCT/YzgsnqmZg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.42.0", - "@typescript-eslint/typescript-estree": "8.42.0", - "@typescript-eslint/utils": "8.42.0", + "@typescript-eslint/types": "8.49.0", + "@typescript-eslint/typescript-estree": "8.49.0", + "@typescript-eslint/utils": "8.49.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -2461,9 +3323,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.42.0.tgz", - "integrity": "sha512-LdtAWMiFmbRLNP7JNeY0SqEtJvGMYSzfiWBSmx+VSZ1CH+1zyl8Mmw1TT39OrtsRvIYShjJWzTDMPWZJCpwBlw==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.49.0.tgz", + "integrity": "sha512-e9k/fneezorUo6WShlQpMxXh8/8wfyc+biu6tnAqA81oWrEic0k21RHzP9uqqpyBBeBKu4T+Bsjy9/b8u7obXQ==", "dev": true, "license": "MIT", "engines": { @@ -2475,21 +3337,20 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.42.0.tgz", - "integrity": "sha512-ku/uYtT4QXY8sl9EDJETD27o3Ewdi72hcXg1ah/kkUgBvAYHLwj2ofswFFNXS+FL5G+AGkxBtvGt8pFBHKlHsQ==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.49.0.tgz", + "integrity": "sha512-jrLdRuAbPfPIdYNppHJ/D0wN+wwNfJ32YTAm10eJVsFmrVpXQnDWBn8niCSMlWjvml8jsce5E/O+86IQtTbJWA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.42.0", - "@typescript-eslint/tsconfig-utils": "8.42.0", - "@typescript-eslint/types": "8.42.0", - "@typescript-eslint/visitor-keys": "8.42.0", + "@typescript-eslint/project-service": "8.49.0", + "@typescript-eslint/tsconfig-utils": "8.49.0", + "@typescript-eslint/types": "8.49.0", + "@typescript-eslint/visitor-keys": "8.49.0", "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", + "tinyglobby": "^0.2.15", "ts-api-utils": "^2.1.0" }, "engines": { @@ -2513,36 +3374,6 @@ "balanced-match": "^1.0.0" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -2559,17 +3390,30 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@typescript-eslint/utils": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.42.0.tgz", - "integrity": "sha512-JnIzu7H3RH5BrKC4NoZqRfmjqCIS1u3hGZltDYJgkVdqAezl4L9d1ZLw+36huCujtSBSAirGINF/S4UxOcR+/g==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.49.0.tgz", + "integrity": "sha512-N3W7rJw7Rw+z1tRsHZbK395TWSYvufBXumYtEGzypgMUthlg0/hmCImeA8hgO2d2G4pd7ftpxxul2J8OdtdaFA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.42.0", - "@typescript-eslint/types": "8.42.0", - "@typescript-eslint/typescript-estree": "8.42.0" + "@typescript-eslint/scope-manager": "8.49.0", + "@typescript-eslint/types": "8.49.0", + "@typescript-eslint/typescript-estree": "8.49.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2584,13 +3428,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.42.0.tgz", - "integrity": "sha512-3WbiuzoEowaEn8RSnhJBrxSwX8ULYE9CXaPepS2C2W3NSA5NNIvBaslpBSBElPq0UGr0xVJlXFWOAKIkyylydQ==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.49.0.tgz", + "integrity": "sha512-LlKaciDe3GmZFphXIc79THF/YYBugZ7FS1pO581E/edlVVNbZKDy93evqmrfQ9/Y4uN0vVhX4iuchq26mK/iiA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.42.0", + "@typescript-eslint/types": "8.49.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -2602,9 +3446,9 @@ } }, "node_modules/@typespec/ts-http-runtime": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.0.tgz", - "integrity": "sha512-sOx1PKSuFwnIl7z4RN0Ls7N9AQawmR9r66eI5rFCzLDIs8HTIYrIpH9QjYWoX0lkgGrkLxXhi4QnK7MizPRrIg==", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.2.tgz", + "integrity": "sha512-IlqQ/Gv22xUC1r/WQm4StLkYQmaaTsXAhUVsNE0+xiyf0yRFiH5++q78U3bw6bLKDCTmh0uqKB9eG9+Bt75Dkg==", "license": "MIT", "dependencies": { "http-proxy-agent": "^7.0.0", @@ -3172,9 +4016,9 @@ } }, "node_modules/axe-core": { - "version": "4.10.3", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.3.tgz", - "integrity": "sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==", + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.0.tgz", + "integrity": "sha512-ilYanEU8vxxBexpJd8cWM4ElSQq4QctCLKih0TSfjIfCQTeyH/6zVrmIJfLPrKTKJRbiG+cfnZbQIjAlJmF1jQ==", "dev": true, "license": "MPL-2.0", "engines": { @@ -3198,6 +4042,16 @@ "dev": true, "license": "MIT" }, + "node_modules/baseline-browser-mapping": { + "version": "2.9.5", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.5.tgz", + "integrity": "sha512-D5vIoztZOq1XM54LUdttJVc96ggEsIfju2JBvht06pSzpckp3C7HReun67Bghzrtdsq9XdMGbSSB3v3GhMNmAA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -3222,6 +4076,40 @@ "node": ">=8" } }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, "node_modules/bson": { "version": "6.10.4", "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz", @@ -3292,9 +4180,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001739", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001739.tgz", - "integrity": "sha512-y+j60d6ulelrNSwpPyrHdl+9mJnQzHBr08xm48Qno0nSk4h3Qojh+ziv2qE6rXf4k3tadF4o1J/1tAbVm1NtnA==", + "version": "1.0.30001759", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001759.tgz", + "integrity": "sha512-Pzfx9fOKoKvevQf8oCXoyNRQ5QyxJj+3O0Rqx2V5oxT61KGx8+n6hV/IUyJeifUci2clnmmKVpvtiqRzgiWjSw==", "funding": [ { "type": "opencollective", @@ -3328,16 +4216,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/chownr": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", - "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, "node_modules/class-variance-authority": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", @@ -3365,25 +4243,11 @@ "node": ">=6" } }, - "node_modules/color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "license": "MIT", - "optional": true, - "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - }, - "engines": { - "node": ">=12.5.0" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -3396,20 +4260,9 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "devOptional": true, + "dev": true, "license": "MIT" }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "license": "MIT", - "optional": true, - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -3417,6 +4270,13 @@ "dev": true, "license": "MIT" }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, "node_modules/cookie": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", @@ -3456,9 +4316,9 @@ } }, "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "devOptional": true, "license": "MIT" }, @@ -3524,9 +4384,9 @@ } }, "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -3584,9 +4444,9 @@ } }, "node_modules/detect-libc": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", - "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "devOptional": true, "license": "Apache-2.0", "engines": { @@ -3627,6 +4487,13 @@ "node": ">= 0.4" } }, + "node_modules/electron-to-chromium": { + "version": "1.5.266", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.266.tgz", + "integrity": "sha512-kgWEglXvkEfMH7rxP5OSZZwnaDWT7J9EoZCujhnpLbfi0bbNtRkgdX2E3gt0Uer11c61qCYktB3hwkAS325sJg==", + "dev": true, + "license": "ISC" + }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -3825,6 +4692,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -3839,25 +4716,24 @@ } }, "node_modules/eslint": { - "version": "9.34.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.34.0.tgz", - "integrity": "sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg==", + "version": "9.39.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz", + "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.0", - "@eslint/config-helpers": "^0.3.1", - "@eslint/core": "^0.15.2", + "@eslint/config-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.34.0", - "@eslint/plugin-kit": "^0.3.5", + "@eslint/js": "9.39.1", + "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", @@ -3900,25 +4776,24 @@ } }, "node_modules/eslint-config-next": { - "version": "15.5.2", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.5.2.tgz", - "integrity": "sha512-3hPZghsLupMxxZ2ggjIIrat/bPniM2yRpsVPVM40rp8ZMzKWOJp2CGWn7+EzoV2ddkUr5fxNfHpF+wU1hGt/3g==", + "version": "16.0.7", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.0.7.tgz", + "integrity": "sha512-WubFGLFHfk2KivkdRGfx6cGSFhaQqhERRfyO8BRx+qiGPGp7WLKcPvYC4mdx1z3VhVRcrfFzczjjTrbJZOpnEQ==", "dev": true, "license": "MIT", "dependencies": { - "@next/eslint-plugin-next": "15.5.2", - "@rushstack/eslint-patch": "^1.10.3", - "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", - "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "@next/eslint-plugin-next": "16.0.7", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", - "eslint-plugin-import": "^2.31.0", + "eslint-plugin-import": "^2.32.0", "eslint-plugin-jsx-a11y": "^6.10.0", "eslint-plugin-react": "^7.37.0", - "eslint-plugin-react-hooks": "^5.0.0" + "eslint-plugin-react-hooks": "^7.0.0", + "globals": "16.4.0", + "typescript-eslint": "^8.46.0" }, "peerDependencies": { - "eslint": "^7.23.0 || ^8.0.0 || ^9.0.0", + "eslint": ">=9.0.0", "typescript": ">=3.3.1" }, "peerDependenciesMeta": { @@ -3927,6 +4802,19 @@ } } }, + "node_modules/eslint-config-next/node_modules/globals": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", + "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eslint-config-prettier": { "version": "10.1.8", "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", @@ -4072,16 +4960,6 @@ "ms": "^2.1.1" } }, - "node_modules/eslint-plugin-import/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/eslint-plugin-jsx-a11y": { "version": "6.10.2", "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", @@ -4146,13 +5024,20 @@ } }, "node_modules/eslint-plugin-react-hooks": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", - "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz", + "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==", "dev": true, "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, "engines": { - "node": ">=10" + "node": ">=18" }, "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" @@ -4176,16 +5061,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-plugin-react/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/eslint-scope": { "version": "8.4.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", @@ -4341,9 +5216,9 @@ "license": "MIT" }, "node_modules/fast-xml-parser": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", - "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.3.2.tgz", + "integrity": "sha512-n8v8b6p4Z1sMgqRmqLJm3awW4NX7NkaKPfb3uJIBTSH7Pdvufi3PQ3/lJLQrvxcMYl7JI2jnDO90siPEpD8JBA==", "funding": [ { "type": "github", @@ -4489,6 +5364,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -4556,9 +5451,9 @@ } }, "node_modules/get-tsconfig": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", - "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", + "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4631,13 +5526,6 @@ "dev": true, "license": "ISC" }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true, - "license": "MIT" - }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", @@ -4732,6 +5620,23 @@ "node": ">= 0.4" } }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, "node_modules/http-proxy-agent": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", @@ -4828,13 +5733,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "license": "MIT", - "optional": true - }, "node_modules/is-async-function": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", @@ -4898,6 +5796,19 @@ "semver": "^7.7.1" } }, + "node_modules/is-bun-module/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -4989,14 +5900,15 @@ } }, "node_modules/is-generator-function": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", - "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.3", - "get-proto": "^1.0.0", + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" }, @@ -5251,9 +6163,9 @@ } }, "node_modules/jiti": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", - "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", "dev": true, "license": "MIT", "bin": { @@ -5277,9 +6189,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "license": "MIT", "dependencies": { @@ -5289,6 +6201,19 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -5311,16 +6236,16 @@ "license": "MIT" }, "node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, "license": "MIT", - "dependencies": { - "minimist": "^1.2.0" - }, "bin": { "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" } }, "node_modules/jsx-ast-utils": { @@ -5384,9 +6309,9 @@ } }, "node_modules/lightningcss": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", - "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz", + "integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==", "dev": true, "license": "MPL-2.0", "dependencies": { @@ -5400,22 +6325,44 @@ "url": "https://opencollective.com/parcel" }, "optionalDependencies": { - "lightningcss-darwin-arm64": "1.30.1", - "lightningcss-darwin-x64": "1.30.1", - "lightningcss-freebsd-x64": "1.30.1", - "lightningcss-linux-arm-gnueabihf": "1.30.1", - "lightningcss-linux-arm64-gnu": "1.30.1", - "lightningcss-linux-arm64-musl": "1.30.1", - "lightningcss-linux-x64-gnu": "1.30.1", - "lightningcss-linux-x64-musl": "1.30.1", - "lightningcss-win32-arm64-msvc": "1.30.1", - "lightningcss-win32-x64-msvc": "1.30.1" + "lightningcss-android-arm64": "1.30.2", + "lightningcss-darwin-arm64": "1.30.2", + "lightningcss-darwin-x64": "1.30.2", + "lightningcss-freebsd-x64": "1.30.2", + "lightningcss-linux-arm-gnueabihf": "1.30.2", + "lightningcss-linux-arm64-gnu": "1.30.2", + "lightningcss-linux-arm64-musl": "1.30.2", + "lightningcss-linux-x64-gnu": "1.30.2", + "lightningcss-linux-x64-musl": "1.30.2", + "lightningcss-win32-arm64-msvc": "1.30.2", + "lightningcss-win32-x64-msvc": "1.30.2" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz", + "integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-darwin-arm64": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", - "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz", + "integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==", "cpu": [ "arm64" ], @@ -5434,9 +6381,9 @@ } }, "node_modules/lightningcss-darwin-x64": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", - "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz", + "integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==", "cpu": [ "x64" ], @@ -5455,9 +6402,9 @@ } }, "node_modules/lightningcss-freebsd-x64": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", - "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz", + "integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==", "cpu": [ "x64" ], @@ -5476,9 +6423,9 @@ } }, "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", - "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz", + "integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==", "cpu": [ "arm" ], @@ -5497,9 +6444,9 @@ } }, "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", - "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz", + "integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==", "cpu": [ "arm64" ], @@ -5518,9 +6465,9 @@ } }, "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", - "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz", + "integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==", "cpu": [ "arm64" ], @@ -5539,9 +6486,9 @@ } }, "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", - "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz", + "integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==", "cpu": [ "x64" ], @@ -5560,9 +6507,9 @@ } }, "node_modules/lightningcss-linux-x64-musl": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", - "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz", + "integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==", "cpu": [ "x64" ], @@ -5581,9 +6528,9 @@ } }, "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", - "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz", + "integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==", "cpu": [ "arm64" ], @@ -5602,9 +6549,9 @@ } }, "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", - "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz", + "integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==", "cpu": [ "x64" ], @@ -5659,23 +6606,15 @@ } }, "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, "license": "ISC", "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" + "yallist": "^3.0.2" } }, - "node_modules/lru-cache/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC" - }, "node_modules/lucide-react": { "version": "0.542.0", "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.542.0.tgz", @@ -5686,9 +6625,9 @@ } }, "node_modules/magic-string": { - "version": "0.30.18", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.18.tgz", - "integrity": "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==", + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5758,54 +6697,15 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/minizlib": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", - "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", - "dev": true, - "license": "MIT", - "dependencies": { - "minipass": "^7.1.2" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/mongodb": { - "version": "6.19.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.19.0.tgz", - "integrity": "sha512-H3GtYujOJdeKIMLKBT9PwlDhGrQfplABNF1G904w6r5ZXKWyv77aB0X9B+rhmaAwjtllHzaEkvi9mkGVZxs2Bw==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.21.0.tgz", + "integrity": "sha512-URyb/VXMjJ4da46OeSXg+puO39XH9DeQpWCslifrRn9JWugy0D+DvvBvkm2WxmHe61O/H19JM66p1z7RHVkZ6A==", "license": "Apache-2.0", "dependencies": { - "@mongodb-js/saslprep": "^1.1.9", + "@mongodb-js/saslprep": "^1.3.0", "bson": "^6.10.4", - "mongodb-connection-string-url": "^3.0.0" + "mongodb-connection-string-url": "^3.0.2" }, "engines": { "node": ">=16.20.1" @@ -5878,9 +6778,9 @@ } }, "node_modules/napi-postinstall": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.3.tgz", - "integrity": "sha512-uTp172LLXSxuSYHv/kou+f6KW3SMppU9ivthaVTXian9sOt3XM/zHYHpRZiLgQoxeWfYUnslNWQHF1+G71xcow==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", + "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", "dev": true, "license": "MIT", "bin": { @@ -5901,12 +6801,12 @@ "license": "MIT" }, "node_modules/next": { - "version": "15.5.2", - "resolved": "https://registry.npmjs.org/next/-/next-15.5.2.tgz", - "integrity": "sha512-H8Otr7abj1glFhbGnvUt3gz++0AF1+QoCXEBmd/6aKbfdFwrn0LpA836Ed5+00va/7HQSDD+mOoVhn3tNy3e/Q==", + "version": "16.0.7", + "resolved": "https://registry.npmjs.org/next/-/next-16.0.7.tgz", + "integrity": "sha512-3mBRJyPxT4LOxAJI6IsXeFtKfiJUbjCLgvXO02fV8Wy/lIhPvP94Fe7dGhUgHXcQy4sSuYwQNcOLhIfOm0rL0A==", "license": "MIT", "dependencies": { - "@next/env": "15.5.2", + "@next/env": "16.0.7", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", @@ -5916,18 +6816,18 @@ "next": "dist/bin/next" }, "engines": { - "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" + "node": ">=20.9.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "15.5.2", - "@next/swc-darwin-x64": "15.5.2", - "@next/swc-linux-arm64-gnu": "15.5.2", - "@next/swc-linux-arm64-musl": "15.5.2", - "@next/swc-linux-x64-gnu": "15.5.2", - "@next/swc-linux-x64-musl": "15.5.2", - "@next/swc-win32-arm64-msvc": "15.5.2", - "@next/swc-win32-x64-msvc": "15.5.2", - "sharp": "^0.34.3" + "@next/swc-darwin-arm64": "16.0.7", + "@next/swc-darwin-x64": "16.0.7", + "@next/swc-linux-arm64-gnu": "16.0.7", + "@next/swc-linux-arm64-musl": "16.0.7", + "@next/swc-linux-x64-gnu": "16.0.7", + "@next/swc-linux-x64-musl": "16.0.7", + "@next/swc-win32-arm64-msvc": "16.0.7", + "@next/swc-win32-x64-msvc": "16.0.7", + "sharp": "^0.34.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", @@ -5953,9 +6853,9 @@ } }, "node_modules/next-auth": { - "version": "4.24.11", - "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.24.11.tgz", - "integrity": "sha512-pCFXzIDQX7xmHFs4KVH4luCjaCbuPRtZ9oBUjUhOk84mZ9WVPf94n87TxYI4rSRf9HmfHEF8Yep3JrYDVOo3Cw==", + "version": "4.24.13", + "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.24.13.tgz", + "integrity": "sha512-sgObCfcfL7BzIK76SS5TnQtc3yo2Oifp/yIpfv6fMfeBOiBJkDWF3A2y9+yqnmJ4JKc2C+nMjSjmgDeTwgN1rQ==", "license": "ISC", "dependencies": { "@babel/runtime": "^7.20.13", @@ -5969,9 +6869,9 @@ "uuid": "^8.3.2" }, "peerDependencies": { - "@auth/core": "0.34.2", - "next": "^12.2.5 || ^13 || ^14 || ^15", - "nodemailer": "^6.6.5", + "@auth/core": "0.34.3", + "next": "^12.2.5 || ^13 || ^14 || ^15 || ^16", + "nodemailer": "^7.0.7", "react": "^17.0.2 || ^18 || ^19", "react-dom": "^17.0.2 || ^18 || ^19" }, @@ -6022,6 +6922,13 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, "node_modules/oauth": { "version": "0.9.15", "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", @@ -6161,9 +7068,9 @@ } }, "node_modules/oidc-token-hash": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.1.1.tgz", - "integrity": "sha512-D7EmwxJV6DsEB6vOFLrBM2OzsVgQzgPWyHlV2OOAVj772n+WTXpudC9e9u5BVKQnYwaD30Ivhi9b+4UeBcGu9g==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.2.0.tgz", + "integrity": "sha512-6gj2m8cJZ+iSW8bm0FXdGF0YhIQbKrfP4yWTNzxc31U6MOjfEmB1rHvlYvxI1B7t7BCi1F2vYTT6YhtQRG4hxw==", "license": "MIT", "engines": { "node": "^10.13.0 || >=12.0.0" @@ -6184,6 +7091,24 @@ "url": "https://github.com/sponsors/panva" } }, + "node_modules/openid-client/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/openid-client/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -6351,9 +7276,9 @@ } }, "node_modules/preact": { - "version": "10.27.1", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.27.1.tgz", - "integrity": "sha512-V79raXEWch/rbqoNc7nT9E4ep7lu+mI3+sBmfRD4i1M73R3WLYcCtdI0ibxGVf4eQL8ZIz2nFacqEC+rmnOORQ==", + "version": "10.28.0", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.28.0.tgz", + "integrity": "sha512-rytDAoiXr3+t6OIP3WGlDd0ouCUG1iCWzkcY3++Nreuoi17y6T5i/zRhe6uYfoVcxq6YU+sBtJouuRDsq8vvqA==", "license": "MIT", "funding": { "type": "opencollective", @@ -6383,9 +7308,9 @@ } }, "node_modules/prettier": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", - "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz", + "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", "dev": true, "license": "MIT", "bin": { @@ -6484,9 +7409,9 @@ "license": "MIT" }, "node_modules/react-remove-scroll": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.1.tgz", - "integrity": "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==", + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", + "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==", "license": "MIT", "dependencies": { "react-remove-scroll-bar": "^2.3.7", @@ -6597,13 +7522,13 @@ } }, "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", "dev": true, "license": "MIT", "dependencies": { - "is-core-module": "^2.16.0", + "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -6740,16 +7665,13 @@ "license": "MIT" }, "node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "devOptional": true, + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" } }, "node_modules/set-function-length": { @@ -6802,16 +7724,16 @@ } }, "node_modules/sharp": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.3.tgz", - "integrity": "sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", "hasInstallScript": true, "license": "Apache-2.0", "optional": true, "dependencies": { - "color": "^4.2.3", - "detect-libc": "^2.0.4", - "semver": "^7.7.2" + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" }, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" @@ -6820,28 +7742,43 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.3", - "@img/sharp-darwin-x64": "0.34.3", - "@img/sharp-libvips-darwin-arm64": "1.2.0", - "@img/sharp-libvips-darwin-x64": "1.2.0", - "@img/sharp-libvips-linux-arm": "1.2.0", - "@img/sharp-libvips-linux-arm64": "1.2.0", - "@img/sharp-libvips-linux-ppc64": "1.2.0", - "@img/sharp-libvips-linux-s390x": "1.2.0", - "@img/sharp-libvips-linux-x64": "1.2.0", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.0", - "@img/sharp-libvips-linuxmusl-x64": "1.2.0", - "@img/sharp-linux-arm": "0.34.3", - "@img/sharp-linux-arm64": "0.34.3", - "@img/sharp-linux-ppc64": "0.34.3", - "@img/sharp-linux-s390x": "0.34.3", - "@img/sharp-linux-x64": "0.34.3", - "@img/sharp-linuxmusl-arm64": "0.34.3", - "@img/sharp-linuxmusl-x64": "0.34.3", - "@img/sharp-wasm32": "0.34.3", - "@img/sharp-win32-arm64": "0.34.3", - "@img/sharp-win32-ia32": "0.34.3", - "@img/sharp-win32-x64": "0.34.3" + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/sharp/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/shebang-command": { @@ -6943,16 +7880,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "license": "MIT", - "optional": true, - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, "node_modules/sonner": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.7.tgz", @@ -7200,9 +8127,9 @@ } }, "node_modules/tailwind-merge": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.1.tgz", - "integrity": "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.4.0.tgz", + "integrity": "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==", "license": "MIT", "funding": { "type": "github", @@ -7210,16 +8137,16 @@ } }, "node_modules/tailwindcss": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.13.tgz", - "integrity": "sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz", + "integrity": "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==", "dev": true, "license": "MIT" }, "node_modules/tapable": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.3.tgz", - "integrity": "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", "dev": true, "license": "MIT", "engines": { @@ -7230,33 +8157,15 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/tar": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", - "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", - "dev": true, - "license": "ISC", - "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.0.1", - "mkdirp": "^3.0.1", - "yallist": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/tinyglobby": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", - "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, "license": "MIT", "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { "node": ">=12.0.0" @@ -7347,6 +8256,19 @@ "strip-bom": "^3.0.0" } }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -7363,9 +8285,9 @@ } }, "node_modules/tw-animate-css": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.3.8.tgz", - "integrity": "sha512-Qrk3PZ7l7wUcGYhwZloqfkWCmaXZAoqjkdbIDvzfGshwGtexa/DAs9koXxIkrpEasyevandomzCBAV1Yyop5rw==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.4.0.tgz", + "integrity": "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==", "dev": true, "license": "MIT", "funding": { @@ -7464,9 +8386,9 @@ } }, "node_modules/typescript": { - "version": "5.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", - "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -7477,6 +8399,30 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.49.0.tgz", + "integrity": "sha512-zRSVH1WXD0uXczCXw+nsdjGPUdx4dfrs5VQoHnUWmv1U3oNlAKv4FUNdLDhVUg+gYn+a5hUESqch//Rv5wVhrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.49.0", + "@typescript-eslint/parser": "8.49.0", + "@typescript-eslint/typescript-estree": "8.49.0", + "@typescript-eslint/utils": "8.49.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, "node_modules/uid-safe": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", @@ -7550,6 +8496,37 @@ "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" } }, + "node_modules/update-browserslist-db": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.2.tgz", + "integrity": "sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -7604,9 +8581,9 @@ } }, "node_modules/use-sync-external-store": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", - "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", "license": "MIT", "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" @@ -7759,14 +8736,11 @@ } }, "node_modules/yallist": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", - "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } + "license": "ISC" }, "node_modules/yocto-queue": { "version": "0.1.0", @@ -7782,13 +8756,26 @@ } }, "node_modules/zod": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz", - "integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.13.tgz", + "integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } } } } diff --git a/package.json b/package.json index 9d880ca..9b78307 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "csrf": "^3.1.0", "lucide-react": "^0.542.0", "mongodb": "^6.19.0", - "next": "15.5.2", + "next": "16.0.7", "next-auth": "^4.24.11", "next-themes": "^0.4.6", "react": "19.1.0", @@ -42,7 +42,7 @@ "@types/react": "^19", "@types/react-dom": "^19", "eslint": "^9", - "eslint-config-next": "15.5.2", + "eslint-config-next": "16.0.7", "eslint-config-prettier": "^10.1.8", "prettier": "^3.6.2", "tailwindcss": "^4", From e534a6363cd58f433c91e80fbe6529e4cdb82435 Mon Sep 17 00:00:00 2001 From: Abul Date: Sat, 13 Dec 2025 19:23:56 +0530 Subject: [PATCH 05/14] Implement file search functionality --- app/api/files/search/route.ts | 55 ++++++++++ components/files/FileDisplay.tsx | 17 +-- components/layout/DashboardHeader.tsx | 143 +++++++++++++++++++++----- components/layout/DashboardLayout.tsx | 25 ++++- components/search/SearchDropdown.tsx | 128 +++++++++++++++++++++++ components/ui/badge.tsx | 3 +- hooks/search/useSearch.ts | 97 +++++++++++++++++ package-lock.json | 106 +++++++++---------- package.json | 8 +- tsconfig.json | 24 ++++- 10 files changed, 505 insertions(+), 101 deletions(-) create mode 100644 app/api/files/search/route.ts create mode 100644 components/search/SearchDropdown.tsx create mode 100644 hooks/search/useSearch.ts diff --git a/app/api/files/search/route.ts b/app/api/files/search/route.ts new file mode 100644 index 0000000..c77ed40 --- /dev/null +++ b/app/api/files/search/route.ts @@ -0,0 +1,55 @@ +import { NextRequest } from "next/server"; +import { connectToDatabase } from "@/lib/services/mongodb"; +import { + getAuthenticatedUser, + createErrorResponse, + createSuccessResponse, +} from "@/lib/api/api-helpers"; +import { COLLECTIONS, ERROR_MESSAGES } from "@/lib/config/constants"; +import type { FileDocument } from "@/types"; + +const SEARCH_LIMIT = 8; + +export async function GET(request: NextRequest) { + try { + const { error, user } = await getAuthenticatedUser(request); + if (error) { + return error; + } + + const { searchParams } = new URL(request.url); + const query = searchParams.get("q")?.trim(); + + if (!query || query.length < 2) { + return createSuccessResponse({ files: [] }); + } + + const { db } = await connectToDatabase(); + + // Escape special regex characters for safety + const escapedQuery = query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + + const files = await db + .collection(COLLECTIONS.FILES) + .find({ + userId: user!.id, + deletedAt: { $exists: false }, + originalFileName: { $regex: escapedQuery, $options: "i" }, + }) + .sort({ uploadedAt: -1 }) + .limit(SEARCH_LIMIT) + .project({ + _id: 1, + originalFileName: 1, + fileType: 1, + fileSize: 1, + uploadedAt: 1, + }) + .toArray(); + + return createSuccessResponse({ files }); + } catch (error) { + console.error("Search error:", error); + return createErrorResponse(ERROR_MESSAGES.INTERNAL_ERROR, 500); + } +} diff --git a/components/files/FileDisplay.tsx b/components/files/FileDisplay.tsx index ad0768f..79116a5 100644 --- a/components/files/FileDisplay.tsx +++ b/components/files/FileDisplay.tsx @@ -2,7 +2,6 @@ import { useState } from "react"; import { TooltipProvider } from "@/components/ui/tooltip"; -import { ViewFileModal } from "@/components/modals/ViewFileModal"; import { FileEmptyState } from "@/components/files/FileEmptyState"; import { FileListView } from "@/components/files/FileListView"; import { FileGridView } from "@/components/files/FileGridView"; @@ -16,6 +15,7 @@ interface FileDisplayProps { mode: "files" | "trash"; onFileDeleted: () => void; onFileUpdated?: () => void; + onViewFile: (file: FileDocument) => void; } export function FileDisplay({ @@ -23,10 +23,9 @@ export function FileDisplay({ mode, onFileDeleted, onFileUpdated, + onViewFile, }: FileDisplayProps) { const [viewMode, setViewMode] = useState<"grid" | "list">("grid"); - const [viewFile, setViewFile] = useState(null); - const [isViewModalOpen, setIsViewModalOpen] = useState(false); const { deletingFiles, @@ -53,8 +52,7 @@ export function FileDisplay({ const handleView = (fileId: string) => { const file = files.find(f => f._id!.toString() === fileId); if (file) { - setViewFile(file); - setIsViewModalOpen(true); + onViewFile(file); } }; @@ -126,15 +124,6 @@ export function FileDisplay({ onClose={() => setRenameDialogOpen(false)} onConfirm={confirmRename} /> - - { - setIsViewModalOpen(false); - setViewFile(null); - }} - /> ); } diff --git a/components/layout/DashboardHeader.tsx b/components/layout/DashboardHeader.tsx index b4e082b..a401eef 100644 --- a/components/layout/DashboardHeader.tsx +++ b/components/layout/DashboardHeader.tsx @@ -13,18 +13,34 @@ import { DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { TooltipProvider } from "@/components/ui/tooltip"; -import { Search, SlidersHorizontal, Menu } from "lucide-react"; +import { Search, Menu, X } from "lucide-react"; import ThemeToggleButton from "@/components/ui/theme-toggle-button"; import Image from "next/image"; +import { useSearch } from "@/hooks/search/useSearch"; +import { SearchDropdown } from "@/components/search/SearchDropdown"; +import type { FileDocument } from "@/types"; interface DashboardHeaderProps { onUploadClick?: () => void; onMenuClick?: () => void; + onFileSelect?: (file: FileDocument) => void; } -export function DashboardHeader({ onMenuClick }: DashboardHeaderProps) { +export function DashboardHeader({ + onMenuClick, + onFileSelect, +}: DashboardHeaderProps) { const { data: session } = useSession(); - const [searchQuery, setSearchQuery] = useState(""); + const [isMobileSearchOpen, setIsMobileSearchOpen] = useState(false); + const { + searchQuery, + setSearchQuery, + searchResults, + isSearching, + isSearchOpen, + setIsSearchOpen, + clearSearch, + } = useSearch(); const handleSignOut = () => { signOut({ callbackUrl: "/" }); @@ -42,11 +58,28 @@ export function DashboardHeader({ onMenuClick }: DashboardHeaderProps) { .substring(0, 2); }; + const handleFileSelect = (file: FileDocument) => { + onFileSelect?.(file); + clearSearch(); + setIsMobileSearchOpen(false); + }; + + const handleSearchClose = () => { + setIsSearchOpen(false); + }; + + const handleMobileSearchToggle = () => { + if (isMobileSearchOpen) { + clearSearch(); + } + setIsMobileSearchOpen(!isMobileSearchOpen); + }; + return (
-
+
+
-
-
- - setSearchQuery(e.target.value)} - className="pl-9 md:pl-10 pr-10 md:pr-12 py-1.5 md:py-2 w-full bg-muted border-0 rounded-full focus:bg-card focus:shadow-md focus:ring-2 focus:ring-ring transition-all text-sm" - /> - -
+ + + )} +
- @@ -147,6 +205,45 @@ export function DashboardHeader({ onMenuClick }: DashboardHeaderProps) {
+ + {/* Mobile Search Bar */} + {isMobileSearchOpen && ( +
+
+ + setSearchQuery(e.target.value)} + onFocus={() => { + if (searchQuery.trim().length >= 2) { + setIsSearchOpen(true); + } + }} + autoFocus + className="pl-9 pr-9 py-2 w-full bg-muted border-0 rounded-full focus:bg-card focus:shadow-md focus:ring-2 focus:ring-ring transition-all text-sm" + /> + {searchQuery && ( + + )} + +
+
+ )}
); diff --git a/components/layout/DashboardLayout.tsx b/components/layout/DashboardLayout.tsx index f463ab7..8abb2bc 100644 --- a/components/layout/DashboardLayout.tsx +++ b/components/layout/DashboardLayout.tsx @@ -5,6 +5,7 @@ import { DashboardHeader } from "@/components/layout/DashboardHeader"; import { Sidebar } from "@/components/layout/SidebarComponent"; import { FileDisplay } from "@/components/files/FileDisplay"; import { UploadModal } from "@/components/modals/UploadModal"; +import { ViewFileModal } from "@/components/modals/ViewFileModal"; import { PaginationControls } from "@/components/common/PaginationControls"; import type { FileDocument } from "@/types"; @@ -47,10 +48,25 @@ export function DashboardLayout({ onGoToPage, }: DashboardLayoutProps) { const [isMobileSidebarOpen, setIsMobileSidebarOpen] = useState(false); + const [viewFile, setViewFile] = useState(null); + const [isViewModalOpen, setIsViewModalOpen] = useState(false); + + const handleViewFile = (file: FileDocument) => { + setViewFile(file); + setIsViewModalOpen(true); + }; + + const handleCloseViewModal = () => { + setIsViewModalOpen(false); + setViewFile(null); + }; return (
- setIsMobileSidebarOpen(true)} /> + setIsMobileSidebarOpen(true)} + onFileSelect={handleViewFile} + />
{/* Desktop Sidebar */} @@ -93,6 +109,7 @@ export function DashboardLayout({ files={files} onFileDeleted={onFileAction} onFileUpdated={onFileAction} + onViewFile={handleViewFile} /> {pagination && onNextPage && onPreviousPage && onGoToPage && ( + +
); } diff --git a/components/search/SearchDropdown.tsx b/components/search/SearchDropdown.tsx new file mode 100644 index 0000000..651fc36 --- /dev/null +++ b/components/search/SearchDropdown.tsx @@ -0,0 +1,128 @@ +"use client"; + +import { useEffect, useRef } from "react"; +import { Loader2, SearchX } from "lucide-react"; +import { formatBytes } from "@/lib/utils/utils"; +import { getFileIcon, getFileTypeColor } from "@/lib/utils/file-utils"; +import type { FileDocument } from "@/types"; + +interface SearchDropdownProps { + isOpen: boolean; + isSearching: boolean; + searchResults: FileDocument[]; + searchQuery: string; + onClose: () => void; + onFileSelect: (file: FileDocument) => void; +} + +export function SearchDropdown({ + isOpen, + isSearching, + searchResults, + searchQuery, + onClose, + onFileSelect, +}: SearchDropdownProps) { + const dropdownRef = useRef(null); + + // Handle click outside + useEffect(() => { + function handleClickOutside(event: MouseEvent) { + if ( + dropdownRef.current && + !dropdownRef.current.contains(event.target as Node) + ) { + onClose(); + } + } + + if (isOpen) { + document.addEventListener("mousedown", handleClickOutside); + } + + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; + }, [isOpen, onClose]); + + // Handle escape key + useEffect(() => { + function handleEscape(event: KeyboardEvent) { + if (event.key === "Escape") { + onClose(); + } + } + + if (isOpen) { + document.addEventListener("keydown", handleEscape); + } + + return () => { + document.removeEventListener("keydown", handleEscape); + }; + }, [isOpen, onClose]); + + if (!isOpen || searchQuery.trim().length < 2) { + return null; + } + + const handleFileClick = (file: FileDocument) => { + onFileSelect(file); + onClose(); + }; + + return ( +
+ {isSearching ? ( +
+ + + Searching... + +
+ ) : searchResults.length === 0 ? ( +
+ +

+ No files match "{searchQuery}" +

+
+ ) : ( +
    + {searchResults.map(file => { + const Icon = getFileIcon(file.fileType); + const colorClass = getFileTypeColor(file.fileType); + + return ( +
  • + +
  • + ); + })} +
+ )} +
+ ); +} diff --git a/components/ui/badge.tsx b/components/ui/badge.tsx index f61295c..e788b08 100644 --- a/components/ui/badge.tsx +++ b/components/ui/badge.tsx @@ -24,7 +24,8 @@ const badgeVariants = cva( ); export interface BadgeProps - extends React.HTMLAttributes, + extends + React.HTMLAttributes, VariantProps {} function Badge({ className, variant, ...props }: BadgeProps) { diff --git a/hooks/search/useSearch.ts b/hooks/search/useSearch.ts new file mode 100644 index 0000000..f3e0505 --- /dev/null +++ b/hooks/search/useSearch.ts @@ -0,0 +1,97 @@ +import { useState, useEffect, useCallback, useRef } from "react"; +import type { FileDocument } from "@/types"; + +interface UseSearchResult { + searchQuery: string; + setSearchQuery: (query: string) => void; + searchResults: FileDocument[]; + isSearching: boolean; + isSearchOpen: boolean; + setIsSearchOpen: (open: boolean) => void; + clearSearch: () => void; +} + +const DEBOUNCE_DELAY = 300; + +export function useSearch(): UseSearchResult { + const [searchQuery, setSearchQuery] = useState(""); + const [searchResults, setSearchResults] = useState([]); + const [isSearching, setIsSearching] = useState(false); + const [isSearchOpen, setIsSearchOpen] = useState(false); + const abortControllerRef = useRef(null); + + const performSearch = useCallback(async (query: string) => { + // Cancel any pending request + if (abortControllerRef.current) { + abortControllerRef.current.abort(); + } + + if (!query || query.trim().length < 2) { + setSearchResults([]); + setIsSearching(false); + return; + } + + abortControllerRef.current = new AbortController(); + setIsSearching(true); + + try { + const response = await fetch( + `/api/files/search?q=${encodeURIComponent(query.trim())}`, + { signal: abortControllerRef.current.signal } + ); + + if (response.ok) { + const data = await response.json(); + setSearchResults(data.files); + setIsSearchOpen(true); + } + } catch (error) { + // Ignore abort errors + if (error instanceof Error && error.name !== "AbortError") { + console.error("Search failed:", error); + setSearchResults([]); + } + } finally { + setIsSearching(false); + } + }, []); + + useEffect(() => { + const timer = setTimeout(() => { + performSearch(searchQuery); + }, DEBOUNCE_DELAY); + + return () => { + clearTimeout(timer); + }; + }, [searchQuery, performSearch]); + + // Cleanup abort controller on unmount + useEffect(() => { + return () => { + if (abortControllerRef.current) { + abortControllerRef.current.abort(); + } + }; + }, []); + + const clearSearch = useCallback(() => { + setSearchQuery(""); + setSearchResults([]); + setIsSearchOpen(false); + if (abortControllerRef.current) { + abortControllerRef.current.abort(); + } + }, []); + + return { + searchQuery, + setSearchQuery, + searchResults, + isSearching, + isSearchOpen, + setIsSearchOpen, + clearSearch, + }; +} diff --git a/package-lock.json b/package-lock.json index 3c33d12..e845f3a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,11 +20,11 @@ "csrf": "^3.1.0", "lucide-react": "^0.542.0", "mongodb": "^6.19.0", - "next": "16.0.7", + "next": "16.0.10", "next-auth": "^4.24.11", "next-themes": "^0.4.6", - "react": "19.1.0", - "react-dom": "19.1.0", + "react": "19.2.3", + "react-dom": "19.2.3", "sonner": "^2.0.7", "tailwind-merge": "^3.3.1", "zod": "^4.1.12" @@ -1313,9 +1313,9 @@ } }, "node_modules/@next/env": { - "version": "16.0.7", - "resolved": "https://registry.npmjs.org/@next/env/-/env-16.0.7.tgz", - "integrity": "sha512-gpaNgUh5nftFKRkRQGnVi5dpcYSKGcZZkQffZ172OrG/XkrnS7UBTQ648YY+8ME92cC4IojpI2LqTC8sTDhAaw==", + "version": "16.0.10", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.0.10.tgz", + "integrity": "sha512-8tuaQkyDVgeONQ1MeT9Mkk8pQmZapMKFh5B+OrFUlG3rVmYTXcXlBetBgTurKXGaIZvkoqRT9JL5K3phXcgang==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { @@ -1329,9 +1329,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "16.0.7", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.0.7.tgz", - "integrity": "sha512-LlDtCYOEj/rfSnEn/Idi+j1QKHxY9BJFmxx7108A6D8K0SB+bNgfYQATPk/4LqOl4C0Wo3LACg2ie6s7xqMpJg==", + "version": "16.0.10", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.0.10.tgz", + "integrity": "sha512-4XgdKtdVsaflErz+B5XeG0T5PeXKDdruDf3CRpnhN+8UebNa5N2H58+3GDgpn/9GBurrQ1uWW768FfscwYkJRg==", "cpu": [ "arm64" ], @@ -1345,9 +1345,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "16.0.7", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.0.7.tgz", - "integrity": "sha512-rtZ7BhnVvO1ICf3QzfW9H3aPz7GhBrnSIMZyr4Qy6boXF0b5E3QLs+cvJmg3PsTCG2M1PBoC+DANUi4wCOKXpA==", + "version": "16.0.10", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.0.10.tgz", + "integrity": "sha512-spbEObMvRKkQ3CkYVOME+ocPDFo5UqHb8EMTS78/0mQ+O1nqE8toHJVioZo4TvebATxgA8XMTHHrScPrn68OGw==", "cpu": [ "x64" ], @@ -1361,9 +1361,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "16.0.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.0.7.tgz", - "integrity": "sha512-mloD5WcPIeIeeZqAIP5c2kdaTa6StwP4/2EGy1mUw8HiexSHGK/jcM7lFuS3u3i2zn+xH9+wXJs6njO7VrAqww==", + "version": "16.0.10", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.0.10.tgz", + "integrity": "sha512-uQtWE3X0iGB8apTIskOMi2w/MKONrPOUCi5yLO+v3O8Mb5c7K4Q5KD1jvTpTF5gJKa3VH/ijKjKUq9O9UhwOYw==", "cpu": [ "arm64" ], @@ -1377,9 +1377,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "16.0.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.0.7.tgz", - "integrity": "sha512-+ksWNrZrthisXuo9gd1XnjHRowCbMtl/YgMpbRvFeDEqEBd523YHPWpBuDjomod88U8Xliw5DHhekBC3EOOd9g==", + "version": "16.0.10", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.0.10.tgz", + "integrity": "sha512-llA+hiDTrYvyWI21Z0L1GiXwjQaanPVQQwru5peOgtooeJ8qx3tlqRV2P7uH2pKQaUfHxI/WVarvI5oYgGxaTw==", "cpu": [ "arm64" ], @@ -1393,9 +1393,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "16.0.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.0.7.tgz", - "integrity": "sha512-4WtJU5cRDxpEE44Ana2Xro1284hnyVpBb62lIpU5k85D8xXxatT+rXxBgPkc7C1XwkZMWpK5rXLXTh9PFipWsA==", + "version": "16.0.10", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.0.10.tgz", + "integrity": "sha512-AK2q5H0+a9nsXbeZ3FZdMtbtu9jxW4R/NgzZ6+lrTm3d6Zb7jYrWcgjcpM1k8uuqlSy4xIyPR2YiuUr+wXsavA==", "cpu": [ "x64" ], @@ -1409,9 +1409,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "16.0.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.0.7.tgz", - "integrity": "sha512-HYlhqIP6kBPXalW2dbMTSuB4+8fe+j9juyxwfMwCe9kQPPeiyFn7NMjNfoFOfJ2eXkeQsoUGXg+O2SE3m4Qg2w==", + "version": "16.0.10", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.0.10.tgz", + "integrity": "sha512-1TDG9PDKivNw5550S111gsO4RGennLVl9cipPhtkXIFVwo31YZ73nEbLjNC8qG3SgTz/QZyYyaFYMeY4BKZR/g==", "cpu": [ "x64" ], @@ -1425,9 +1425,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "16.0.7", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.0.7.tgz", - "integrity": "sha512-EviG+43iOoBRZg9deGauXExjRphhuYmIOJ12b9sAPy0eQ6iwcPxfED2asb/s2/yiLYOdm37kPaiZu8uXSYPs0Q==", + "version": "16.0.10", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.0.10.tgz", + "integrity": "sha512-aEZIS4Hh32xdJQbHz121pyuVZniSNoqDVx1yIr2hy+ZwJGipeqnMZBJHyMxv2tiuAXGx6/xpTcQJ6btIiBjgmg==", "cpu": [ "arm64" ], @@ -1441,9 +1441,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "16.0.7", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.0.7.tgz", - "integrity": "sha512-gniPjy55zp5Eg0896qSrf3yB1dw4F/3s8VK1ephdsZZ129j2n6e1WqCbE2YgcKhW9hPB9TVZENugquWJD5x0ug==", + "version": "16.0.10", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.0.10.tgz", + "integrity": "sha512-E+njfCoFLb01RAFEnGZn6ERoOqhK1Gl3Lfz1Kjnj0Ulfu7oJbuMyvBKNj/bw8XZnenHDASlygTjZICQW+rYW1Q==", "cpu": [ "x64" ], @@ -6801,12 +6801,12 @@ "license": "MIT" }, "node_modules/next": { - "version": "16.0.7", - "resolved": "https://registry.npmjs.org/next/-/next-16.0.7.tgz", - "integrity": "sha512-3mBRJyPxT4LOxAJI6IsXeFtKfiJUbjCLgvXO02fV8Wy/lIhPvP94Fe7dGhUgHXcQy4sSuYwQNcOLhIfOm0rL0A==", + "version": "16.0.10", + "resolved": "https://registry.npmjs.org/next/-/next-16.0.10.tgz", + "integrity": "sha512-RtWh5PUgI+vxlV3HdR+IfWA1UUHu0+Ram/JBO4vWB54cVPentCD0e+lxyAYEsDTqGGMg7qpjhKh6dc6aW7W/sA==", "license": "MIT", "dependencies": { - "@next/env": "16.0.7", + "@next/env": "16.0.10", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", @@ -6819,14 +6819,14 @@ "node": ">=20.9.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "16.0.7", - "@next/swc-darwin-x64": "16.0.7", - "@next/swc-linux-arm64-gnu": "16.0.7", - "@next/swc-linux-arm64-musl": "16.0.7", - "@next/swc-linux-x64-gnu": "16.0.7", - "@next/swc-linux-x64-musl": "16.0.7", - "@next/swc-win32-arm64-msvc": "16.0.7", - "@next/swc-win32-x64-msvc": "16.0.7", + "@next/swc-darwin-arm64": "16.0.10", + "@next/swc-darwin-x64": "16.0.10", + "@next/swc-linux-arm64-gnu": "16.0.10", + "@next/swc-linux-arm64-musl": "16.0.10", + "@next/swc-linux-x64-gnu": "16.0.10", + "@next/swc-linux-x64-musl": "16.0.10", + "@next/swc-win32-arm64-msvc": "16.0.10", + "@next/swc-win32-x64-msvc": "16.0.10", "sharp": "^0.34.4" }, "peerDependencies": { @@ -7381,24 +7381,24 @@ } }, "node_modules/react": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", - "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", + "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", - "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", "license": "MIT", "dependencies": { - "scheduler": "^0.26.0" + "scheduler": "^0.27.0" }, "peerDependencies": { - "react": "^19.1.0" + "react": "^19.2.3" } }, "node_modules/react-is": { @@ -7659,9 +7659,9 @@ } }, "node_modules/scheduler": { - "version": "0.26.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", - "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", "license": "MIT" }, "node_modules/semver": { diff --git a/package.json b/package.json index 9b78307..c8c3346 100644 --- a/package.json +++ b/package.json @@ -25,11 +25,11 @@ "csrf": "^3.1.0", "lucide-react": "^0.542.0", "mongodb": "^6.19.0", - "next": "16.0.7", + "next": "16.0.10", "next-auth": "^4.24.11", "next-themes": "^0.4.6", - "react": "19.1.0", - "react-dom": "19.1.0", + "react": "19.2.3", + "react-dom": "19.2.3", "sonner": "^2.0.7", "tailwind-merge": "^3.3.1", "zod": "^4.1.12" @@ -42,7 +42,7 @@ "@types/react": "^19", "@types/react-dom": "^19", "eslint": "^9", - "eslint-config-next": "16.0.7", + "eslint-config-next": "16.0.10", "eslint-config-prettier": "^10.1.8", "prettier": "^3.6.2", "tailwindcss": "^4", diff --git a/tsconfig.json b/tsconfig.json index d8b9323..e7ff3a2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,11 @@ { "compilerOptions": { "target": "ES2017", - "lib": ["dom", "dom.iterable", "esnext"], + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], "allowJs": true, "skipLibCheck": true, "strict": true, @@ -11,7 +15,7 @@ "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, - "jsx": "preserve", + "jsx": "react-jsx", "incremental": true, "plugins": [ { @@ -19,9 +23,19 @@ } ], "paths": { - "@/*": ["./*"] + "@/*": [ + "./*" + ] } }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules"] + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts" + ], + "exclude": [ + "node_modules" + ] } From 797871e39d7a83499fd753a2c74f7e7f0905b480 Mon Sep 17 00:00:00 2001 From: Abul Date: Sat, 13 Dec 2025 23:34:54 +0530 Subject: [PATCH 06/14] landing page with animations --- app/globals.css | 121 ++++++++++ app/page.tsx | 234 ++----------------- components/landing/Architecture.tsx | 212 +++++++++++++++++ components/landing/FeaturesGrid.tsx | 342 ++++++++++++++++++++++++++++ components/landing/Footer.tsx | 54 +++++ components/landing/Hero.tsx | 172 ++++++++++++++ components/landing/Navigation.tsx | 72 ++++++ components/landing/Pricing.tsx | 140 ++++++++++++ components/landing/TrustGrid.tsx | 48 ++++ components/landing/index.ts | 7 + tsconfig.json | 14 +- 11 files changed, 1196 insertions(+), 220 deletions(-) create mode 100644 components/landing/Architecture.tsx create mode 100644 components/landing/FeaturesGrid.tsx create mode 100644 components/landing/Footer.tsx create mode 100644 components/landing/Hero.tsx create mode 100644 components/landing/Navigation.tsx create mode 100644 components/landing/Pricing.tsx create mode 100644 components/landing/TrustGrid.tsx create mode 100644 components/landing/index.ts diff --git a/app/globals.css b/app/globals.css index f1da802..3cdb8ca 100644 --- a/app/globals.css +++ b/app/globals.css @@ -168,3 +168,124 @@ @apply bg-background text-foreground; } } + +/* Landing Page Animations */ +@keyframes fadeUp { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes drawBorder { + from { + clip-path: inset(0 100% 0 0); + } + to { + clip-path: inset(0 0 0 0); + } +} + +@keyframes contentFade { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes slideInRight { + from { + opacity: 0; + transform: translateX(-20px); + } + to { + opacity: 0.5; + transform: translateX(0); + } +} + +@keyframes slideInLeft { + from { + opacity: 0; + transform: translateX(20px); + } + to { + opacity: 1; + transform: translateX(0); + } +} + +@keyframes slowRotate { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +.animate-fade-up { + animation: fadeUp 0.6s cubic-bezier(0.16, 1, 0.3, 1) forwards; + opacity: 0; +} + +.animate-fade-in { + animation: fadeIn 0.3s ease-out forwards; +} + +.animate-draw-border { + animation: drawBorder 0.8s cubic-bezier(0.16, 1, 0.3, 1) forwards; +} + +.animate-content-fade { + animation: contentFade 0.8s cubic-bezier(0.16, 1, 0.3, 1) 0.4s forwards; + opacity: 0; +} + +.animate-slide-in-right { + animation: slideInRight 0.5s cubic-bezier(0.16, 1, 0.3, 1) forwards; +} + +.animate-slide-in-left { + animation: slideInLeft 0.5s cubic-bezier(0.16, 1, 0.3, 1) forwards; +} + +.animation-delay-100 { + animation-delay: 100ms; +} + +.animation-delay-200 { + animation-delay: 200ms; +} + +.animation-delay-300 { + animation-delay: 300ms; +} + +@media (prefers-reduced-motion: reduce) { + .animate-fade-up, + .animate-fade-in, + .animate-draw-border, + .animate-content-fade, + .animate-slide-in-right, + .animate-slide-in-left { + animation: none; + opacity: 1; + transform: none; + } +} diff --git a/app/page.tsx b/app/page.tsx index bd67dcf..233d49d 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -3,10 +3,16 @@ import { useSession } from "next-auth/react"; import { useRouter } from "next/navigation"; import { useEffect, useState } from "react"; -import { Button } from "@/components/ui/button"; -import Image from "next/image"; import { SigninModal } from "@/components/modals/SigninModal"; -import ThemeToggleButton from "@/components/ui/theme-toggle-button"; +import { + Navigation, + Hero, + TrustGrid, + FeaturesGrid, + Architecture, + Pricing, + Footer, +} from "@/components/landing"; export default function Home() { const { data: session, status } = useSession(); @@ -14,228 +20,38 @@ export default function Home() { const [isSigninModalOpen, setIsSigninModalOpen] = useState(false); useEffect(() => { - if (status === "loading") { - return; - } - - if (session) { - router.push("/dashboard"); - } + if (status === "loading") return; + if (session) router.push("/dashboard"); }, [session, status, router]); - const handleGetStarted = () => { - setIsSigninModalOpen(true); - }; + const handleGetStarted = () => setIsSigninModalOpen(true); if (status === "loading") { return ( -
+
-
-

Loading...

+
+

Loading...

); } - if (session) { - return null; // Will redirect to dashboard - } + if (session) return null; return ( -
- {/* Header */} -
-
-
-
- Mini Drive Logo -

- Mini Drive -

-
-
- - -
-
-
-
- - {/* Main Content */} -
-
-
- {/* Left Content */} -
-
-
- - 5GB free cloud storage -
-

- Your files, -
- anywhere -

-

- Secure cloud storage with Google authentication. Access your - files from any device, anywhere in the world. -

-
- -
- -
- - {/* Trust indicators */} -
-
-
- -
- No credit card required -
-
-
- 🔒 -
- Google secure -
-
-
- - {/* Right Content - Visual Element */} -
-
- {/* Background elements */} -
-
- - {/* Main visual */} -
-
- {/* App mockup */} -
-
-
- Mini Drive Logo - - Mini Drive - -
-
-
-
-
-
-
- - {/* File list mockup */} -
-
-
- 📄 -
-
-
- Project Notes.pdf -
-
- 2.4 MB • Modified today -
-
-
- -
-
- 📷 -
-
-
- Vacation Photos -
-
- 15 files • 245 MB -
-
-
- -
-
- - + - -
-
- Drop files here -
-
-
-
- - {/* Stats */} -
-
-
- 5GB -
-
- Free storage -
-
-
-
-
- Devices -
-
-
-
- 🔒 -
-
- Secure -
-
-
-
-
-
-
-
-
-
- - {/* Sign in Modal */} +
+ + + + + + +
setIsSigninModalOpen(false)} /> -
+ ); } diff --git a/components/landing/Architecture.tsx b/components/landing/Architecture.tsx new file mode 100644 index 0000000..baa2e82 --- /dev/null +++ b/components/landing/Architecture.tsx @@ -0,0 +1,212 @@ +"use client"; + +import { useEffect, useRef, useState } from "react"; +import { Key, Shield, Zap } from "lucide-react"; + +export function Architecture() { + const [isVisible, setIsVisible] = useState(false); + const sectionRef = useRef(null); + + useEffect(() => { + const observer = new IntersectionObserver( + ([entry]) => { + if (entry.isIntersecting) { + setIsVisible(true); + } + }, + { threshold: 0.3 } + ); + + if (sectionRef.current) { + observer.observe(sectionRef.current); + } + + return () => observer.disconnect(); + }, []); + + return ( +
+
+
+
+ + Security + +

+ Built on a fortress. +

+

+ Minidrive isn't just storage; it's a vault. End-to-end + encryption ensures that even we can't see your data. +

+ +
+
+
+ +
+
+

+ AES-256 Encryption +

+

+ Military-grade encryption at rest and in transit. +

+
+
+ +
+
+ +
+
+

+ 2-Factor Authentication +

+

+ Secure your account with TOTP or hardware keys. +

+
+
+ +
+
+ +
+
+

+ {"< 50ms Latency"} +

+

+ Global edge network for instant file access. +

+
+
+
+
+ +
+
+ + + + + + + + + + + + +
+
+
+
+
+ ); +} diff --git a/components/landing/FeaturesGrid.tsx b/components/landing/FeaturesGrid.tsx new file mode 100644 index 0000000..5e1ed28 --- /dev/null +++ b/components/landing/FeaturesGrid.tsx @@ -0,0 +1,342 @@ +"use client"; + +import { useRef, useState } from "react"; +import { Activity, Copy, Search, Trash2 } from "lucide-react"; + +export function FeaturesGrid() { + return ( +
+
+
+ + Core Features + +

+ Intelligent by design. +

+
+ +
+ + + + +
+
+
+ ); +} + +function ActivityCard() { + const [hovered, setHovered] = useState(false); + const [showTooltip, setShowTooltip] = useState(false); + + return ( +
{ + setHovered(true); + setTimeout(() => setShowTooltip(true), 300); + }} + onMouseLeave={() => { + setHovered(false); + setShowTooltip(false); + }} + > +
+ + + Activity + +
+

+ Total visibility. +

+

+ Track every movement. See who viewed, downloaded, or modified a file in + real-time. +

+ +
+
+ {[ + { + name: "Madison Scott", + action: "Downloaded 'Q3-Report.pdf'", + time: "20m ago", + highlight: true, + }, + { + name: "Alex Chen", + action: "Viewed 'Design-System.fig'", + time: "1h ago", + highlight: false, + }, + { + name: "Jordan Lee", + action: "Modified 'Brand-Guidelines'", + time: "3h ago", + highlight: false, + }, + { + name: "Sam Parker", + action: "Uploaded 'Invoice_2024.pdf'", + time: "5h ago", + highlight: false, + }, + ].map((item, i) => ( +
+
+
+ {item.name + .split(" ") + .map(n => n[0]) + .join("")} +
+ + {item.name} + +
+ {item.time} + {item.highlight && showTooltip && ( +
+ {item.action} + + — {item.time} + +
+ )} +
+ ))} +
+
+
+ ); +} + +function DuplicateCard() { + const [hovered, setHovered] = useState(false); + const [merged, setMerged] = useState(false); + + return ( +
{ + setHovered(true); + setTimeout(() => setMerged(true), 500); + }} + onMouseLeave={() => { + setHovered(false); + setMerged(false); + }} + > +
+ + + Deduplication + +
+

+ Zero redundancy. +

+

+ Automatic hash-matching identifies and merges duplicate files. +

+ +
+
+
+
+
+
+ {merged && ( +
+ 1 +
+ )} +
+
+
+ ); +} + +function SearchCard() { + const [hovered, setHovered] = useState(false); + const [searchText, setSearchText] = useState(""); + const [showDropdown, setShowDropdown] = useState(false); + const searchRef = useRef(null); + + const handleHover = () => { + setHovered(true); + let i = 0; + const text = "inv..."; + searchRef.current = setInterval(() => { + if (i <= text.length) { + setSearchText(text.slice(0, i)); + i++; + } else { + if (searchRef.current) clearInterval(searchRef.current); + setShowDropdown(true); + } + }, 100); + }; + + const handleLeave = () => { + setHovered(false); + if (searchRef.current) clearInterval(searchRef.current); + setSearchText(""); + setShowDropdown(false); + }; + + return ( +
+
+ + + Search + +
+

+ Deep Query. +

+

+ Search inside PDFs, Docs, and code snippets. +

+ +
+
+ + {searchText} + + | +
+ {showDropdown && ( +
+
+ + + Inv + + oice_2024.pdf + +
+
+ + + Inv + + estment_Plan.docx + +
+
+ )} +
+
+ ); +} + +function TrashCard() { + const [hovered, setHovered] = useState(false); + const [filePosition, setFilePosition] = useState< + "active" | "trash" | "restoring" + >("active"); + const [showUndo, setShowUndo] = useState(false); + + const animateTrash = () => { + setHovered(true); + setTimeout(() => setFilePosition("trash"), 300); + setTimeout(() => setShowUndo(true), 600); + setTimeout(() => { + setFilePosition("restoring"); + setShowUndo(false); + }, 1500); + setTimeout(() => setFilePosition("active"), 2000); + }; + + return ( +
{ + setHovered(false); + setFilePosition("active"); + setShowUndo(false); + }} + > +
+ + + Recovery + +
+

+ Forgiving, yet secure. +

+

+ 30-day retention for deleted items. One-click restoration. +

+ +
+
+ + Active + +
+ {(filePosition === "active" || filePosition === "restoring") && ( +
+
+ Report.pdf +
+ )} +
+
+
+ + Trash + +
+ {filePosition === "trash" && ( +
+
+ Report.pdf +
+ )} +
+
+
+ + {showUndo && ( +
+ File deleted. + +
+ )} +
+ ); +} diff --git a/components/landing/Footer.tsx b/components/landing/Footer.tsx new file mode 100644 index 0000000..1163a7b --- /dev/null +++ b/components/landing/Footer.tsx @@ -0,0 +1,54 @@ +import Link from "next/link"; + +const footerLinks = { + Product: ["Changelog", "Features", "Security", "Pricing"], + Company: ["About", "Careers", "Brand", "Contact"], + Resources: ["Blog", "Help Center", "API Docs", "System Status"], + Legal: ["Privacy", "Terms", "Cookie Settings"], +}; + +export function Footer() { + return ( +
+
+
+
+ + Minidrive + +

Designed for focus.

+
+ + {Object.entries(footerLinks).map(([category, links]) => ( +
+

+ {category} +

+
    + {links.map(link => ( +
  • + + {link} + +
  • + ))} +
+
+ ))} +
+
+ +
+ + MINIDRIVE © 2025 + +
+
+ ); +} diff --git a/components/landing/Hero.tsx b/components/landing/Hero.tsx new file mode 100644 index 0000000..adde073 --- /dev/null +++ b/components/landing/Hero.tsx @@ -0,0 +1,172 @@ +"use client"; + +import { useEffect, useRef } from "react"; +import { ArrowRight } from "lucide-react"; + +interface HeroProps { + onGetStarted: () => void; +} + +export function Hero({ onGetStarted }: HeroProps) { + const containerRef = useRef(null); + const shardsRef = useRef(null); + + useEffect(() => { + const handleMouseMove = (e: MouseEvent) => { + if (!shardsRef.current || !containerRef.current) return; + const rect = containerRef.current.getBoundingClientRect(); + const x = (e.clientX - rect.left - rect.width / 2) / 50; + const y = (e.clientY - rect.top - rect.height / 2) / 50; + + const shards = shardsRef.current.querySelectorAll(".shard"); + shards.forEach((shard, i) => { + const depth = (i + 1) * 0.5; + (shard as HTMLElement).style.transform = + `translate(${x * depth}px, ${y * depth}px)`; + }); + }; + + const container = containerRef.current; + container?.addEventListener("mousemove", handleMouseMove); + return () => container?.removeEventListener("mousemove", handleMouseMove); + }, []); + + return ( +
+
+
+ + Version 2.0 is live — Intelligent Storage + +
+ +

+ File management, perfected. +

+ +

+ Minidrive is the high-performance workspace for your life's data. + 5GB free. Zero-knowledge encryption. Native speed. +

+ +
+ + + View the architecture + + +
+ +
+
+
+
+
+
+ + All Files + +
+
+
+
+
+
+ +
+ {[ + { + name: "Q3-Report.pdf", + size: "2.4 MB", + modified: "2 hours ago", + }, + { + name: "Design-System.fig", + size: "18.2 MB", + modified: "Yesterday", + }, + { + name: "Invoice_2024.pdf", + size: "340 KB", + modified: "3 days ago", + }, + { + name: "Brand-Guidelines.pdf", + size: "5.1 MB", + modified: "Last week", + }, + ].map((file, i) => ( +
+
+
+ + {file.name} + +
+
+ + {file.size} + + + {file.modified} + +
+
+ ))} +
+
+
+ +
+
+
+ Uploading... +
+
+
+
+
+ +
+
+
+ + AES-256 + +
+ Encrypted +
+ +
+ + Duplicate Detected + +
+ + +
+
+
+
+
+ ); +} diff --git a/components/landing/Navigation.tsx b/components/landing/Navigation.tsx new file mode 100644 index 0000000..5f72434 --- /dev/null +++ b/components/landing/Navigation.tsx @@ -0,0 +1,72 @@ +"use client"; + +import Link from "next/link"; +import { useState, useEffect } from "react"; + +interface NavigationProps { + onSignIn: () => void; + onGetStarted: () => void; +} + +export function Navigation({ onSignIn, onGetStarted }: NavigationProps) { + const [scrolled, setScrolled] = useState(false); + + useEffect(() => { + const handleScroll = () => setScrolled(window.scrollY > 10); + window.addEventListener("scroll", handleScroll); + return () => window.removeEventListener("scroll", handleScroll); + }, []); + + return ( + + ); +} diff --git a/components/landing/Pricing.tsx b/components/landing/Pricing.tsx new file mode 100644 index 0000000..efd81e7 --- /dev/null +++ b/components/landing/Pricing.tsx @@ -0,0 +1,140 @@ +"use client"; + +import { useState } from "react"; +import { Check } from "lucide-react"; + +interface PricingProps { + onGetStarted: () => void; +} + +export function Pricing({ onGetStarted }: PricingProps) { + const [annual, setAnnual] = useState(false); + + const plans = [ + { + name: "Starter", + price: "$0", + sub: "Forever.", + features: ["5 GB Storage", "Basic Search", "Standard Encryption"], + cta: "Start now", + highlighted: false, + }, + { + name: "Pro", + price: annual ? "$10" : "$12", + sub: "per user / month", + features: [ + "1 TB Storage", + "Duplicate Detection", + "Advanced Activity Logs", + "Priority Support", + ], + cta: "Go Pro", + highlighted: true, + }, + { + name: "Enterprise", + price: "Custom", + sub: "Tailored for scale.", + features: [ + "Unlimited Storage", + "SSO / SAML", + "Dedicated Success Manager", + "Custom Integrations", + ], + cta: "Contact Sales", + highlighted: false, + }, + ]; + + return ( +
+
+
+ + Pricing + +

+ Predictable scaling. +

+
+ +
+
+ + +
+
+
+ +
+ {plans.map(plan => ( +
+

+ {plan.name} +

+
+ + {plan.price} + + {plan.sub} +
+ +
+ {plan.features.map(feature => ( +
+ + + {feature} + +
+ ))} +
+ + +
+ ))} +
+
+
+ ); +} diff --git a/components/landing/TrustGrid.tsx b/components/landing/TrustGrid.tsx new file mode 100644 index 0000000..1dde381 --- /dev/null +++ b/components/landing/TrustGrid.tsx @@ -0,0 +1,48 @@ +"use client"; + +import { useState } from "react"; + +const trustItems = [ + { name: "SOC2", label: "Compliant" }, + { name: "AES-256", label: "Encryption" }, + { name: "AWS", label: "Infrastructure" }, + { name: "Stripe", label: "Payments" }, + { name: "99.99%", label: "Uptime" }, + { name: "GDPR", label: "Compliant" }, +]; + +export function TrustGrid() { + const [hoveredIndex, setHoveredIndex] = useState(null); + + return ( +
+
+ {trustItems.map((item, i) => ( +
setHoveredIndex(i)} + onMouseLeave={() => setHoveredIndex(null)} + > + + {item.name} + + + {item.label} + +
+ ))} +
+
+ ); +} diff --git a/components/landing/index.ts b/components/landing/index.ts new file mode 100644 index 0000000..a10986b --- /dev/null +++ b/components/landing/index.ts @@ -0,0 +1,7 @@ +export { Navigation } from "./Navigation"; +export { Hero } from "./Hero"; +export { TrustGrid } from "./TrustGrid"; +export { FeaturesGrid } from "./FeaturesGrid"; +export { Architecture } from "./Architecture"; +export { Pricing } from "./Pricing"; +export { Footer } from "./Footer"; diff --git a/tsconfig.json b/tsconfig.json index e7ff3a2..705f5ce 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,11 +1,7 @@ { "compilerOptions": { "target": "ES2017", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], + "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "strict": true, @@ -23,9 +19,7 @@ } ], "paths": { - "@/*": [ - "./*" - ] + "@/*": ["./*"] } }, "include": [ @@ -35,7 +29,5 @@ ".next/types/**/*.ts", ".next/dev/types/**/*.ts" ], - "exclude": [ - "node_modules" - ] + "exclude": ["node_modules"] } From 0d3a2b1f1db158a7c42b0b1cab488aaa871b2413 Mon Sep 17 00:00:00 2001 From: Abul Date: Sun, 14 Dec 2025 00:29:11 +0530 Subject: [PATCH 07/14] style: Refactor design system --- app/globals.css | 192 ++++++++------- components/activity/ActivityList.tsx | 12 +- components/landing/Architecture.tsx | 59 ++--- components/landing/FeaturesGrid.tsx | 326 +++++++++++++++---------- components/landing/Hero.tsx | 6 +- components/landing/Navigation.tsx | 6 +- components/landing/Pricing.tsx | 181 ++++++++------ components/landing/TrustGrid.tsx | 20 +- components/layout/DashboardHeader.tsx | 4 +- components/layout/SidebarComponent.tsx | 3 +- lib/utils/file-utils.ts | 14 +- package-lock.json | 59 ++++- package.json | 1 + 13 files changed, 508 insertions(+), 375 deletions(-) diff --git a/app/globals.css b/app/globals.css index 3cdb8ca..fd50e5f 100644 --- a/app/globals.css +++ b/app/globals.css @@ -4,107 +4,91 @@ @custom-variant dark (&:is(.dark *)); :root { - --background: oklch(1 0 0); - --foreground: oklch(0 0 0); - --card: oklch(1 0 0); - --card-foreground: oklch(0 0 0); - --popover: oklch(1 0 0); - --popover-foreground: oklch(0 0 0); - --primary: oklch(0.6489 0.237 26.9728); - --primary-foreground: oklch(1 0 0); - --secondary: oklch(0.968 0.211 109.7692); - --secondary-foreground: oklch(0 0 0); - --muted: oklch(0.9551 0 0); - --muted-foreground: oklch(0.3211 0 0); - --accent: oklch(0.5635 0.2408 260.8178); - --accent-foreground: oklch(1 0 0); - --destructive: oklch(0 0 0); - --destructive-foreground: oklch(1 0 0); - --border: oklch(0 0 0); - --input: oklch(0 0 0); - --ring: oklch(0.6489 0.237 26.9728); - --chart-1: oklch(0.6489 0.237 26.9728); - --chart-2: oklch(0.968 0.211 109.7692); - --chart-3: oklch(0.5635 0.2408 260.8178); - --chart-4: oklch(0.7323 0.2492 142.4953); - --chart-5: oklch(0.5931 0.2726 328.3634); - --sidebar: oklch(0.9551 0 0); - --sidebar-foreground: oklch(0 0 0); - --sidebar-primary: oklch(0.6489 0.237 26.9728); - --sidebar-primary-foreground: oklch(1 0 0); - --sidebar-accent: oklch(0.5635 0.2408 260.8178); - --sidebar-accent-foreground: oklch(1 0 0); - --sidebar-border: oklch(0 0 0); - --sidebar-ring: oklch(0.6489 0.237 26.9728); - --font-sans: Roboto, ui-sans-serif, sans-serif, system-ui; - --font-serif: Roboto Slab, ui-serif, serif; - --font-mono: Roboto Mono, ui-monospace, monospace; + /* Monochromatic color scheme - Light mode */ + --background: #ffffff; + --foreground: #18181b; + --card: #ffffff; + --card-foreground: #18181b; + --popover: #ffffff; + --popover-foreground: #18181b; + --primary: #18181b; + --primary-foreground: #fafafa; + --secondary: #f4f4f5; + --secondary-foreground: #18181b; + --muted: #f4f4f5; + --muted-foreground: #71717a; + --accent: #e4e4e7; + --accent-foreground: #18181b; + --destructive: #18181b; + --destructive-foreground: #fafafa; + --border: #d4d4d8; + --input: #d4d4d8; + --ring: #a1a1aa; + --chart-1: #18181b; + --chart-2: #52525b; + --chart-3: #71717a; + --chart-4: #a1a1aa; + --chart-5: #d4d4d8; + --sidebar: #fafafa; + --sidebar-foreground: #18181b; + --sidebar-primary: #18181b; + --sidebar-primary-foreground: #fafafa; + --sidebar-accent: #f4f4f5; + --sidebar-accent-foreground: #18181b; + --sidebar-border: #d4d4d8; + --sidebar-ring: #a1a1aa; + --font-sans: "Inter", ui-sans-serif, sans-serif, system-ui; + --font-serif: "Inter", ui-serif, serif; + --font-mono: "Geist Mono", ui-monospace, monospace; --radius: 0px; - --shadow-2xs: 4px 4px 0px 0px hsl(0 0% 0% / 0.5); - --shadow-xs: 4px 4px 0px 0px hsl(0 0% 0% / 0.5); - --shadow-sm: - 4px 4px 0px 0px hsl(0 0% 0% / 1), 4px 1px 2px -1px hsl(0 0% 0% / 1); - --shadow: 4px 4px 0px 0px hsl(0 0% 0% / 1), 4px 1px 2px -1px hsl(0 0% 0% / 1); - --shadow-md: - 4px 4px 0px 0px hsl(0 0% 0% / 1), 4px 2px 4px -1px hsl(0 0% 0% / 1); + --shadow-2xs: 0 1px 2px 0 rgb(0 0 0 / 0.05); + --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.05); + --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); + --shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); + --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); --shadow-lg: - 4px 4px 0px 0px hsl(0 0% 0% / 1), 4px 4px 6px -1px hsl(0 0% 0% / 1); + 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); --shadow-xl: - 4px 4px 0px 0px hsl(0 0% 0% / 1), 4px 8px 10px -1px hsl(0 0% 0% / 1); - --shadow-2xl: 4px 4px 0px 0px hsl(0 0% 0% / 2.5); + 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1); + --shadow-2xl: 0 25px 50px -12px rgb(0 0 0 / 0.25); --tracking-normal: 0em; --spacing: 0.25rem; } .dark { - --background: oklch(0 0 0); - --foreground: oklch(1 0 0); - --card: oklch(0 0 0); - --card-foreground: oklch(1 0 0); - --popover: oklch(0.3211 0 0); - --popover-foreground: oklch(1 0 0); - --primary: oklch(0.7044 0.1872 23.1858); - --primary-foreground: oklch(0 0 0); - --secondary: oklch(0.9691 0.2005 109.6228); - --secondary-foreground: oklch(0 0 0); - --muted: oklch(0.3211 0 0); - --muted-foreground: oklch(0.8452 0 0); - --accent: oklch(0.6755 0.1765 252.2592); - --accent-foreground: oklch(0 0 0); - --destructive: oklch(1 0 0); - --destructive-foreground: oklch(0 0 0); - --border: oklch(1 0 0); - --input: oklch(1 0 0); - --ring: oklch(0.7044 0.1872 23.1858); - --chart-1: oklch(0.7044 0.1872 23.1858); - --chart-2: oklch(0.9691 0.2005 109.6228); - --chart-3: oklch(0.6755 0.1765 252.2592); - --chart-4: oklch(0.7395 0.2268 142.8504); - --chart-5: oklch(0.6131 0.2458 328.0714); - --sidebar: oklch(0 0 0); - --sidebar-foreground: oklch(1 0 0); - --sidebar-primary: oklch(0.7044 0.1872 23.1858); - --sidebar-primary-foreground: oklch(0 0 0); - --sidebar-accent: oklch(0.6755 0.1765 252.2592); - --sidebar-accent-foreground: oklch(0 0 0); - --sidebar-border: oklch(1 0 0); - --sidebar-ring: oklch(0.7044 0.1872 23.1858); - --font-sans: Roboto, ui-sans-serif, sans-serif, system-ui; - --font-serif: Roboto Slab, ui-serif, serif; - --font-mono: Roboto Mono, ui-monospace, monospace; - --radius: 0px; - --shadow-2xs: 4px 4px 0px 0px hsl(0 0% 0% / 0.5); - --shadow-xs: 4px 4px 0px 0px hsl(0 0% 0% / 0.5); - --shadow-sm: - 4px 4px 0px 0px hsl(0 0% 0% / 1), 4px 1px 2px -1px hsl(0 0% 0% / 1); - --shadow: 4px 4px 0px 0px hsl(0 0% 0% / 1), 4px 1px 2px -1px hsl(0 0% 0% / 1); - --shadow-md: - 4px 4px 0px 0px hsl(0 0% 0% / 1), 4px 2px 4px -1px hsl(0 0% 0% / 1); - --shadow-lg: - 4px 4px 0px 0px hsl(0 0% 0% / 1), 4px 4px 6px -1px hsl(0 0% 0% / 1); - --shadow-xl: - 4px 4px 0px 0px hsl(0 0% 0% / 1), 4px 8px 10px -1px hsl(0 0% 0% / 1); - --shadow-2xl: 4px 4px 0px 0px hsl(0 0% 0% / 2.5); + /* Monochromatic color scheme - Dark mode */ + --background: #09090b; + --foreground: #fafafa; + --card: #09090b; + --card-foreground: #fafafa; + --popover: #18181b; + --popover-foreground: #fafafa; + --primary: #fafafa; + --primary-foreground: #18181b; + --secondary: #27272a; + --secondary-foreground: #fafafa; + --muted: #27272a; + --muted-foreground: #a1a1aa; + --accent: #27272a; + --accent-foreground: #fafafa; + --destructive: #fafafa; + --destructive-foreground: #18181b; + --border: #3f3f46; + --input: #3f3f46; + --ring: #52525b; + --chart-1: #fafafa; + --chart-2: #d4d4d8; + --chart-3: #a1a1aa; + --chart-4: #71717a; + --chart-5: #52525b; + --sidebar: #09090b; + --sidebar-foreground: #fafafa; + --sidebar-primary: #fafafa; + --sidebar-primary-foreground: #18181b; + --sidebar-accent: #27272a; + --sidebar-accent-foreground: #fafafa; + --sidebar-border: #3f3f46; + --sidebar-ring: #52525b; } @theme inline { @@ -239,6 +223,18 @@ } } +@keyframes pulseZoom { + 0%, + 100% { + transform: scale(1); + opacity: 1; + } + 50% { + transform: scale(1.15); + opacity: 0.85; + } +} + .animate-fade-up { animation: fadeUp 0.6s cubic-bezier(0.16, 1, 0.3, 1) forwards; opacity: 0; @@ -289,3 +285,13 @@ transform: none; } } + +/* Architecture section dot pattern */ +.architecture-dots { + background-image: radial-gradient(circle, #a1a1aa 1px, transparent 1px); + background-size: 20px 20px; +} + +.dark .architecture-dots { + background-image: radial-gradient(circle, #3f3f46 1px, transparent 1px); +} diff --git a/components/activity/ActivityList.tsx b/components/activity/ActivityList.tsx index 9ff6ae4..218d024 100644 --- a/components/activity/ActivityList.tsx +++ b/components/activity/ActivityList.tsx @@ -35,17 +35,17 @@ function getActivityIcon(action: ActivityAction) { function getActivityColor(action: ActivityAction) { switch (action) { case "upload": - return "bg-green-500/10 text-green-600 dark:text-green-400"; + return "bg-zinc-100 text-zinc-700 dark:bg-zinc-800 dark:text-zinc-300"; case "view": - return "bg-blue-500/10 text-blue-600 dark:text-blue-400"; + return "bg-zinc-100 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-400"; case "download": - return "bg-purple-500/10 text-purple-600 dark:text-purple-400"; + return "bg-zinc-200 text-zinc-700 dark:bg-zinc-700 dark:text-zinc-300"; case "rename": - return "bg-yellow-500/10 text-yellow-600 dark:text-yellow-400"; + return "bg-zinc-100 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-400"; case "delete": - return "bg-red-500/10 text-red-600 dark:text-red-400"; + return "bg-zinc-200 text-zinc-800 dark:bg-zinc-700 dark:text-zinc-200"; case "restore": - return "bg-cyan-500/10 text-cyan-600 dark:text-cyan-400"; + return "bg-zinc-100 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-400"; } } diff --git a/components/landing/Architecture.tsx b/components/landing/Architecture.tsx index baa2e82..29c2d61 100644 --- a/components/landing/Architecture.tsx +++ b/components/landing/Architecture.tsx @@ -28,11 +28,7 @@ export function Architecture() {
@@ -103,57 +99,53 @@ export function Architecture() {
-
+
+ {/* Lock body */} + {/* Lock shackle */} + {/* Keyhole circle */} + {/* Keyhole line */} + {/* Grid lines - horizontal */} @@ -162,7 +154,7 @@ export function Architecture() { y1="130" x2="150" y2="130" - stroke="#A1A1AA" + className="stroke-zinc-300 dark:stroke-zinc-600" strokeWidth="0.5" strokeDasharray="4 4" /> @@ -171,16 +163,17 @@ export function Architecture() { y1="150" x2="150" y2="150" - stroke="#A1A1AA" + className="stroke-zinc-300 dark:stroke-zinc-600" strokeWidth="0.5" strokeDasharray="4 4" /> + {/* Grid lines - vertical */} @@ -189,7 +182,7 @@ export function Architecture() { y1="90" x2="100" y2="170" - stroke="#A1A1AA" + className="stroke-zinc-300 dark:stroke-zinc-600" strokeWidth="0.5" strokeDasharray="4 4" /> @@ -198,7 +191,7 @@ export function Architecture() { y1="90" x2="125" y2="170" - stroke="#A1A1AA" + className="stroke-zinc-300 dark:stroke-zinc-600" strokeWidth="0.5" strokeDasharray="4 4" /> diff --git a/components/landing/FeaturesGrid.tsx b/components/landing/FeaturesGrid.tsx index 5e1ed28..ca74b82 100644 --- a/components/landing/FeaturesGrid.tsx +++ b/components/landing/FeaturesGrid.tsx @@ -1,7 +1,8 @@ "use client"; -import { useRef, useState } from "react"; -import { Activity, Copy, Search, Trash2 } from "lucide-react"; +import { useEffect, useRef, useState } from "react"; +import { motion } from "framer-motion"; +import { Activity, Copy, File, RotateCcw, Search, Trash2 } from "lucide-react"; export function FeaturesGrid() { return ( @@ -19,7 +20,7 @@ export function FeaturesGrid() {
-
+
@@ -31,22 +32,45 @@ export function FeaturesGrid() { } function ActivityCard() { - const [hovered, setHovered] = useState(false); + const [isActive, setIsActive] = useState(false); const [showTooltip, setShowTooltip] = useState(false); + const cardRef = useRef(null); + + useEffect(() => { + const observer = new IntersectionObserver( + ([entry]) => { + if (entry.isIntersecting) { + setIsActive(true); + } + }, + { threshold: 0.5 } + ); + + if (cardRef.current) observer.observe(cardRef.current); + return () => observer.disconnect(); + }, []); + + useEffect(() => { + if (!isActive) return; + + const showTimer = setTimeout(() => setShowTooltip(true), 800); + const hideTimer = setTimeout(() => setShowTooltip(false), 3000); + const loopTimer = setInterval(() => { + setShowTooltip(true); + setTimeout(() => setShowTooltip(false), 2200); + }, 4000); + + return () => { + clearTimeout(showTimer); + clearTimeout(hideTimer); + clearInterval(loopTimer); + }; + }, [isActive]); return (
{ - setHovered(true); - setTimeout(() => setShowTooltip(true), 300); - }} - onMouseLeave={() => { - setHovered(false); - setShowTooltip(false); - }} + ref={cardRef} + className="relative bg-white dark:bg-zinc-950 p-8 lg:col-span-2 lg:p-10" >
@@ -92,8 +116,8 @@ function ActivityCard() { ].map((item, i) => (
@@ -125,20 +149,33 @@ function ActivityCard() { } function DuplicateCard() { - const [hovered, setHovered] = useState(false); const [merged, setMerged] = useState(false); + const cardRef = useRef(null); + + useEffect(() => { + const observer = new IntersectionObserver( + ([entry]) => { + if (entry.isIntersecting) { + const animate = () => { + setTimeout(() => setMerged(true), 500); + setTimeout(() => setMerged(false), 3000); + }; + animate(); + const interval = setInterval(animate, 4000); + return () => clearInterval(interval); + } + }, + { threshold: 0.5 } + ); + + if (cardRef.current) observer.observe(cardRef.current); + return () => observer.disconnect(); + }, []); return (
{ - setHovered(true); - setTimeout(() => setMerged(true), 500); - }} - onMouseLeave={() => { - setHovered(false); - setMerged(false); - }} + ref={cardRef} + className="relative bg-white dark:bg-zinc-950 p-8 lg:p-10" >
@@ -175,38 +212,55 @@ function DuplicateCard() { } function SearchCard() { - const [hovered, setHovered] = useState(false); const [searchText, setSearchText] = useState(""); const [showDropdown, setShowDropdown] = useState(false); - const searchRef = useRef(null); + const cardRef = useRef(null); + const intervalRef = useRef(null); - const handleHover = () => { - setHovered(true); - let i = 0; - const text = "inv..."; - searchRef.current = setInterval(() => { - if (i <= text.length) { - setSearchText(text.slice(0, i)); - i++; - } else { - if (searchRef.current) clearInterval(searchRef.current); - setShowDropdown(true); - } - }, 100); - }; + useEffect(() => { + const observer = new IntersectionObserver( + ([entry]) => { + if (entry.isIntersecting) { + const animate = () => { + let i = 0; + const text = "inv..."; + setSearchText(""); + setShowDropdown(false); - const handleLeave = () => { - setHovered(false); - if (searchRef.current) clearInterval(searchRef.current); - setSearchText(""); - setShowDropdown(false); - }; + intervalRef.current = setInterval(() => { + if (i <= text.length) { + setSearchText(text.slice(0, i)); + i++; + } else { + if (intervalRef.current) clearInterval(intervalRef.current); + setShowDropdown(true); + setTimeout(() => { + setShowDropdown(false); + setSearchText(""); + }, 2000); + } + }, 100); + }; + + animate(); + const loopInterval = setInterval(animate, 4000); + return () => { + clearInterval(loopInterval); + if (intervalRef.current) clearInterval(intervalRef.current); + }; + } + }, + { threshold: 0.5 } + ); + + if (cardRef.current) observer.observe(cardRef.current); + return () => observer.disconnect(); + }, []); return (
@@ -226,63 +280,49 @@ function SearchCard() { {searchText} - | + + | +
- {showDropdown && ( -
-
- - - Inv - - oice_2024.pdf - -
-
- - - Inv - - estment_Plan.docx - -
+ +
+ + + Inv + + oice_2024.pdf + +
+
+ + + Inv + + estment_Plan.docx +
- )} +
); } function TrashCard() { - const [hovered, setHovered] = useState(false); - const [filePosition, setFilePosition] = useState< - "active" | "trash" | "restoring" - >("active"); - const [showUndo, setShowUndo] = useState(false); - - const animateTrash = () => { - setHovered(true); - setTimeout(() => setFilePosition("trash"), 300); - setTimeout(() => setShowUndo(true), 600); - setTimeout(() => { - setFilePosition("restoring"); - setShowUndo(false); - }, 1500); - setTimeout(() => setFilePosition("active"), 2000); - }; - return ( -
{ - setHovered(false); - setFilePosition("active"); - setShowUndo(false); - }} - > +
@@ -296,47 +336,67 @@ function TrashCard() { 30-day retention for deleted items. One-click restoration.

-
-
- +
+ {/* Active Panel */} +
+ Active -
- {(filePosition === "active" || filePosition === "restoring") && ( -
-
- Report.pdf -
- )} -
+ + +
+
-
- + + {/* Trash Panel */} +
+ Trash -
- {filePosition === "trash" && ( -
-
- Report.pdf -
- )} -
+ + +
+
-
- {showUndo && ( -
- File deleted. - -
- )} + +
); } diff --git a/components/landing/Hero.tsx b/components/landing/Hero.tsx index adde073..4f2909f 100644 --- a/components/landing/Hero.tsx +++ b/components/landing/Hero.tsx @@ -43,11 +43,11 @@ export function Hero({ onGetStarted }: HeroProps) {
-

+

File management, perfected.

-

+

Minidrive is the high-performance workspace for your life's data. 5GB free. Zero-knowledge encryption. Native speed.

@@ -61,7 +61,7 @@ export function Hero({ onGetStarted }: HeroProps) { View the architecture diff --git a/components/landing/Navigation.tsx b/components/landing/Navigation.tsx index 5f72434..72e6198 100644 --- a/components/landing/Navigation.tsx +++ b/components/landing/Navigation.tsx @@ -2,6 +2,7 @@ import Link from "next/link"; import { useState, useEffect } from "react"; +import ThemeToggleButton from "@/components/ui/theme-toggle-button"; interface NavigationProps { onSignIn: () => void; @@ -52,10 +53,11 @@ export function Navigation({ onSignIn, onGetStarted }: NavigationProps) {
-
+
+ diff --git a/components/landing/Pricing.tsx b/components/landing/Pricing.tsx index efd81e7..020ae53 100644 --- a/components/landing/Pricing.tsx +++ b/components/landing/Pricing.tsx @@ -7,45 +7,59 @@ interface PricingProps { onGetStarted: () => void; } +const plans = [ + { + name: "Starter", + price: "$0", + period: "Forever.", + features: [ + "5 GB Storage", + "Basic Search", + "Standard Encryption", + "Email Support", + ], + cta: "Start now", + highlighted: false, + }, + { + name: "Pro", + price: "$12", + period: "per user / month", + features: [ + "1 TB Storage", + "Duplicate Detection", + "Advanced Activity Logs", + "Priority Support", + "API Access", + ], + cta: "Go Pro", + highlighted: true, + }, + { + name: "Enterprise", + price: "Custom", + period: "Contact us", + features: [ + "Unlimited Storage", + "SSO / SAML", + "Dedicated Success Manager", + "Custom Integrations", + "SLA Guarantee", + ], + cta: "Contact Sales", + highlighted: false, + }, +]; + export function Pricing({ onGetStarted }: PricingProps) { - const [annual, setAnnual] = useState(false); + const [isYearly, setIsYearly] = useState(false); - const plans = [ - { - name: "Starter", - price: "$0", - sub: "Forever.", - features: ["5 GB Storage", "Basic Search", "Standard Encryption"], - cta: "Start now", - highlighted: false, - }, - { - name: "Pro", - price: annual ? "$10" : "$12", - sub: "per user / month", - features: [ - "1 TB Storage", - "Duplicate Detection", - "Advanced Activity Logs", - "Priority Support", - ], - cta: "Go Pro", - highlighted: true, - }, - { - name: "Enterprise", - price: "Custom", - sub: "Tailored for scale.", - features: [ - "Unlimited Storage", - "SSO / SAML", - "Dedicated Success Manager", - "Custom Integrations", - ], - cta: "Contact Sales", - highlighted: false, - }, - ]; + const getPrice = (price: string) => { + if (isYearly && price !== "Custom" && price !== "$0") { + return `$${Math.round(parseInt(price.slice(1)) * 0.8)}`; + } + return price; + }; return (
-
+ {/* Header */} +
Pricing

Predictable scaling.

-
-
-
+ {/* Toggle */} +
-
-
- {plans.map(plan => ( + {/* Pricing Grid */} +
+ {plans.map((plan, index) => (
-

- {plan.name} -

-
- - {plan.price} - - {plan.sub} + {/* Plan Header */} +
+

+ {plan.name} +

+
+ + {getPrice(plan.price)} + +
+

{plan.period}

-
+ {/* Features */} +
{plan.features.map(feature => (
- + {feature}
))}
- + {/* CTA */} +
+ +
))}
diff --git a/components/landing/TrustGrid.tsx b/components/landing/TrustGrid.tsx index 1dde381..6e60af6 100644 --- a/components/landing/TrustGrid.tsx +++ b/components/landing/TrustGrid.tsx @@ -5,7 +5,7 @@ import { useState } from "react"; const trustItems = [ { name: "SOC2", label: "Compliant" }, { name: "AES-256", label: "Encryption" }, - { name: "AWS", label: "Infrastructure" }, + { name: "AZURE", label: "Infrastructure" }, { name: "Stripe", label: "Payments" }, { name: "99.99%", label: "Uptime" }, { name: "GDPR", label: "Compliant" }, @@ -16,20 +16,22 @@ export function TrustGrid() { return (
-
+
{trustItems.map((item, i) => (
= 4 ? "border-b-0 sm:border-b md:border-b-0" : ""} + ${i >= 3 ? "sm:border-b-0 md:border-b-0" : ""} + ${hoveredIndex === i ? "bg-zinc-50 dark:bg-zinc-900" : "bg-white dark:bg-zinc-950"}`} onMouseEnter={() => setHoveredIndex(i)} onMouseLeave={() => setHoveredIndex(null)} > {item.name} - + {item.label}
diff --git a/components/layout/DashboardHeader.tsx b/components/layout/DashboardHeader.tsx index a401eef..c208609 100644 --- a/components/layout/DashboardHeader.tsx +++ b/components/layout/DashboardHeader.tsx @@ -117,7 +117,7 @@ export function DashboardHeader({ setIsSearchOpen(true); } }} - className="pl-9 md:pl-10 pr-10 md:pr-12 py-1.5 md:py-2 w-full bg-muted border-0 rounded-full focus:bg-card focus:shadow-md focus:ring-2 focus:ring-ring transition-all text-sm" + className="pl-9 md:pl-10 pr-10 md:pr-12 py-1.5 md:py-2 w-full bg-muted border border-border rounded-full focus:bg-card focus:shadow-md focus:ring-2 focus:ring-ring transition-all text-sm" /> {searchQuery && (
+ +

+ No credit card required • Secure with Google • Access anywhere +

+
+ ); diff --git a/components/ui/dialog.tsx b/components/ui/dialog.tsx index f022c81..cc9d7ac 100644 --- a/components/ui/dialog.tsx +++ b/components/ui/dialog.tsx @@ -15,13 +15,13 @@ const DialogPortal = DialogPrimitive.Portal; const DialogClose = DialogPrimitive.Close; const DialogOverlay = React.forwardRef< - React.ElementRef, + React.ComponentRef, React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( { + showCloseButton?: boolean; +} + const DialogContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, ...props }, ref) => ( + React.ComponentRef, + DialogContentProps +>(({ className, children, showCloseButton = true, ...props }, ref) => ( {children} - - - Close - + {showCloseButton && ( + + + Close + + )} )); @@ -59,7 +67,7 @@ const DialogHeader = ({ }: React.HTMLAttributes) => (
, + React.ComponentRef, React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( , + React.ComponentRef, React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( )); From 972f787e6d4572716a7233f32af6be67c041e225 Mon Sep 17 00:00:00 2001 From: Abul Date: Sun, 14 Dec 2025 11:44:58 +0530 Subject: [PATCH 09/14] feat(landing): Enhance Hero --- components/landing/Hero.tsx | 172 +++++++++++++++++++++++++++++------- 1 file changed, 141 insertions(+), 31 deletions(-) diff --git a/components/landing/Hero.tsx b/components/landing/Hero.tsx index 4f2909f..68565e9 100644 --- a/components/landing/Hero.tsx +++ b/components/landing/Hero.tsx @@ -1,7 +1,10 @@ "use client"; -import { useEffect, useRef } from "react"; -import { ArrowRight } from "lucide-react"; +import { useEffect, useRef, useState } from "react"; +import { motion, AnimatePresence } from "framer-motion"; +import { ArrowRight, Lock, Check, AlertTriangle } from "lucide-react"; + +type DuplicateStatus = "pending" | "ignored" | "merged"; interface HeroProps { onGetStarted: () => void; @@ -10,25 +13,52 @@ interface HeroProps { export function Hero({ onGetStarted }: HeroProps) { const containerRef = useRef(null); const shardsRef = useRef(null); + const [duplicateStatus, setDuplicateStatus] = useState("pending"); + + // Auto-reset duplicate status after showing success + useEffect(() => { + if (duplicateStatus !== "pending") { + const timer = setTimeout(() => setDuplicateStatus("pending"), 2000); + return () => clearTimeout(timer); + } + }, [duplicateStatus]); useEffect(() => { + let animationFrameId: number | null = null; + const handleMouseMove = (e: MouseEvent) => { if (!shardsRef.current || !containerRef.current) return; - const rect = containerRef.current.getBoundingClientRect(); - const x = (e.clientX - rect.left - rect.width / 2) / 50; - const y = (e.clientY - rect.top - rect.height / 2) / 50; - - const shards = shardsRef.current.querySelectorAll(".shard"); - shards.forEach((shard, i) => { - const depth = (i + 1) * 0.5; - (shard as HTMLElement).style.transform = - `translate(${x * depth}px, ${y * depth}px)`; + + // Cancel any pending animation frame to avoid stacking + if (animationFrameId) { + cancelAnimationFrame(animationFrameId); + } + + animationFrameId = requestAnimationFrame(() => { + if (!shardsRef.current || !containerRef.current) return; + + const rect = containerRef.current.getBoundingClientRect(); + const x = (e.clientX - rect.left - rect.width / 2) / 50; + const y = (e.clientY - rect.top - rect.height / 2) / 50; + + const shards = shardsRef.current.querySelectorAll(".shard"); + shards.forEach((shard, i) => { + const depth = (i + 1) * 0.5; + (shard as HTMLElement).style.transform = + `translate(${x * depth}px, ${y * depth}px)`; + }); }); }; const container = containerRef.current; container?.addEventListener("mousemove", handleMouseMove); - return () => container?.removeEventListener("mousemove", handleMouseMove); + + return () => { + container?.removeEventListener("mousemove", handleMouseMove); + if (animationFrameId) { + cancelAnimationFrame(animationFrameId); + } + }; }, []); return ( @@ -132,19 +162,54 @@ export function Hero({ onGetStarted }: HeroProps) {
-
+ {/* Uploading Box */} +
-
- Uploading... + + + Uploading + + {[0, 1, 2].map((i) => ( + + . + + ))} + +
-
-
+
+
-
+ {/* Encryption Box */} +
-
+ + + AES-256 @@ -152,18 +217,63 @@ export function Hero({ onGetStarted }: HeroProps) { Encrypted
-
- - Duplicate Detected - -
- - -
+ {/* Duplicate Detected Box */} +
+ + {duplicateStatus === "pending" ? ( + +
+ + + + + Duplicate Detected + +
+
+ setDuplicateStatus("ignored")} + > + Ignore + + setDuplicateStatus("merged")} + > + Merge + +
+
+ ) : ( + + + + {duplicateStatus === "ignored" ? "Ignored Successfully" : "Merged Successfully"} + + + )} +
From 8015f4998c4b5a96c96f26934ecfd93d3b5d993f Mon Sep 17 00:00:00 2001 From: Abul Date: Sun, 14 Dec 2025 12:12:45 +0530 Subject: [PATCH 10/14] feat(landing): Enhance FeaturesGrid --- components/landing/FeaturesGrid.tsx | 61 +++++++++++++++++++++++++++-- components/landing/Hero.tsx | 43 +++++++++++++++----- 2 files changed, 91 insertions(+), 13 deletions(-) diff --git a/components/landing/FeaturesGrid.tsx b/components/landing/FeaturesGrid.tsx index f8228e8..5cde369 100644 --- a/components/landing/FeaturesGrid.tsx +++ b/components/landing/FeaturesGrid.tsx @@ -8,6 +8,7 @@ import { Eye, File, FileEdit, + MousePointer2, RotateCcw, Search, Share2, @@ -469,7 +470,9 @@ function TrashCard() { className="mt-6 sm:mt-8 w-full p-2 border border-zinc-200 dark:border-zinc-700 flex items-center space-x-2 sm:space-x-3 bg-white dark:bg-zinc-900 z-10 relative" > -
+ + Invoice_2024.pdf +
@@ -486,10 +489,12 @@ function TrashCard() { repeat: Infinity, repeatDelay: 1, }} - className="mt-6 sm:mt-8 w-full p-2 border border-zinc-200 dark:border-zinc-700 flex items-center space-x-2 sm:space-x-3 bg-white dark:bg-zinc-900 opacity-50 grayscale" + className="mt-6 sm:mt-8 w-full p-2 border border-zinc-200 dark:border-zinc-700 flex items-center space-x-2 sm:space-x-3 bg-white dark:bg-zinc-900 opacity-50 grayscale blur-[1px]" > -
+ + Invoice_2024.pdf +
@@ -509,6 +514,56 @@ function TrashCard() { Undo + + {/* Animated Mouse Pointer */} + + +
); diff --git a/components/landing/Hero.tsx b/components/landing/Hero.tsx index 68565e9..ff590ea 100644 --- a/components/landing/Hero.tsx +++ b/components/landing/Hero.tsx @@ -13,7 +13,8 @@ interface HeroProps { export function Hero({ onGetStarted }: HeroProps) { const containerRef = useRef(null); const shardsRef = useRef(null); - const [duplicateStatus, setDuplicateStatus] = useState("pending"); + const [duplicateStatus, setDuplicateStatus] = + useState("pending"); // Auto-reset duplicate status after showing success useEffect(() => { @@ -36,7 +37,7 @@ export function Hero({ onGetStarted }: HeroProps) { animationFrameId = requestAnimationFrame(() => { if (!shardsRef.current || !containerRef.current) return; - + const rect = containerRef.current.getBoundingClientRect(); const x = (e.clientX - rect.left - rect.width / 2) / 50; const y = (e.clientY - rect.top - rect.height / 2) / 50; @@ -52,7 +53,7 @@ export function Hero({ onGetStarted }: HeroProps) { const container = containerRef.current; container?.addEventListener("mousemove", handleMouseMove); - + return () => { container?.removeEventListener("mousemove", handleMouseMove); if (animationFrameId) { @@ -168,12 +169,16 @@ export function Hero({ onGetStarted }: HeroProps) { Uploading - {[0, 1, 2].map((i) => ( + {[0, 1, 2].map(i => (
@@ -206,7 +215,12 @@ export function Hero({ onGetStarted }: HeroProps) {
@@ -231,7 +245,11 @@ export function Hero({ onGetStarted }: HeroProps) {
@@ -242,7 +260,10 @@ export function Hero({ onGetStarted }: HeroProps) {
setDuplicateStatus("ignored")} > @@ -269,7 +290,9 @@ export function Hero({ onGetStarted }: HeroProps) { > - {duplicateStatus === "ignored" ? "Ignored Successfully" : "Merged Successfully"} + {duplicateStatus === "ignored" + ? "Ignored Successfully" + : "Merged Successfully"} )} From ea232fc8d73566530845b6d0f31110ff5576ad06 Mon Sep 17 00:00:00 2001 From: Abul Date: Sun, 14 Dec 2025 12:30:39 +0530 Subject: [PATCH 11/14] Improve navigation and pricing UX --- app/page.tsx | 4 + components/landing/Navigation.tsx | 30 ++-- components/landing/Pricing.tsx | 226 +++++++++++------------------- hooks/useScrollRestore.ts | 37 +++++ 4 files changed, 144 insertions(+), 153 deletions(-) create mode 100644 hooks/useScrollRestore.ts diff --git a/app/page.tsx b/app/page.tsx index 233d49d..69ac072 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -13,12 +13,16 @@ import { Pricing, Footer, } from "@/components/landing"; +import { useScrollRestore } from "@/hooks/useScrollRestore"; export default function Home() { const { data: session, status } = useSession(); const router = useRouter(); const [isSigninModalOpen, setIsSigninModalOpen] = useState(false); + // Restore scroll position on page refresh (only after loading completes) + useScrollRestore(status !== "loading" && !session); + useEffect(() => { if (status === "loading") return; if (session) router.push("/dashboard"); diff --git a/components/landing/Navigation.tsx b/components/landing/Navigation.tsx index 72e6198..99e6c50 100644 --- a/components/landing/Navigation.tsx +++ b/components/landing/Navigation.tsx @@ -33,24 +33,36 @@ export function Navigation({ onSignIn, onGetStarted }: NavigationProps) {
- + document + .getElementById("features") + ?.scrollIntoView({ behavior: "smooth" }) + } className="text-sm text-zinc-900 dark:text-zinc-100 transition-colors hover:text-zinc-500" > Features - - +
diff --git a/components/landing/Pricing.tsx b/components/landing/Pricing.tsx index 020ae53..69caf04 100644 --- a/components/landing/Pricing.tsx +++ b/components/landing/Pricing.tsx @@ -1,163 +1,101 @@ "use client"; -import { useState } from "react"; import { Check } from "lucide-react"; interface PricingProps { onGetStarted: () => void; } -const plans = [ - { - name: "Starter", - price: "$0", - period: "Forever.", - features: [ - "5 GB Storage", - "Basic Search", - "Standard Encryption", - "Email Support", - ], - cta: "Start now", - highlighted: false, - }, - { - name: "Pro", - price: "$12", - period: "per user / month", - features: [ - "1 TB Storage", - "Duplicate Detection", - "Advanced Activity Logs", - "Priority Support", - "API Access", - ], - cta: "Go Pro", - highlighted: true, - }, - { - name: "Enterprise", - price: "Custom", - period: "Contact us", - features: [ - "Unlimited Storage", - "SSO / SAML", - "Dedicated Success Manager", - "Custom Integrations", - "SLA Guarantee", - ], - cta: "Contact Sales", - highlighted: false, - }, -]; - -export function Pricing({ onGetStarted }: PricingProps) { - const [isYearly, setIsYearly] = useState(false); +interface PricingCardProps { + title: string; + price: string; + features: string[]; + highlighted?: boolean; + onGetStarted: () => void; +} - const getPrice = (price: string) => { - if (isYearly && price !== "Custom" && price !== "$0") { - return `$${Math.round(parseInt(price.slice(1)) * 0.8)}`; - } - return price; - }; +function PricingCard({ + title, + price, + features, + highlighted, + onGetStarted, +}: PricingCardProps) { + return ( +
+

+ {title} +

+
+ + {price} + +
+
    + {features.map((feature, i) => ( +
  • + + {feature} +
  • + ))} +
+ +
+ ); +} +export function Pricing({ onGetStarted }: PricingProps) { return (
-
- {/* Header */} -
- - Pricing - -

- Predictable scaling. +
+
+

+ Simple pricing

- - {/* Toggle */} -
- - -
+

+ No hidden fees. No complicated tiers. +

- - {/* Pricing Grid */} -
- {plans.map((plan, index) => ( -
- {/* Plan Header */} -
-

- {plan.name} -

-
- - {getPrice(plan.price)} - -
-

{plan.period}

-
- - {/* Features */} -
- {plan.features.map(feature => ( -
- - - {feature} - -
- ))} -
- - {/* CTA */} -
- -
-
- ))} +
+ +

diff --git a/hooks/useScrollRestore.ts b/hooks/useScrollRestore.ts new file mode 100644 index 0000000..ecd330d --- /dev/null +++ b/hooks/useScrollRestore.ts @@ -0,0 +1,37 @@ +"use client"; + +import { useEffect, useRef } from "react"; + +const SCROLL_KEY = "landing_scroll_position"; + +export function useScrollRestore(isReady: boolean = true) { + const hasRestored = useRef(false); + + useEffect(() => { + // Save scroll position before unload + const handleBeforeUnload = () => { + sessionStorage.setItem(SCROLL_KEY, window.scrollY.toString()); + }; + + window.addEventListener("beforeunload", handleBeforeUnload); + + return () => { + window.removeEventListener("beforeunload", handleBeforeUnload); + }; + }, []); + + useEffect(() => { + // Only restore once when content is ready + if (!isReady || hasRestored.current) return; + + const savedPosition = sessionStorage.getItem(SCROLL_KEY); + if (savedPosition) { + const position = parseInt(savedPosition, 10); + // Delay to ensure DOM is fully rendered + setTimeout(() => { + window.scrollTo({ top: position, behavior: "instant" }); + }, 100); + } + hasRestored.current = true; + }, [isReady]); +} From 028ee3f7f146e76c9a8305e33c3100b1fa78e87a Mon Sep 17 00:00:00 2001 From: Abul Date: Sun, 14 Dec 2025 13:10:07 +0530 Subject: [PATCH 12/14] feat(landing): enhance landing pages --- components/landing/Architecture.tsx | 315 ++++++++++++++-------------- components/landing/FeaturesGrid.tsx | 149 ++++++++----- components/landing/Footer.tsx | 12 +- components/landing/Hero.tsx | 166 ++++++++++----- 4 files changed, 376 insertions(+), 266 deletions(-) diff --git a/components/landing/Architecture.tsx b/components/landing/Architecture.tsx index 29c2d61..3411be1 100644 --- a/components/landing/Architecture.tsx +++ b/components/landing/Architecture.tsx @@ -1,201 +1,212 @@ "use client"; -import { useEffect, useRef, useState } from "react"; import { Key, Shield, Zap } from "lucide-react"; export function Architecture() { - const [isVisible, setIsVisible] = useState(false); - const sectionRef = useRef(null); - - useEffect(() => { - const observer = new IntersectionObserver( - ([entry]) => { - if (entry.isIntersecting) { - setIsVisible(true); - } - }, - { threshold: 0.3 } - ); - - if (sectionRef.current) { - observer.observe(sectionRef.current); - } - - return () => observer.disconnect(); - }, []); - return (
-
-
+ {/* Dot Grid Background */} +
+ +
+
+ {/* Left - Copy */}
- +

Security - -

+

+

Built on a fortress.

-

+

Minidrive isn't just storage; it's a vault. End-to-end encryption ensures that even we can't see your data.

-
-
-
- -
-
-

- AES-256 Encryption -

-

- Military-grade encryption at rest and in transit. -

-
+ {/* Feature List */} +
+
+ + + AES-256 Encryption +
- -
-
- -
-
-

- 2-Factor Authentication -

-

- Secure your account with TOTP or hardware keys. -

-
+
+ + + 2-Factor Authentication +
- -
-
- -
-
-

- {"< 50ms Latency"} -

-

- Global edge network for instant file access. -

-
+
+ + + < 50ms Latency +
+ {/* Right - Wireframe Lock Visualization */}
-
+
+ {/* Wireframe Lock SVG */} - {/* Lock body */} + {/* Lock Body - Outer */} - {/* Lock shackle */} + + {/* Lock Body - Inner Grid */} + + + + + + {/* Lock Shackle */} - {/* Keyhole circle */} + + {/* Keyhole */} + + + {/* Corner Details */} + - {/* Keyhole line */} - - {/* Grid lines - horizontal */} - - + + + {/* Decorative Lines */} - {/* Grid lines - vertical */} + x1="20" + y1="100" + x2="35" + y2="100" + className="text-zinc-300 dark:text-zinc-600" + strokeDasharray="2 2" + /> + + {/* Floating Labels */} +
+ 256-bit +
+
+ encrypted +
diff --git a/components/landing/FeaturesGrid.tsx b/components/landing/FeaturesGrid.tsx index 5cde369..d2c6230 100644 --- a/components/landing/FeaturesGrid.tsx +++ b/components/landing/FeaturesGrid.tsx @@ -232,50 +232,8 @@ function ActivityCard() { } function DuplicateCard() { - const [merged, setMerged] = useState(false); - const cardRef = useRef(null); - const intervalRef = useRef(null); - const timeoutRefs = useRef([]); - - useEffect(() => { - const clearAllTimers = () => { - if (intervalRef.current) clearInterval(intervalRef.current); - timeoutRefs.current.forEach(clearTimeout); - timeoutRefs.current = []; - }; - - const animate = () => { - const t1 = setTimeout(() => setMerged(true), 500); - const t2 = setTimeout(() => setMerged(false), 3000); - timeoutRefs.current.push(t1, t2); - }; - - const observer = new IntersectionObserver( - ([entry]) => { - if (entry.isIntersecting) { - clearAllTimers(); - animate(); - intervalRef.current = setInterval(animate, 4000); - } else { - clearAllTimers(); - setMerged(false); - } - }, - { threshold: 0.5 } - ); - - if (cardRef.current) observer.observe(cardRef.current); - return () => { - observer.disconnect(); - clearAllTimers(); - }; - }, []); - return ( -
+
@@ -289,22 +247,99 @@ function DuplicateCard() { Automatic hash-matching identifies and merges duplicate files.

-
+
+ + {/* Primary File (The one that stays) */} + -
-
-
-
- {merged && ( -
- 1 -
- )} -
+ +
+
+ + + {/* Duplicate File (The one that merges) */} + + +
+
+ + + {/* Scanner Line */} + + + {/* Match Text */} + + HASH MATCH + + + {/* Saved Text */} + +
+ DEDUPLICATED +
+
); diff --git a/components/landing/Footer.tsx b/components/landing/Footer.tsx index 1163a7b..4dca7d6 100644 --- a/components/landing/Footer.tsx +++ b/components/landing/Footer.tsx @@ -10,7 +10,7 @@ const footerLinks = { export function Footer() { return (
-
+
Minidrive -

Designed for focus.

+

+ Your files. Precisely managed. +

{Object.entries(footerLinks).map(([category, links]) => ( @@ -44,9 +46,9 @@ export function Footer() {
-
- - MINIDRIVE © 2025 +
+ + MINIDRIVE
diff --git a/components/landing/Hero.tsx b/components/landing/Hero.tsx index ff590ea..5973921 100644 --- a/components/landing/Hero.tsx +++ b/components/landing/Hero.tsx @@ -2,7 +2,15 @@ import { useEffect, useRef, useState } from "react"; import { motion, AnimatePresence } from "framer-motion"; -import { ArrowRight, Lock, Check, AlertTriangle } from "lucide-react"; +import { + ArrowRight, + Lock, + Check, + AlertTriangle, + FileText, + Folder, + MoreHorizontal, +} from "lucide-react"; type DuplicateStatus = "pending" | "ignored" | "merged"; @@ -101,64 +109,118 @@ export function Hero({ onGetStarted }: HeroProps) {
-
-
-
-
- - All Files - +
+ {/* Mock Window Header */} +
+
+
+
+
-
-
-
+
+ MiniDrive Safe
+
-
- {[ - { - name: "Q3-Report.pdf", - size: "2.4 MB", - modified: "2 hours ago", - }, - { - name: "Design-System.fig", - size: "18.2 MB", - modified: "Yesterday", - }, - { - name: "Invoice_2024.pdf", - size: "340 KB", - modified: "3 days ago", - }, - { - name: "Brand-Guidelines.pdf", - size: "5.1 MB", - modified: "Last week", - }, - ].map((file, i) => ( -
-
-
- - {file.name} - + {/* Mock Window Body */} +
+
+ + All Files + +
+ +
+ {[ + { + name: "Project_Alpha_Specs.pdf", + size: "2.4 MB", + type: "pdf", + }, + { + name: "Financials_Q3.xlsx", + size: "1.1 MB", + type: "sheet", + }, + { + name: "Design_System_v2.fig", + size: "14.2 MB", + type: "fig", + }, + { + name: "Architecture_Review.docx", + size: "890 KB", + type: "doc", + }, + ].map((file, i) => ( +
+
+ {file.type === "pdf" ? ( + + ) : ( + + )} + + {file.name} + +
+
+ + {file.size} + + +
-
- - {file.size} - - - {file.modified} + ))} + + {/* Animated Upload Row */} + +
+ + + Uploading Files + + {[0, 1, 2].map(i => ( + + . + + ))} +
-
- ))} + +
From 77da3d661ff8359326743e9e6bfe776c9c52d31c Mon Sep 17 00:00:00 2001 From: Abul Date: Sun, 14 Dec 2025 13:21:37 +0530 Subject: [PATCH 13/14] feat(landing): Adjust landing --- components/landing/Architecture.tsx | 16 ++++++++-------- components/landing/Hero.tsx | 2 +- components/landing/TrustGrid.tsx | 10 +++++----- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/components/landing/Architecture.tsx b/components/landing/Architecture.tsx index 3411be1..18d7116 100644 --- a/components/landing/Architecture.tsx +++ b/components/landing/Architecture.tsx @@ -34,25 +34,25 @@ export function Architecture() { {/* Feature List */}
-
- - - AES-256 Encryption - -
- 2-Factor Authentication + Google OAuth Authentication + +
+
+ + + CSRF Token Protection
- < 50ms Latency + Time-Limited SAS URLs
diff --git a/components/landing/Hero.tsx b/components/landing/Hero.tsx index 5973921..af2b24a 100644 --- a/components/landing/Hero.tsx +++ b/components/landing/Hero.tsx @@ -294,7 +294,7 @@ export function Hero({ onGetStarted }: HeroProps) {
{/* Duplicate Detected Box */} -
+
{duplicateStatus === "pending" ? ( Date: Fri, 13 Feb 2026 18:56:50 +0530 Subject: [PATCH 14/14] update readme --- README.md | 74 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 53 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index e215bc4..4f9f2a8 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,68 @@ -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). +# Mini Drive -## Getting Started +A personal cloud storage application built with Next.js and Azure Blob Storage. -First, run the development server: +## Features + +- Google OAuth authentication +- File upload with progress tracking (up to 5GB) +- File management (rename, delete, star, restore) +- Search and filtering +- Activity tracking +- Dark mode + +## Tech Stack + +- Next.js 16 (App Router) +- TypeScript +- MongoDB +- Azure Blob Storage +- NextAuth.js +- Tailwind CSS + shadcn/ui + +## Setup + +1. Clone and install dependencies: ```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev +npm install +``` + +2. Copy `.env.example` to `.env.local` and fill in your credentials: + +```bash +cp .env.example .env.local ``` -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +3. Set up required services: + - Google OAuth credentials + - MongoDB database + - Azure Storage account -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. +4. Run the development server: + +```bash +npm run dev +``` -This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. +Open [http://localhost:3000](http://localhost:3000) -## Learn More +## Commands -To learn more about Next.js, take a look at the following resources: +```bash +npm run dev # Start dev server +npm run build # Build for production +npm run lint # Run ESLint +npm run type # TypeScript check +npm run format # Format code with Prettier +``` -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. +## How It Works -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! +Files are uploaded directly from the browser to Azure Blob Storage using pre-signed URLs. File metadata is stored in MongoDB. This keeps the server lightweight and uploads fast. -## Deploy on Vercel +Authentication uses NextAuth with Google OAuth. All routes under `/dashboard` and `/api` are protected by middleware. -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. +## License -Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. +MIT