feat: complete new UI for Profiles, fix notfound error on username change
This commit is contained in:
parent
024e30bd99
commit
ceeb1fbce3
@ -9,19 +9,38 @@ import {
|
|||||||
CardDescription,
|
CardDescription,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
} from "@/components/ui/card"
|
} from "@/components/ui/card"
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormDescription,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from "@/components/ui/form"
|
||||||
import {
|
import {
|
||||||
HoverCard,
|
HoverCard,
|
||||||
HoverCardContent,
|
HoverCardContent,
|
||||||
HoverCardTrigger,
|
HoverCardTrigger,
|
||||||
} from "@/components/ui/hover-card"
|
} from "@/components/ui/hover-card"
|
||||||
import { Label } from "@/components/ui/label"
|
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
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 { deleteSandbox, updateSandbox, updateUser } from "@/lib/actions"
|
||||||
|
import { socialIcons } from "@/lib/data"
|
||||||
|
import { editUserSchema, EditUserSchema } from "@/lib/schema"
|
||||||
import { TIERS } from "@/lib/tiers"
|
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 { useUser } from "@clerk/nextjs"
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
import {
|
import {
|
||||||
Edit,
|
Edit,
|
||||||
|
Globe,
|
||||||
Heart,
|
Heart,
|
||||||
Info,
|
Info,
|
||||||
Loader2,
|
Loader2,
|
||||||
@ -29,17 +48,27 @@ import {
|
|||||||
Package2,
|
Package2,
|
||||||
PlusCircle,
|
PlusCircle,
|
||||||
Sparkles,
|
Sparkles,
|
||||||
|
Trash2,
|
||||||
X,
|
X,
|
||||||
} from "lucide-react"
|
} from "lucide-react"
|
||||||
import { useRouter } from "next/navigation"
|
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 { useFormState, useFormStatus } from "react-dom"
|
||||||
|
import { useFieldArray, useForm } from "react-hook-form"
|
||||||
import { toast } from "sonner"
|
import { toast } from "sonner"
|
||||||
import Avatar from "../ui/avatar"
|
import Avatar from "../ui/avatar"
|
||||||
import { Badge } from "../ui/badge"
|
import { Badge } from "../ui/badge"
|
||||||
import { Input } from "../ui/input"
|
import { Input } from "../ui/input"
|
||||||
import { Progress } from "../ui/progress"
|
import { Progress } from "../ui/progress"
|
||||||
|
import { Textarea } from "../ui/textarea"
|
||||||
// #region Profile Page
|
// #region Profile Page
|
||||||
export default function ProfilePage({
|
export default function ProfilePage({
|
||||||
publicSandboxes,
|
publicSandboxes,
|
||||||
@ -75,6 +104,9 @@ export default function ProfilePage({
|
|||||||
generations={isOwnProfile ? loggedInUser.generations : undefined}
|
generations={isOwnProfile ? loggedInUser.generations : undefined}
|
||||||
isOwnProfile={isOwnProfile}
|
isOwnProfile={isOwnProfile}
|
||||||
tier={profileOwner.tier}
|
tier={profileOwner.tier}
|
||||||
|
bio={profileOwner.bio}
|
||||||
|
personalWebsite={profileOwner.personalWebsite}
|
||||||
|
socialLinks={profileOwner.links}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="md:col-span-2">
|
<div className="md:col-span-2">
|
||||||
@ -101,11 +133,17 @@ function ProfileCard({
|
|||||||
joinedDate,
|
joinedDate,
|
||||||
generations,
|
generations,
|
||||||
isOwnProfile,
|
isOwnProfile,
|
||||||
|
bio,
|
||||||
|
personalWebsite,
|
||||||
|
socialLinks,
|
||||||
tier,
|
tier,
|
||||||
}: {
|
}: {
|
||||||
name: string
|
name: string
|
||||||
username: string
|
username: string
|
||||||
avatarUrl: string | null
|
avatarUrl: string | null
|
||||||
|
bio: string | null
|
||||||
|
personalWebsite: string | null
|
||||||
|
socialLinks: UserLink[]
|
||||||
sandboxes: SandboxWithLiked[]
|
sandboxes: SandboxWithLiked[]
|
||||||
joinedDate: Date
|
joinedDate: Date
|
||||||
generations?: number
|
generations?: number
|
||||||
@ -113,9 +151,8 @@ function ProfileCard({
|
|||||||
tier: string
|
tier: string
|
||||||
}) {
|
}) {
|
||||||
const { user } = useUser()
|
const { user } = useUser()
|
||||||
const router = useRouter()
|
|
||||||
const [isEditing, setIsEditing] = useState(false)
|
const [isEditing, setIsEditing] = useState(false)
|
||||||
const [formState, formAction] = useFormState(updateUser, {})
|
|
||||||
const joinedAt = useMemo(() => {
|
const joinedAt = useMemo(() => {
|
||||||
const date = new Date(joinedDate).toLocaleDateString("en-US", {
|
const date = new Date(joinedDate).toLocaleDateString("en-US", {
|
||||||
month: "long",
|
month: "long",
|
||||||
@ -140,117 +177,331 @@ function ProfileCard({
|
|||||||
}
|
}
|
||||||
}, [sandboxes])
|
}, [sandboxes])
|
||||||
|
|
||||||
useEffect(() => {
|
const showAddMoreInfoBanner = useMemo(() => {
|
||||||
if ("message" in formState) {
|
return !bio && !personalWebsite && socialLinks.length === 0
|
||||||
toast.success(formState.message as String)
|
}, [personalWebsite, bio, socialLinks])
|
||||||
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])
|
|
||||||
return (
|
return (
|
||||||
<Card className="mb-6 md:mb-0 sticky top-6">
|
<Card className="mb-6 md:mb-0 sticky top-6">
|
||||||
{isOwnProfile && (
|
{isOwnProfile && (
|
||||||
<Button
|
<div className="absolute top-2 right-2 flex flex-col gap-2">
|
||||||
onClick={toggleEdit}
|
<TooltipProvider>
|
||||||
aria-label={isEditing ? "close edit form" : "open edit form"}
|
<Tooltip>
|
||||||
size="smIcon"
|
<TooltipTrigger asChild>
|
||||||
variant="secondary"
|
<Button
|
||||||
className="rounded-full absolute top-2 right-2"
|
onClick={toggleEdit}
|
||||||
>
|
aria-label={isEditing ? "close edit form" : "open edit form"}
|
||||||
{isEditing ? <X className="size-4" /> : <Edit className="size-4" />}
|
size="smIcon"
|
||||||
</Button>
|
variant="secondary"
|
||||||
|
className="rounded-full relative"
|
||||||
|
>
|
||||||
|
{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">
|
<CardContent className="flex flex-col gap-4 items-center pt-6">
|
||||||
<Avatar name={name} avatarUrl={avatarUrl} className="size-36" />
|
<Avatar name={name} avatarUrl={avatarUrl} className="size-36" />
|
||||||
|
|
||||||
{!isEditing ? (
|
{isEditing ? (
|
||||||
<div className="space-y-2">
|
<EditProfileForm
|
||||||
<CardTitle className="text-2xl text-center">{name}</CardTitle>
|
{...{
|
||||||
<CardDescription className="text-center">{`@${username}`}</CardDescription>
|
name,
|
||||||
</div>
|
username,
|
||||||
|
avatarUrl,
|
||||||
|
bio,
|
||||||
|
personalWebsite,
|
||||||
|
socialLinks,
|
||||||
|
toggleEdit,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<form action={formAction} className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2.5 items-center">
|
||||||
<Input
|
<div className="space-y-1.5">
|
||||||
name="id"
|
<div className="">
|
||||||
placeholder="ID"
|
<CardTitle className="text-2xl text-center">{name}</CardTitle>
|
||||||
className="hidden "
|
<CardDescription className="text-center">{`@${username}`}</CardDescription>
|
||||||
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>
|
||||||
|
{typeof generations === "number" && (
|
||||||
|
<div className="flex justify-center">
|
||||||
|
<SubscriptionBadge
|
||||||
|
generations={generations}
|
||||||
|
tier={tier as keyof typeof TIERS}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex gap-4">
|
||||||
<SubmitButton />
|
|
||||||
</form>
|
|
||||||
)}
|
|
||||||
{!isEditing && (
|
|
||||||
<>
|
|
||||||
<div className="flex gap-6">
|
|
||||||
<StatsItem icon={Package2} label={stats.sandboxes} />
|
<StatsItem icon={Package2} label={stats.sandboxes} />
|
||||||
<StatsItem icon={Heart} label={stats.likes} />
|
<StatsItem icon={Heart} label={stats.likes} />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col items-center gap-2">
|
{bio && <p className="text-sm text-center">{bio}</p>}
|
||||||
<p className="text-xs text-muted-foreground">{joinedAt}</p>
|
{(socialLinks.length > 0 || personalWebsite) && (
|
||||||
{typeof generations === "number" && (
|
<div className="flex gap-2 justify-center">
|
||||||
<SubscriptionBadge
|
{personalWebsite && (
|
||||||
generations={generations}
|
<Button variant="ghost" size="smIcon" asChild>
|
||||||
tier={tier as keyof typeof TIERS}
|
<a
|
||||||
/>
|
href={personalWebsite}
|
||||||
)}
|
target="_blank"
|
||||||
</div>
|
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>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function SubmitButton() {
|
function EditProfileForm(props: {
|
||||||
const { pending } = useFormStatus()
|
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 (
|
return (
|
||||||
<Button size="sm" type="submit" className="w-full mt-2" disabled={pending}>
|
<Form {...form}>
|
||||||
{pending && <Loader2 className="animate-spin mr-2 h-4 w-4" />}
|
<form
|
||||||
Save
|
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>
|
</Button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -441,8 +692,8 @@ interface StatsItemProps {
|
|||||||
|
|
||||||
const StatsItem = ({ icon: Icon, label }: StatsItemProps) => (
|
const StatsItem = ({ icon: Icon, label }: StatsItemProps) => (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Icon size={18} />
|
<Icon size={16} />
|
||||||
<span className="text-sm text-muted-foreground">{label}</span>
|
<span className="text-sm text-muted-foreground">{label}</span>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
// #endregion
|
// #endregion
|
||||||
@ -463,7 +714,7 @@ const SubscriptionBadge = ({
|
|||||||
<HoverCard>
|
<HoverCard>
|
||||||
<HoverCardTrigger>
|
<HoverCardTrigger>
|
||||||
<Button variant="ghost" size="smIcon">
|
<Button variant="ghost" size="smIcon">
|
||||||
<Info size={20} />
|
<Info size={16} />
|
||||||
</Button>
|
</Button>
|
||||||
</HoverCardTrigger>
|
</HoverCardTrigger>
|
||||||
<HoverCardContent>
|
<HoverCardContent>
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
import * as LabelPrimitive from "@radix-ui/react-label"
|
import * as LabelPrimitive from "@radix-ui/react-label"
|
||||||
import { Slot } from "@radix-ui/react-slot"
|
import { Slot } from "@radix-ui/react-slot"
|
||||||
import * as React from "react"
|
|
||||||
import {
|
import {
|
||||||
Controller,
|
Controller,
|
||||||
ControllerProps,
|
ControllerProps,
|
||||||
@ -10,8 +12,8 @@ import {
|
|||||||
useFormContext,
|
useFormContext,
|
||||||
} from "react-hook-form"
|
} from "react-hook-form"
|
||||||
|
|
||||||
import { Label } from "@/components/ui/label"
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
import { Label } from "@/components/ui/label"
|
||||||
|
|
||||||
const Form = FormProvider
|
const Form = FormProvider
|
||||||
|
|
||||||
@ -93,7 +95,7 @@ const FormLabel = React.forwardRef<
|
|||||||
return (
|
return (
|
||||||
<Label
|
<Label
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(className)}
|
className={cn(error && "text-destructive", className)}
|
||||||
htmlFor={formItemId}
|
htmlFor={formItemId}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -165,12 +167,12 @@ const FormMessage = React.forwardRef<
|
|||||||
FormMessage.displayName = "FormMessage"
|
FormMessage.displayName = "FormMessage"
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
useFormField,
|
||||||
Form,
|
Form,
|
||||||
FormControl,
|
|
||||||
FormDescription,
|
|
||||||
FormField,
|
|
||||||
FormItem,
|
FormItem,
|
||||||
FormLabel,
|
FormLabel,
|
||||||
|
FormControl,
|
||||||
|
FormDescription,
|
||||||
FormMessage,
|
FormMessage,
|
||||||
useFormField,
|
FormField,
|
||||||
}
|
}
|
||||||
|
22
frontend/components/ui/textarea.tsx
Normal file
22
frontend/components/ui/textarea.tsx
Normal 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 }
|
@ -1,7 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip"
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
|
import * as TooltipPrimitive from "@radix-ui/react-tooltip"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
@ -29,4 +29,4 @@ const TooltipContent = React.forwardRef<
|
|||||||
))
|
))
|
||||||
TooltipContent.displayName = TooltipPrimitive.Content.displayName
|
TooltipContent.displayName = TooltipPrimitive.Content.displayName
|
||||||
|
|
||||||
export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger }
|
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
import { revalidatePath } from "next/cache"
|
import { revalidatePath } from "next/cache"
|
||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
|
import { editUserSchema } from "./schema"
|
||||||
|
import { UserLink } from "./types"
|
||||||
|
import { parseSocialLink } from "./utils"
|
||||||
|
|
||||||
export async function createSandbox(body: {
|
export async function createSandbox(body: {
|
||||||
type: string
|
type: string
|
||||||
@ -123,20 +126,31 @@ const UpdateErrorSchema = z.object({
|
|||||||
.optional(),
|
.optional(),
|
||||||
})
|
})
|
||||||
|
|
||||||
export async function updateUser(prevState: any, formData: FormData) {
|
interface FormState {
|
||||||
const data = Object.fromEntries(formData)
|
message: string
|
||||||
|
error?: any
|
||||||
const schema = z.object({
|
newRoute?: string
|
||||||
id: z.string(),
|
fields?: Record<string, unknown>
|
||||||
username: z.string(),
|
}
|
||||||
oldUsername: z.string(),
|
export async function updateUser(
|
||||||
name: z.string(),
|
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 {
|
try {
|
||||||
const validatedData = schema.parse(data)
|
const validatedData = editUserSchema.parse(data)
|
||||||
|
|
||||||
const changedUsername = validatedData.username !== validatedData.oldUsername
|
const changedUsername = validatedData.username !== validatedData.oldUsername
|
||||||
const res = await fetch(
|
const res = await fetch(
|
||||||
`${process.env.NEXT_PUBLIC_DATABASE_WORKER_URL}/api/user`,
|
`${process.env.NEXT_PUBLIC_DATABASE_WORKER_URL}/api/user`,
|
||||||
@ -150,6 +164,9 @@ export async function updateUser(prevState: any, formData: FormData) {
|
|||||||
id: validatedData.id,
|
id: validatedData.id,
|
||||||
username: data.username ?? undefined,
|
username: data.username ?? undefined,
|
||||||
name: data.name ?? 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)
|
const parseResult = UpdateErrorSchema.safeParse(responseData)
|
||||||
|
|
||||||
if (!parseResult.success) {
|
if (!parseResult.success) {
|
||||||
return { error: "Unexpected error occurred" }
|
return {
|
||||||
}
|
message: "Unexpected error occurred",
|
||||||
|
error: parseResult.error,
|
||||||
if (parseResult.data.error) {
|
fields: validatedData,
|
||||||
return parseResult.data
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changedUsername) {
|
if (changedUsername) {
|
||||||
@ -175,12 +192,13 @@ export async function updateUser(prevState: any, formData: FormData) {
|
|||||||
return { message: "Successfully updated" }
|
return { message: "Successfully updated" }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof z.ZodError) {
|
if (error instanceof z.ZodError) {
|
||||||
console.log(error)
|
|
||||||
return {
|
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 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
14
frontend/lib/constants/index.ts
Normal file
14
frontend/lib/constants/index.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
export const KNOWN_PLATFORMS = [
|
||||||
|
"github",
|
||||||
|
"twitter",
|
||||||
|
"instagram",
|
||||||
|
"bluesky",
|
||||||
|
"linkedin",
|
||||||
|
"youtube",
|
||||||
|
"twitch",
|
||||||
|
"discord",
|
||||||
|
"mastodon",
|
||||||
|
"threads",
|
||||||
|
"gitlab",
|
||||||
|
"generic",
|
||||||
|
] as const
|
@ -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: {
|
export const projectTemplates: {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
|
20
frontend/lib/schema/index.ts
Normal file
20
frontend/lib/schema/index.ts
Normal 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>
|
@ -1,5 +1,7 @@
|
|||||||
// DB Types
|
// DB Types
|
||||||
|
|
||||||
|
import { KNOWN_PLATFORMS } from "./constants"
|
||||||
|
|
||||||
export type User = {
|
export type User = {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
@ -8,11 +10,20 @@ export type User = {
|
|||||||
avatarUrl: string | null
|
avatarUrl: string | null
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
generations: number
|
generations: number
|
||||||
sandbox: Sandbox[]
|
bio: string | null
|
||||||
usersToSandboxes: UsersToSandboxes[]
|
personalWebsite: string | null
|
||||||
|
links: UserLink[]
|
||||||
tier: "FREE" | "PRO" | "ENTERPRISE"
|
tier: "FREE" | "PRO" | "ENTERPRISE"
|
||||||
tierExpiresAt: Date
|
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 = {
|
export type Sandbox = {
|
||||||
|
@ -2,7 +2,7 @@ import { type ClassValue, clsx } from "clsx"
|
|||||||
// import { toast } from "sonner"
|
// import { toast } from "sonner"
|
||||||
import { twMerge } from "tailwind-merge"
|
import { twMerge } from "tailwind-merge"
|
||||||
import fileExtToLang from "./file-extension-to-language.json"
|
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[]) {
|
export function cn(...inputs: ClassValue[]) {
|
||||||
return twMerge(clsx(inputs))
|
return twMerge(clsx(inputs))
|
||||||
@ -98,3 +98,57 @@ export function sortFileExplorer(
|
|||||||
return item
|
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",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
343
frontend/package-lock.json
generated
343
frontend/package-lock.json
generated
@ -13,7 +13,7 @@
|
|||||||
"@clerk/nextjs": "^4.29.12",
|
"@clerk/nextjs": "^4.29.12",
|
||||||
"@clerk/themes": "^1.7.12",
|
"@clerk/themes": "^1.7.12",
|
||||||
"@codemirror/lang-javascript": "^6.2.2",
|
"@codemirror/lang-javascript": "^6.2.2",
|
||||||
"@hookform/resolvers": "^3.3.4",
|
"@hookform/resolvers": "^3.9.1",
|
||||||
"@liveblocks/client": "^1.12.0",
|
"@liveblocks/client": "^1.12.0",
|
||||||
"@liveblocks/node": "^1.12.0",
|
"@liveblocks/node": "^1.12.0",
|
||||||
"@liveblocks/react": "^1.12.0",
|
"@liveblocks/react": "^1.12.0",
|
||||||
@ -27,14 +27,14 @@
|
|||||||
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
||||||
"@radix-ui/react-hover-card": "^1.1.2",
|
"@radix-ui/react-hover-card": "^1.1.2",
|
||||||
"@radix-ui/react-icons": "^1.3.0",
|
"@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-popover": "^1.1.1",
|
||||||
"@radix-ui/react-progress": "^1.1.0",
|
"@radix-ui/react-progress": "^1.1.0",
|
||||||
"@radix-ui/react-select": "^2.0.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-switch": "^1.0.3",
|
||||||
"@radix-ui/react-tabs": "^1.1.1",
|
"@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",
|
"@react-three/fiber": "^8.16.6",
|
||||||
"@uiw/codemirror-theme-vscode": "^4.23.5",
|
"@uiw/codemirror-theme-vscode": "^4.23.5",
|
||||||
"@uiw/react-codemirror": "^4.23.5",
|
"@uiw/react-codemirror": "^4.23.5",
|
||||||
@ -58,7 +58,7 @@
|
|||||||
"posthog-js": "^1.147.0",
|
"posthog-js": "^1.147.0",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18",
|
"react-dom": "^18",
|
||||||
"react-hook-form": "^7.51.3",
|
"react-hook-form": "^7.54.2",
|
||||||
"react-markdown": "^9.0.1",
|
"react-markdown": "^9.0.1",
|
||||||
"react-resizable-panels": "^2.0.16",
|
"react-resizable-panels": "^2.0.16",
|
||||||
"react-syntax-highlighter": "^15.5.0",
|
"react-syntax-highlighter": "^15.5.0",
|
||||||
@ -73,7 +73,7 @@
|
|||||||
"y-monaco": "^0.1.5",
|
"y-monaco": "^0.1.5",
|
||||||
"y-protocols": "^1.0.6",
|
"y-protocols": "^1.0.6",
|
||||||
"yjs": "^13.6.15",
|
"yjs": "^13.6.15",
|
||||||
"zod": "^3.23.8"
|
"zod": "^3.24.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/estree": "^1.0.6",
|
"@types/estree": "^1.0.6",
|
||||||
@ -909,9 +909,9 @@
|
|||||||
"integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q=="
|
"integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q=="
|
||||||
},
|
},
|
||||||
"node_modules/@hookform/resolvers": {
|
"node_modules/@hookform/resolvers": {
|
||||||
"version": "3.3.4",
|
"version": "3.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.9.1.tgz",
|
||||||
"integrity": "sha512-o5cgpGOuJYrd+iMKvkttOclgwRW86EsWJZZRC23prf0uU2i48Htq4PuT73AVb9ionFyZrwYEITuOFGF+BydEtQ==",
|
"integrity": "sha512-ud2HqmGBM0P0IABqoskKWI6PEf6ZDDBZkFqe2Vnl+mTHCEHzr3ISjjZyCwTjC/qpL25JC9aIDkloQejvMeq0ug==",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react-hook-form": "^7.0.0"
|
"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": {
|
"node_modules/@radix-ui/react-arrow": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz",
|
"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": {
|
"node_modules/@radix-ui/react-compose-refs": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz",
|
"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": {
|
"node_modules/@radix-ui/react-direction": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.1.tgz",
|
"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": {
|
"node_modules/@radix-ui/react-label": {
|
||||||
"version": "2.0.2",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.1.tgz",
|
||||||
"integrity": "sha512-N5ehvlM7qoTLx7nWPodsPYPgMzA5WM8zZChQg8nyFJKnDO5WHdba1vv5/H6IO5LtJMfD2Q3wh1qHFGNtK0w3bQ==",
|
"integrity": "sha512-UUw5E4e/2+4kFMH7+YxORXGWggtY6sM8WIwh5RZchhLuUg2H1hc98Py+pr8HMz6rdaYrK2t296ZEjYLOCO5uUw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.13.10",
|
"@radix-ui/react-primitive": "2.0.1"
|
||||||
"@radix-ui/react-primitive": "1.0.3"
|
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "*",
|
"@types/react": "*",
|
||||||
"@types/react-dom": "*",
|
"@types/react-dom": "*",
|
||||||
"react": "^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"
|
"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": {
|
"peerDependenciesMeta": {
|
||||||
"@types/react": {
|
"@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": {
|
"node_modules/@radix-ui/react-popover": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.1.tgz",
|
"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": {
|
"node_modules/@radix-ui/react-progress": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.0.tgz",
|
"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",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz",
|
||||||
"integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==",
|
"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": {
|
"node_modules/@radix-ui/react-switch": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.0.3.tgz",
|
"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": {
|
"node_modules/@radix-ui/react-tooltip": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.6",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.1.6.tgz",
|
||||||
"integrity": "sha512-Z4w1FIS0BqVFI2c1jZvb/uDVJijJjJ2ZMuPV81oVgTZ7g3BZxobplnMVvXtFWgtozdvYJ+MFWtwkM5S2HnAong==",
|
"integrity": "sha512-TLB5D8QLExS1uDn7+wH/bjEmRurNMTzNrtq7IjaS4kjion9NtzsTGkvR5+i7yc9q01Pi2KMM2cN3f8UG4IvvXA==",
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/primitive": "1.1.0",
|
"@radix-ui/primitive": "1.1.1",
|
||||||
"@radix-ui/react-compose-refs": "1.1.0",
|
"@radix-ui/react-compose-refs": "1.1.1",
|
||||||
"@radix-ui/react-context": "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-id": "1.1.0",
|
||||||
"@radix-ui/react-popper": "1.2.0",
|
"@radix-ui/react-popper": "1.2.1",
|
||||||
"@radix-ui/react-portal": "1.1.2",
|
"@radix-ui/react-portal": "1.1.3",
|
||||||
"@radix-ui/react-presence": "1.1.1",
|
"@radix-ui/react-presence": "1.1.2",
|
||||||
"@radix-ui/react-primitive": "2.0.0",
|
"@radix-ui/react-primitive": "2.0.1",
|
||||||
"@radix-ui/react-slot": "1.1.0",
|
"@radix-ui/react-slot": "1.1.1",
|
||||||
"@radix-ui/react-use-controllable-state": "1.1.0",
|
"@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": {
|
"peerDependencies": {
|
||||||
"@types/react": "*",
|
"@types/react": "*",
|
||||||
@ -3226,18 +3367,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/primitive": {
|
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/primitive": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz",
|
||||||
"integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==",
|
"integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA=="
|
||||||
"license": "MIT"
|
|
||||||
},
|
},
|
||||||
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-arrow": {
|
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-arrow": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.1.tgz",
|
||||||
"integrity": "sha512-FmlW1rCg7hBpEBwFbjHwCW6AmWLQM6g/v0Sn8XbP9NvmSZ2San1FpQeyPtufzOMSIx7Y4dzjlHoifhp+7NkZhw==",
|
"integrity": "sha512-NaVpZfmv8SKeZbn4ijN2V3jlHA9ngBG16VnIIm22nUR0Yk8KUALyBxT3KYEUnNuch9sTE8UTsS3whzBgKOL30w==",
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/react-primitive": "2.0.0"
|
"@radix-ui/react-primitive": "2.0.1"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "*",
|
"@types/react": "*",
|
||||||
@ -3255,10 +3394,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-compose-refs": {
|
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-compose-refs": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz",
|
||||||
"integrity": "sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==",
|
"integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==",
|
||||||
"license": "MIT",
|
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "*",
|
"@types/react": "*",
|
||||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
"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": {
|
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-dismissable-layer": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.3.tgz",
|
||||||
"integrity": "sha512-QSxg29lfr/xcev6kSz7MAlmDnzbP1eI/Dwn3Tp1ip0KT5CUELsxkekFEMVBEoykI3oV39hKT4TKZzBNMbcTZYQ==",
|
"integrity": "sha512-onrWn/72lQoEucDmJnr8uczSNTujT0vJnA/X5+3AkChVPowr8n1yvIKIabhWyMQeMvvmdpsvcyDqx3X1LEXCPg==",
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/primitive": "1.1.0",
|
"@radix-ui/primitive": "1.1.1",
|
||||||
"@radix-ui/react-compose-refs": "1.1.0",
|
"@radix-ui/react-compose-refs": "1.1.1",
|
||||||
"@radix-ui/react-primitive": "2.0.0",
|
"@radix-ui/react-primitive": "2.0.1",
|
||||||
"@radix-ui/react-use-callback-ref": "1.1.0",
|
"@radix-ui/react-use-callback-ref": "1.1.0",
|
||||||
"@radix-ui/react-use-escape-keydown": "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": {
|
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-popper": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.1.tgz",
|
||||||
"integrity": "sha512-ZnRMshKF43aBxVWPWvbj21+7TQCvhuULWJ4gNIKYpRlQt5xGRhLx66tMp8pya2UkGHTSlhpXwmjqltDYHhw7Vg==",
|
"integrity": "sha512-3kn5Me69L+jv82EKRuQCXdYyf1DqHwD2U/sxoNgBGCB7K9TRc3bQamQ+5EPM9EvyPdli0W41sROd+ZU1dTCztw==",
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@floating-ui/react-dom": "^2.0.0",
|
"@floating-ui/react-dom": "^2.0.0",
|
||||||
"@radix-ui/react-arrow": "1.1.0",
|
"@radix-ui/react-arrow": "1.1.1",
|
||||||
"@radix-ui/react-compose-refs": "1.1.0",
|
"@radix-ui/react-compose-refs": "1.1.1",
|
||||||
"@radix-ui/react-context": "1.1.0",
|
"@radix-ui/react-context": "1.1.1",
|
||||||
"@radix-ui/react-primitive": "2.0.0",
|
"@radix-ui/react-primitive": "2.0.1",
|
||||||
"@radix-ui/react-use-callback-ref": "1.1.0",
|
"@radix-ui/react-use-callback-ref": "1.1.0",
|
||||||
"@radix-ui/react-use-layout-effect": "1.1.0",
|
"@radix-ui/react-use-layout-effect": "1.1.0",
|
||||||
"@radix-ui/react-use-rect": "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": {
|
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-portal": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.3.tgz",
|
||||||
"integrity": "sha512-WeDYLGPxJb/5EGBoedyJbT0MpoULmwnIPMJMSldkuiMsBAv7N1cRdsTWZWht9vpPOiN3qyiGAtbK2is47/uMFg==",
|
"integrity": "sha512-NciRqhXnGojhT93RPyDaMPfLH3ZSl4jjIFbZQ1b/vxvZEdHsBZ49wP9w8L3HzUQwep01LcWtkUvm0OVB5JAHTw==",
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/react-primitive": "2.0.0",
|
"@radix-ui/react-primitive": "2.0.1",
|
||||||
"@radix-ui/react-use-layout-effect": "1.1.0"
|
"@radix-ui/react-use-layout-effect": "1.1.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
@ -3401,12 +3521,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-presence": {
|
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-presence": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.2.tgz",
|
||||||
"integrity": "sha512-IeFXVi4YS1K0wVZzXNrbaaUvIJ3qdY+/Ih4eHFhWA9SwGR9UDX7Ck8abvL57C4cv3wwMvUE0OG69Qc3NCcTe/A==",
|
"integrity": "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==",
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"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"
|
"@radix-ui/react-use-layout-effect": "1.1.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
@ -3425,12 +3544,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-primitive": {
|
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-primitive": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz",
|
||||||
"integrity": "sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==",
|
"integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==",
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/react-slot": "1.1.0"
|
"@radix-ui/react-slot": "1.1.1"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "*",
|
"@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": {
|
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-use-callback-ref": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz",
|
"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",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz",
|
||||||
"integrity": "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==",
|
"integrity": "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==",
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/react-use-callback-ref": "1.1.0"
|
"@radix-ui/react-use-callback-ref": "1.1.0"
|
||||||
},
|
},
|
||||||
@ -3535,7 +3634,6 @@
|
|||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz",
|
||||||
"integrity": "sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==",
|
"integrity": "sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==",
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/rect": "1.1.0"
|
"@radix-ui/rect": "1.1.0"
|
||||||
},
|
},
|
||||||
@ -3553,7 +3651,6 @@
|
|||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz",
|
||||||
"integrity": "sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==",
|
"integrity": "sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==",
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/react-use-layout-effect": "1.1.0"
|
"@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": {
|
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-visually-hidden": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.1.tgz",
|
||||||
"integrity": "sha512-N8MDZqtgCgG5S3aV60INAB475osJousYpZ4cTJ2cFbMpdHS5Y6loLTH8LPtkj2QN0x93J30HT/M3qJXM0+lyeQ==",
|
"integrity": "sha512-vVfA2IZ9q/J+gEamvj761Oq1FpWgCDaNOOIfbPVp2MVPLEomUr5+Vf7kJGwQ24YxZSlQVar7Bes8kyTo5Dshpg==",
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/react-primitive": "2.0.0"
|
"@radix-ui/react-primitive": "2.0.1"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "*",
|
"@types/react": "*",
|
||||||
@ -3593,8 +3689,7 @@
|
|||||||
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/rect": {
|
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/rect": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.0.tgz",
|
||||||
"integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==",
|
"integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg=="
|
||||||
"license": "MIT"
|
|
||||||
},
|
},
|
||||||
"node_modules/@radix-ui/react-use-callback-ref": {
|
"node_modules/@radix-ui/react-use-callback-ref": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
@ -8028,18 +8123,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-hook-form": {
|
"node_modules/react-hook-form": {
|
||||||
"version": "7.51.3",
|
"version": "7.54.2",
|
||||||
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.51.3.tgz",
|
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.54.2.tgz",
|
||||||
"integrity": "sha512-cvJ/wbHdhYx8aviSWh28w9ImjmVsb5Y05n1+FW786vEZQJV5STNM0pW6ujS+oiBecb0ARBxJFyAnXj9+GHXACQ==",
|
"integrity": "sha512-eHpAUgUjWbZocoQYUHposymRb4ZP6d0uwUnooL2uOybA9/3tPUvoAKqEWK1WaSiTxxOfTpffNZP7QwlnM3/gEg==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12.22.0"
|
"node": ">=18.0.0"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
"url": "https://opencollective.com/react-hook-form"
|
"url": "https://opencollective.com/react-hook-form"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "^16.8.0 || ^17 || ^18"
|
"react": "^16.8.0 || ^17 || ^18 || ^19"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-markdown": {
|
"node_modules/react-markdown": {
|
||||||
@ -9724,9 +9819,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/zod": {
|
"node_modules/zod": {
|
||||||
"version": "3.23.8",
|
"version": "3.24.1",
|
||||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz",
|
"resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz",
|
||||||
"integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==",
|
"integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==",
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/colinhacks"
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
"@clerk/nextjs": "^4.29.12",
|
"@clerk/nextjs": "^4.29.12",
|
||||||
"@clerk/themes": "^1.7.12",
|
"@clerk/themes": "^1.7.12",
|
||||||
"@codemirror/lang-javascript": "^6.2.2",
|
"@codemirror/lang-javascript": "^6.2.2",
|
||||||
"@hookform/resolvers": "^3.3.4",
|
"@hookform/resolvers": "^3.9.1",
|
||||||
"@liveblocks/client": "^1.12.0",
|
"@liveblocks/client": "^1.12.0",
|
||||||
"@liveblocks/node": "^1.12.0",
|
"@liveblocks/node": "^1.12.0",
|
||||||
"@liveblocks/react": "^1.12.0",
|
"@liveblocks/react": "^1.12.0",
|
||||||
@ -28,14 +28,14 @@
|
|||||||
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
||||||
"@radix-ui/react-hover-card": "^1.1.2",
|
"@radix-ui/react-hover-card": "^1.1.2",
|
||||||
"@radix-ui/react-icons": "^1.3.0",
|
"@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-popover": "^1.1.1",
|
||||||
"@radix-ui/react-progress": "^1.1.0",
|
"@radix-ui/react-progress": "^1.1.0",
|
||||||
"@radix-ui/react-select": "^2.0.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-switch": "^1.0.3",
|
||||||
"@radix-ui/react-tabs": "^1.1.1",
|
"@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",
|
"@react-three/fiber": "^8.16.6",
|
||||||
"@uiw/codemirror-theme-vscode": "^4.23.5",
|
"@uiw/codemirror-theme-vscode": "^4.23.5",
|
||||||
"@uiw/react-codemirror": "^4.23.5",
|
"@uiw/react-codemirror": "^4.23.5",
|
||||||
@ -59,7 +59,7 @@
|
|||||||
"posthog-js": "^1.147.0",
|
"posthog-js": "^1.147.0",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18",
|
"react-dom": "^18",
|
||||||
"react-hook-form": "^7.51.3",
|
"react-hook-form": "^7.54.2",
|
||||||
"react-markdown": "^9.0.1",
|
"react-markdown": "^9.0.1",
|
||||||
"react-resizable-panels": "^2.0.16",
|
"react-resizable-panels": "^2.0.16",
|
||||||
"react-syntax-highlighter": "^15.5.0",
|
"react-syntax-highlighter": "^15.5.0",
|
||||||
@ -74,7 +74,7 @@
|
|||||||
"y-monaco": "^0.1.5",
|
"y-monaco": "^0.1.5",
|
||||||
"y-protocols": "^1.0.6",
|
"y-protocols": "^1.0.6",
|
||||||
"yjs": "^13.6.15",
|
"yjs": "^13.6.15",
|
||||||
"zod": "^3.23.8"
|
"zod": "^3.24.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/estree": "^1.0.6",
|
"@types/estree": "^1.0.6",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user