feat: complete new UI for Profiles, fix notfound error on username change

This commit is contained in:
Hamzat Victor 2025-01-06 02:52:32 +01:00
parent 024e30bd99
commit ceeb1fbce3
12 changed files with 784 additions and 263 deletions

View File

@ -9,19 +9,38 @@ import {
CardDescription,
CardTitle,
} from "@/components/ui/card"
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form"
import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from "@/components/ui/hover-card"
import { Label } from "@/components/ui/label"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip"
import { deleteSandbox, updateSandbox, updateUser } from "@/lib/actions"
import { socialIcons } from "@/lib/data"
import { editUserSchema, EditUserSchema } from "@/lib/schema"
import { TIERS } from "@/lib/tiers"
import { SandboxWithLiked, User } from "@/lib/types"
import { SandboxWithLiked, User, UserLink } from "@/lib/types"
import { cn, parseSocialLink } from "@/lib/utils"
import { useUser } from "@clerk/nextjs"
import { zodResolver } from "@hookform/resolvers/zod"
import {
Edit,
Globe,
Heart,
Info,
Loader2,
@ -29,17 +48,27 @@ import {
Package2,
PlusCircle,
Sparkles,
Trash2,
X,
} from "lucide-react"
import { useRouter } from "next/navigation"
import { Fragment, useCallback, useEffect, useMemo, useState } from "react"
import {
Fragment,
useCallback,
useEffect,
useMemo,
useRef,
useState,
useTransition,
} from "react"
import { useFormState, useFormStatus } from "react-dom"
import { useFieldArray, useForm } from "react-hook-form"
import { toast } from "sonner"
import Avatar from "../ui/avatar"
import { Badge } from "../ui/badge"
import { Input } from "../ui/input"
import { Progress } from "../ui/progress"
import { Textarea } from "../ui/textarea"
// #region Profile Page
export default function ProfilePage({
publicSandboxes,
@ -75,6 +104,9 @@ export default function ProfilePage({
generations={isOwnProfile ? loggedInUser.generations : undefined}
isOwnProfile={isOwnProfile}
tier={profileOwner.tier}
bio={profileOwner.bio}
personalWebsite={profileOwner.personalWebsite}
socialLinks={profileOwner.links}
/>
</div>
<div className="md:col-span-2">
@ -101,11 +133,17 @@ function ProfileCard({
joinedDate,
generations,
isOwnProfile,
bio,
personalWebsite,
socialLinks,
tier,
}: {
name: string
username: string
avatarUrl: string | null
bio: string | null
personalWebsite: string | null
socialLinks: UserLink[]
sandboxes: SandboxWithLiked[]
joinedDate: Date
generations?: number
@ -113,9 +151,8 @@ function ProfileCard({
tier: string
}) {
const { user } = useUser()
const router = useRouter()
const [isEditing, setIsEditing] = useState(false)
const [formState, formAction] = useFormState(updateUser, {})
const joinedAt = useMemo(() => {
const date = new Date(joinedDate).toLocaleDateString("en-US", {
month: "long",
@ -140,117 +177,331 @@ function ProfileCard({
}
}, [sandboxes])
useEffect(() => {
if ("message" in formState) {
toast.success(formState.message as String)
toggleEdit()
if ("newRoute" in formState && typeof formState.newRoute === "string") {
router.replace(formState.newRoute)
}
}
if ("error" in formState) {
const error = formState.error
if (typeof error === "string") {
toast.error(error)
} else {
toast.error("An Error Occured")
}
}
}, [formState])
const showAddMoreInfoBanner = useMemo(() => {
return !bio && !personalWebsite && socialLinks.length === 0
}, [personalWebsite, bio, socialLinks])
return (
<Card className="mb-6 md:mb-0 sticky top-6">
{isOwnProfile && (
<div className="absolute top-2 right-2 flex flex-col gap-2">
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
onClick={toggleEdit}
aria-label={isEditing ? "close edit form" : "open edit form"}
size="smIcon"
variant="secondary"
className="rounded-full absolute top-2 right-2"
className="rounded-full relative"
>
{isEditing ? <X className="size-4" /> : <Edit className="size-4" />}
{isEditing ? (
<X className="size-4" />
) : showAddMoreInfoBanner ? (
<>
<Sparkles className="size-4 text-yellow-400 z-[2]" />
<div className="z-[1] absolute inset-0 rounded-full bg-secondary animate-ping" />
</>
) : (
<Edit className="size-4" />
)}
</Button>
</TooltipTrigger>
<TooltipContent>
<p>
{showAddMoreInfoBanner
? "Add more information to your profile"
: "Edit your profile"}
</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
)}
<CardContent className="flex flex-col gap-4 items-center pt-6">
<Avatar name={name} avatarUrl={avatarUrl} className="size-36" />
{!isEditing ? (
<div className="space-y-2">
{isEditing ? (
<EditProfileForm
{...{
name,
username,
avatarUrl,
bio,
personalWebsite,
socialLinks,
toggleEdit,
}}
/>
) : (
<div className="flex flex-col gap-2.5 items-center">
<div className="space-y-1.5">
<div className="">
<CardTitle className="text-2xl text-center">{name}</CardTitle>
<CardDescription className="text-center">{`@${username}`}</CardDescription>
</div>
) : (
<form action={formAction} className="flex flex-col gap-2">
<Input
name="id"
placeholder="ID"
className="hidden "
value={user?.id}
/>
<Input
name="oldUsername"
placeholder="ID"
className="hidden "
value={user?.username ?? undefined}
/>
<div className="space-y-1">
<Label htmlFor="input-name">Name</Label>
<Input
id="input-name"
name="name"
placeholder="Name"
defaultValue={name}
/>
</div>
<div className="space-y-1">
<Label htmlFor="input-username">User name</Label>
<div className="relative">
<Input
id="input-username"
className="peer ps-6"
type="text"
name="username"
placeholder="Username"
defaultValue={username}
/>
<span className="pointer-events-none absolute inset-y-0 start-0 flex items-center justify-center ps-2 text-sm text-muted-foreground peer-disabled:opacity-50">
@
</span>
</div>
</div>
<SubmitButton />
</form>
)}
{!isEditing && (
<>
<div className="flex gap-6">
<StatsItem icon={Package2} label={stats.sandboxes} />
<StatsItem icon={Heart} label={stats.likes} />
</div>
<div className="flex flex-col items-center gap-2">
<p className="text-xs text-muted-foreground">{joinedAt}</p>
{typeof generations === "number" && (
<div className="flex justify-center">
<SubscriptionBadge
generations={generations}
tier={tier as keyof typeof TIERS}
/>
</div>
)}
</div>
</>
<div className="flex gap-4">
<StatsItem icon={Package2} label={stats.sandboxes} />
<StatsItem icon={Heart} label={stats.likes} />
</div>
{bio && <p className="text-sm text-center">{bio}</p>}
{(socialLinks.length > 0 || personalWebsite) && (
<div className="flex gap-2 justify-center">
{personalWebsite && (
<Button variant="ghost" size="smIcon" asChild>
<a
href={personalWebsite}
target="_blank"
rel="noopener noreferrer"
>
<Globe className="size-4" />
<span className="sr-only">Personal Website</span>
</a>
</Button>
)}
{socialLinks.map((link, index) => {
const Icon = socialIcons[link.platform]
return (
<Button key={index} variant="ghost" size="smIcon" asChild>
<a
href={link.url}
target="_blank"
rel="noopener noreferrer"
>
<Icon className="size-4" />
<span className="sr-only">{link.platform}</span>
</a>
</Button>
)
})}
</div>
)}
<p className="text-xs mt-2 text-muted-foreground text-center">
{joinedAt}
</p>
</div>
)}
</CardContent>
</Card>
)
}
function SubmitButton() {
const { pending } = useFormStatus()
function EditProfileForm(props: {
name: string
username: string
avatarUrl: string | null
bio: string | null
personalWebsite: string | null
socialLinks: UserLink[]
toggleEdit: () => void
}) {
const router = useRouter()
const { user } = useUser()
const formRef = useRef<HTMLFormElement>(null)
const [formState, formAction] = useFormState(updateUser, {
message: "",
})
const [isPending, startTransition] = useTransition()
const { name, username, bio, personalWebsite, socialLinks, toggleEdit } =
props
const form = useForm<EditUserSchema>({
resolver: zodResolver(editUserSchema),
defaultValues: {
oldUsername: username,
id: user?.id,
name,
username,
bio: bio ?? "",
personalWebsite: personalWebsite ?? "",
links:
socialLinks.length > 0
? socialLinks
: [{ url: "", platform: "generic" }],
...(formState.fields ?? {}),
},
})
const { fields, append, remove } = useFieldArray({
name: "links",
control: form.control,
})
useEffect(() => {
const message = formState.message
if (!Boolean(message)) return
if ("error" in formState) {
toast.error(formState.message)
return
}
toast.success(formState.message as String)
toggleEdit()
if (formState?.newRoute) {
router.replace(formState.newRoute)
}
}, [formState])
return (
<Button size="sm" type="submit" className="w-full mt-2" disabled={pending}>
{pending && <Loader2 className="animate-spin mr-2 h-4 w-4" />}
Save
<Form {...form}>
<form
ref={formRef}
action={formAction}
onSubmit={(evt) => {
evt.preventDefault()
form.handleSubmit(() => {
startTransition(() => {
formAction(new FormData(formRef.current!))
})
})(evt)
}}
className="space-y-3 w-full"
>
<input type="hidden" name="id" value={user?.id} />
<input type="hidden" name="oldUsername" value={username} />
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Name</FormLabel>
<FormControl>
<Input placeholder="marie doe" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>User name</FormLabel>
<FormControl>
<div className="relative">
<Input
className="peer ps-6"
type="text"
placeholder="Username"
{...field}
/>
<span className="pointer-events-none absolute inset-y-0 start-0 flex items-center justify-center ps-2 text-sm text-muted-foreground peer-disabled:opacity-50">
@
</span>
</div>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="bio"
render={({ field }) => (
<FormItem>
<FormLabel>Bio</FormLabel>
<FormControl>
<Textarea
placeholder="hi, I love building things!"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="personalWebsite"
render={({ field }) => (
<FormItem>
<FormLabel>Personal Website</FormLabel>
<FormControl>
<Input placeholder="https://chillguy.dev" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<div>
{fields.map((field, index) => (
<FormField
control={form.control}
key={field.id}
name={`links.${index}`}
render={({ field: { onChange, value, ...field } }) => {
const Icon = socialIcons[value.platform] ?? socialIcons.generic
return (
<FormItem>
<FormLabel className={cn(index !== 0 && "sr-only")}>
Social Links
</FormLabel>
<FormDescription className={cn(index !== 0 && "sr-only")}>
Add links to your blogs or social media profiles.
</FormDescription>
<FormControl>
<div className="flex gap-2">
<div className="relative flex-1">
<Input
{...field}
className="peer ps-9"
value={value.url}
onChange={(e) =>
onChange(parseSocialLink(e.currentTarget.value))
}
/>
<div className="pointer-events-none absolute inset-y-0 start-0 flex items-center justify-center ps-3 text-muted-foreground/80 peer-disabled:opacity-50">
<Icon
size={16}
strokeWidth={2}
aria-hidden="true"
/>
</div>
</div>
<Button
size="smIcon"
type="button"
variant="secondary"
onClick={() => remove(index)}
>
<Trash2 className="size-4" />
</Button>
</div>
</FormControl>
<FormMessage />
</FormItem>
)
}}
/>
))}
<Button
type="button"
variant="outline"
size="sm"
className="mt-2"
onClick={() => append({ url: "", platform: "generic" })}
>
Add URL
</Button>
</div>
<SubmitButton {...{ isPending }} />
</form>
</Form>
)
}
function SubmitButton({ isPending }: { isPending: boolean }) {
const formStatus = useFormStatus()
const { pending } = formStatus
const pend = pending || isPending
return (
<Button size="sm" type="submit" className="w-full mt-2" disabled={pend}>
{pend && <Loader2 className="animate-spin mr-2 h-4 w-4" />}
Save Changes
</Button>
)
}
@ -441,7 +692,7 @@ interface StatsItemProps {
const StatsItem = ({ icon: Icon, label }: StatsItemProps) => (
<div className="flex items-center gap-2">
<Icon size={18} />
<Icon size={16} />
<span className="text-sm text-muted-foreground">{label}</span>
</div>
)
@ -463,7 +714,7 @@ const SubscriptionBadge = ({
<HoverCard>
<HoverCardTrigger>
<Button variant="ghost" size="smIcon">
<Info size={20} />
<Info size={16} />
</Button>
</HoverCardTrigger>
<HoverCardContent>

View File

@ -1,6 +1,8 @@
"use client"
import * as React from "react"
import * as LabelPrimitive from "@radix-ui/react-label"
import { Slot } from "@radix-ui/react-slot"
import * as React from "react"
import {
Controller,
ControllerProps,
@ -10,8 +12,8 @@ import {
useFormContext,
} from "react-hook-form"
import { Label } from "@/components/ui/label"
import { cn } from "@/lib/utils"
import { Label } from "@/components/ui/label"
const Form = FormProvider
@ -93,7 +95,7 @@ const FormLabel = React.forwardRef<
return (
<Label
ref={ref}
className={cn(className)}
className={cn(error && "text-destructive", className)}
htmlFor={formItemId}
{...props}
/>
@ -165,12 +167,12 @@ const FormMessage = React.forwardRef<
FormMessage.displayName = "FormMessage"
export {
useFormField,
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormControl,
FormDescription,
FormMessage,
useFormField,
FormField,
}

View File

@ -0,0 +1,22 @@
import * as React from "react"
import { cn } from "@/lib/utils"
const Textarea = React.forwardRef<
HTMLTextAreaElement,
React.ComponentProps<"textarea">
>(({ className, ...props }, ref) => {
return (
<textarea
className={cn(
"flex min-h-[60px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
className
)}
ref={ref}
{...props}
/>
)
})
Textarea.displayName = "Textarea"
export { Textarea }

View File

@ -1,7 +1,7 @@
"use client"
import * as TooltipPrimitive from "@radix-ui/react-tooltip"
import * as React from "react"
import * as TooltipPrimitive from "@radix-ui/react-tooltip"
import { cn } from "@/lib/utils"
@ -29,4 +29,4 @@ const TooltipContent = React.forwardRef<
))
TooltipContent.displayName = TooltipPrimitive.Content.displayName
export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger }
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }

View File

@ -2,6 +2,9 @@
import { revalidatePath } from "next/cache"
import { z } from "zod"
import { editUserSchema } from "./schema"
import { UserLink } from "./types"
import { parseSocialLink } from "./utils"
export async function createSandbox(body: {
type: string
@ -123,20 +126,31 @@ const UpdateErrorSchema = z.object({
.optional(),
})
export async function updateUser(prevState: any, formData: FormData) {
const data = Object.fromEntries(formData)
const schema = z.object({
id: z.string(),
username: z.string(),
oldUsername: z.string(),
name: z.string(),
interface FormState {
message: string
error?: any
newRoute?: string
fields?: Record<string, unknown>
}
export async function updateUser(
prevState: any,
formData: FormData
): Promise<FormState> {
let data = Object.fromEntries(formData)
let links: UserLink[] = []
Object.entries(data).forEach(([key, value]) => {
if (key.startsWith("link")) {
const [_, index] = key.split(".")
if (value) {
links.splice(parseInt(index), 0, parseSocialLink(value as string))
delete data[key]
}
}
})
console.log(data)
// @ts-ignore
data.links = links
try {
const validatedData = schema.parse(data)
const validatedData = editUserSchema.parse(data)
const changedUsername = validatedData.username !== validatedData.oldUsername
const res = await fetch(
`${process.env.NEXT_PUBLIC_DATABASE_WORKER_URL}/api/user`,
@ -150,6 +164,9 @@ export async function updateUser(prevState: any, formData: FormData) {
id: validatedData.id,
username: data.username ?? undefined,
name: data.name ?? undefined,
bio: data.bio ?? undefined,
personalWebsite: data.personalWebsite ?? undefined,
links: data.links ?? undefined,
}),
}
)
@ -160,11 +177,11 @@ export async function updateUser(prevState: any, formData: FormData) {
const parseResult = UpdateErrorSchema.safeParse(responseData)
if (!parseResult.success) {
return { error: "Unexpected error occurred" }
return {
message: "Unexpected error occurred",
error: parseResult.error,
fields: validatedData,
}
if (parseResult.data.error) {
return parseResult.data
}
if (changedUsername) {
@ -175,12 +192,13 @@ export async function updateUser(prevState: any, formData: FormData) {
return { message: "Successfully updated" }
} catch (error) {
if (error instanceof z.ZodError) {
console.log(error)
return {
error: error.errors?.[0].message,
message: "Invalid data",
error: error.errors,
fields: data,
}
}
return { error: "An unexpected error occurred" }
return { message: "An unexpected error occurred", fields: data }
}
}

View File

@ -0,0 +1,14 @@
export const KNOWN_PLATFORMS = [
"github",
"twitter",
"instagram",
"bluesky",
"linkedin",
"youtube",
"twitch",
"discord",
"mastodon",
"threads",
"gitlab",
"generic",
] as const

View File

@ -1,3 +1,37 @@
import {
AtSign,
Github,
GitlabIcon as GitlabLogo,
Globe,
Instagram,
Link,
Linkedin,
MessageCircle,
Twitch,
Twitter,
Youtube,
} from "lucide-react"
import { KnownPlatform } from "../types"
export const socialIcons: Record<
KnownPlatform | "website",
React.ComponentType<any>
> = {
github: Github,
twitter: Twitter,
instagram: Instagram,
bluesky: AtSign,
linkedin: Linkedin,
youtube: Youtube,
twitch: Twitch,
discord: MessageCircle,
mastodon: AtSign,
threads: AtSign,
gitlab: GitlabLogo,
generic: Link,
website: Globe,
}
export const projectTemplates: {
id: string
name: string

View File

@ -0,0 +1,20 @@
import { z } from "zod"
import { KNOWN_PLATFORMS } from "../constants"
export const editUserSchema = z.object({
id: z.string().trim(),
username: z.string().trim().min(1, "Username must be at least 1 character"),
oldUsername: z.string().trim(),
name: z.string().trim().min(1, "Name must be at least 1 character"),
bio: z.string().trim().optional(),
personalWebsite: z.string().trim().optional(),
links: z
.array(
z.object({
url: z.string().trim(),
platform: z.enum(KNOWN_PLATFORMS),
})
)
.catch([]),
})
export type EditUserSchema = z.infer<typeof editUserSchema>

View File

@ -1,5 +1,7 @@
// DB Types
import { KNOWN_PLATFORMS } from "./constants"
export type User = {
id: string
name: string
@ -8,11 +10,20 @@ export type User = {
avatarUrl: string | null
createdAt: Date
generations: number
sandbox: Sandbox[]
usersToSandboxes: UsersToSandboxes[]
bio: string | null
personalWebsite: string | null
links: UserLink[]
tier: "FREE" | "PRO" | "ENTERPRISE"
tierExpiresAt: Date
lastResetDate?: number
lastResetDate: number
sandbox: Sandbox[]
usersToSandboxes: UsersToSandboxes[]
}
export type KnownPlatform = (typeof KNOWN_PLATFORMS)[number]
export type UserLink = {
url: string
platform: KnownPlatform
}
export type Sandbox = {

View File

@ -2,7 +2,7 @@ import { type ClassValue, clsx } from "clsx"
// import { toast } from "sonner"
import { twMerge } from "tailwind-merge"
import fileExtToLang from "./file-extension-to-language.json"
import { TFile, TFolder } from "./types"
import { KnownPlatform, TFile, TFolder, UserLink } from "./types"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
@ -98,3 +98,57 @@ export function sortFileExplorer(
return item
})
}
export function parseSocialLink(url: string): UserLink {
try {
// Handle empty or invalid URLs
if (!url) return { url: "", platform: "generic" }
// Remove protocol and www prefix for consistent parsing
const cleanUrl = url
.toLowerCase()
.replace(/^https?:\/\//, "")
.replace(/^www\./, "")
.split("/")[0] // Get just the domain part
// Platform detection mapping
const platformPatterns: Record<
Exclude<KnownPlatform, "generic">,
RegExp
> = {
github: /github\.com/,
twitter: /(?:twitter\.com|x\.com|t\.co)/,
instagram: /instagram\.com/,
bluesky: /(?:bsky\.app|bluesky\.social)/,
linkedin: /linkedin\.com/,
youtube: /(?:youtube\.com|youtu\.be)/,
twitch: /twitch\.tv/,
discord: /discord\.(?:gg|com)/,
mastodon: /mastodon\.(?:social|online|world)/,
threads: /threads\.net/,
gitlab: /gitlab\.com/,
}
// Check URL against each pattern
for (const [platform, pattern] of Object.entries(platformPatterns)) {
if (pattern.test(cleanUrl)) {
return {
url,
platform: platform as KnownPlatform,
}
}
}
// Fall back to generic if no match found
return {
url,
platform: "generic",
}
} catch (error) {
console.error("Error parsing social link:", error)
return {
url: url || "",
platform: "generic",
}
}
}

View File

@ -13,7 +13,7 @@
"@clerk/nextjs": "^4.29.12",
"@clerk/themes": "^1.7.12",
"@codemirror/lang-javascript": "^6.2.2",
"@hookform/resolvers": "^3.3.4",
"@hookform/resolvers": "^3.9.1",
"@liveblocks/client": "^1.12.0",
"@liveblocks/node": "^1.12.0",
"@liveblocks/react": "^1.12.0",
@ -27,14 +27,14 @@
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-hover-card": "^1.1.2",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-label": "^2.1.1",
"@radix-ui/react-popover": "^1.1.1",
"@radix-ui/react-progress": "^1.1.0",
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-slot": "^1.1.1",
"@radix-ui/react-switch": "^1.0.3",
"@radix-ui/react-tabs": "^1.1.1",
"@radix-ui/react-tooltip": "^1.1.3",
"@radix-ui/react-tooltip": "^1.1.6",
"@react-three/fiber": "^8.16.6",
"@uiw/codemirror-theme-vscode": "^4.23.5",
"@uiw/react-codemirror": "^4.23.5",
@ -58,7 +58,7 @@
"posthog-js": "^1.147.0",
"react": "^18.3.1",
"react-dom": "^18",
"react-hook-form": "^7.51.3",
"react-hook-form": "^7.54.2",
"react-markdown": "^9.0.1",
"react-resizable-panels": "^2.0.16",
"react-syntax-highlighter": "^15.5.0",
@ -73,7 +73,7 @@
"y-monaco": "^0.1.5",
"y-protocols": "^1.0.6",
"yjs": "^13.6.15",
"zod": "^3.23.8"
"zod": "^3.24.1"
},
"devDependencies": {
"@types/estree": "^1.0.6",
@ -909,9 +909,9 @@
"integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q=="
},
"node_modules/@hookform/resolvers": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.3.4.tgz",
"integrity": "sha512-o5cgpGOuJYrd+iMKvkttOclgwRW86EsWJZZRC23prf0uU2i48Htq4PuT73AVb9ionFyZrwYEITuOFGF+BydEtQ==",
"version": "3.9.1",
"resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.9.1.tgz",
"integrity": "sha512-ud2HqmGBM0P0IABqoskKWI6PEf6ZDDBZkFqe2Vnl+mTHCEHzr3ISjjZyCwTjC/qpL25JC9aIDkloQejvMeq0ug==",
"peerDependencies": {
"react-hook-form": "^7.0.0"
}
@ -1365,6 +1365,24 @@
}
}
},
"node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-slot": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz",
"integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-compose-refs": "1.0.1"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-arrow": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz",
@ -1534,6 +1552,24 @@
}
}
},
"node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz",
"integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-compose-refs": "1.0.1"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-compose-refs": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz",
@ -1632,6 +1668,24 @@
}
}
},
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz",
"integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-compose-refs": "1.0.1"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-direction": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.1.tgz",
@ -2116,18 +2170,39 @@
}
},
"node_modules/@radix-ui/react-label": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.0.2.tgz",
"integrity": "sha512-N5ehvlM7qoTLx7nWPodsPYPgMzA5WM8zZChQg8nyFJKnDO5WHdba1vv5/H6IO5LtJMfD2Q3wh1qHFGNtK0w3bQ==",
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.1.tgz",
"integrity": "sha512-UUw5E4e/2+4kFMH7+YxORXGWggtY6sM8WIwh5RZchhLuUg2H1hc98Py+pr8HMz6rdaYrK2t296ZEjYLOCO5uUw==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-primitive": "1.0.3"
"@radix-ui/react-primitive": "2.0.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0"
"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-label/node_modules/@radix-ui/react-primitive": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz",
"integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==",
"dependencies": {
"@radix-ui/react-slot": "1.1.1"
},
"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": {
@ -2178,6 +2253,24 @@
}
}
},
"node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-slot": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz",
"integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-compose-refs": "1.0.1"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-popover": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.1.tgz",
@ -2693,6 +2786,24 @@
}
}
},
"node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz",
"integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-compose-refs": "1.0.1"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-progress": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.0.tgz",
@ -2861,7 +2972,7 @@
}
}
},
"node_modules/@radix-ui/react-slot": {
"node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-slot": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz",
"integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==",
@ -2879,6 +2990,37 @@
}
}
},
"node_modules/@radix-ui/react-slot": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz",
"integrity": "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.1"
},
"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-slot/node_modules/@radix-ui/react-compose-refs": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz",
"integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==",
"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-switch": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.0.3.tgz",
@ -3192,23 +3334,22 @@
}
},
"node_modules/@radix-ui/react-tooltip": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.1.3.tgz",
"integrity": "sha512-Z4w1FIS0BqVFI2c1jZvb/uDVJijJjJ2ZMuPV81oVgTZ7g3BZxobplnMVvXtFWgtozdvYJ+MFWtwkM5S2HnAong==",
"license": "MIT",
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.1.6.tgz",
"integrity": "sha512-TLB5D8QLExS1uDn7+wH/bjEmRurNMTzNrtq7IjaS4kjion9NtzsTGkvR5+i7yc9q01Pi2KMM2cN3f8UG4IvvXA==",
"dependencies": {
"@radix-ui/primitive": "1.1.0",
"@radix-ui/react-compose-refs": "1.1.0",
"@radix-ui/primitive": "1.1.1",
"@radix-ui/react-compose-refs": "1.1.1",
"@radix-ui/react-context": "1.1.1",
"@radix-ui/react-dismissable-layer": "1.1.1",
"@radix-ui/react-dismissable-layer": "1.1.3",
"@radix-ui/react-id": "1.1.0",
"@radix-ui/react-popper": "1.2.0",
"@radix-ui/react-portal": "1.1.2",
"@radix-ui/react-presence": "1.1.1",
"@radix-ui/react-primitive": "2.0.0",
"@radix-ui/react-slot": "1.1.0",
"@radix-ui/react-popper": "1.2.1",
"@radix-ui/react-portal": "1.1.3",
"@radix-ui/react-presence": "1.1.2",
"@radix-ui/react-primitive": "2.0.1",
"@radix-ui/react-slot": "1.1.1",
"@radix-ui/react-use-controllable-state": "1.1.0",
"@radix-ui/react-visually-hidden": "1.1.0"
"@radix-ui/react-visually-hidden": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
@ -3226,18 +3367,16 @@
}
},
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/primitive": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.0.tgz",
"integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==",
"license": "MIT"
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz",
"integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA=="
},
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-arrow": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.0.tgz",
"integrity": "sha512-FmlW1rCg7hBpEBwFbjHwCW6AmWLQM6g/v0Sn8XbP9NvmSZ2San1FpQeyPtufzOMSIx7Y4dzjlHoifhp+7NkZhw==",
"license": "MIT",
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.1.tgz",
"integrity": "sha512-NaVpZfmv8SKeZbn4ijN2V3jlHA9ngBG16VnIIm22nUR0Yk8KUALyBxT3KYEUnNuch9sTE8UTsS3whzBgKOL30w==",
"dependencies": {
"@radix-ui/react-primitive": "2.0.0"
"@radix-ui/react-primitive": "2.0.1"
},
"peerDependencies": {
"@types/react": "*",
@ -3255,10 +3394,9 @@
}
},
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-compose-refs": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz",
"integrity": "sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==",
"license": "MIT",
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz",
"integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
@ -3285,14 +3423,13 @@
}
},
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-dismissable-layer": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.1.tgz",
"integrity": "sha512-QSxg29lfr/xcev6kSz7MAlmDnzbP1eI/Dwn3Tp1ip0KT5CUELsxkekFEMVBEoykI3oV39hKT4TKZzBNMbcTZYQ==",
"license": "MIT",
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.3.tgz",
"integrity": "sha512-onrWn/72lQoEucDmJnr8uczSNTujT0vJnA/X5+3AkChVPowr8n1yvIKIabhWyMQeMvvmdpsvcyDqx3X1LEXCPg==",
"dependencies": {
"@radix-ui/primitive": "1.1.0",
"@radix-ui/react-compose-refs": "1.1.0",
"@radix-ui/react-primitive": "2.0.0",
"@radix-ui/primitive": "1.1.1",
"@radix-ui/react-compose-refs": "1.1.1",
"@radix-ui/react-primitive": "2.0.1",
"@radix-ui/react-use-callback-ref": "1.1.0",
"@radix-ui/react-use-escape-keydown": "1.1.0"
},
@ -3330,16 +3467,15 @@
}
},
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-popper": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.0.tgz",
"integrity": "sha512-ZnRMshKF43aBxVWPWvbj21+7TQCvhuULWJ4gNIKYpRlQt5xGRhLx66tMp8pya2UkGHTSlhpXwmjqltDYHhw7Vg==",
"license": "MIT",
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.1.tgz",
"integrity": "sha512-3kn5Me69L+jv82EKRuQCXdYyf1DqHwD2U/sxoNgBGCB7K9TRc3bQamQ+5EPM9EvyPdli0W41sROd+ZU1dTCztw==",
"dependencies": {
"@floating-ui/react-dom": "^2.0.0",
"@radix-ui/react-arrow": "1.1.0",
"@radix-ui/react-compose-refs": "1.1.0",
"@radix-ui/react-context": "1.1.0",
"@radix-ui/react-primitive": "2.0.0",
"@radix-ui/react-arrow": "1.1.1",
"@radix-ui/react-compose-refs": "1.1.1",
"@radix-ui/react-context": "1.1.1",
"@radix-ui/react-primitive": "2.0.1",
"@radix-ui/react-use-callback-ref": "1.1.0",
"@radix-ui/react-use-layout-effect": "1.1.0",
"@radix-ui/react-use-rect": "1.1.0",
@ -3361,28 +3497,12 @@
}
}
},
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-popper/node_modules/@radix-ui/react-context": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.0.tgz",
"integrity": "sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==",
"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-portal": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.2.tgz",
"integrity": "sha512-WeDYLGPxJb/5EGBoedyJbT0MpoULmwnIPMJMSldkuiMsBAv7N1cRdsTWZWht9vpPOiN3qyiGAtbK2is47/uMFg==",
"license": "MIT",
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.3.tgz",
"integrity": "sha512-NciRqhXnGojhT93RPyDaMPfLH3ZSl4jjIFbZQ1b/vxvZEdHsBZ49wP9w8L3HzUQwep01LcWtkUvm0OVB5JAHTw==",
"dependencies": {
"@radix-ui/react-primitive": "2.0.0",
"@radix-ui/react-primitive": "2.0.1",
"@radix-ui/react-use-layout-effect": "1.1.0"
},
"peerDependencies": {
@ -3401,12 +3521,11 @@
}
},
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-presence": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.1.tgz",
"integrity": "sha512-IeFXVi4YS1K0wVZzXNrbaaUvIJ3qdY+/Ih4eHFhWA9SwGR9UDX7Ck8abvL57C4cv3wwMvUE0OG69Qc3NCcTe/A==",
"license": "MIT",
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.2.tgz",
"integrity": "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.0",
"@radix-ui/react-compose-refs": "1.1.1",
"@radix-ui/react-use-layout-effect": "1.1.0"
},
"peerDependencies": {
@ -3425,12 +3544,11 @@
}
},
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-primitive": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz",
"integrity": "sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==",
"license": "MIT",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz",
"integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==",
"dependencies": {
"@radix-ui/react-slot": "1.1.0"
"@radix-ui/react-slot": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
@ -3447,24 +3565,6 @@
}
}
},
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-slot": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz",
"integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.0"
},
"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-use-callback-ref": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz",
@ -3502,7 +3602,6 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz",
"integrity": "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-use-callback-ref": "1.1.0"
},
@ -3535,7 +3634,6 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz",
"integrity": "sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==",
"license": "MIT",
"dependencies": {
"@radix-ui/rect": "1.1.0"
},
@ -3553,7 +3651,6 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz",
"integrity": "sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-use-layout-effect": "1.1.0"
},
@ -3568,12 +3665,11 @@
}
},
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-visually-hidden": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.0.tgz",
"integrity": "sha512-N8MDZqtgCgG5S3aV60INAB475osJousYpZ4cTJ2cFbMpdHS5Y6loLTH8LPtkj2QN0x93J30HT/M3qJXM0+lyeQ==",
"license": "MIT",
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.1.tgz",
"integrity": "sha512-vVfA2IZ9q/J+gEamvj761Oq1FpWgCDaNOOIfbPVp2MVPLEomUr5+Vf7kJGwQ24YxZSlQVar7Bes8kyTo5Dshpg==",
"dependencies": {
"@radix-ui/react-primitive": "2.0.0"
"@radix-ui/react-primitive": "2.0.1"
},
"peerDependencies": {
"@types/react": "*",
@ -3593,8 +3689,7 @@
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/rect": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.0.tgz",
"integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==",
"license": "MIT"
"integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg=="
},
"node_modules/@radix-ui/react-use-callback-ref": {
"version": "1.0.1",
@ -8028,18 +8123,18 @@
}
},
"node_modules/react-hook-form": {
"version": "7.51.3",
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.51.3.tgz",
"integrity": "sha512-cvJ/wbHdhYx8aviSWh28w9ImjmVsb5Y05n1+FW786vEZQJV5STNM0pW6ujS+oiBecb0ARBxJFyAnXj9+GHXACQ==",
"version": "7.54.2",
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.54.2.tgz",
"integrity": "sha512-eHpAUgUjWbZocoQYUHposymRb4ZP6d0uwUnooL2uOybA9/3tPUvoAKqEWK1WaSiTxxOfTpffNZP7QwlnM3/gEg==",
"engines": {
"node": ">=12.22.0"
"node": ">=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/react-hook-form"
},
"peerDependencies": {
"react": "^16.8.0 || ^17 || ^18"
"react": "^16.8.0 || ^17 || ^18 || ^19"
}
},
"node_modules/react-markdown": {
@ -9724,9 +9819,9 @@
}
},
"node_modules/zod": {
"version": "3.23.8",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz",
"integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==",
"version": "3.24.1",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz",
"integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==",
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}

View File

@ -14,7 +14,7 @@
"@clerk/nextjs": "^4.29.12",
"@clerk/themes": "^1.7.12",
"@codemirror/lang-javascript": "^6.2.2",
"@hookform/resolvers": "^3.3.4",
"@hookform/resolvers": "^3.9.1",
"@liveblocks/client": "^1.12.0",
"@liveblocks/node": "^1.12.0",
"@liveblocks/react": "^1.12.0",
@ -28,14 +28,14 @@
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-hover-card": "^1.1.2",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-label": "^2.1.1",
"@radix-ui/react-popover": "^1.1.1",
"@radix-ui/react-progress": "^1.1.0",
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-slot": "^1.1.1",
"@radix-ui/react-switch": "^1.0.3",
"@radix-ui/react-tabs": "^1.1.1",
"@radix-ui/react-tooltip": "^1.1.3",
"@radix-ui/react-tooltip": "^1.1.6",
"@react-three/fiber": "^8.16.6",
"@uiw/codemirror-theme-vscode": "^4.23.5",
"@uiw/react-codemirror": "^4.23.5",
@ -59,7 +59,7 @@
"posthog-js": "^1.147.0",
"react": "^18.3.1",
"react-dom": "^18",
"react-hook-form": "^7.51.3",
"react-hook-form": "^7.54.2",
"react-markdown": "^9.0.1",
"react-resizable-panels": "^2.0.16",
"react-syntax-highlighter": "^15.5.0",
@ -74,7 +74,7 @@
"y-monaco": "^0.1.5",
"y-protocols": "^1.0.6",
"yjs": "^13.6.15",
"zod": "^3.23.8"
"zod": "^3.24.1"
},
"devDependencies": {
"@types/estree": "^1.0.6",