feat: Add support for multiple email domains and enhance email creation dialog

This commit is contained in:
beilunyang
2024-12-24 00:23:28 +08:00
parent ef7fd4fc52
commit 774736af38
7 changed files with 303 additions and 26 deletions

View File

@@ -302,7 +302,7 @@ pnpx cloudflared tunnel --url http://localhost:3001
- `AUTH_SECRET`: NextAuth Secret用来加密 session请设置一个随机字符串
### 邮箱配置
- `NEXT_PUBLIC_EMAIL_DOMAIN`: 邮箱域名 (例如: moemail.app)
- `NEXT_PUBLIC_EMAIL_DOMAIN`: 邮箱域名,支持多域名,用逗号分隔 (例如: moemail.app,bitibiti.com)
### Cloudflare 配置
- `CLOUDFLARE_API_TOKEN`: Cloudflare API Token

View File

@@ -27,32 +27,39 @@ export async function POST(request: Request) {
if (Number(activeEmailsCount[0].count) >= EMAIL_CONFIG.MAX_ACTIVE_EMAILS) {
return NextResponse.json(
{ error: `Reached the maximum email limit (${EMAIL_CONFIG.MAX_ACTIVE_EMAILS})` },
{ error: `已达到最大邮箱数量限制 (${EMAIL_CONFIG.MAX_ACTIVE_EMAILS})` },
{ status: 403 }
)
}
const { name, expiryTime } = await request.json<{
const { name, expiryTime, domain } = await request.json<{
name: string
expiryTime: number
expiryTime: number
domain: string
}>()
// Validate expiry time
if (!EXPIRY_OPTIONS.some(option => option.value === expiryTime)) {
return NextResponse.json(
{ error: "Invalid expiry time" },
{ error: "无效的过期时间" },
{ status: 400 }
)
}
const address = `${name || nanoid(8)}@${EMAIL_CONFIG.DOMAIN}`
if (!EMAIL_CONFIG.DOMAINS.includes(domain)) {
return NextResponse.json(
{ error: "无效的域名" },
{ status: 400 }
)
}
const address = `${name || nanoid(8)}@${domain}`
const existingEmail = await db.query.emails.findFirst({
where: eq(emails.address, address)
})
if (existingEmail) {
return NextResponse.json(
{ error: "Email already exists" },
{ error: "该邮箱地址已被使用" },
{ status: 409 }
)
}
@@ -80,7 +87,7 @@ export async function POST(request: Request) {
} catch (error) {
console.error('Failed to generate email:', error)
return NextResponse.json(
{ error: "Failed to generate email" },
{ error: "创建邮箱失败" },
{ status: 500 }
)
}

View File

@@ -9,6 +9,7 @@ import { useToast } from "@/components/ui/use-toast"
import { nanoid } from "nanoid"
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
import { Label } from "@/components/ui/label"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { EXPIRY_OPTIONS } from "@/types/email"
import { EMAIL_CONFIG } from "@/config"
@@ -20,7 +21,8 @@ export function CreateDialog({ onEmailCreated }: CreateDialogProps) {
const [open, setOpen] = useState(false)
const [loading, setLoading] = useState(false)
const [emailName, setEmailName] = useState("")
const [expiryTime, setExpiryTime] = useState(EXPIRY_OPTIONS[1].value.toString()) // Default to 24 hours
const [domain, setDomain] = useState(EMAIL_CONFIG.DOMAINS[0])
const [expiryTime, setExpiryTime] = useState(EXPIRY_OPTIONS[1].value.toString())
const { toast } = useToast()
const generateRandomName = () => setEmailName(nanoid(8))
@@ -42,30 +44,21 @@ export function CreateDialog({ onEmailCreated }: CreateDialogProps) {
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
name: emailName,
expiryTime: parseInt(expiryTime) // 确保转换为数字
domain,
expiryTime: parseInt(expiryTime)
})
})
if (response.status === 409) {
if (!response.ok) {
const data = await response.json()
toast({
title: "错误",
description: "该邮箱名已被使用",
description: (data as { error: string }).error,
variant: "destructive"
})
return
}
if (response.status === 403) {
toast({
title: "错误",
description: "已达到最大邮箱数量限制",
variant: "destructive"
})
return
}
if (!response.ok) throw new Error("Failed to create email")
toast({
title: "成功",
description: "已创建新的临时邮箱"
@@ -104,6 +97,18 @@ export function CreateDialog({ onEmailCreated }: CreateDialogProps) {
placeholder="输入邮箱名"
className="flex-1"
/>
{EMAIL_CONFIG.DOMAINS.length > 1 && (
<Select value={domain} onValueChange={setDomain}>
<SelectTrigger className="w-[180px]">
<SelectValue />
</SelectTrigger>
<SelectContent>
{EMAIL_CONFIG.DOMAINS.map(d => (
<SelectItem key={d} value={d}>@{d}</SelectItem>
))}
</SelectContent>
</Select>
)}
<Button
variant="outline"
size="icon"
@@ -133,7 +138,7 @@ export function CreateDialog({ onEmailCreated }: CreateDialogProps) {
</div>
<div className="text-sm text-gray-500">
: {emailName ? `${emailName}@${EMAIL_CONFIG.DOMAIN}` : "..."}
: {emailName ? `${emailName}@${domain}` : "..."}
</div>
</div>
<div className="flex justify-end gap-2">

View File

@@ -0,0 +1,91 @@
"use client"
import * as React from "react"
import * as SelectPrimitive from "@radix-ui/react-select"
import { Check, ChevronDown } from "lucide-react"
import { cn } from "@/lib/utils"
const Select = SelectPrimitive.Root
const SelectTrigger = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
<SelectPrimitive.Trigger
ref={ref}
className={cn(
"flex h-9 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
{...props}
>
{children}
<SelectPrimitive.Icon asChild>
<ChevronDown className="h-4 w-4 opacity-50" />
</SelectPrimitive.Icon>
</SelectPrimitive.Trigger>
))
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
const SelectContent = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
>(({ className, children, position = "popper", ...props }, ref) => (
<SelectPrimitive.Portal>
<SelectPrimitive.Content
ref={ref}
className={cn(
"relative z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
position === "popper" &&
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
className
)}
position={position}
{...props}
>
<SelectPrimitive.Viewport
className={cn(
"p-1",
position === "popper" &&
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
)}
>
{children}
</SelectPrimitive.Viewport>
</SelectPrimitive.Content>
</SelectPrimitive.Portal>
))
SelectContent.displayName = SelectPrimitive.Content.displayName
const SelectItem = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
>(({ className, children, ...props }, ref) => (
<SelectPrimitive.Item
ref={ref}
className={cn(
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
)}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<SelectPrimitive.ItemIndicator>
<Check className="h-4 w-4" />
</SelectPrimitive.ItemIndicator>
</span>
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
</SelectPrimitive.Item>
))
SelectItem.displayName = SelectPrimitive.Item.displayName
const SelectValue = SelectPrimitive.Value
export {
Select,
SelectTrigger,
SelectContent,
SelectItem,
SelectValue,
}

View File

@@ -1,7 +1,7 @@
export const EMAIL_CONFIG = {
MAX_ACTIVE_EMAILS: 30, // Maximum number of active emails
POLL_INTERVAL: 10_000, // Polling interval in milliseconds
DOMAIN: process.env.NEXT_PUBLIC_EMAIL_DOMAIN || 'moemail.app', // Email domain
DOMAINS: (process.env.NEXT_PUBLIC_EMAIL_DOMAIN || 'moemail.app').split(','), // Email domains array
} as const
export type EmailConfig = typeof EMAIL_CONFIG

View File

@@ -25,6 +25,7 @@
"@radix-ui/react-dialog": "^1.1.2",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-radio-group": "^1.2.1",
"@radix-ui/react-select": "^2.1.4",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-switch": "^1.1.2",
"@radix-ui/react-toast": "^1.1.5",

173
pnpm-lock.yaml generated
View File

@@ -23,6 +23,9 @@ importers:
'@radix-ui/react-radio-group':
specifier: ^1.2.1
version: 1.2.1(@types/react-dom@18.3.2)(@types/react@18.3.14)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@radix-ui/react-select':
specifier: ^2.1.4
version: 2.1.4(@types/react-dom@18.3.2)(@types/react@18.3.14)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@radix-ui/react-slot':
specifier: ^1.0.2
version: 1.1.0(@types/react@18.3.14)(react@19.0.0)
@@ -1575,6 +1578,9 @@ packages:
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
'@radix-ui/number@1.1.0':
resolution: {integrity: sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==}
'@radix-ui/primitive@1.1.0':
resolution: {integrity: sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==}
@@ -1607,6 +1613,19 @@ packages:
'@types/react-dom':
optional: true
'@radix-ui/react-collection@1.1.1':
resolution: {integrity: sha512-LwT3pSho9Dljg+wY2KN2mrrh6y3qELfftINERIzBUO9e0N+t0oMTyn3k9iv+ZqgrwGkRnLpNJrsMv9BZlt2yuA==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-compose-refs@1.1.0':
resolution: {integrity: sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==}
peerDependencies:
@@ -1713,6 +1732,19 @@ packages:
'@types/react-dom':
optional: true
'@radix-ui/react-focus-scope@1.1.1':
resolution: {integrity: sha512-01omzJAYRxXdG2/he/+xy+c8a8gCydoQ1yOxnWNcRhrrBW5W+RQJ22EK1SaO8tb3WoUsuEw7mJjBozPzihDFjA==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-id@1.1.0':
resolution: {integrity: sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==}
peerDependencies:
@@ -1852,6 +1884,19 @@ packages:
'@types/react-dom':
optional: true
'@radix-ui/react-select@2.1.4':
resolution: {integrity: sha512-pOkb2u8KgO47j/h7AylCj7dJsm69BXcjkrvTqMptFqsE2i0p8lHkfgneXKjAgPzBMivnoMyt8o4KiV4wYzDdyQ==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-slot@1.1.0':
resolution: {integrity: sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==}
peerDependencies:
@@ -4581,6 +4626,16 @@ packages:
'@types/react':
optional: true
react-remove-scroll-bar@2.3.8:
resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==}
engines: {node: '>=10'}
peerDependencies:
'@types/react': '*'
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
peerDependenciesMeta:
'@types/react':
optional: true
react-remove-scroll@2.6.0:
resolution: {integrity: sha512-I2U4JVEsQenxDAKaVa3VZ/JeJZe0/2DxPWL8Tj8yLKctQJQiZM52pn/GWFpSp8dftjM3pSAHVJZscAnC/y+ySQ==}
engines: {node: '>=10'}
@@ -4591,6 +4646,16 @@ packages:
'@types/react':
optional: true
react-remove-scroll@2.6.2:
resolution: {integrity: sha512-KmONPx5fnlXYJQqC62Q+lwIeAk64ws/cUw6omIumRzMRPqgnYqhSSti99nbj0Ry13bv7dF+BKn7NB+OqkdZGTw==}
engines: {node: '>=10'}
peerDependencies:
'@types/react': '*'
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
react-style-singleton@2.2.1:
resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==}
engines: {node: '>=10'}
@@ -4601,6 +4666,16 @@ packages:
'@types/react':
optional: true
react-style-singleton@2.2.3:
resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==}
engines: {node: '>=10'}
peerDependencies:
'@types/react': '*'
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
react@19.0.0:
resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==}
engines: {node: '>=0.10.0'}
@@ -5205,6 +5280,16 @@ packages:
'@types/react':
optional: true
use-callback-ref@1.3.3:
resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==}
engines: {node: '>=10'}
peerDependencies:
'@types/react': '*'
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
use-sidecar@1.1.2:
resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==}
engines: {node: '>=10'}
@@ -6714,6 +6799,8 @@ snapshots:
'@pkgjs/parseargs@0.11.0':
optional: true
'@radix-ui/number@1.1.0': {}
'@radix-ui/primitive@1.1.0': {}
'@radix-ui/primitive@1.1.1': {}
@@ -6739,6 +6826,18 @@ snapshots:
'@types/react': 18.3.14
'@types/react-dom': 18.3.2
'@radix-ui/react-collection@1.1.1(@types/react-dom@18.3.2)(@types/react@18.3.14)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
'@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.14)(react@19.0.0)
'@radix-ui/react-context': 1.1.1(@types/react@18.3.14)(react@19.0.0)
'@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.2)(@types/react@18.3.14)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@radix-ui/react-slot': 1.1.1(@types/react@18.3.14)(react@19.0.0)
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
optionalDependencies:
'@types/react': 18.3.14
'@types/react-dom': 18.3.2
'@radix-ui/react-compose-refs@1.1.0(@types/react@18.3.14)(react@19.0.0)':
dependencies:
react: 19.0.0
@@ -6834,6 +6933,17 @@ snapshots:
'@types/react': 18.3.14
'@types/react-dom': 18.3.2
'@radix-ui/react-focus-scope@1.1.1(@types/react-dom@18.3.2)(@types/react@18.3.14)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
'@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.14)(react@19.0.0)
'@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.2)(@types/react@18.3.14)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.14)(react@19.0.0)
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
optionalDependencies:
'@types/react': 18.3.14
'@types/react-dom': 18.3.2
'@radix-ui/react-id@1.1.0(@types/react@18.3.14)(react@19.0.0)':
dependencies:
'@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.14)(react@19.0.0)
@@ -6961,6 +7071,35 @@ snapshots:
'@types/react': 18.3.14
'@types/react-dom': 18.3.2
'@radix-ui/react-select@2.1.4(@types/react-dom@18.3.2)(@types/react@18.3.14)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
'@radix-ui/number': 1.1.0
'@radix-ui/primitive': 1.1.1
'@radix-ui/react-collection': 1.1.1(@types/react-dom@18.3.2)(@types/react@18.3.14)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.14)(react@19.0.0)
'@radix-ui/react-context': 1.1.1(@types/react@18.3.14)(react@19.0.0)
'@radix-ui/react-direction': 1.1.0(@types/react@18.3.14)(react@19.0.0)
'@radix-ui/react-dismissable-layer': 1.1.3(@types/react-dom@18.3.2)(@types/react@18.3.14)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@radix-ui/react-focus-guards': 1.1.1(@types/react@18.3.14)(react@19.0.0)
'@radix-ui/react-focus-scope': 1.1.1(@types/react-dom@18.3.2)(@types/react@18.3.14)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@radix-ui/react-id': 1.1.0(@types/react@18.3.14)(react@19.0.0)
'@radix-ui/react-popper': 1.2.1(@types/react-dom@18.3.2)(@types/react@18.3.14)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@radix-ui/react-portal': 1.1.3(@types/react-dom@18.3.2)(@types/react@18.3.14)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.2)(@types/react@18.3.14)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@radix-ui/react-slot': 1.1.1(@types/react@18.3.14)(react@19.0.0)
'@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.14)(react@19.0.0)
'@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.14)(react@19.0.0)
'@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.14)(react@19.0.0)
'@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.14)(react@19.0.0)
'@radix-ui/react-visually-hidden': 1.1.1(@types/react-dom@18.3.2)(@types/react@18.3.14)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
aria-hidden: 1.2.4
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
react-remove-scroll: 2.6.2(@types/react@18.3.14)(react@19.0.0)
optionalDependencies:
'@types/react': 18.3.14
'@types/react-dom': 18.3.2
'@radix-ui/react-slot@1.1.0(@types/react@18.3.14)(react@19.0.0)':
dependencies:
'@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.14)(react@19.0.0)
@@ -9855,6 +9994,14 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.14
react-remove-scroll-bar@2.3.8(@types/react@18.3.14)(react@19.0.0):
dependencies:
react: 19.0.0
react-style-singleton: 2.2.3(@types/react@18.3.14)(react@19.0.0)
tslib: 2.8.1
optionalDependencies:
'@types/react': 18.3.14
react-remove-scroll@2.6.0(@types/react@18.3.14)(react@19.0.0):
dependencies:
react: 19.0.0
@@ -9866,6 +10013,17 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.14
react-remove-scroll@2.6.2(@types/react@18.3.14)(react@19.0.0):
dependencies:
react: 19.0.0
react-remove-scroll-bar: 2.3.8(@types/react@18.3.14)(react@19.0.0)
react-style-singleton: 2.2.1(@types/react@18.3.14)(react@19.0.0)
tslib: 2.8.1
use-callback-ref: 1.3.3(@types/react@18.3.14)(react@19.0.0)
use-sidecar: 1.1.2(@types/react@18.3.14)(react@19.0.0)
optionalDependencies:
'@types/react': 18.3.14
react-style-singleton@2.2.1(@types/react@18.3.14)(react@19.0.0):
dependencies:
get-nonce: 1.0.1
@@ -9875,6 +10033,14 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.14
react-style-singleton@2.2.3(@types/react@18.3.14)(react@19.0.0):
dependencies:
get-nonce: 1.0.1
react: 19.0.0
tslib: 2.8.1
optionalDependencies:
'@types/react': 18.3.14
react@19.0.0: {}
read-cache@1.0.0:
@@ -10567,6 +10733,13 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.14
use-callback-ref@1.3.3(@types/react@18.3.14)(react@19.0.0):
dependencies:
react: 19.0.0
tslib: 2.8.1
optionalDependencies:
'@types/react': 18.3.14
use-sidecar@1.1.2(@types/react@18.3.14)(react@19.0.0):
dependencies:
detect-node-es: 1.1.0