This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #12 from cosmik-network/chore/mantine-migration

chore: migrate from tailwind and radix-ui to mantine-ui

+1714 -2789
+7 -8
docs/browser-extension-setup.md
··· 75 75 **popup.tsx:** 76 76 ```typescript 77 77 import { useAuth } from "./hooks/useAuth" 78 - import { Button } from "./components/ui/button" 79 - import "./app/globals.css" // Reuse your styles 78 + import { Button, Center, Title } from "@mantine/core" 79 + import "@mantine/core/styles.css"; // Reuse your styles 80 80 81 81 function IndexPopup() { 82 82 const { isAuthenticated, login } = useAuth() 83 - 83 + 84 84 return ( 85 - <div className="w-80 p-4"> 86 - <h1>Your App Extension</h1> 85 + <Center> 86 + <Title order={1}>Your App Extension</Title> 87 87 {isAuthenticated ? ( 88 88 <div>Welcome back!</div> 89 89 ) : ( 90 90 <Button onClick={() => login()}>Sign In</Button> 91 91 )} 92 - </div> 92 + </Center> 93 93 ) 94 94 } 95 95 ··· 118 118 const useAuth = () => { 119 119 // Use chrome.storage for extension, localStorage for webapp 120 120 const storage = isExtension ? chrome.storage.local : localStorage 121 - 121 + 122 122 // Rest of your existing auth logic... 123 123 } 124 124 ``` ··· 130 130 ```typescript 131 131 // Move these to shared/components/ 132 132 - UrlCard.tsx 133 - - Sidebar.tsx 134 133 - ui/ components 135 134 ``` 136 135
+133 -115
src/webapp/app/(authenticated)/cards/[cardId]/page.tsx
··· 1 1 "use client"; 2 2 3 - import { Button } from "@/components/ui/button"; 4 - import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; 5 - import { Badge } from "@/components/ui/badge"; 6 3 import { useEffect, useState } from "react"; 7 4 import { useRouter, useParams } from "next/navigation"; 8 5 import { getAccessToken } from "@/services/auth"; 9 6 import { ApiClient } from "@/api-client/ApiClient"; 10 7 import type { GetUrlCardViewResponse } from "@/api-client/types"; 8 + import { 9 + Button, 10 + Loader, 11 + Stack, 12 + Text, 13 + Card, 14 + Title, 15 + Anchor, 16 + Blockquote, 17 + Box, 18 + Group, 19 + Badge, 20 + Image, 21 + Divider, 22 + } from "@mantine/core"; 11 23 12 24 export default function CardPage() { 13 25 const [card, setCard] = useState<GetUrlCardViewResponse | null>(null); ··· 20 32 // Create API client instance 21 33 const apiClient = new ApiClient( 22 34 process.env.NEXT_PUBLIC_API_BASE_URL || "http://localhost:3000", 23 - () => getAccessToken() 35 + () => getAccessToken(), 24 36 ); 25 37 26 38 useEffect(() => { ··· 43 55 }, [cardId]); 44 56 45 57 if (loading) { 46 - return ( 47 - <div className="flex justify-center items-center h-64"> 48 - <div className="animate-spin rounded-full h-10 w-10 border-b-2 border-blue-500"></div> 49 - </div> 50 - ); 58 + return <Loader />; 51 59 } 52 60 53 61 if (error || !card) { 54 62 return ( 55 - <div className="text-center py-12"> 56 - <p className="text-red-600 mb-4">{error || "Card not found"}</p> 63 + <Stack align="center"> 64 + <Text c={"red"}>{error || "Card not found"}</Text> 57 65 <Button onClick={() => router.back()}>Go Back</Button> 58 - </div> 66 + </Stack> 59 67 ); 60 68 } 61 69 62 70 return ( 63 - <div className="max-w-4xl mx-auto space-y-6"> 64 - <div className="flex justify-between items-start"> 65 - <Button variant="outline" onClick={() => router.back()}> 66 - ← Back 67 - </Button> 68 - </div> 71 + <Box> 72 + <Stack> 73 + <Group> 74 + <Button variant="outline" onClick={() => router.back()}> 75 + ← Back 76 + </Button> 77 + </Group> 69 78 70 - <Card> 71 - <CardHeader> 72 - <div className="flex justify-between items-start gap-4"> 73 - <div className="flex-1"> 74 - <CardTitle className="text-2xl mb-2"> 75 - {card.cardContent.title || "Untitled"} 76 - </CardTitle> 77 - <div className="flex items-center gap-2 text-sm text-gray-500 mb-4"> 78 - <Badge variant="secondary">{card.type}</Badge> 79 - <span> 80 - Added {new Date(card.createdAt).toLocaleDateString()} 81 - </span> 82 - </div> 83 - </div> 84 - {card.cardContent.thumbnailUrl && ( 85 - <img 86 - src={card.cardContent.thumbnailUrl} 87 - alt={card.cardContent.title || "Card thumbnail"} 88 - className="w-32 h-20 object-cover rounded-md" 89 - /> 90 - )} 91 - </div> 92 - </CardHeader> 93 - <CardContent className="space-y-6"> 94 - {/* URL */} 95 - <div> 96 - <h3 className="font-semibold mb-2">URL</h3> 97 - <a 98 - href={card.url} 99 - target="_blank" 100 - rel="noopener noreferrer" 101 - className="text-blue-600 hover:text-blue-800 underline break-all" 102 - > 103 - {card.url} 104 - </a> 105 - </div> 79 + <Card withBorder> 80 + <Stack> 81 + <Stack> 82 + <Group wrap="nowrap"> 83 + <Stack> 84 + <Text fz={"xl"} fw={600}> 85 + {card.cardContent.title || "Untitled"} 86 + </Text> 87 + <Group gap={"xs"}> 88 + <Badge variant="outline">{card.type}</Badge> 89 + <Text fz={"sm"} c={"gray"}> 90 + Added {new Date(card.createdAt).toLocaleDateString()} 91 + </Text> 92 + </Group> 93 + </Stack> 94 + {card.cardContent.thumbnailUrl && ( 95 + <Image 96 + src={card.cardContent.thumbnailUrl} 97 + alt={card.cardContent.title || "Card thumbnail"} 98 + w={128} 99 + radius={"md"} 100 + /> 101 + )} 102 + </Group> 103 + </Stack> 104 + <Stack gap={"xl"}> 105 + {/* URL */} 106 + <Stack gap={"xs"}> 107 + <Title order={3} fz={"sm"}> 108 + URL 109 + </Title> 110 + <Anchor 111 + c="blue" 112 + href={card.url} 113 + target="_blank" 114 + rel="noopener noreferrer" 115 + > 116 + {card.url} 117 + </Anchor> 118 + </Stack> 106 119 107 - {/* Description */} 108 - {card.cardContent.description && ( 109 - <div> 110 - <h3 className="font-semibold mb-2">Description</h3> 111 - <p className="text-gray-700 leading-relaxed"> 112 - {card.cardContent.description} 113 - </p> 114 - </div> 115 - )} 120 + {/* Description */} 121 + {card.cardContent.description && ( 122 + <Stack gap={"xs"}> 123 + <Title order={3} fz={"sm"}> 124 + Description 125 + </Title> 126 + <Text c={"gray"}>{card.cardContent.description}</Text> 127 + </Stack> 128 + )} 116 129 117 - {/* Author */} 118 - {card.cardContent.author && ( 119 - <div> 120 - <h3 className="font-semibold mb-2">Author</h3> 121 - <p className="text-gray-700">{card.cardContent.author}</p> 122 - </div> 123 - )} 130 + {/* Author */} 131 + {card.cardContent.author && ( 132 + <Stack gap={"xs"}> 133 + <Title order={3} fz={"sm"}> 134 + Author 135 + </Title> 136 + <Text c={"gray"}>{card.cardContent.author}</Text> 137 + </Stack> 138 + )} 124 139 125 - {/* Note */} 126 - {card.note && ( 127 - <div> 128 - <h3 className="font-semibold mb-2">Note</h3> 129 - <div className="bg-gray-50 p-4 rounded-md"> 130 - <p className="text-gray-700 leading-relaxed"> 131 - {card.note.text} 132 - </p> 133 - </div> 134 - </div> 135 - )} 140 + {/* Note */} 141 + {card.note && ( 142 + <Stack gap={"xs"}> 143 + <Title order={3} fz={"sm"}> 144 + Note 145 + </Title> 146 + <Blockquote color="yellow" p={"xs"} fz={"sm"}> 147 + {card.note.text} 148 + </Blockquote> 149 + </Stack> 150 + )} 136 151 137 - {/* Collections */} 138 - {card.collections && card.collections.length > 0 && ( 139 - <div> 140 - <h3 className="font-semibold mb-2">Collections</h3> 141 - <div className="flex flex-wrap gap-2"> 142 - {card.collections.map((collection) => ( 143 - <Badge 144 - key={collection.id} 145 - variant="outline" 146 - className="cursor-pointer hover:bg-gray-100" 147 - onClick={() => router.push(`/collections/${collection.id}`)} 148 - > 149 - {collection.name} 150 - </Badge> 151 - ))} 152 - </div> 153 - </div> 154 - )} 152 + {/* Collections */} 153 + {card.collections && card.collections.length > 0 && ( 154 + <Stack gap={"xs"}> 155 + <Title order={3} fz={"sm"}> 156 + Collections 157 + </Title> 158 + <Group> 159 + {card.collections.map((collection) => ( 160 + <Badge 161 + key={collection.id} 162 + variant="outline" 163 + onClick={() => 164 + router.push(`/collections/${collection.id}`) 165 + } 166 + > 167 + {collection.name} 168 + </Badge> 169 + ))} 170 + </Group> 171 + </Stack> 172 + )} 155 173 156 - {/* Metadata */} 157 - <div className="pt-4 border-t border-gray-200"> 158 - <div className="grid grid-cols-2 gap-4 text-sm text-gray-500"> 159 - <div> 160 - <span className="font-medium">Created:</span>{" "} 161 - {new Date(card.createdAt).toLocaleString()} 162 - </div> 163 - <div> 164 - <span className="font-medium">Updated:</span>{" "} 165 - {new Date(card.updatedAt).toLocaleString()} 166 - </div> 167 - </div> 168 - </div> 169 - </CardContent> 170 - </Card> 171 - </div> 174 + {/* Metadata */} 175 + <Divider /> 176 + 177 + <Group justify="space-between"> 178 + <Text fz={"sm"} fw={500} c={"gray"}> 179 + Created: {new Date(card.createdAt).toLocaleString()} 180 + </Text> 181 + <Text fz={"sm"} fw={500} c={"gray"}> 182 + Updated: {new Date(card.updatedAt).toLocaleString()} 183 + </Text> 184 + </Group> 185 + </Stack> 186 + </Stack> 187 + </Card> 188 + </Stack> 189 + </Box> 172 190 ); 173 191 }
+80 -77
src/webapp/app/(authenticated)/cards/add/page.tsx
··· 1 1 "use client"; 2 2 3 - import { Button } from "@/components/ui/button"; 4 - import { Input } from "@/components/ui/input"; 5 - import { Textarea } from "@/components/ui/textarea"; 6 3 import { useState } from "react"; 7 4 import { useRouter } from "next/navigation"; 8 5 import { getAccessToken } from "@/services/auth"; 9 6 import { ApiClient } from "@/api-client/ApiClient"; 10 - import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; 11 - import { Label } from "@/components/ui/label"; 7 + import { 8 + Box, 9 + Stack, 10 + Text, 11 + Title, 12 + Card, 13 + TextInput, 14 + Textarea, 15 + Button, 16 + Group, 17 + } from "@mantine/core"; 18 + import { useForm } from "@mantine/form"; 12 19 13 20 export default function AddCardPage() { 14 - const [url, setUrl] = useState(""); 15 - const [note, setNote] = useState(""); 21 + const form = useForm({ 22 + initialValues: { 23 + url: "", 24 + note: "", 25 + }, 26 + }); 16 27 const [loading, setLoading] = useState(false); 17 28 const [error, setError] = useState(""); 18 29 const router = useRouter(); ··· 20 31 // Create API client instance 21 32 const apiClient = new ApiClient( 22 33 process.env.NEXT_PUBLIC_API_BASE_URL || "http://localhost:3000", 23 - () => getAccessToken() 34 + () => getAccessToken(), 24 35 ); 25 36 26 37 const handleSubmit = async (e: React.FormEvent) => { 27 38 e.preventDefault(); 28 39 29 - if (!url.trim()) { 40 + if (!form.getValues().url.trim()) { 30 41 setError("URL is required"); 31 42 return; 32 43 } 33 44 34 45 // Basic URL validation 35 46 try { 36 - new URL(url); 47 + new URL(form.getValues().url); 37 48 } catch { 38 49 setError("Please enter a valid URL"); 39 50 return; ··· 44 55 45 56 try { 46 57 await apiClient.addUrlToLibrary({ 47 - url: url.trim(), 48 - note: note.trim() || undefined, 58 + url: form.getValues().url.trim(), 59 + note: form.getValues().note.trim() || undefined, 49 60 }); 50 61 51 62 // Redirect to dashboard or cards page on success ··· 59 70 }; 60 71 61 72 return ( 62 - <div className="max-w-2xl mx-auto"> 63 - <div className="mb-6"> 64 - <h1 className="text-3xl font-bold">Add Card</h1> 65 - <p className="text-gray-600 mt-2"> 66 - Add a URL to your library with an optional note. 67 - </p> 68 - </div> 73 + <Box> 74 + <Stack> 75 + <Stack gap={0}> 76 + <Title order={1}>Add Card</Title> 77 + <Text c={"gray"}> 78 + Add a URL to your library with an optional note. 79 + </Text> 80 + </Stack> 69 81 70 - <Card> 71 - <CardHeader> 72 - <CardTitle>Add URL to Library</CardTitle> 73 - </CardHeader> 74 - <CardContent> 75 - <form onSubmit={handleSubmit} className="space-y-4"> 76 - <div className="space-y-2"> 77 - <Label htmlFor="url">URL *</Label> 78 - <Input 79 - id="url" 80 - type="url" 81 - placeholder="https://example.com" 82 - value={url} 83 - onChange={(e) => setUrl(e.target.value)} 84 - disabled={loading} 85 - required 86 - /> 87 - </div> 82 + <Card withBorder> 83 + <Stack> 84 + <Title order={3}>Add URL to Library</Title> 85 + 86 + <Stack> 87 + <form onSubmit={handleSubmit}> 88 + <Stack> 89 + <Stack> 90 + <TextInput 91 + id="url" 92 + label="URL" 93 + type="url" 94 + placeholder="https://example.com" 95 + disabled={loading} 96 + required 97 + key={form.key("url")} 98 + {...form.getInputProps("url")} 99 + /> 88 100 89 - <div className="space-y-2"> 90 - <Label htmlFor="note">Note (optional)</Label> 91 - <Textarea 92 - id="note" 93 - placeholder="Add a note about this URL..." 94 - value={note} 95 - onChange={(e) => setNote(e.target.value)} 96 - disabled={loading} 97 - rows={3} 98 - /> 99 - </div> 101 + <Textarea 102 + id="note" 103 + label="Note" 104 + placeholder="Add a note about this URL..." 105 + disabled={loading} 106 + rows={3} 107 + key={form.key("note")} 108 + {...form.getInputProps("note")} 109 + /> 110 + </Stack> 100 111 101 - {error && ( 102 - <div className="text-red-600 text-sm bg-red-50 p-3 rounded-md"> 103 - {error} 104 - </div> 105 - )} 112 + {error && <Text c={"red"}>{error}</Text>} 106 113 107 - <div className="flex gap-3 pt-4"> 108 - <Button type="submit" disabled={loading}> 109 - {loading ? ( 110 - <> 111 - <div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2"></div> 112 - Adding... 113 - </> 114 - ) : ( 115 - "Add Card" 116 - )} 117 - </Button> 118 - <Button 119 - type="button" 120 - variant="outline" 121 - onClick={() => router.back()} 122 - disabled={loading} 123 - > 124 - Cancel 125 - </Button> 126 - </div> 127 - </form> 128 - </CardContent> 129 - </Card> 130 - </div> 114 + <Group> 115 + <Button type="submit" loading={loading}> 116 + {loading ? "Adding..." : "Add Card"} 117 + </Button> 118 + <Button 119 + type="button" 120 + variant="outline" 121 + onClick={() => router.back()} 122 + disabled={loading} 123 + > 124 + Cancel 125 + </Button> 126 + </Group> 127 + </Stack> 128 + </form> 129 + </Stack> 130 + </Stack> 131 + </Card> 132 + </Stack> 133 + </Box> 131 134 ); 132 135 }
+101 -99
src/webapp/app/(authenticated)/collections/[collectionId]/page.tsx
··· 1 1 "use client"; 2 2 3 - import { Button } from "@/components/ui/button"; 4 - import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; 5 - import { Badge } from "@/components/ui/badge"; 6 3 import { useEffect, useState } from "react"; 7 4 import { useRouter, useParams } from "next/navigation"; 8 5 import { getAccessToken } from "@/services/auth"; 9 6 import { ApiClient } from "@/api-client/ApiClient"; 10 7 import { UrlCard } from "@/components/UrlCard"; 11 8 import type { GetCollectionPageResponse } from "@/api-client/types"; 9 + import { 10 + Button, 11 + Group, 12 + Loader, 13 + Stack, 14 + Text, 15 + Card, 16 + Title, 17 + Box, 18 + SimpleGrid, 19 + } from "@mantine/core"; 12 20 13 21 export default function CollectionPage() { 14 - const [collection, setCollection] = useState<GetCollectionPageResponse | null>(null); 22 + const [collection, setCollection] = 23 + useState<GetCollectionPageResponse | null>(null); 15 24 const [loading, setLoading] = useState(true); 16 25 const [error, setError] = useState(""); 17 26 const router = useRouter(); ··· 21 30 // Create API client instance 22 31 const apiClient = new ApiClient( 23 32 process.env.NEXT_PUBLIC_API_BASE_URL || "http://localhost:3000", 24 - () => getAccessToken() 33 + () => getAccessToken(), 25 34 ); 26 35 27 36 useEffect(() => { 28 37 const fetchCollection = async () => { 29 38 try { 30 39 setLoading(true); 31 - const response = await apiClient.getCollectionPage(collectionId, { limit: 50 }); 40 + const response = await apiClient.getCollectionPage(collectionId, { 41 + limit: 50, 42 + }); 32 43 setCollection(response); 33 44 } catch (error: any) { 34 45 console.error("Error fetching collection:", error); ··· 44 55 }, [collectionId]); 45 56 46 57 if (loading) { 47 - return ( 48 - <div className="flex justify-center items-center h-64"> 49 - <div className="animate-spin rounded-full h-10 w-10 border-b-2 border-blue-500"></div> 50 - </div> 51 - ); 58 + return <Loader />; 52 59 } 53 60 54 61 if (error || !collection) { 55 62 return ( 56 - <div className="text-center py-12"> 57 - <p className="text-red-600 mb-4">{error || "Collection not found"}</p> 63 + <Stack align="center"> 64 + <Text c={"red"}>{error || "Collection not found"}</Text> 58 65 <Button onClick={() => router.back()}>Go Back</Button> 59 - </div> 66 + </Stack> 60 67 ); 61 68 } 62 69 63 70 return ( 64 - <div className="space-y-6"> 65 - <div className="flex justify-between items-start"> 66 - <Button variant="outline" onClick={() => router.back()}> 67 - ← Back 68 - </Button> 69 - <div className="flex gap-2"> 70 - <Button variant="outline" onClick={() => router.push(`/collections/${collectionId}/edit`)}> 71 - Edit Collection 72 - </Button> 73 - <Button onClick={() => router.push("/cards/add")}> 74 - Add Card 71 + <Box> 72 + <Stack> 73 + <Group justify="space-between"> 74 + <Button variant="outline" onClick={() => router.back()}> 75 + ← Back 75 76 </Button> 76 - </div> 77 - </div> 77 + <Group> 78 + <Button 79 + variant="outline" 80 + onClick={() => router.push(`/collections/${collectionId}/edit`)} 81 + > 82 + Edit Collection 83 + </Button> 84 + <Button onClick={() => router.push("/cards/add")}>Add Card</Button> 85 + </Group> 86 + </Group> 78 87 79 - {/* Collection Header */} 80 - <Card> 81 - <CardHeader> 82 - <div className="flex justify-between items-start"> 83 - <div className="flex-1"> 84 - <div className="flex items-center gap-3 mb-2"> 85 - <CardTitle className="text-3xl">{collection.name}</CardTitle> 86 - </div> 87 - {collection.description && ( 88 - <CardDescription className="text-base leading-relaxed"> 89 - {collection.description} 90 - </CardDescription> 91 - )} 92 - </div> 93 - </div> 94 - </CardHeader> 95 - <CardContent> 96 - <div className="flex items-center gap-6 text-sm text-gray-500"> 97 - <div> 98 - <span className="font-medium">{collection.urlCards.length}</span> cards 99 - </div> 100 - <div> 88 + {/* Collection Header */} 89 + <Card withBorder> 90 + <Stack> 91 + <Text fw={600} lineClamp={1}> 92 + {collection.name} 93 + </Text> 94 + {collection.description && ( 95 + <Text lineClamp={2}>{collection.description}</Text> 96 + )} 97 + </Stack> 98 + 99 + <Group> 100 + <Text fz={"sm"} c={"gray"}> 101 + {collection.urlCards.length} cards 102 + </Text> 103 + <Text fz={"sm"} c={"gray"}> 101 104 By {collection.author.name} (@{collection.author.handle}) 102 - </div> 103 - </div> 104 - </CardContent> 105 - </Card> 105 + </Text> 106 + </Group> 107 + </Card> 106 108 107 - {/* Cards Section */} 108 - <div> 109 - <div className="flex justify-between items-center mb-6"> 110 - <h2 className="text-2xl font-semibold">Cards</h2> 111 - {collection.pagination && collection.pagination.totalCount > 0 && ( 112 - <p className="text-sm text-gray-500"> 113 - Showing {collection.urlCards.length} of {collection.pagination.totalCount} cards 114 - </p> 115 - )} 116 - </div> 109 + {/* Cards Section */} 110 + <Stack> 111 + <Group justify="space-between"> 112 + <Title order={2}>Cards</Title> 113 + {collection.pagination && collection.pagination.totalCount > 0 && ( 114 + <Text fz={"sm"} c={"gray"}> 115 + Showing {collection.urlCards.length} of{" "} 116 + {collection.pagination.totalCount} cards 117 + </Text> 118 + )} 119 + </Group> 117 120 118 - {collection.urlCards.length > 0 ? ( 119 - <> 120 - <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3"> 121 - {collection.urlCards.map((card) => ( 122 - <UrlCard 123 - key={card.id} 124 - cardId={card.id} 125 - url={card.url} 126 - title={card.cardContent.title} 127 - description={card.cardContent.description} 128 - author={card.cardContent.author} 129 - imageUrl={card.cardContent.thumbnailUrl} 130 - addedAt={card.createdAt} 131 - note={card.note?.text} 132 - /> 133 - ))} 134 - </div> 121 + {collection.urlCards.length > 0 ? ( 122 + <Box> 123 + <SimpleGrid cols={{ base: 1, sm: 2, lg: 3 }} spacing={"md"}> 124 + {collection.urlCards.map((card) => ( 125 + <UrlCard 126 + key={card.id} 127 + cardId={card.id} 128 + url={card.url} 129 + title={card.cardContent.title} 130 + description={card.cardContent.description} 131 + author={card.cardContent.author} 132 + imageUrl={card.cardContent.thumbnailUrl} 133 + addedAt={card.createdAt} 134 + note={card.note?.text} 135 + /> 136 + ))} 137 + </SimpleGrid> 135 138 136 - {/* Pagination */} 137 - {collection.pagination && collection.pagination.hasMore && ( 138 - <div className="text-center mt-8"> 139 - <Button variant="outline"> 140 - Load More Cards 141 - </Button> 142 - </div> 143 - )} 144 - </> 145 - ) : ( 146 - <div className="text-center py-12 bg-gray-50 rounded-lg"> 147 - <p className="text-gray-500 mb-4">No cards in this collection yet</p> 148 - <Button onClick={() => router.push("/cards/add")}> 149 - Add Your First Card 150 - </Button> 151 - </div> 152 - )} 153 - </div> 154 - </div> 139 + {/* Pagination */} 140 + {collection.pagination && collection.pagination.hasMore && ( 141 + <Stack align="center"> 142 + <Button variant="outline">Load More Cards</Button> 143 + </Stack> 144 + )} 145 + </Box> 146 + ) : ( 147 + <Stack align="center"> 148 + <Text c={"gray"}>No cards in this collection yet</Text> 149 + <Button onClick={() => router.push("/cards/add")}> 150 + Add Your First Card 151 + </Button> 152 + </Stack> 153 + )} 154 + </Stack> 155 + </Stack> 156 + </Box> 155 157 ); 156 158 }
+87 -80
src/webapp/app/(authenticated)/collections/create/page.tsx
··· 1 1 "use client"; 2 2 3 - import { Button } from "@/components/ui/button"; 4 - import { Input } from "@/components/ui/input"; 5 - import { Textarea } from "@/components/ui/textarea"; 6 - import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; 7 - import { Label } from "@/components/ui/label"; 8 3 import { useState } from "react"; 9 4 import { useRouter } from "next/navigation"; 10 5 import { getAccessToken } from "@/services/auth"; 11 6 import { ApiClient } from "@/api-client/ApiClient"; 7 + import { 8 + Stack, 9 + Title, 10 + Text, 11 + Container, 12 + Card, 13 + Button, 14 + TextInput, 15 + Textarea, 16 + Group, 17 + Alert, 18 + } from "@mantine/core"; 19 + import { useForm } from "@mantine/form"; 12 20 13 21 export default function CreateCollectionPage() { 14 - const [name, setName] = useState(""); 15 - const [description, setDescription] = useState(""); 22 + const form = useForm({ 23 + initialValues: { 24 + name: "", 25 + description: "", 26 + }, 27 + }); 28 + 16 29 const [loading, setLoading] = useState(false); 17 30 const [error, setError] = useState(""); 18 31 const router = useRouter(); ··· 20 33 // Create API client instance 21 34 const apiClient = new ApiClient( 22 35 process.env.NEXT_PUBLIC_API_BASE_URL || "http://localhost:3000", 23 - () => getAccessToken() 36 + () => getAccessToken(), 24 37 ); 25 38 26 39 const handleSubmit = async (e: React.FormEvent) => { 27 40 e.preventDefault(); 28 - 29 - if (!name.trim()) { 41 + 42 + if (!form.getValues().name.trim()) { 30 43 setError("Collection name is required"); 31 44 return; 32 45 } ··· 36 49 37 50 try { 38 51 await apiClient.createCollection({ 39 - name: name.trim(), 40 - description: description.trim() || undefined, 52 + name: form.getValues().name.trim(), 53 + description: form.getValues().description.trim() || undefined, 41 54 }); 42 55 43 56 // Redirect to collections page on success 44 57 router.push("/collections"); 45 58 } catch (error: any) { 46 59 console.error("Error creating collection:", error); 47 - setError(error.message || "Failed to create collection. Please try again."); 60 + setError( 61 + error.message || "Failed to create collection. Please try again.", 62 + ); 48 63 } finally { 49 64 setLoading(false); 50 65 } 51 66 }; 52 67 53 68 return ( 54 - <div className="max-w-2xl mx-auto"> 55 - <div className="mb-6"> 56 - <h1 className="text-3xl font-bold">Create Collection</h1> 57 - <p className="text-gray-600 mt-2"> 58 - Create a new collection to organize your cards. 59 - </p> 60 - </div> 69 + <Container> 70 + <Stack> 71 + <Stack gap={0}> 72 + <Title order={1}>Create Collection</Title> 73 + <Text c={"gray"}> 74 + Create a new collection to organize your cards. 75 + </Text> 76 + </Stack> 61 77 62 - <Card> 63 - <CardHeader> 64 - <CardTitle>Collection Details</CardTitle> 65 - </CardHeader> 66 - <CardContent> 67 - <form onSubmit={handleSubmit} className="space-y-4"> 68 - <div className="space-y-2"> 69 - <Label htmlFor="name">Name *</Label> 70 - <Input 71 - id="name" 72 - type="text" 73 - placeholder="Enter collection name" 74 - value={name} 75 - onChange={(e) => setName(e.target.value)} 76 - disabled={loading} 77 - required 78 - maxLength={100} 79 - /> 80 - </div> 78 + <Card withBorder> 79 + <Stack> 80 + <Title order={3}>Collection Details</Title> 81 + 82 + <Stack> 83 + <form onSubmit={handleSubmit}> 84 + <Stack> 85 + <Stack> 86 + <TextInput 87 + id="name" 88 + label="Name" 89 + type="text" 90 + placeholder="Enter collection name" 91 + disabled={loading} 92 + required 93 + maxLength={100} 94 + key={form.key("name")} 95 + {...form.getInputProps("name")} 96 + /> 81 97 82 - <div className="space-y-2"> 83 - <Label htmlFor="description">Description (optional)</Label> 84 - <Textarea 85 - id="description" 86 - placeholder="Describe what this collection is about..." 87 - value={description} 88 - onChange={(e) => setDescription(e.target.value)} 89 - disabled={loading} 90 - rows={3} 91 - maxLength={500} 92 - /> 93 - </div> 98 + <Textarea 99 + id="description" 100 + label="Description" 101 + placeholder="Describe what this collection is about..." 102 + disabled={loading} 103 + rows={3} 104 + maxLength={500} 105 + key={form.key("description")} 106 + {...form.getInputProps("description")} 107 + /> 108 + </Stack> 94 109 95 - {error && ( 96 - <div className="text-red-600 text-sm bg-red-50 p-3 rounded-md"> 97 - {error} 98 - </div> 99 - )} 110 + {error && <Alert color="red" title={error} />} 100 111 101 - <div className="flex gap-3 pt-4"> 102 - <Button type="submit" disabled={loading}> 103 - {loading ? ( 104 - <> 105 - <div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2"></div> 106 - Creating... 107 - </> 108 - ) : ( 109 - "Create Collection" 110 - )} 111 - </Button> 112 - <Button 113 - type="button" 114 - variant="outline" 115 - onClick={() => router.back()} 116 - disabled={loading} 117 - > 118 - Cancel 119 - </Button> 120 - </div> 121 - </form> 122 - </CardContent> 123 - </Card> 124 - </div> 112 + <Group> 113 + <Button type="submit" loading={loading}> 114 + {loading ? "Creating..." : "Create Collection"} 115 + </Button> 116 + <Button 117 + type="button" 118 + variant="outline" 119 + onClick={() => router.back()} 120 + disabled={loading} 121 + > 122 + Cancel 123 + </Button> 124 + </Group> 125 + </Stack> 126 + </form> 127 + </Stack> 128 + </Stack> 129 + </Card> 130 + </Stack> 131 + </Container> 125 132 ); 126 133 }
+68 -71
src/webapp/app/(authenticated)/collections/page.tsx
··· 1 1 "use client"; 2 2 3 - import { Button } from "@/components/ui/button"; 4 - import { 5 - Card, 6 - CardContent, 7 - CardDescription, 8 - CardHeader, 9 - CardTitle, 10 - } from "@/components/ui/card"; 11 - import { Badge } from "@/components/ui/badge"; 12 3 import { useEffect, useState } from "react"; 13 4 import { useRouter } from "next/navigation"; 14 5 import { getAccessToken } from "@/services/auth"; 15 6 import { ApiClient } from "@/api-client/ApiClient"; 16 7 import type { GetMyCollectionsResponse } from "@/api-client/types"; 8 + import { 9 + Box, 10 + Button, 11 + Card, 12 + Container, 13 + Group, 14 + Loader, 15 + SimpleGrid, 16 + Stack, 17 + Text, 18 + Title, 19 + } from "@mantine/core"; 17 20 18 21 export default function CollectionsPage() { 19 22 const [collections, setCollections] = useState< ··· 26 29 // Create API client instance 27 30 const apiClient = new ApiClient( 28 31 process.env.NEXT_PUBLIC_API_BASE_URL || "http://localhost:3000", 29 - () => getAccessToken() 32 + () => getAccessToken(), 30 33 ); 31 34 32 35 useEffect(() => { ··· 47 50 }, []); 48 51 49 52 if (loading) { 50 - return ( 51 - <div className="flex justify-center items-center h-64"> 52 - <div className="animate-spin rounded-full h-10 w-10 border-b-2 border-blue-500"></div> 53 - </div> 54 - ); 53 + return <Loader />; 55 54 } 56 55 57 56 return ( 58 - <div className="space-y-6"> 59 - <div className="flex justify-between items-center"> 60 - <div> 61 - <h1 className="text-3xl font-bold">Collections</h1> 62 - <p className="text-gray-600 mt-2"> 63 - Organize your cards into collections 64 - </p> 65 - </div> 66 - <Button onClick={() => router.push("/collections/create")}> 67 - Create Collection 68 - </Button> 69 - </div> 57 + <Box> 58 + <Stack> 59 + <Group justify="space-between"> 60 + <Stack gap={0}> 61 + <Title order={1}>Collections</Title> 62 + <Text c={"gray"}>Organize your cards into collections</Text> 63 + </Stack> 64 + <Button onClick={() => router.push("/collections/create")}> 65 + Create Collection 66 + </Button> 67 + </Group> 68 + 69 + {error && <Text c="red">{error}</Text>} 70 70 71 - {error && ( 72 - <div className="text-red-600 text-sm bg-red-50 p-3 rounded-md"> 73 - {error} 74 - </div> 75 - )} 71 + {collections.length > 0 ? ( 72 + <SimpleGrid cols={{ base: 1, sm: 2, lg: 3 }} spacing={"md"}> 73 + {collections.map((collection) => ( 74 + <Card 75 + key={collection.id} 76 + component="button" 77 + withBorder 78 + onClick={() => router.push(`/collections/${collection.id}`)} 79 + > 80 + <Stack> 81 + <Stack align="start"> 82 + <Text fw={600} lineClamp={1}> 83 + {collection.name} 84 + </Text> 76 85 77 - {collections.length > 0 ? ( 78 - <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3"> 79 - {collections.map((collection) => ( 80 - <Card 81 - key={collection.id} 82 - className="cursor-pointer hover:shadow-md transition-shadow" 83 - onClick={() => router.push(`/collections/${collection.id}`)} 84 - > 85 - <CardHeader> 86 - <div className="flex justify-between items-start"> 87 - <CardTitle className="text-lg">{collection.name}</CardTitle> 88 - </div> 89 - {collection.description && ( 90 - <CardDescription className="line-clamp-2"> 91 - {collection.description} 92 - </CardDescription> 93 - )} 94 - </CardHeader> 95 - <CardContent> 96 - <div className="flex justify-between items-center text-sm text-gray-500"> 97 - <span>{collection.cardCount} cards</span> 98 - <span> 99 - Created{" "} 100 - {new Date(collection.createdAt).toLocaleDateString()} 101 - </span> 102 - </div> 103 - </CardContent> 104 - </Card> 105 - ))} 106 - </div> 107 - ) : ( 108 - <div className="text-center py-12 bg-gray-50 rounded-lg"> 109 - <p className="text-gray-500 mb-4">No collections yet</p> 110 - <Button onClick={() => router.push("/collections/create")}> 111 - Create Your First Collection 112 - </Button> 113 - </div> 114 - )} 115 - </div> 86 + {collection.description && ( 87 + <Text lineClamp={2}>{collection.description}</Text> 88 + )} 89 + </Stack> 90 + <Stack> 91 + <Group justify="space-between"> 92 + <Text c={"gray"}>{collection.cardCount} cards</Text> 93 + <Text c={"gray"}> 94 + Created{" "} 95 + {new Date(collection.createdAt).toLocaleDateString()} 96 + </Text> 97 + </Group> 98 + </Stack> 99 + </Stack> 100 + </Card> 101 + ))} 102 + </SimpleGrid> 103 + ) : ( 104 + <Stack align="center"> 105 + <Text>No collections yet</Text> 106 + <Button onClick={() => router.push("/collections/create")}> 107 + Create Your First Collection 108 + </Button> 109 + </Stack> 110 + )} 111 + </Stack> 112 + </Box> 116 113 ); 117 114 }
+63 -21
src/webapp/app/(authenticated)/layout.tsx
··· 1 1 "use client"; 2 2 3 - import { Sidebar } from "@/components/Sidebar"; 4 - import { useEffect, useState } from "react"; 5 - import { useRouter } from "next/navigation"; 3 + import { useEffect } from "react"; 4 + import { usePathname, useRouter } from "next/navigation"; 6 5 import { useAuth } from "@/hooks/useAuth"; 6 + import { useDisclosure, useMediaQuery } from "@mantine/hooks"; 7 + import { ActionIcon, AppShell, Group, NavLink, Text } from "@mantine/core"; 8 + import { FiSidebar } from "react-icons/fi"; 9 + import { IoDocumentTextOutline } from "react-icons/io5"; 10 + import { BsFolder2 } from "react-icons/bs"; 11 + import { BiUser } from "react-icons/bi"; 7 12 8 13 export default function AuthenticatedLayout({ 9 14 children, ··· 13 18 const { isAuthenticated, isLoading } = useAuth(); 14 19 const router = useRouter(); 15 20 21 + const [mobileOpened, { toggle: toggleMobile }] = useDisclosure(); 22 + const [desktopOpened, { toggle: toggleDesktop }] = useDisclosure(true); 23 + const isMobile = useMediaQuery("(max-width: 768px)"); 24 + 25 + const pathname = usePathname(); 26 + 16 27 useEffect(() => { 17 - // Check if user is authenticated 18 28 if (!isLoading && !isAuthenticated) { 19 29 router.push("/login"); 20 30 } 21 31 }, [isAuthenticated, isLoading, router]); 22 32 23 - if (isLoading) { 24 - return ( 25 - <div className="flex min-h-screen flex-col items-center justify-center"> 26 - <div className="animate-spin rounded-full h-10 w-10 border-b-2 border-blue-500"></div> 27 - </div> 28 - ); 29 - } 30 - 31 33 if (!isAuthenticated) { 32 - return null; // Will redirect in the useEffect 34 + return null; // Redirecting 33 35 } 34 36 35 37 return ( 36 - <div className="flex min-h-screen"> 37 - <Sidebar /> 38 - <main className="flex-1 p-8"> 39 - <div className="max-w-4xl mx-auto"> 40 - {children} 41 - </div> 42 - </main> 43 - </div> 38 + <AppShell 39 + header={{ height: 60 }} 40 + navbar={{ 41 + width: 300, 42 + breakpoint: "sm", 43 + collapsed: { mobile: !mobileOpened, desktop: !desktopOpened }, 44 + }} 45 + padding="md" 46 + > 47 + <AppShell.Header> 48 + <Group h="100%" px="md" gap={"xs"}> 49 + <ActionIcon 50 + variant="subtle" 51 + size="lg" 52 + onClick={() => { 53 + isMobile ? toggleMobile() : toggleDesktop(); 54 + }} 55 + > 56 + <FiSidebar /> 57 + </ActionIcon> 58 + <Text fw={600}>Annos</Text> 59 + </Group> 60 + </AppShell.Header> 61 + 62 + <AppShell.Navbar p="md"> 63 + <NavLink 64 + href="/library" 65 + label="My cards" 66 + active={pathname === "/library"} 67 + leftSection={<IoDocumentTextOutline />} 68 + /> 69 + <NavLink 70 + href="/collections" 71 + label="My collections" 72 + active={pathname === "/collections"} 73 + leftSection={<BsFolder2 />} 74 + /> 75 + <NavLink 76 + href="/profile" 77 + label="Profile" 78 + active={pathname === "/profile"} 79 + leftSection={<BiUser />} 80 + mt="auto" 81 + /> 82 + </AppShell.Navbar> 83 + 84 + <AppShell.Main>{children}</AppShell.Main> 85 + </AppShell> 44 86 ); 45 87 }
+49 -64
src/webapp/app/(authenticated)/library/page.tsx
··· 1 1 "use client"; 2 2 3 - import { Button } from "@/components/ui/button"; 4 3 import { useEffect, useState } from "react"; 5 4 import { useRouter } from "next/navigation"; 6 5 import { authService } from "@/services/api"; 7 - import { getAccessToken, clearAuth } from "@/services/auth"; 6 + import { getAccessToken } from "@/services/auth"; 8 7 import { ApiClient } from "@/api-client/ApiClient"; 9 8 import { UrlCard } from "@/components/UrlCard"; 10 9 import type { GetMyUrlCardsResponse } from "@/api-client/types"; 10 + import { 11 + Button, 12 + Group, 13 + Loader, 14 + SimpleGrid, 15 + Stack, 16 + Title, 17 + Text, 18 + } from "@mantine/core"; 11 19 12 20 export default function DashboardPage() { 13 21 const [user, setUser] = useState<any>(null); ··· 19 27 // Create API client instance 20 28 const apiClient = new ApiClient( 21 29 process.env.NEXT_PUBLIC_API_BASE_URL || "http://localhost:3000", 22 - () => getAccessToken() 30 + () => getAccessToken(), 23 31 ); 24 32 25 33 useEffect(() => { ··· 45 53 46 54 fetchData(); 47 55 }, []); 48 - 49 - const handleLogout = async () => { 50 - try { 51 - const refreshToken = localStorage.getItem("refreshToken"); 52 - if (refreshToken) { 53 - await authService.logout(refreshToken); 54 - } 55 - } catch (error) { 56 - console.error("Error during logout:", error); 57 - } finally { 58 - // Clear auth tokens regardless of API success 59 - clearAuth(); 60 - router.push("/"); 61 - } 62 - }; 63 56 64 57 if (loading) { 65 - return ( 66 - <div className="flex justify-center items-center h-64"> 67 - <div className="animate-spin rounded-full h-10 w-10 border-b-2 border-blue-500"></div> 68 - </div> 69 - ); 58 + return <Loader />; 70 59 } 71 60 72 61 return ( 73 - <> 74 - <div className="space-y-8"> 75 - {/* Recent Cards Section */} 76 - <div> 77 - <div className="flex justify-between items-center mb-6"> 78 - <h2 className="text-2xl font-semibold">Recent Cards</h2> 79 - <Button variant="outline" onClick={() => router.push("/cards")}> 80 - View All Cards 62 + <div> 63 + {/* Recent Cards Section */} 64 + <Stack> 65 + <Group justify="space-between"> 66 + <Title order={1}>Recent Cards</Title> 67 + <Button variant="outline" onClick={() => router.push("/cards")}> 68 + View All Cards 69 + </Button> 70 + </Group> 71 + 72 + {cardsLoading ? ( 73 + <Loader /> 74 + ) : urlCards.length > 0 ? ( 75 + <SimpleGrid cols={{ base: 1, sm: 2, lg: 3 }} spacing={"md"}> 76 + {urlCards.map((card) => ( 77 + <UrlCard 78 + key={card.id} 79 + cardId={card.id} 80 + url={card.url} 81 + title={card.cardContent.title} 82 + description={card.cardContent.description} 83 + author={card.cardContent.author} 84 + imageUrl={card.cardContent.thumbnailUrl} 85 + addedAt={card.createdAt} 86 + note={card.note?.text} 87 + /> 88 + ))} 89 + </SimpleGrid> 90 + ) : ( 91 + <Stack align="center" gap={"xs"}> 92 + <Text c={"grey"}>No cards yet</Text> 93 + <Button onClick={() => router.push("/cards/add")}> 94 + Add Your First Card 81 95 </Button> 82 - </div> 83 - 84 - {cardsLoading ? ( 85 - <div className="flex justify-center items-center h-32"> 86 - <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500"></div> 87 - </div> 88 - ) : urlCards.length > 0 ? ( 89 - <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3"> 90 - {urlCards.map((card) => ( 91 - <UrlCard 92 - key={card.id} 93 - cardId={card.id} 94 - url={card.url} 95 - title={card.cardContent.title} 96 - description={card.cardContent.description} 97 - author={card.cardContent.author} 98 - imageUrl={card.cardContent.thumbnailUrl} 99 - addedAt={card.createdAt} 100 - note={card.note?.text} 101 - /> 102 - ))} 103 - </div> 104 - ) : ( 105 - <div className="text-center py-12 bg-gray-50 rounded-lg"> 106 - <p className="text-gray-500 mb-4">No cards yet</p> 107 - <Button onClick={() => router.push("/cards/add")}> 108 - Add Your First Card 109 - </Button> 110 - </div> 111 - )} 112 - </div> 113 - </div> 114 - </> 96 + </Stack> 97 + )} 98 + </Stack> 99 + </div> 115 100 ); 116 101 }
+67 -68
src/webapp/app/(authenticated)/profile/page.tsx
··· 1 1 "use client"; 2 2 3 - import { Button } from "@/components/ui/button"; 3 + import { useEffect, useState } from "react"; 4 + import { useRouter } from "next/navigation"; 4 5 import { 6 + Avatar, 7 + Button, 5 8 Card, 6 - CardContent, 7 - CardDescription, 8 - CardHeader, 9 - CardTitle, 10 - } from "@/components/ui/card"; 11 - import { useEffect, useState } from "react"; 12 - import { useRouter } from "next/navigation"; 9 + Code, 10 + Group, 11 + Loader, 12 + Stack, 13 + Text, 14 + Title, 15 + } from "@mantine/core"; 13 16 import { getAccessToken } from "@/services/auth"; 14 17 import { ApiClient } from "@/api-client/ApiClient"; 15 18 import type { GetMyProfileResponse } from "@/api-client/types"; ··· 25 28 // Create API client instance 26 29 const apiClient = new ApiClient( 27 30 process.env.NEXT_PUBLIC_API_BASE_URL || "http://localhost:3000", 28 - () => getAccessToken() 31 + () => getAccessToken(), 29 32 ); 30 33 31 34 useEffect(() => { ··· 55 58 }; 56 59 57 60 if (loading) { 58 - return ( 59 - <div className="flex justify-center items-center h-64"> 60 - <div className="animate-spin rounded-full h-10 w-10 border-b-2 border-blue-500"></div> 61 - </div> 62 - ); 61 + return <Loader />; 63 62 } 64 63 65 64 if (error || !profile) { 66 65 return ( 67 - <div className="text-center py-12"> 68 - <p className="text-red-600 mb-4">{error || "Failed to load profile"}</p> 66 + <Stack align="center"> 67 + <Text fw={500}>{error || "Failed to load profile"}</Text> 69 68 <Button onClick={() => window.location.reload()}>Try Again</Button> 70 - </div> 69 + </Stack> 71 70 ); 72 71 } 73 72 74 73 return ( 75 - <div className="space-y-6"> 76 - <div className="flex justify-between items-start"> 77 - <h1 className="text-3xl font-bold">Profile</h1> 78 - <Button variant="destructive" onClick={handleLogout}> 74 + <Stack> 75 + <Group justify="space-between"> 76 + <Title order={1}>Profile</Title> 77 + <Button color="red" onClick={handleLogout}> 79 78 Logout 80 79 </Button> 81 - </div> 80 + </Group> 82 81 83 82 {/* Profile Information */} 84 - <Card> 85 - <CardHeader> 86 - <div className="flex items-center gap-4"> 83 + <Card withBorder> 84 + <Stack> 85 + <Group> 87 86 {profile.avatarUrl && ( 88 - <img 87 + <Avatar 89 88 src={profile.avatarUrl} 90 89 alt={`${profile.name}'s avatar`} 91 - className="w-16 h-16 rounded-full object-cover" 90 + w={64} 91 + h={64} 92 92 /> 93 93 )} 94 94 <div> 95 - <CardTitle className="text-2xl">{profile.name}</CardTitle> 96 - <CardDescription className="text-lg"> 95 + <Title order={2}>{profile.name}</Title> 96 + <Text fw={500} c="gray"> 97 97 @{profile.handle} 98 - </CardDescription> 98 + </Text> 99 99 </div> 100 - </div> 101 - </CardHeader> 102 - {profile.description && ( 103 - <CardContent> 104 - <p className="text-gray-700 leading-relaxed"> 105 - {profile.description} 106 - </p> 107 - </CardContent> 108 - )} 100 + </Group> 101 + {profile.description && <Text>{profile.description}</Text>} 102 + </Stack> 109 103 </Card> 110 104 111 105 {/* Profile Details */} 112 - <Card> 113 - <CardHeader> 114 - <CardTitle>Account Details</CardTitle> 115 - </CardHeader> 116 - <CardContent className="space-y-4"> 117 - <div> 118 - <label className="text-sm font-medium text-gray-500">User ID</label> 119 - <p className="text-sm font-mono bg-gray-100 p-2 rounded"> 120 - {profile.id} 121 - </p> 122 - </div> 123 - <div> 124 - <label className="text-sm font-medium text-gray-500">Handle</label> 125 - <p className="text-sm">@{profile.handle}</p> 126 - </div> 127 - <div> 128 - <label className="text-sm font-medium text-gray-500"> 129 - Display Name 130 - </label> 131 - <p className="text-sm">{profile.name}</p> 132 - </div> 133 - {profile.description && ( 106 + <Card withBorder> 107 + <Stack> 108 + <Title order={3}>Account Details</Title> 109 + 110 + <Stack> 111 + <div> 112 + <Text fw={500} fz={"sm"} c={"gray"}> 113 + User ID 114 + </Text> 115 + <Code bg={"gray.1"} p={"sm"}> 116 + {profile.id} 117 + </Code> 118 + </div> 119 + <div> 120 + <Text fw={500} fz={"sm"} c={"gray"}> 121 + Handle 122 + </Text> 123 + <Text fz={"sm"}>@{profile.handle}</Text> 124 + </div> 134 125 <div> 135 - <label className="text-sm font-medium text-gray-500"> 136 - Description 137 - </label> 138 - <p className="text-sm">{profile.description}</p> 126 + <Text fw={500} fz={"sm"} c={"gray"}> 127 + Display Name 128 + </Text> 129 + <Text fz={"sm"}>{profile.name}</Text> 139 130 </div> 140 - )} 141 - </CardContent> 131 + {profile.description && ( 132 + <div> 133 + <Text fw={500} fz={"sm"} c={"gray"}> 134 + Description 135 + </Text> 136 + <Text fz={"sm"}>{profile.description}</Text> 137 + </div> 138 + )} 139 + </Stack> 140 + </Stack> 142 141 </Card> 143 - </div> 142 + </Stack> 144 143 ); 145 144 }
+16 -21
src/webapp/app/auth/complete/page.tsx
··· 3 3 import { useEffect, Suspense } from "react"; 4 4 import { useRouter, useSearchParams } from "next/navigation"; 5 5 import { useAuth } from "@/hooks/useAuth"; 6 + import { Card, Center, Loader, Stack, Title, Text } from "@mantine/core"; 6 7 7 8 function AuthCompleteContent() { 8 9 const router = useRouter(); ··· 36 37 }, [router, searchParams, setTokens]); 37 38 38 39 return ( 39 - <div className="z-10 max-w-5xl w-full items-center justify-center font-mono text-sm flex flex-col"> 40 - <div className="bg-white p-8 rounded-lg shadow-md w-full max-w-md text-center"> 41 - <h2 className="text-2xl font-semibold mb-4">Completing Sign In</h2> 42 - <p className="mb-4"> 43 - Please wait while we complete your authentication... 44 - </p> 45 - <div className="flex justify-center"> 46 - <div className="animate-spin rounded-full h-10 w-10 border-b-2 border-blue-500"></div> 47 - </div> 48 - </div> 49 - </div> 40 + <Stack align="center"> 41 + <Stack gap={0} align="center"> 42 + <Title order={2}>Completing Sign In</Title> 43 + <Text>Please wait while we complete your authentication...</Text> 44 + </Stack> 45 + <Loader type="bars" /> 46 + </Stack> 50 47 ); 51 48 } 52 49 53 50 export default function AuthCompletePage() { 54 51 return ( 55 - <main className="flex min-h-screen flex-col items-center justify-center p-24"> 52 + <Center h={"100svh"}> 56 53 <Suspense 57 54 fallback={ 58 - <div className="z-10 max-w-5xl w-full items-center justify-center font-mono text-sm flex flex-col"> 59 - <div className="bg-white p-8 rounded-lg shadow-md w-full max-w-md text-center"> 60 - <h2 className="text-2xl font-semibold mb-4">Loading</h2> 61 - <div className="flex justify-center"> 62 - <div className="animate-spin rounded-full h-10 w-10 border-b-2 border-blue-500"></div> 63 - </div> 64 - </div> 65 - </div> 55 + <Card> 56 + <Stack align="center"> 57 + <Title order={2}>Loading</Title> 58 + <Loader /> 59 + </Stack> 60 + </Card> 66 61 } 67 62 > 68 63 <AuthCompleteContent /> 69 64 </Suspense> 70 - </main> 65 + </Center> 71 66 ); 72 67 }
-76
src/webapp/app/globals.css
··· 1 - @tailwind base; 2 - @tailwind components; 3 - @tailwind utilities; 4 - 5 - @layer base { 6 - :root { 7 - --background: 0 0% 100%; 8 - --foreground: 222.2 84% 4.9%; 9 - 10 - --card: 0 0% 100%; 11 - --card-foreground: 222.2 84% 4.9%; 12 - 13 - --popover: 0 0% 100%; 14 - --popover-foreground: 222.2 84% 4.9%; 15 - 16 - --primary: 222.2 47.4% 11.2%; 17 - --primary-foreground: 210 40% 98%; 18 - 19 - --secondary: 210 40% 96.1%; 20 - --secondary-foreground: 222.2 47.4% 11.2%; 21 - 22 - --muted: 210 40% 96.1%; 23 - --muted-foreground: 215.4 16.3% 46.9%; 24 - 25 - --accent: 210 40% 96.1%; 26 - --accent-foreground: 222.2 47.4% 11.2%; 27 - 28 - --destructive: 0 84.2% 60.2%; 29 - --destructive-foreground: 210 40% 98%; 30 - 31 - --border: 214.3 31.8% 91.4%; 32 - --input: 214.3 31.8% 91.4%; 33 - --ring: 222.2 84% 4.9%; 34 - 35 - --radius: 0.5rem; 36 - } 37 - 38 - .dark { 39 - --background: 222.2 84% 4.9%; 40 - --foreground: 210 40% 98%; 41 - 42 - --card: 222.2 84% 4.9%; 43 - --card-foreground: 210 40% 98%; 44 - 45 - --popover: 222.2 84% 4.9%; 46 - --popover-foreground: 210 40% 98%; 47 - 48 - --primary: 210 40% 98%; 49 - --primary-foreground: 222.2 47.4% 11.2%; 50 - 51 - --secondary: 217.2 32.6% 17.5%; 52 - --secondary-foreground: 210 40% 98%; 53 - 54 - --muted: 217.2 32.6% 17.5%; 55 - --muted-foreground: 215 20.2% 65.1%; 56 - 57 - --accent: 217.2 32.6% 17.5%; 58 - --accent-foreground: 210 40% 98%; 59 - 60 - --destructive: 0 62.8% 30.6%; 61 - --destructive-foreground: 210 40% 98%; 62 - 63 - --border: 217.2 32.6% 17.5%; 64 - --input: 217.2 32.6% 17.5%; 65 - --ring: 212.7 26.8% 83.9%; 66 - } 67 - } 68 - 69 - @layer base { 70 - * { 71 - @apply border-border; 72 - } 73 - body { 74 - @apply bg-background text-foreground; 75 - } 76 - }
+15 -7
src/webapp/app/layout.tsx
··· 1 1 import type { Metadata } from "next"; 2 - import { Inter } from "next/font/google"; 3 - import "./globals.css"; 2 + import { 3 + ColorSchemeScript, 4 + mantineHtmlProps, 5 + MantineProvider, 6 + } from "@mantine/core"; 7 + import "@mantine/core/styles.css"; 4 8 import { AuthProvider } from "@/hooks/useAuth"; 5 - 6 - const inter = Inter({ subsets: ["latin"] }); 9 + import { theme } from "@/styles/theme"; 7 10 8 11 export const metadata: Metadata = { 9 12 title: "Annos", ··· 16 19 children: React.ReactNode; 17 20 }) { 18 21 return ( 19 - <html lang="en"> 20 - <body className={inter.className}> 21 - <AuthProvider>{children}</AuthProvider> 22 + <html lang="en" {...mantineHtmlProps}> 23 + <head> 24 + <ColorSchemeScript /> 25 + </head> 26 + <body> 27 + <MantineProvider theme={theme}> 28 + <AuthProvider>{children}</AuthProvider> 29 + </MantineProvider> 22 30 </body> 23 31 </html> 24 32 );
+89 -92
src/webapp/app/login/page.tsx
··· 1 1 "use client"; 2 2 3 - import { Button } from "@/components/ui/button"; 4 3 import { useState } from "react"; 5 4 import { useRouter } from "next/navigation"; 6 - import { ApiClient } from "@/api-client/ApiClient"; 7 5 import { useAuth } from "@/hooks/useAuth"; 6 + import { ApiClient } from "@/api-client/ApiClient"; 7 + import { 8 + Title, 9 + Button, 10 + Stack, 11 + Center, 12 + Card, 13 + TextInput, 14 + PasswordInput, 15 + Text, 16 + Group, 17 + } from "@mantine/core"; 8 18 9 19 export default function LoginPage() { 10 20 const [handle, setHandle] = useState(""); ··· 18 28 // Create API client instance 19 29 const apiClient = new ApiClient( 20 30 process.env.NEXT_PUBLIC_API_BASE_URL || "http://localhost:3000", 21 - () => null // No auth token needed for login 31 + () => null, // No auth token needed for login 22 32 ); 23 33 24 34 const handleOAuthSubmit = async (e: React.FormEvent) => { ··· 73 83 }; 74 84 75 85 return ( 76 - <main className="flex min-h-screen flex-col items-center justify-center p-24"> 77 - <div className="z-10 max-w-5xl w-full items-center justify-center font-mono text-sm flex flex-col"> 78 - <h1 className="text-4xl font-bold mb-8">Sign in with Bluesky</h1> 86 + <Center h={"100svh"}> 87 + <Stack align="center"> 88 + <Title order={1}>Sign in with Bluesky</Title> 79 89 80 - <div className="bg-white p-8 rounded-lg shadow-md w-full max-w-md"> 90 + <Card withBorder w={400}> 81 91 {!useAppPassword ? ( 82 92 <form onSubmit={handleOAuthSubmit}> 83 - <div className="mb-6"> 84 - <label 85 - htmlFor="handle" 86 - className="block text-sm font-medium text-gray-700 mb-2" 87 - > 88 - Enter your Bluesky handle 89 - </label> 90 - <input 91 - type="text" 92 - id="handle" 93 - className="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" 94 - placeholder="username.bsky.social" 95 - value={handle} 96 - onChange={(e) => setHandle(e.target.value)} 97 - /> 98 - {error && <p className="mt-2 text-sm text-red-600">{error}</p>} 99 - </div> 93 + <Stack> 94 + <Stack> 95 + <TextInput 96 + id="handle" 97 + label="Enter your Bluesky handle" 98 + placeholder="username.bsky.social" 99 + value={handle} 100 + onChange={(e) => setHandle(e.target.value)} 101 + /> 102 + {error && ( 103 + <Text fz={"sm"} c={"red"}> 104 + {error} 105 + </Text> 106 + )} 107 + </Stack> 100 108 101 - <Button 102 - type="submit" 103 - className="w-full mb-4" 104 - disabled={isLoading} 105 - > 106 - {isLoading ? "Connecting..." : "Continue"} 107 - </Button> 109 + <Group grow> 110 + <Button type="submit" loading={isLoading}> 111 + {isLoading ? "Connecting..." : "Continue"} 112 + </Button> 113 + </Group> 108 114 109 - <div className="text-center"> 110 - <button 111 - type="button" 112 - onClick={() => setUseAppPassword(true)} 113 - className="text-sm text-blue-600 hover:text-blue-800 underline" 114 - > 115 - Sign in with app password 116 - </button> 117 - </div> 115 + <Stack> 116 + <Button 117 + type="button" 118 + onClick={() => setUseAppPassword(true)} 119 + variant="transparent" 120 + color="blue" 121 + > 122 + Sign in with app password 123 + </Button> 124 + </Stack> 125 + </Stack> 118 126 </form> 119 127 ) : ( 120 128 <form onSubmit={handleAppPasswordSubmit}> 121 - <div className="mb-4"> 122 - <label 123 - htmlFor="handle-app" 124 - className="block text-sm font-medium text-gray-700 mb-2" 125 - > 126 - Bluesky handle 127 - </label> 128 - <input 129 - type="text" 130 - id="handle-app" 131 - className="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" 132 - placeholder="username.bsky.social" 133 - value={handle} 134 - onChange={(e) => setHandle(e.target.value)} 135 - /> 136 - </div> 129 + <Stack> 130 + <Stack> 131 + <TextInput 132 + id="handle-app" 133 + label="Bluesky handle" 134 + placeholder="username.bsky.social" 135 + value={handle} 136 + onChange={(e) => setHandle(e.target.value)} 137 + /> 137 138 138 - <div className="mb-6"> 139 - <label 140 - htmlFor="app-password" 141 - className="block text-sm font-medium text-gray-700 mb-2" 142 - > 143 - App password 144 - </label> 145 - <input 146 - type="password" 147 - id="app-password" 148 - className="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" 149 - placeholder="xxxx-xxxx-xxxx-xxxx" 150 - value={appPassword} 151 - onChange={(e) => setAppPassword(e.target.value)} 152 - /> 153 - {error && <p className="mt-2 text-sm text-red-600">{error}</p>} 154 - </div> 139 + <Stack> 140 + <PasswordInput 141 + id="app-password" 142 + label="App password" 143 + placeholder="xxxx-xxxx-xxxx-xxxx" 144 + value={appPassword} 145 + onChange={(e) => setAppPassword(e.target.value)} 146 + /> 147 + {error && ( 148 + <Text fz={"sm"} c={"red"}> 149 + {error} 150 + </Text> 151 + )} 152 + </Stack> 153 + </Stack> 155 154 156 - <Button 157 - type="submit" 158 - className="w-full mb-4" 159 - disabled={isLoading} 160 - > 161 - {isLoading ? "Signing in..." : "Sign in"} 162 - </Button> 155 + <Button type="submit" disabled={isLoading} loading={isLoading}> 156 + {isLoading ? "Signing in..." : "Sign in"} 157 + </Button> 163 158 164 - <div className="text-center"> 165 - <button 166 - type="button" 167 - onClick={() => setUseAppPassword(false)} 168 - className="text-sm text-blue-600 hover:text-blue-800 underline" 169 - > 170 - Back to OAuth sign in 171 - </button> 172 - </div> 159 + <Stack> 160 + <Button 161 + type="button" 162 + onClick={() => setUseAppPassword(false)} 163 + variant="transparent" 164 + color="blue" 165 + > 166 + Back to OAuth sign in 167 + </Button> 168 + </Stack> 169 + </Stack> 173 170 </form> 174 171 )} 175 - </div> 176 - </div> 177 - </main> 172 + </Card> 173 + </Stack> 174 + </Center> 178 175 ); 179 176 }
+55 -53
src/webapp/app/oauth/callback/page.tsx
··· 3 3 import { useEffect, useState, Suspense } from "react"; 4 4 import { useRouter, useSearchParams } from "next/navigation"; 5 5 import { useAuth } from "@/hooks/useAuth"; 6 + import { 7 + Button, 8 + Stack, 9 + Title, 10 + Text, 11 + Loader, 12 + Card, 13 + Center, 14 + } from "@mantine/core"; 6 15 7 16 function CallbackContent() { 8 17 const [status, setStatus] = useState<"loading" | "success" | "error">( 9 - "loading" 18 + "loading", 10 19 ); 11 20 const [message, setMessage] = useState("Processing your login..."); 12 21 const router = useRouter(); ··· 49 58 }, [completeOAuth]); 50 59 51 60 return ( 52 - <div className="z-10 max-w-5xl w-full items-center justify-center font-mono text-sm flex flex-col"> 53 - <div className="bg-white p-8 rounded-lg shadow-md w-full max-w-md text-center"> 54 - {status === "loading" && ( 55 - <> 56 - <h2 className="text-2xl font-semibold mb-4"> 57 - Completing Sign In 58 - </h2> 59 - <p className="mb-4"> 60 - Please wait while we complete your authentication... 61 - </p> 62 - <div className="flex justify-center"> 63 - <div className="animate-spin rounded-full h-10 w-10 border-b-2 border-blue-500"></div> 64 - </div> 65 - </> 66 - )} 61 + <Card withBorder shadow="md"> 62 + {status === "loading" && ( 63 + <Stack align="center"> 64 + <Stack gap={0} align="center"> 65 + <Title order={2}>Completing Sign In</Title> 66 + <Text>Please wait while we complete your authentication...</Text> 67 + </Stack> 68 + <Loader type="bars" /> 69 + </Stack> 70 + )} 67 71 68 - {status === "success" && ( 69 - <> 70 - <h2 className="text-2xl font-semibold mb-4 text-green-600"> 71 - Success! 72 - </h2> 73 - <p>{message}</p> 74 - <p className="mt-2">Redirecting you to your dashboard...</p> 75 - </> 76 - )} 72 + {status === "success" && ( 73 + <Stack align="center"> 74 + <Stack gap={0} align="center"> 75 + <Title order={2} c={"green"}> 76 + Success! 77 + </Title> 78 + <Text>{message}</Text> 79 + </Stack> 80 + <Text>Redirecting you to your dashboard...</Text> 81 + </Stack> 82 + )} 77 83 78 - {status === "error" && ( 79 - <> 80 - <h2 className="text-2xl font-semibold mb-4 text-red-600"> 81 - Error 82 - </h2> 83 - <p>{message}</p> 84 - <button 85 - onClick={() => router.push("/login")} 86 - className="mt-4 px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600" 87 - > 88 - Try Again 89 - </button> 90 - </> 91 - )} 92 - </div> 93 - </div> 84 + {status === "error" && ( 85 + <Stack align="center"> 86 + <Stack gap={0} align="center"> 87 + <Title order={2} c={"red"}> 88 + Error 89 + </Title> 90 + <Text>{message}</Text> 91 + </Stack> 92 + <Button onClick={() => router.push("/login")}>Try Again</Button> 93 + </Stack> 94 + )} 95 + </Card> 94 96 ); 95 97 } 96 98 97 99 export default function OAuthCallbackPage() { 98 100 return ( 99 - <main className="flex min-h-screen flex-col items-center justify-center p-24"> 100 - <Suspense fallback={ 101 - <div className="z-10 max-w-5xl w-full items-center justify-center font-mono text-sm flex flex-col"> 102 - <div className="bg-white p-8 rounded-lg shadow-md w-full max-w-md text-center"> 103 - <h2 className="text-2xl font-semibold mb-4">Loading</h2> 104 - <div className="flex justify-center"> 105 - <div className="animate-spin rounded-full h-10 w-10 border-b-2 border-blue-500"></div> 106 - </div> 107 - </div> 108 - </div> 109 - }> 101 + <Center h={"100svh"}> 102 + <Suspense 103 + fallback={ 104 + <Card> 105 + <Stack align="center"> 106 + <Title order={2}>Loading</Title> 107 + <Loader /> 108 + </Stack> 109 + </Card> 110 + } 111 + > 110 112 <CallbackContent /> 111 113 </Suspense> 112 - </main> 114 + </Center> 113 115 ); 114 116 }
+20 -19
src/webapp/app/page.tsx
··· 1 1 "use client"; 2 2 3 - import { Button } from "@/components/ui/button"; 4 - import Link from "next/link"; 5 3 import { useAuth } from "@/hooks/useAuth"; 6 4 import { useEffect } from "react"; 7 5 import { useRouter } from "next/navigation"; 6 + import { Title, Text, Stack, Button, Center } from "@mantine/core"; 7 + import { FaBluesky } from "react-icons/fa6"; 8 8 9 9 export default function Home() { 10 10 const { isAuthenticated, isLoading } = useAuth(); ··· 17 17 }, [isAuthenticated, isLoading, router]); 18 18 19 19 if (isLoading) { 20 - return <div>Loading...</div>; 20 + return <>Loading...</>; 21 21 } 22 22 23 23 return ( 24 - <main className="flex min-h-screen flex-col items-center justify-center p-24"> 25 - <div className="z-10 max-w-5xl w-full items-center justify-center font-mono text-sm flex flex-col"> 26 - <h1 className="text-4xl font-bold mb-8">Welcome to Annos</h1> 27 - <p className="text-xl mb-8">Your annotation platform</p> 24 + <Center h={"100svh"}> 25 + <Stack align="center"> 26 + <Stack align="center" gap={0}> 27 + <Title order={1}>Welcome to Annos</Title> 28 + <Text fw={600} fz={"xl"} c={"dark.4"}> 29 + Your annotation platform 30 + </Text> 31 + </Stack> 28 32 29 - <div className="bg-white p-8 rounded-lg shadow-md w-full max-w-md"> 30 - <h2 className="text-2xl font-semibold mb-6 text-center">Sign In</h2> 31 - <p className="mb-6 text-center text-gray-600"> 33 + <Stack align="center"> 34 + <Text fw={500} c={"gray"}> 32 35 Please sign in to access your annotations 33 - </p> 34 - <div className="flex justify-center"> 35 - <Link href="/login" className="w-full"> 36 - <Button className="w-full">Sign in with Bluesky</Button> 37 - </Link> 38 - </div> 39 - </div> 40 - </div> 41 - </main> 36 + </Text> 37 + <Button component="a" href="/login" leftSection={<FaBluesky />}> 38 + Sign in with Bluesky 39 + </Button> 40 + </Stack> 41 + </Stack> 42 + </Center> 42 43 ); 43 44 }
+102 -101
src/webapp/components/AddToCollectionModal.tsx
··· 1 1 "use client"; 2 2 3 3 import { useState, useEffect } from "react"; 4 - import { Button } from "@/components/ui/button"; 5 - import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; 6 - import { Checkbox } from "@/components/ui/checkbox"; 7 4 import { getAccessToken } from "@/services/auth"; 8 5 import { ApiClient } from "@/api-client/ApiClient"; 9 6 import type { GetMyCollectionsResponse } from "@/api-client/types"; 7 + import { 8 + Box, 9 + Button, 10 + Center, 11 + Checkbox, 12 + Group, 13 + Loader, 14 + Modal, 15 + ScrollArea, 16 + Stack, 17 + Text, 18 + } from "@mantine/core"; 10 19 11 20 interface AddToCollectionModalProps { 12 21 cardId: string; ··· 15 24 onSuccess?: () => void; 16 25 } 17 26 18 - export function AddToCollectionModal({ cardId, isOpen, onClose, onSuccess }: AddToCollectionModalProps) { 19 - const [collections, setCollections] = useState<GetMyCollectionsResponse["collections"]>([]); 20 - const [selectedCollections, setSelectedCollections] = useState<Set<string>>(new Set()); 27 + export function AddToCollectionModal({ 28 + cardId, 29 + isOpen, 30 + onClose, 31 + onSuccess, 32 + }: AddToCollectionModalProps) { 33 + const [collections, setCollections] = useState< 34 + GetMyCollectionsResponse["collections"] 35 + >([]); 36 + const [selectedCollections, setSelectedCollections] = useState<Set<string>>( 37 + new Set(), 38 + ); 21 39 const [loading, setLoading] = useState(false); 22 40 const [submitting, setSubmitting] = useState(false); 23 41 const [error, setError] = useState(""); ··· 25 43 // Create API client instance 26 44 const apiClient = new ApiClient( 27 45 process.env.NEXT_PUBLIC_API_BASE_URL || "http://localhost:3000", 28 - () => getAccessToken() 46 + () => getAccessToken(), 29 47 ); 30 48 31 49 useEffect(() => { ··· 69 87 70 88 try { 71 89 // Add card to all selected collections in a single request 72 - await apiClient.addCardToCollection({ 73 - cardId, 74 - collectionIds: Array.from(selectedCollections) 90 + await apiClient.addCardToCollection({ 91 + cardId, 92 + collectionIds: Array.from(selectedCollections), 75 93 }); 76 94 77 95 // Success ··· 97 115 if (!isOpen) return null; 98 116 99 117 return ( 100 - <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4"> 101 - <Card className="w-full max-w-md max-h-[80vh] overflow-hidden"> 102 - <CardHeader> 103 - <div className="flex justify-between items-center"> 104 - <CardTitle>Add to Collections</CardTitle> 118 + <Modal 119 + opened={isOpen} 120 + onClose={onClose} 121 + title="Add to Collections" 122 + centered 123 + > 124 + <Stack p={"sm"}> 125 + {loading ? ( 126 + <Center> 127 + <Loader /> 128 + </Center> 129 + ) : error && collections.length === 0 ? ( 130 + <Stack align="center"> 131 + <Text c={"red"}>{error}</Text> 132 + <Button onClick={fetchCollections} variant="outline" size="sm"> 133 + Try Again 134 + </Button> 135 + </Stack> 136 + ) : collections.length === 0 ? ( 137 + <Stack align="center"> 138 + <Text c={"red"}>No collections found</Text> 105 139 <Button 106 - variant="ghost" 140 + onClick={() => window.open("/collections/create", "_blank")} 141 + variant="outline" 107 142 size="sm" 108 - onClick={handleClose} 109 - disabled={submitting} 110 143 > 111 - 144 + Create Collection 112 145 </Button> 113 - </div> 114 - </CardHeader> 115 - <CardContent className="space-y-4"> 116 - {loading ? ( 117 - <div className="flex justify-center items-center py-8"> 118 - <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500"></div> 119 - </div> 120 - ) : error && collections.length === 0 ? ( 121 - <div className="text-center py-8"> 122 - <p className="text-red-600 mb-4">{error}</p> 123 - <Button onClick={fetchCollections} variant="outline" size="sm"> 124 - Try Again 146 + </Stack> 147 + ) : ( 148 + <Box> 149 + <ScrollArea h={250}> 150 + {collections.map((collection) => ( 151 + <Group key={collection.id}> 152 + <Checkbox 153 + id={collection.id} 154 + checked={selectedCollections.has(collection.id)} 155 + onChange={() => handleCollectionToggle(collection.id)} 156 + disabled={submitting} 157 + /> 158 + 159 + <Stack gap={"0"}> 160 + <Text fw={500}>{collection.name}</Text> 161 + {collection.description && ( 162 + <Text fz={"sm"} fw={500} c={"gray"} truncate="end"> 163 + {collection.description} 164 + </Text> 165 + )} 166 + <Text fz={"sm"} fw={500} c={"gray.5"}> 167 + {collection.cardCount} cards 168 + </Text> 169 + </Stack> 170 + </Group> 171 + ))} 172 + </ScrollArea> 173 + 174 + {error && <Text c={"red"}>{error}</Text>} 175 + 176 + <Group grow> 177 + <Button 178 + onClick={handleSubmit} 179 + disabled={submitting || selectedCollections.size === 0} 180 + loading={submitting} 181 + > 182 + {submitting 183 + ? "Adding..." 184 + : `Add to ${selectedCollections.size} Collection${selectedCollections.size !== 1 ? "s" : ""}`} 125 185 </Button> 126 - </div> 127 - ) : collections.length === 0 ? ( 128 - <div className="text-center py-8"> 129 - <p className="text-gray-500 mb-4">No collections found</p> 130 - <Button onClick={() => window.open('/collections/create', '_blank')} variant="outline" size="sm"> 131 - Create Collection 186 + <Button 187 + variant="outline" 188 + onClick={handleClose} 189 + disabled={submitting} 190 + > 191 + Cancel 132 192 </Button> 133 - </div> 134 - ) : ( 135 - <> 136 - <div className="max-h-60 overflow-y-auto space-y-2"> 137 - {collections.map((collection) => ( 138 - <div key={collection.id} className="flex items-center space-x-3 p-2 hover:bg-gray-50 rounded"> 139 - <Checkbox 140 - id={collection.id} 141 - checked={selectedCollections.has(collection.id)} 142 - onCheckedChange={() => handleCollectionToggle(collection.id)} 143 - disabled={submitting} 144 - /> 145 - <label 146 - htmlFor={collection.id} 147 - className="flex-1 cursor-pointer" 148 - > 149 - <div className="font-medium">{collection.name}</div> 150 - {collection.description && ( 151 - <div className="text-sm text-gray-500 truncate"> 152 - {collection.description} 153 - </div> 154 - )} 155 - <div className="text-xs text-gray-400"> 156 - {collection.cardCount} cards 157 - </div> 158 - </label> 159 - </div> 160 - ))} 161 - </div> 162 - 163 - {error && ( 164 - <div className="text-red-600 text-sm bg-red-50 p-3 rounded-md"> 165 - {error} 166 - </div> 167 - )} 168 - 169 - <div className="flex gap-3 pt-4"> 170 - <Button 171 - onClick={handleSubmit} 172 - disabled={submitting || selectedCollections.size === 0} 173 - className="flex-1" 174 - > 175 - {submitting ? ( 176 - <> 177 - <div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2"></div> 178 - Adding... 179 - </> 180 - ) : ( 181 - `Add to ${selectedCollections.size} Collection${selectedCollections.size !== 1 ? 's' : ''}` 182 - )} 183 - </Button> 184 - <Button 185 - variant="outline" 186 - onClick={handleClose} 187 - disabled={submitting} 188 - > 189 - Cancel 190 - </Button> 191 - </div> 192 - </> 193 - )} 194 - </CardContent> 195 - </Card> 196 - </div> 193 + </Group> 194 + </Box> 195 + )} 196 + </Stack> 197 + </Modal> 197 198 ); 198 199 }
+3 -6
src/webapp/components/ProtectedRoute.tsx
··· 3 3 import { useEffect, useState, ReactNode } from "react"; 4 4 import { useRouter } from "next/navigation"; 5 5 import { isAuthenticated } from "@/services/auth"; 6 + import { Loader } from "@mantine/core"; 6 7 7 8 interface ProtectedRouteProps { 8 9 children: ReactNode; ··· 19 20 router.push("/"); 20 21 return; 21 22 } 22 - 23 + 23 24 setIsAuthorized(true); 24 25 setIsLoading(false); 25 26 }, [router]); 26 27 27 28 if (isLoading) { 28 - return ( 29 - <div className="flex min-h-screen flex-col items-center justify-center"> 30 - <div className="animate-spin rounded-full h-10 w-10 border-b-2 border-blue-500"></div> 31 - </div> 32 - ); 29 + return <Loader />; 33 30 } 34 31 35 32 return isAuthorized ? <>{children}</> : null;
-109
src/webapp/components/Sidebar.tsx
··· 1 - "use client"; 2 - 3 - import Link from "next/link"; 4 - import { usePathname } from "next/navigation"; 5 - import { cn } from "@/lib/utils"; 6 - 7 - interface SidebarProps { 8 - className?: string; 9 - } 10 - 11 - export function Sidebar({ className }: SidebarProps) { 12 - const pathname = usePathname(); 13 - 14 - const navItems = [ 15 - { 16 - name: "My Cards", 17 - href: "/library", 18 - icon: ( 19 - <svg 20 - xmlns="http://www.w3.org/2000/svg" 21 - className="h-5 w-5" 22 - viewBox="0 0 24 24" 23 - fill="none" 24 - stroke="currentColor" 25 - strokeWidth="2" 26 - > 27 - <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" /> 28 - <polyline points="14,2 14,8 20,8" /> 29 - <line x1="16" y1="13" x2="8" y2="13" /> 30 - <line x1="16" y1="17" x2="8" y2="17" /> 31 - <polyline points="10,9 9,9 8,9" /> 32 - </svg> 33 - ), 34 - }, 35 - { 36 - name: "My Collections", 37 - href: "/collections", 38 - icon: ( 39 - <svg 40 - xmlns="http://www.w3.org/2000/svg" 41 - className="h-5 w-5" 42 - viewBox="0 0 24 24" 43 - fill="none" 44 - stroke="currentColor" 45 - strokeWidth="2" 46 - > 47 - <path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2v11z" /> 48 - </svg> 49 - ), 50 - }, 51 - ]; 52 - 53 - return ( 54 - <div 55 - className={cn("pb-12 w-64 bg-gray-50 h-screen flex flex-col", className)} 56 - > 57 - <div className="flex-1 space-y-4 py-4"> 58 - <div className="px-4 py-2"> 59 - <h2 className="mb-2 px-2 text-xl font-semibold tracking-tight"> 60 - Annos 61 - </h2> 62 - <div className="space-y-1"> 63 - {navItems.map((item) => ( 64 - <Link 65 - key={item.href} 66 - href={item.href} 67 - className={cn( 68 - "flex items-center rounded-md px-3 py-2 text-sm font-medium hover:bg-gray-100 hover:text-gray-900", 69 - pathname === item.href 70 - ? "bg-gray-200 text-gray-900" 71 - : "text-gray-500" 72 - )} 73 - > 74 - {item.icon} 75 - <span className="ml-3">{item.name}</span> 76 - </Link> 77 - ))} 78 - </div> 79 - </div> 80 - </div> 81 - 82 - {/* User Profile Section at Bottom */} 83 - <div className="px-4 py-2 border-t border-gray-200"> 84 - <Link 85 - href="/profile" 86 - className={cn( 87 - "flex items-center rounded-md px-3 py-2 text-sm font-medium hover:bg-gray-100 hover:text-gray-900", 88 - pathname === "/profile" 89 - ? "bg-gray-200 text-gray-900" 90 - : "text-gray-500" 91 - )} 92 - > 93 - <svg 94 - xmlns="http://www.w3.org/2000/svg" 95 - className="h-5 w-5" 96 - viewBox="0 0 24 24" 97 - fill="none" 98 - stroke="currentColor" 99 - strokeWidth="2" 100 - > 101 - <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" /> 102 - <circle cx="12" cy="7" r="4" /> 103 - </svg> 104 - <span className="ml-3">Profile</span> 105 - </Link> 106 - </div> 107 - </div> 108 - ); 109 - }
+62 -50
src/webapp/components/UrlCard.tsx
··· 1 1 "use client"; 2 2 3 - import { useState } from "react"; 4 - import { Card, CardContent, CardHeader } from "@/components/ui/card"; 5 - import { Button } from "@/components/ui/button"; 6 3 import { BiPlus, BiLinkExternal } from "react-icons/bi"; 7 4 import { AddToCollectionModal } from "./AddToCollectionModal"; 5 + import { 6 + ActionIcon, 7 + Blockquote, 8 + Box, 9 + Card, 10 + Group, 11 + Image, 12 + Stack, 13 + Text, 14 + } from "@mantine/core"; 15 + import { useDisclosure } from "@mantine/hooks"; 8 16 9 17 interface UrlCardProps { 10 18 cardId: string; ··· 29 37 addedAt, 30 38 note, 31 39 }: UrlCardProps) { 32 - const [showAddToCollectionModal, setShowAddToCollectionModal] = 33 - useState(false); 40 + const [modalOpened, { open, close }] = useDisclosure(false); 34 41 35 42 const formatDate = (dateString: string) => { 36 43 return new Date(dateString).toLocaleDateString("en-US", { ··· 54 61 }; 55 62 56 63 return ( 57 - <> 58 - <Card className="w-full hover:shadow-md transition-shadow"> 59 - <CardHeader className="pb-3"> 60 - <div className="flex items-start justify-between"> 61 - <div className="flex-1 min-w-0"> 62 - <h3 className="font-semibold text-lg leading-tight mb-1 truncate"> 64 + <Box> 65 + <Card withBorder> 66 + <Stack> 67 + <Group justify="space-between" wrap="nowrap"> 68 + <Stack gap={0}> 69 + <Text fw={600} lineClamp={1}> 63 70 {title || getDomain(url)} 64 - </h3> 65 - <p className="text-sm text-gray-500 truncate"> 71 + </Text> 72 + <Text fz={"sm"} fw={500} lineClamp={1} c={"gray"}> 66 73 {getDomain(url)} • {formatDate(addedAt)} 67 - </p> 68 - </div> 69 - <div className="ml-4 flex items-center gap-2"> 74 + </Text> 75 + </Stack> 76 + <Group wrap="nowrap"> 70 77 {imageUrl && ( 71 - <img 78 + <Image 72 79 src={imageUrl} 73 80 alt={title || "Card image"} 74 - className="w-16 h-16 object-cover rounded-md" 81 + w={64} 82 + h={64} 83 + radius={"md"} 75 84 /> 76 85 )} 77 - <div className="flex flex-col gap-1"> 78 - <Button 79 - variant="ghost" 80 - size="sm" 81 - onClick={() => setShowAddToCollectionModal(true)} 86 + <Stack gap={"xs"}> 87 + <ActionIcon 88 + variant="transparent" 89 + onClick={() => open()} 82 90 title="Add to collection" 83 91 > 84 - <BiPlus className="h-4 w-4" /> 85 - </Button> 86 - <Button 87 - variant="ghost" 88 - size="sm" 92 + <BiPlus /> 93 + </ActionIcon> 94 + <ActionIcon 95 + variant="transparent" 89 96 onClick={() => window.open(url, "_blank")} 90 97 title="Open link" 91 98 > 92 - <BiLinkExternal className="h-4 w-4" /> 93 - </Button> 94 - </div> 95 - </div> 96 - </div> 97 - </CardHeader> 98 - <CardContent className="pt-0"> 99 - {description && ( 100 - <p className="text-gray-700 text-sm mb-3 line-clamp-2"> 101 - {description} 102 - </p> 103 - )} 104 - {author && <p className="text-xs text-gray-500 mb-2">By {author}</p>} 105 - {note && ( 106 - <div className="bg-yellow-50 border-l-4 border-yellow-200 p-2 mb-3"> 107 - <p className="text-sm text-gray-700">{note}</p> 108 - </div> 109 - )} 110 - </CardContent> 99 + <BiLinkExternal /> 100 + </ActionIcon> 101 + </Stack> 102 + </Group> 103 + </Group> 104 + 105 + <Stack> 106 + {description && ( 107 + <Text lineClamp={2} fz={"sm"}> 108 + {description} 109 + </Text> 110 + )} 111 + {author && ( 112 + <Text fw={500} fz={"xs"} c={"gray"}> 113 + By {author} 114 + </Text> 115 + )} 116 + {note && ( 117 + <Blockquote color="yellow" p={"xs"} fz={"sm"}> 118 + {note} 119 + </Blockquote> 120 + )} 121 + </Stack> 122 + </Stack> 111 123 </Card> 112 124 113 125 <AddToCollectionModal 114 126 cardId={cardId} 115 - isOpen={showAddToCollectionModal} 116 - onClose={() => setShowAddToCollectionModal(false)} 127 + isOpen={modalOpened} 128 + onClose={close} 117 129 onSuccess={handleAddToCollectionSuccess} 118 130 /> 119 - </> 131 + </Box> 120 132 ); 121 133 }
+92 -82
src/webapp/components/extension/SaveCardPage.tsx
··· 1 1 import { useState, useEffect } from "react"; 2 2 import { useExtensionAuth } from "../../hooks/useExtensionAuth"; 3 3 import { ApiClient } from "../../api-client/ApiClient"; 4 + import { IoMdCheckmark } from "react-icons/io"; 5 + import { 6 + Alert, 7 + AspectRatio, 8 + Box, 9 + Button, 10 + Divider, 11 + Group, 12 + Image, 13 + Loader, 14 + Paper, 15 + Stack, 16 + Text, 17 + Textarea, 18 + Title, 19 + } from "@mantine/core"; 4 20 5 21 interface UrlMetadata { 6 22 url: string; ··· 24 40 25 41 const apiClient = new ApiClient( 26 42 process.env.PLASMO_PUBLIC_API_URL || "http://localhost:3000", 27 - () => accessToken 43 + () => accessToken, 28 44 ); 29 45 30 46 // Get current tab URL and fetch metadata when popup opens ··· 54 70 } else { 55 71 console.error("No URL found in tab:", tab); 56 72 setError( 57 - "Cannot access this page's URL. Make sure the extension has proper permissions." 73 + "Cannot access this page's URL. Make sure the extension has proper permissions.", 58 74 ); 59 75 } 60 76 } else { ··· 94 110 95 111 if (success) { 96 112 return ( 97 - <div className="w-80 p-4 bg-white"> 98 - <div className="flex items-center justify-center py-8"> 99 - <div className="text-center"> 100 - <div className="text-green-600 text-2xl mb-2">✓</div> 101 - <div className="text-sm text-gray-600"> 102 - Card saved successfully! 103 - </div> 104 - </div> 105 - </div> 106 - </div> 113 + <Stack align="center" gap={"xs"} c={"green"}> 114 + <IoMdCheckmark size={20} /> 115 + <Text fw={500} c={"gray"}> 116 + Card saved successfully! 117 + </Text> 118 + </Stack> 107 119 ); 108 120 } 109 121 110 122 return ( 111 - <div className="w-80 p-4 bg-white"> 112 - <div className="border-b pb-3 mb-4"> 113 - <div className="flex items-center justify-between"> 114 - <h1 className="text-lg font-semibold text-gray-900">Save Card</h1> 115 - <button 116 - onClick={logout} 117 - className="text-xs text-gray-500 hover:text-gray-700" 118 - > 123 + <Box> 124 + <Stack> 125 + <Group justify="space-between"> 126 + <Title order={1} fz={"xl"}> 127 + Save Card 128 + </Title> 129 + <Button variant="subtle" color={"gray"} onClick={logout}> 119 130 Sign out 120 - </button> 121 - </div> 122 - </div> 131 + </Button> 132 + </Group> 123 133 124 - <div className="space-y-4"> 134 + <Divider /> 135 + 125 136 {/* URL Metadata Display */} 126 137 {isLoadingMetadata ? ( 127 - <div className="bg-gray-50 p-3 rounded-lg"> 128 - <div className="text-sm text-gray-600"> 138 + <Stack gap={0} align="center"> 139 + <Loader type="dots" size={"sm"} /> 140 + <Text fz={"sm"} fw={500} c={"gray"}> 129 141 Loading page information... 130 - </div> 131 - </div> 142 + </Text> 143 + </Stack> 132 144 ) : metadata ? ( 133 - <div className="bg-gray-50 p-3 rounded-lg space-y-2"> 134 - {metadata.imageUrl && ( 135 - <img 136 - src={metadata.imageUrl} 137 - alt={metadata.title || "Page preview"} 138 - className="w-full h-24 object-cover rounded" 139 - /> 140 - )} 141 - <div> 142 - <h3 className="font-medium text-sm text-gray-900 line-clamp-2"> 143 - {metadata.title || "Untitled"} 144 - </h3> 145 - {metadata.description && ( 146 - <p className="text-xs text-gray-600 mt-1 line-clamp-2"> 147 - {metadata.description} 148 - </p> 145 + <Paper bg={"gray.2"} p={"sm"}> 146 + <Stack> 147 + {metadata.imageUrl && ( 148 + <AspectRatio ratio={2 / 1}> 149 + <Image 150 + src={metadata.imageUrl} 151 + alt={metadata.title || "Page preview"} 152 + /> 153 + </AspectRatio> 149 154 )} 150 - <p className="text-xs text-gray-500 mt-1 truncate"> 151 - {metadata.siteName || new URL(currentUrl).hostname} 152 - </p> 153 - </div> 154 - </div> 155 + <Stack gap={"xs"}> 156 + <Stack gap={"0"}> 157 + <Title order={3} lineClamp={2} fz={"md"} fw={500}> 158 + {metadata.title || "Untitled"} 159 + </Title> 160 + {metadata.description && ( 161 + <Text fz={"sm"} fw={500} c={"gray"} lineClamp={2}> 162 + {metadata.description} 163 + </Text> 164 + )} 165 + </Stack> 166 + <Text fz={"xs"} c={"gray"}> 167 + {metadata.siteName || new URL(currentUrl).hostname} 168 + </Text> 169 + </Stack> 170 + </Stack> 171 + </Paper> 155 172 ) : ( 156 - <div className="bg-gray-50 p-3 rounded-lg"> 157 - <p className="text-xs text-gray-500 mb-1">Current page:</p> 158 - <p className="text-sm text-gray-800 truncate" title={currentUrl}> 173 + <Stack gap={0}> 174 + <Text fz={"sm"} c={"gray"}> 175 + Current page: 176 + </Text> 177 + <Text fz={"sm"} truncate={"end"}> 159 178 {currentUrl || "Loading..."} 160 - </p> 161 - </div> 179 + </Text> 180 + </Stack> 162 181 )} 163 182 164 183 {/* Note Input */} 165 - <div> 166 - <label className="block text-sm font-medium text-gray-700 mb-1"> 167 - Note (optional) 168 - </label> 169 - <textarea 170 - placeholder="Add a note about this page..." 171 - value={note} 172 - onChange={(e) => setNote(e.target.value)} 173 - className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent resize-none" 174 - rows={3} 175 - disabled={isSaving} 176 - /> 177 - </div> 184 + <Textarea 185 + label="Note (optional)" 186 + placeholder="Add a note about this page..." 187 + value={note} 188 + onChange={(e) => setNote(e.target.value)} 189 + rows={3} 190 + disabled={isSaving} 191 + /> 178 192 179 193 {/* Error Display */} 180 - {error && ( 181 - <div className="bg-red-50 border border-red-200 rounded-md p-3"> 182 - <p className="text-sm text-red-600">{error}</p> 183 - </div> 184 - )} 194 + {error && <Alert color={"red"} title={error} />} 185 195 186 196 {/* Action Buttons */} 187 - <div className="space-y-2"> 188 - <button 197 + <Stack gap={"xs"}> 198 + <Button 189 199 onClick={handleSaveCard} 190 200 disabled={!currentUrl || isSaving} 191 - className="w-full bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 disabled:bg-gray-400 disabled:cursor-not-allowed transition-colors" 201 + loading={isSaving} 192 202 > 193 203 {isSaving ? "Saving..." : "Save Card"} 194 - </button> 204 + </Button> 195 205 196 - <button 206 + <Button 207 + variant="subtle" 197 208 onClick={() => window.close()} 198 - className="w-full bg-gray-100 text-gray-700 py-2 px-4 rounded-md hover:bg-gray-200 transition-colors" 199 209 disabled={isSaving} 200 210 > 201 211 Cancel 202 - </button> 203 - </div> 204 - </div> 205 - </div> 212 + </Button> 213 + </Stack> 214 + </Stack> 215 + </Box> 206 216 ); 207 217 }
+69 -51
src/webapp/components/extension/SignInPage.tsx
··· 1 + "use client"; 2 + 1 3 import { useState } from "react"; 2 4 import { useExtensionAuth } from "../../hooks/useExtensionAuth"; 5 + import { 6 + Alert, 7 + Button, 8 + Center, 9 + Divider, 10 + Loader, 11 + PasswordInput, 12 + Stack, 13 + Text, 14 + TextInput, 15 + Title, 16 + } from "@mantine/core"; 17 + import { useForm } from "@mantine/form"; 3 18 4 19 export function SignInPage() { 5 20 const { loginWithAppPassword, error, isLoading } = useExtensionAuth(); 6 - const [handle, setHandle] = useState(""); 7 - const [password, setPassword] = useState(""); 21 + const form = useForm({ 22 + initialValues: { 23 + handle: "", 24 + password: "", 25 + }, 26 + }); 8 27 const [isSubmitting, setIsSubmitting] = useState(false); 9 28 10 29 const handleLogin = async (e: React.FormEvent) => { 11 30 e.preventDefault(); 12 - if (!handle.trim() || !password.trim()) return; 31 + if (!form.getValues().handle.trim() || !form.getValues().password.trim()) 32 + return; 13 33 14 34 try { 15 35 setIsSubmitting(true); 16 - await loginWithAppPassword(handle.trim(), password.trim()); 36 + await loginWithAppPassword( 37 + form.getValues().handle.trim(), 38 + form.getValues().password.trim(), 39 + ); 17 40 } catch (error) { 18 41 // Error is handled by the auth context 19 42 } finally { ··· 23 46 24 47 if (isLoading) { 25 48 return ( 26 - <div className="w-80 p-4 bg-white"> 27 - <div className="flex items-center justify-center py-8"> 28 - <div className="text-sm text-gray-600">Loading...</div> 29 - </div> 30 - </div> 49 + <Center> 50 + <Loader /> 51 + </Center> 31 52 ); 32 53 } 33 54 34 55 return ( 35 - <div className="w-80 p-4 bg-white"> 36 - <div className="border-b pb-3 mb-4"> 37 - <h1 className="text-lg font-semibold text-gray-900">Card Extension</h1> 38 - <p className="text-sm text-gray-600">Sign in to save content</p> 39 - </div> 40 - 41 - <form onSubmit={handleLogin} className="space-y-4"> 42 - {error && ( 43 - <div className="bg-red-50 border border-red-200 rounded-md p-3"> 44 - <p className="text-sm text-red-600">{error}</p> 45 - </div> 46 - )} 47 - 48 - <div> 49 - <label className="block text-sm font-medium text-gray-700 mb-1"> 50 - Handle 51 - </label> 52 - <input 56 + <Stack> 57 + <Stack gap={0}> 58 + <Title order={1} fz={"xl"}> 59 + Card Extension 60 + </Title> 61 + <Text c={"gray"} fz={"sm"} fw={500}> 62 + Sign in to save content 63 + </Text> 64 + </Stack> 65 + 66 + <Divider /> 67 + 68 + <form onSubmit={handleLogin}> 69 + <Stack> 70 + {error && <Alert color={"red"} title={error} />} 71 + 72 + <TextInput 53 73 type="text" 74 + label="Handle" 54 75 placeholder="user.bsky.social" 55 - value={handle} 56 - onChange={(e) => setHandle(e.target.value)} 57 - className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" 58 76 disabled={isSubmitting} 77 + key={form.key("handle")} 78 + {...form.getInputProps("handle")} 59 79 /> 60 - </div> 61 - 62 - <div> 63 - <label className="block text-sm font-medium text-gray-700 mb-1"> 64 - App Password 65 - </label> 66 - <input 67 - type="password" 80 + 81 + <PasswordInput 82 + label="App Password" 68 83 placeholder="xxxx-xxxx-xxxx-xxxx" 69 - value={password} 70 - onChange={(e) => setPassword(e.target.value)} 71 - className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" 72 84 disabled={isSubmitting} 85 + key={form.key("password")} 86 + {...form.getInputProps("password")} 73 87 /> 74 - </div> 75 - 76 - <button 77 - type="submit" 78 - disabled={!handle.trim() || !password.trim() || isSubmitting} 79 - className="w-full bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 disabled:bg-gray-400 disabled:cursor-not-allowed transition-colors" 80 - > 81 - {isSubmitting ? "Signing in..." : "Sign In"} 82 - </button> 88 + 89 + <Button 90 + type="submit" 91 + disabled={ 92 + !form.getValues().handle.trim() || 93 + !form.getValues().password.trim() || 94 + isSubmitting 95 + } 96 + loading={isSubmitting} 97 + > 98 + {isSubmitting ? "Signing in..." : "Sign In"} 99 + </Button> 100 + </Stack> 83 101 </form> 84 - </div> 102 + </Stack> 85 103 ); 86 104 }
-36
src/webapp/components/ui/badge.tsx
··· 1 - import * as React from "react" 2 - import { cva, type VariantProps } from "class-variance-authority" 3 - 4 - import { cn } from "@/lib/utils" 5 - 6 - const badgeVariants = cva( 7 - "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", 8 - { 9 - variants: { 10 - variant: { 11 - default: 12 - "border-transparent bg-primary text-primary-foreground hover:bg-primary/80", 13 - secondary: 14 - "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", 15 - destructive: 16 - "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80", 17 - outline: "text-foreground", 18 - }, 19 - }, 20 - defaultVariants: { 21 - variant: "default", 22 - }, 23 - } 24 - ) 25 - 26 - export interface BadgeProps 27 - extends React.HTMLAttributes<HTMLDivElement>, 28 - VariantProps<typeof badgeVariants> {} 29 - 30 - function Badge({ className, variant, ...props }: BadgeProps) { 31 - return ( 32 - <div className={cn(badgeVariants({ variant }), className)} {...props} /> 33 - ) 34 - } 35 - 36 - export { Badge, badgeVariants }
-56
src/webapp/components/ui/button.tsx
··· 1 - import * as React from "react" 2 - import { Slot } from "@radix-ui/react-slot" 3 - import { cva, type VariantProps } from "class-variance-authority" 4 - 5 - import { cn } from "@/lib/utils" 6 - 7 - const buttonVariants = cva( 8 - "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", 9 - { 10 - variants: { 11 - variant: { 12 - default: "bg-primary text-primary-foreground hover:bg-primary/90", 13 - destructive: 14 - "bg-destructive text-destructive-foreground hover:bg-destructive/90", 15 - outline: 16 - "border border-input bg-background hover:bg-accent hover:text-accent-foreground", 17 - secondary: 18 - "bg-secondary text-secondary-foreground hover:bg-secondary/80", 19 - ghost: "hover:bg-accent hover:text-accent-foreground", 20 - link: "text-primary underline-offset-4 hover:underline", 21 - }, 22 - size: { 23 - default: "h-10 px-4 py-2", 24 - sm: "h-9 rounded-md px-3", 25 - lg: "h-11 rounded-md px-8", 26 - icon: "h-10 w-10", 27 - }, 28 - }, 29 - defaultVariants: { 30 - variant: "default", 31 - size: "default", 32 - }, 33 - } 34 - ) 35 - 36 - export interface ButtonProps 37 - extends React.ButtonHTMLAttributes<HTMLButtonElement>, 38 - VariantProps<typeof buttonVariants> { 39 - asChild?: boolean 40 - } 41 - 42 - const Button = React.forwardRef<HTMLButtonElement, ButtonProps>( 43 - ({ className, variant, size, asChild = false, ...props }, ref) => { 44 - const Comp = asChild ? Slot : "button" 45 - return ( 46 - <Comp 47 - className={cn(buttonVariants({ variant, size, className }))} 48 - ref={ref} 49 - {...props} 50 - /> 51 - ) 52 - } 53 - ) 54 - Button.displayName = "Button" 55 - 56 - export { Button, buttonVariants }
-79
src/webapp/components/ui/card.tsx
··· 1 - import * as React from "react" 2 - 3 - import { cn } from "@/lib/utils" 4 - 5 - const Card = React.forwardRef< 6 - HTMLDivElement, 7 - React.HTMLAttributes<HTMLDivElement> 8 - >(({ className, ...props }, ref) => ( 9 - <div 10 - ref={ref} 11 - className={cn( 12 - "rounded-lg border bg-card text-card-foreground shadow-sm", 13 - className 14 - )} 15 - {...props} 16 - /> 17 - )) 18 - Card.displayName = "Card" 19 - 20 - const CardHeader = React.forwardRef< 21 - HTMLDivElement, 22 - React.HTMLAttributes<HTMLDivElement> 23 - >(({ className, ...props }, ref) => ( 24 - <div 25 - ref={ref} 26 - className={cn("flex flex-col space-y-1.5 p-6", className)} 27 - {...props} 28 - /> 29 - )) 30 - CardHeader.displayName = "CardHeader" 31 - 32 - const CardTitle = React.forwardRef< 33 - HTMLParagraphElement, 34 - React.HTMLAttributes<HTMLHeadingElement> 35 - >(({ className, ...props }, ref) => ( 36 - <h3 37 - ref={ref} 38 - className={cn( 39 - "text-2xl font-semibold leading-none tracking-tight", 40 - className 41 - )} 42 - {...props} 43 - /> 44 - )) 45 - CardTitle.displayName = "CardTitle" 46 - 47 - const CardDescription = React.forwardRef< 48 - HTMLParagraphElement, 49 - React.HTMLAttributes<HTMLParagraphElement> 50 - >(({ className, ...props }, ref) => ( 51 - <p 52 - ref={ref} 53 - className={cn("text-sm text-muted-foreground", className)} 54 - {...props} 55 - /> 56 - )) 57 - CardDescription.displayName = "CardDescription" 58 - 59 - const CardContent = React.forwardRef< 60 - HTMLDivElement, 61 - React.HTMLAttributes<HTMLDivElement> 62 - >(({ className, ...props }, ref) => ( 63 - <div ref={ref} className={cn("p-6 pt-0", className)} {...props} /> 64 - )) 65 - CardContent.displayName = "CardContent" 66 - 67 - const CardFooter = React.forwardRef< 68 - HTMLDivElement, 69 - React.HTMLAttributes<HTMLDivElement> 70 - >(({ className, ...props }, ref) => ( 71 - <div 72 - ref={ref} 73 - className={cn("flex items-center p-6 pt-0", className)} 74 - {...props} 75 - /> 76 - )) 77 - CardFooter.displayName = "CardFooter" 78 - 79 - export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
-30
src/webapp/components/ui/checkbox.tsx
··· 1 - "use client"; 2 - 3 - import * as React from "react"; 4 - import * as CheckboxPrimitive from "@radix-ui/react-checkbox"; 5 - import { BiCheck } from "react-icons/bi"; 6 - 7 - import { cn } from "@/lib/utils"; 8 - 9 - const Checkbox = React.forwardRef< 10 - React.ElementRef<typeof CheckboxPrimitive.Root>, 11 - React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root> 12 - >(({ className, ...props }, ref) => ( 13 - <CheckboxPrimitive.Root 14 - ref={ref} 15 - className={cn( 16 - "peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground", 17 - className, 18 - )} 19 - {...props} 20 - > 21 - <CheckboxPrimitive.Indicator 22 - className={cn("flex items-center justify-center text-current")} 23 - > 24 - <BiCheck className="h-4 w-4" /> 25 - </CheckboxPrimitive.Indicator> 26 - </CheckboxPrimitive.Root> 27 - )); 28 - Checkbox.displayName = CheckboxPrimitive.Root.displayName; 29 - 30 - export { Checkbox };
-25
src/webapp/components/ui/input.tsx
··· 1 - import * as React from "react" 2 - 3 - import { cn } from "@/lib/utils" 4 - 5 - export interface InputProps 6 - extends React.InputHTMLAttributes<HTMLInputElement> {} 7 - 8 - const Input = React.forwardRef<HTMLInputElement, InputProps>( 9 - ({ className, type, ...props }, ref) => { 10 - return ( 11 - <input 12 - type={type} 13 - className={cn( 14 - "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50", 15 - className 16 - )} 17 - ref={ref} 18 - {...props} 19 - /> 20 - ) 21 - } 22 - ) 23 - Input.displayName = "Input" 24 - 25 - export { Input }
-26
src/webapp/components/ui/label.tsx
··· 1 - "use client" 2 - 3 - import * as React from "react" 4 - import * as LabelPrimitive from "@radix-ui/react-label" 5 - import { cva, type VariantProps } from "class-variance-authority" 6 - 7 - import { cn } from "@/lib/utils" 8 - 9 - const labelVariants = cva( 10 - "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" 11 - ) 12 - 13 - const Label = React.forwardRef< 14 - React.ElementRef<typeof LabelPrimitive.Root>, 15 - React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> & 16 - VariantProps<typeof labelVariants> 17 - >(({ className, ...props }, ref) => ( 18 - <LabelPrimitive.Root 19 - ref={ref} 20 - className={cn(labelVariants(), className)} 21 - {...props} 22 - /> 23 - )) 24 - Label.displayName = LabelPrimitive.Root.displayName 25 - 26 - export { Label }
-160
src/webapp/components/ui/select.tsx
··· 1 - "use client"; 2 - 3 - import * as React from "react"; 4 - import * as SelectPrimitive from "@radix-ui/react-select"; 5 - import { BiCheck, BiSolidChevronDown, BiSolidChevronUp } from "react-icons/bi"; 6 - 7 - import { cn } from "@/lib/utils"; 8 - 9 - const Select = SelectPrimitive.Root; 10 - 11 - const SelectGroup = SelectPrimitive.Group; 12 - 13 - const SelectValue = SelectPrimitive.Value; 14 - 15 - const SelectTrigger = React.forwardRef< 16 - React.ElementRef<typeof SelectPrimitive.Trigger>, 17 - React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> 18 - >(({ className, children, ...props }, ref) => ( 19 - <SelectPrimitive.Trigger 20 - ref={ref} 21 - className={cn( 22 - "flex h-10 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 [&>span]:line-clamp-1", 23 - className, 24 - )} 25 - {...props} 26 - > 27 - {children} 28 - <SelectPrimitive.Icon asChild> 29 - <BiSolidChevronDown className="h-4 w-4 opacity-50" /> 30 - </SelectPrimitive.Icon> 31 - </SelectPrimitive.Trigger> 32 - )); 33 - SelectTrigger.displayName = SelectPrimitive.Trigger.displayName; 34 - 35 - const SelectScrollUpButton = React.forwardRef< 36 - React.ElementRef<typeof SelectPrimitive.ScrollUpButton>, 37 - React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton> 38 - >(({ className, ...props }, ref) => ( 39 - <SelectPrimitive.ScrollUpButton 40 - ref={ref} 41 - className={cn( 42 - "flex cursor-default items-center justify-center py-1", 43 - className, 44 - )} 45 - {...props} 46 - > 47 - <BiSolidChevronUp className="h-4 w-4" /> 48 - </SelectPrimitive.ScrollUpButton> 49 - )); 50 - SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName; 51 - 52 - const SelectScrollDownButton = React.forwardRef< 53 - React.ElementRef<typeof SelectPrimitive.ScrollDownButton>, 54 - React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton> 55 - >(({ className, ...props }, ref) => ( 56 - <SelectPrimitive.ScrollDownButton 57 - ref={ref} 58 - className={cn( 59 - "flex cursor-default items-center justify-center py-1", 60 - className, 61 - )} 62 - {...props} 63 - > 64 - <BiSolidChevronDown className="h-4 w-4" /> 65 - </SelectPrimitive.ScrollDownButton> 66 - )); 67 - SelectScrollDownButton.displayName = 68 - SelectPrimitive.ScrollDownButton.displayName; 69 - 70 - const SelectContent = React.forwardRef< 71 - React.ElementRef<typeof SelectPrimitive.Content>, 72 - React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content> 73 - >(({ className, children, position = "popper", ...props }, ref) => ( 74 - <SelectPrimitive.Portal> 75 - <SelectPrimitive.Content 76 - ref={ref} 77 - className={cn( 78 - "relative z-50 max-h-96 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", 79 - position === "popper" && 80 - "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", 81 - className, 82 - )} 83 - position={position} 84 - {...props} 85 - > 86 - <SelectScrollUpButton /> 87 - <SelectPrimitive.Viewport 88 - className={cn( 89 - "p-1", 90 - position === "popper" && 91 - "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]", 92 - )} 93 - > 94 - {children} 95 - </SelectPrimitive.Viewport> 96 - <SelectScrollDownButton /> 97 - </SelectPrimitive.Content> 98 - </SelectPrimitive.Portal> 99 - )); 100 - SelectContent.displayName = SelectPrimitive.Content.displayName; 101 - 102 - const SelectLabel = React.forwardRef< 103 - React.ElementRef<typeof SelectPrimitive.Label>, 104 - React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label> 105 - >(({ className, ...props }, ref) => ( 106 - <SelectPrimitive.Label 107 - ref={ref} 108 - className={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)} 109 - {...props} 110 - /> 111 - )); 112 - SelectLabel.displayName = SelectPrimitive.Label.displayName; 113 - 114 - const SelectItem = React.forwardRef< 115 - React.ElementRef<typeof SelectPrimitive.Item>, 116 - React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item> 117 - >(({ className, children, ...props }, ref) => ( 118 - <SelectPrimitive.Item 119 - ref={ref} 120 - className={cn( 121 - "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", 122 - className, 123 - )} 124 - {...props} 125 - > 126 - <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> 127 - <SelectPrimitive.ItemIndicator> 128 - <BiCheck className="h-4 w-4" /> 129 - </SelectPrimitive.ItemIndicator> 130 - </span> 131 - 132 - <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText> 133 - </SelectPrimitive.Item> 134 - )); 135 - SelectItem.displayName = SelectPrimitive.Item.displayName; 136 - 137 - const SelectSeparator = React.forwardRef< 138 - React.ElementRef<typeof SelectPrimitive.Separator>, 139 - React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator> 140 - >(({ className, ...props }, ref) => ( 141 - <SelectPrimitive.Separator 142 - ref={ref} 143 - className={cn("-mx-1 my-1 h-px bg-muted", className)} 144 - {...props} 145 - /> 146 - )); 147 - SelectSeparator.displayName = SelectPrimitive.Separator.displayName; 148 - 149 - export { 150 - Select, 151 - SelectGroup, 152 - SelectValue, 153 - SelectTrigger, 154 - SelectContent, 155 - SelectLabel, 156 - SelectItem, 157 - SelectSeparator, 158 - SelectScrollUpButton, 159 - SelectScrollDownButton, 160 - };
-15
src/webapp/components/ui/skeleton.tsx
··· 1 - import { cn } from "@/lib/utils" 2 - 3 - function Skeleton({ 4 - className, 5 - ...props 6 - }: React.HTMLAttributes<HTMLDivElement>) { 7 - return ( 8 - <div 9 - className={cn("animate-pulse rounded-md bg-muted", className)} 10 - {...props} 11 - /> 12 - ) 13 - } 14 - 15 - export { Skeleton }
-24
src/webapp/components/ui/textarea.tsx
··· 1 - import * as React from "react" 2 - 3 - import { cn } from "@/lib/utils" 4 - 5 - export interface TextareaProps 6 - extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {} 7 - 8 - const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>( 9 - ({ className, ...props }, ref) => { 10 - return ( 11 - <textarea 12 - className={cn( 13 - "flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50", 14 - className 15 - )} 16 - ref={ref} 17 - {...props} 18 - /> 19 - ) 20 - } 21 - ) 22 - Textarea.displayName = "Textarea" 23 - 24 - export { Textarea }
+3 -3
src/webapp/hooks/useExtensionAuth.tsx
··· 36 36 const createApiClient = useCallback((token: string | null) => { 37 37 return new ApiClient( 38 38 process.env.PLASMO_PUBLIC_API_URL || "http://localhost:3000", 39 - () => token 39 + () => token, 40 40 ); 41 41 }, []); 42 42 ··· 98 98 99 99 const loginWithAppPassword = async ( 100 100 identifier: string, 101 - appPassword: string 101 + appPassword: string, 102 102 ) => { 103 103 try { 104 104 setError(null); ··· 162 162 const context = useContext(ExtensionAuthContext); 163 163 if (!context) { 164 164 throw new Error( 165 - "useExtensionAuth must be used within ExtensionAuthProvider" 165 + "useExtensionAuth must be used within ExtensionAuthProvider", 166 166 ); 167 167 } 168 168 return context;
-6
src/webapp/lib/utils.ts
··· 1 - import { type ClassValue, clsx } from "clsx" 2 - import { twMerge } from "tailwind-merge" 3 - 4 - export function cn(...inputs: ClassValue[]) { 5 - return twMerge(clsx(inputs)) 6 - }
+480 -959
src/webapp/package-lock.json
··· 8 8 "name": "annos-webapp", 9 9 "version": "0.1.0", 10 10 "dependencies": { 11 - "@radix-ui/react-checkbox": "^1.3.2", 12 - "@radix-ui/react-label": "^2.1.6", 13 - "@radix-ui/react-select": "^2.2.4", 14 - "@radix-ui/react-slot": "^1.0.2", 15 - "class-variance-authority": "^0.7.1", 16 - "clsx": "^2.1.1", 11 + "@mantine/core": "^8.1.3", 12 + "@mantine/dates": "^8.1.3", 13 + "@mantine/dropzone": "^8.1.3", 14 + "@mantine/form": "^8.1.3", 15 + "@mantine/hooks": "^8.1.3", 16 + "@mantine/notifications": "^8.1.3", 17 17 "date-fns": "^4.1.0", 18 + "dayjs": "^1.11.13", 18 19 "next": "15.4.1", 19 20 "react": "19.1.0", 20 21 "react-dom": "19.1.0", 21 - "react-icons": "^5.5.0", 22 - "tailwind-merge": "^2.6.0", 23 - "tailwindcss-animate": "^1.0.7" 22 + "react-icons": "^5.5.0" 24 23 }, 25 24 "devDependencies": { 26 25 "@types/chrome": "^0.0.332", ··· 31 30 "eslint": "^8.55.0", 32 31 "eslint-config-next": "15.4.1", 33 32 "plasmo": "^0.90.5", 34 - "postcss": "^8.4.32", 35 - "tailwindcss": "^3.3.6", 33 + "postcss": "^8.5.6", 34 + "postcss-preset-mantine": "^1.18.0", 35 + "postcss-simple-vars": "^7.0.1", 36 36 "typescript": "^5.3.3" 37 - } 38 - }, 39 - "node_modules/@alloc/quick-lru": { 40 - "version": "5.2.0", 41 - "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", 42 - "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", 43 - "engines": { 44 - "node": ">=10" 45 - }, 46 - "funding": { 47 - "url": "https://github.com/sponsors/sindresorhus" 48 37 } 49 38 }, 50 39 "node_modules/@ampproject/remapping": { ··· 282 271 "version": "7.27.6", 283 272 "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz", 284 273 "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==", 285 - "dev": true, 286 274 "engines": { 287 275 "node": ">=6.9.0" 288 276 } ··· 802 790 "@floating-ui/utils": "^0.2.10" 803 791 } 804 792 }, 793 + "node_modules/@floating-ui/react": { 794 + "version": "0.26.28", 795 + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.28.tgz", 796 + "integrity": "sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw==", 797 + "dependencies": { 798 + "@floating-ui/react-dom": "^2.1.2", 799 + "@floating-ui/utils": "^0.2.8", 800 + "tabbable": "^6.0.0" 801 + }, 802 + "peerDependencies": { 803 + "react": ">=16.8.0", 804 + "react-dom": ">=16.8.0" 805 + } 806 + }, 805 807 "node_modules/@floating-ui/react-dom": { 806 808 "version": "2.1.4", 807 809 "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.4.tgz", ··· 1611 1613 "version": "8.0.2", 1612 1614 "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", 1613 1615 "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", 1616 + "dev": true, 1614 1617 "dependencies": { 1615 1618 "string-width": "^5.1.2", 1616 1619 "string-width-cjs": "npm:string-width@^4.2.0", ··· 1627 1630 "version": "6.1.0", 1628 1631 "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", 1629 1632 "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", 1633 + "dev": true, 1630 1634 "engines": { 1631 1635 "node": ">=12" 1632 1636 }, ··· 1638 1642 "version": "7.1.0", 1639 1643 "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", 1640 1644 "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", 1645 + "dev": true, 1641 1646 "dependencies": { 1642 1647 "ansi-regex": "^6.0.1" 1643 1648 }, ··· 1652 1657 "version": "0.3.12", 1653 1658 "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", 1654 1659 "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", 1660 + "dev": true, 1655 1661 "dependencies": { 1656 1662 "@jridgewell/sourcemap-codec": "^1.5.0", 1657 1663 "@jridgewell/trace-mapping": "^0.3.24" ··· 1661 1667 "version": "3.1.2", 1662 1668 "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", 1663 1669 "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", 1670 + "dev": true, 1664 1671 "engines": { 1665 1672 "node": ">=6.0.0" 1666 1673 } ··· 1668 1675 "node_modules/@jridgewell/sourcemap-codec": { 1669 1676 "version": "1.5.0", 1670 1677 "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", 1671 - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" 1678 + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", 1679 + "dev": true 1672 1680 }, 1673 1681 "node_modules/@jridgewell/trace-mapping": { 1674 1682 "version": "0.3.29", 1675 1683 "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", 1676 1684 "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", 1685 + "dev": true, 1677 1686 "dependencies": { 1678 1687 "@jridgewell/resolve-uri": "^3.1.0", 1679 1688 "@jridgewell/sourcemap-codec": "^1.4.14" ··· 1772 1781 "win32" 1773 1782 ] 1774 1783 }, 1784 + "node_modules/@mantine/core": { 1785 + "version": "8.1.3", 1786 + "resolved": "https://registry.npmjs.org/@mantine/core/-/core-8.1.3.tgz", 1787 + "integrity": "sha512-2WOPC8GSN3MApet0MccSn6LaXRhcP6SVtZnbuHoqJ/atrfK7kLE66ILr4OXov7JAj1ASJ4Xk0bOXmu5fBExAvQ==", 1788 + "dependencies": { 1789 + "@floating-ui/react": "^0.26.28", 1790 + "clsx": "^2.1.1", 1791 + "react-number-format": "^5.4.3", 1792 + "react-remove-scroll": "^2.6.2", 1793 + "react-textarea-autosize": "8.5.9", 1794 + "type-fest": "^4.27.0" 1795 + }, 1796 + "peerDependencies": { 1797 + "@mantine/hooks": "8.1.3", 1798 + "react": "^18.x || ^19.x", 1799 + "react-dom": "^18.x || ^19.x" 1800 + } 1801 + }, 1802 + "node_modules/@mantine/core/node_modules/type-fest": { 1803 + "version": "4.41.0", 1804 + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", 1805 + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", 1806 + "engines": { 1807 + "node": ">=16" 1808 + }, 1809 + "funding": { 1810 + "url": "https://github.com/sponsors/sindresorhus" 1811 + } 1812 + }, 1813 + "node_modules/@mantine/dates": { 1814 + "version": "8.1.3", 1815 + "resolved": "https://registry.npmjs.org/@mantine/dates/-/dates-8.1.3.tgz", 1816 + "integrity": "sha512-CkrjSXTci6d/jIawxLE1n1Mnz42Mb+8NRzrbcojyW15poAymNOcnN5WHXjg3puCJZOajM6Mx/tPF/CoDePyePw==", 1817 + "dependencies": { 1818 + "clsx": "^2.1.1" 1819 + }, 1820 + "peerDependencies": { 1821 + "@mantine/core": "8.1.3", 1822 + "@mantine/hooks": "8.1.3", 1823 + "dayjs": ">=1.0.0", 1824 + "react": "^18.x || ^19.x", 1825 + "react-dom": "^18.x || ^19.x" 1826 + } 1827 + }, 1828 + "node_modules/@mantine/dropzone": { 1829 + "version": "8.1.3", 1830 + "resolved": "https://registry.npmjs.org/@mantine/dropzone/-/dropzone-8.1.3.tgz", 1831 + "integrity": "sha512-JChpIdDrPqr1zFI1URn+wUCG41AH9boCIUDmMO8kd9fEnpzfwXNS3k15AccW9umST3Nwou7WvW1Qt67CQmMdfQ==", 1832 + "dependencies": { 1833 + "react-dropzone": "14.3.8" 1834 + }, 1835 + "peerDependencies": { 1836 + "@mantine/core": "8.1.3", 1837 + "@mantine/hooks": "8.1.3", 1838 + "react": "^18.x || ^19.x", 1839 + "react-dom": "^18.x || ^19.x" 1840 + } 1841 + }, 1842 + "node_modules/@mantine/form": { 1843 + "version": "8.1.3", 1844 + "resolved": "https://registry.npmjs.org/@mantine/form/-/form-8.1.3.tgz", 1845 + "integrity": "sha512-OoSVv2cyjKRZ+C4Rw63VsnO3qjKGZHJkd6DSJTVRQHXfDr10hxmC5yXgxGKsxGQ+xFd4ZCdtzPUU2BoWbHfZAA==", 1846 + "dependencies": { 1847 + "fast-deep-equal": "^3.1.3", 1848 + "klona": "^2.0.6" 1849 + }, 1850 + "peerDependencies": { 1851 + "react": "^18.x || ^19.x" 1852 + } 1853 + }, 1854 + "node_modules/@mantine/hooks": { 1855 + "version": "8.1.3", 1856 + "resolved": "https://registry.npmjs.org/@mantine/hooks/-/hooks-8.1.3.tgz", 1857 + "integrity": "sha512-yL4SbyYjrkmtIhscswajNz9RL0iO2+V8CMtOi0KISch2rPNvTAJNumFuZaXgj4UHeDc0JQYSmcZ+EW8NGm7xcQ==", 1858 + "peerDependencies": { 1859 + "react": "^18.x || ^19.x" 1860 + } 1861 + }, 1862 + "node_modules/@mantine/notifications": { 1863 + "version": "8.1.3", 1864 + "resolved": "https://registry.npmjs.org/@mantine/notifications/-/notifications-8.1.3.tgz", 1865 + "integrity": "sha512-Xy6f/l1yLTo77hz8X80sOuY+HW80e1rn8ucygx9TAexK5+XtyriOv26TQ3EJ6Ej5jlchtZRFEUJ4tJGRWjGCNg==", 1866 + "dependencies": { 1867 + "@mantine/store": "8.1.3", 1868 + "react-transition-group": "4.4.5" 1869 + }, 1870 + "peerDependencies": { 1871 + "@mantine/core": "8.1.3", 1872 + "@mantine/hooks": "8.1.3", 1873 + "react": "^18.x || ^19.x", 1874 + "react-dom": "^18.x || ^19.x" 1875 + } 1876 + }, 1877 + "node_modules/@mantine/store": { 1878 + "version": "8.1.3", 1879 + "resolved": "https://registry.npmjs.org/@mantine/store/-/store-8.1.3.tgz", 1880 + "integrity": "sha512-rO72LfSJqSNCwufqJxTWiHMyOR6sR3mqAcnBcw/f5aTvyOYoHZzlm4q4+TL8/2vYGRVsr9YM2Ez6HQ1vk/RR8g==", 1881 + "peerDependencies": { 1882 + "react": "^18.x || ^19.x" 1883 + } 1884 + }, 1775 1885 "node_modules/@mischnic/json-sourcemap": { 1776 1886 "version": "0.1.1", 1777 1887 "resolved": "https://registry.npmjs.org/@mischnic/json-sourcemap/-/json-sourcemap-0.1.1.tgz", ··· 2054 2164 "version": "2.1.5", 2055 2165 "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", 2056 2166 "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", 2167 + "dev": true, 2057 2168 "dependencies": { 2058 2169 "@nodelib/fs.stat": "2.0.5", 2059 2170 "run-parallel": "^1.1.9" ··· 2066 2177 "version": "2.0.5", 2067 2178 "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", 2068 2179 "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", 2180 + "dev": true, 2069 2181 "engines": { 2070 2182 "node": ">= 8" 2071 2183 } ··· 2074 2186 "version": "1.2.8", 2075 2187 "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", 2076 2188 "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", 2189 + "dev": true, 2077 2190 "dependencies": { 2078 2191 "@nodelib/fs.scandir": "2.1.5", 2079 2192 "fastq": "^1.6.0" ··· 4404 4517 "version": "0.11.0", 4405 4518 "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", 4406 4519 "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", 4520 + "dev": true, 4407 4521 "optional": true, 4408 4522 "engines": { 4409 4523 "node": ">=14" ··· 5450 5564 "node": ">=12" 5451 5565 } 5452 5566 }, 5453 - "node_modules/@radix-ui/number": { 5454 - "version": "1.1.1", 5455 - "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", 5456 - "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==" 5457 - }, 5458 - "node_modules/@radix-ui/primitive": { 5459 - "version": "1.1.2", 5460 - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz", 5461 - "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==" 5462 - }, 5463 - "node_modules/@radix-ui/react-arrow": { 5464 - "version": "1.1.6", 5465 - "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.6.tgz", 5466 - "integrity": "sha512-2JMfHJf/eVnwq+2dewT3C0acmCWD3XiVA1Da+jTDqo342UlU13WvXtqHhG+yJw5JeQmu4ue2eMy6gcEArLBlcw==", 5467 - "dependencies": { 5468 - "@radix-ui/react-primitive": "2.1.2" 5469 - }, 5470 - "peerDependencies": { 5471 - "@types/react": "*", 5472 - "@types/react-dom": "*", 5473 - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", 5474 - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 5475 - }, 5476 - "peerDependenciesMeta": { 5477 - "@types/react": { 5478 - "optional": true 5479 - }, 5480 - "@types/react-dom": { 5481 - "optional": true 5482 - } 5483 - } 5484 - }, 5485 - "node_modules/@radix-ui/react-arrow/node_modules/@radix-ui/react-primitive": { 5486 - "version": "2.1.2", 5487 - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.2.tgz", 5488 - "integrity": "sha512-uHa+l/lKfxuDD2zjN/0peM/RhhSmRjr5YWdk/37EnSv1nJ88uvG85DPexSm8HdFQROd2VdERJ6ynXbkCFi+APw==", 5489 - "dependencies": { 5490 - "@radix-ui/react-slot": "1.2.2" 5491 - }, 5492 - "peerDependencies": { 5493 - "@types/react": "*", 5494 - "@types/react-dom": "*", 5495 - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", 5496 - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 5497 - }, 5498 - "peerDependenciesMeta": { 5499 - "@types/react": { 5500 - "optional": true 5501 - }, 5502 - "@types/react-dom": { 5503 - "optional": true 5504 - } 5505 - } 5506 - }, 5507 - "node_modules/@radix-ui/react-checkbox": { 5508 - "version": "1.3.2", 5509 - "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.2.tgz", 5510 - "integrity": "sha512-yd+dI56KZqawxKZrJ31eENUwqc1QSqg4OZ15rybGjF2ZNwMO+wCyHzAVLRp9qoYJf7kYy0YpZ2b0JCzJ42HZpA==", 5511 - "dependencies": { 5512 - "@radix-ui/primitive": "1.1.2", 5513 - "@radix-ui/react-compose-refs": "1.1.2", 5514 - "@radix-ui/react-context": "1.1.2", 5515 - "@radix-ui/react-presence": "1.1.4", 5516 - "@radix-ui/react-primitive": "2.1.3", 5517 - "@radix-ui/react-use-controllable-state": "1.2.2", 5518 - "@radix-ui/react-use-previous": "1.1.1", 5519 - "@radix-ui/react-use-size": "1.1.1" 5520 - }, 5521 - "peerDependencies": { 5522 - "@types/react": "*", 5523 - "@types/react-dom": "*", 5524 - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", 5525 - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 5526 - }, 5527 - "peerDependenciesMeta": { 5528 - "@types/react": { 5529 - "optional": true 5530 - }, 5531 - "@types/react-dom": { 5532 - "optional": true 5533 - } 5534 - } 5535 - }, 5536 - "node_modules/@radix-ui/react-collection": { 5537 - "version": "1.1.6", 5538 - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.6.tgz", 5539 - "integrity": "sha512-PbhRFK4lIEw9ADonj48tiYWzkllz81TM7KVYyyMMw2cwHO7D5h4XKEblL8NlaRisTK3QTe6tBEhDccFUryxHBQ==", 5540 - "dependencies": { 5541 - "@radix-ui/react-compose-refs": "1.1.2", 5542 - "@radix-ui/react-context": "1.1.2", 5543 - "@radix-ui/react-primitive": "2.1.2", 5544 - "@radix-ui/react-slot": "1.2.2" 5545 - }, 5546 - "peerDependencies": { 5547 - "@types/react": "*", 5548 - "@types/react-dom": "*", 5549 - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", 5550 - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 5551 - }, 5552 - "peerDependenciesMeta": { 5553 - "@types/react": { 5554 - "optional": true 5555 - }, 5556 - "@types/react-dom": { 5557 - "optional": true 5558 - } 5559 - } 5560 - }, 5561 - "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-primitive": { 5562 - "version": "2.1.2", 5563 - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.2.tgz", 5564 - "integrity": "sha512-uHa+l/lKfxuDD2zjN/0peM/RhhSmRjr5YWdk/37EnSv1nJ88uvG85DPexSm8HdFQROd2VdERJ6ynXbkCFi+APw==", 5565 - "dependencies": { 5566 - "@radix-ui/react-slot": "1.2.2" 5567 - }, 5568 - "peerDependencies": { 5569 - "@types/react": "*", 5570 - "@types/react-dom": "*", 5571 - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", 5572 - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 5573 - }, 5574 - "peerDependenciesMeta": { 5575 - "@types/react": { 5576 - "optional": true 5577 - }, 5578 - "@types/react-dom": { 5579 - "optional": true 5580 - } 5581 - } 5582 - }, 5583 - "node_modules/@radix-ui/react-compose-refs": { 5584 - "version": "1.1.2", 5585 - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", 5586 - "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", 5587 - "peerDependencies": { 5588 - "@types/react": "*", 5589 - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 5590 - }, 5591 - "peerDependenciesMeta": { 5592 - "@types/react": { 5593 - "optional": true 5594 - } 5595 - } 5596 - }, 5597 - "node_modules/@radix-ui/react-context": { 5598 - "version": "1.1.2", 5599 - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", 5600 - "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", 5601 - "peerDependencies": { 5602 - "@types/react": "*", 5603 - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 5604 - }, 5605 - "peerDependenciesMeta": { 5606 - "@types/react": { 5607 - "optional": true 5608 - } 5609 - } 5610 - }, 5611 - "node_modules/@radix-ui/react-direction": { 5612 - "version": "1.1.1", 5613 - "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", 5614 - "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", 5615 - "peerDependencies": { 5616 - "@types/react": "*", 5617 - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 5618 - }, 5619 - "peerDependenciesMeta": { 5620 - "@types/react": { 5621 - "optional": true 5622 - } 5623 - } 5624 - }, 5625 - "node_modules/@radix-ui/react-dismissable-layer": { 5626 - "version": "1.1.9", 5627 - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.9.tgz", 5628 - "integrity": "sha512-way197PiTvNp+WBP7svMJasHl+vibhWGQDb6Mgf5mhEWJkgb85z7Lfl9TUdkqpWsf8GRNmoopx9ZxCyDzmgRMQ==", 5629 - "dependencies": { 5630 - "@radix-ui/primitive": "1.1.2", 5631 - "@radix-ui/react-compose-refs": "1.1.2", 5632 - "@radix-ui/react-primitive": "2.1.2", 5633 - "@radix-ui/react-use-callback-ref": "1.1.1", 5634 - "@radix-ui/react-use-escape-keydown": "1.1.1" 5635 - }, 5636 - "peerDependencies": { 5637 - "@types/react": "*", 5638 - "@types/react-dom": "*", 5639 - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", 5640 - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 5641 - }, 5642 - "peerDependenciesMeta": { 5643 - "@types/react": { 5644 - "optional": true 5645 - }, 5646 - "@types/react-dom": { 5647 - "optional": true 5648 - } 5649 - } 5650 - }, 5651 - "node_modules/@radix-ui/react-dismissable-layer/node_modules/@radix-ui/react-primitive": { 5652 - "version": "2.1.2", 5653 - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.2.tgz", 5654 - "integrity": "sha512-uHa+l/lKfxuDD2zjN/0peM/RhhSmRjr5YWdk/37EnSv1nJ88uvG85DPexSm8HdFQROd2VdERJ6ynXbkCFi+APw==", 5655 - "dependencies": { 5656 - "@radix-ui/react-slot": "1.2.2" 5657 - }, 5658 - "peerDependencies": { 5659 - "@types/react": "*", 5660 - "@types/react-dom": "*", 5661 - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", 5662 - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 5663 - }, 5664 - "peerDependenciesMeta": { 5665 - "@types/react": { 5666 - "optional": true 5667 - }, 5668 - "@types/react-dom": { 5669 - "optional": true 5670 - } 5671 - } 5672 - }, 5673 - "node_modules/@radix-ui/react-focus-guards": { 5674 - "version": "1.1.2", 5675 - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.2.tgz", 5676 - "integrity": "sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==", 5677 - "peerDependencies": { 5678 - "@types/react": "*", 5679 - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 5680 - }, 5681 - "peerDependenciesMeta": { 5682 - "@types/react": { 5683 - "optional": true 5684 - } 5685 - } 5686 - }, 5687 - "node_modules/@radix-ui/react-focus-scope": { 5688 - "version": "1.1.6", 5689 - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.6.tgz", 5690 - "integrity": "sha512-r9zpYNUQY+2jWHWZGyddQLL9YHkM/XvSFHVcWs7bdVuxMAnCwTAuy6Pf47Z4nw7dYcUou1vg/VgjjrrH03VeBw==", 5691 - "dependencies": { 5692 - "@radix-ui/react-compose-refs": "1.1.2", 5693 - "@radix-ui/react-primitive": "2.1.2", 5694 - "@radix-ui/react-use-callback-ref": "1.1.1" 5695 - }, 5696 - "peerDependencies": { 5697 - "@types/react": "*", 5698 - "@types/react-dom": "*", 5699 - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", 5700 - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 5701 - }, 5702 - "peerDependenciesMeta": { 5703 - "@types/react": { 5704 - "optional": true 5705 - }, 5706 - "@types/react-dom": { 5707 - "optional": true 5708 - } 5709 - } 5710 - }, 5711 - "node_modules/@radix-ui/react-focus-scope/node_modules/@radix-ui/react-primitive": { 5712 - "version": "2.1.2", 5713 - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.2.tgz", 5714 - "integrity": "sha512-uHa+l/lKfxuDD2zjN/0peM/RhhSmRjr5YWdk/37EnSv1nJ88uvG85DPexSm8HdFQROd2VdERJ6ynXbkCFi+APw==", 5715 - "dependencies": { 5716 - "@radix-ui/react-slot": "1.2.2" 5717 - }, 5718 - "peerDependencies": { 5719 - "@types/react": "*", 5720 - "@types/react-dom": "*", 5721 - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", 5722 - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 5723 - }, 5724 - "peerDependenciesMeta": { 5725 - "@types/react": { 5726 - "optional": true 5727 - }, 5728 - "@types/react-dom": { 5729 - "optional": true 5730 - } 5731 - } 5732 - }, 5733 - "node_modules/@radix-ui/react-id": { 5734 - "version": "1.1.1", 5735 - "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", 5736 - "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", 5737 - "dependencies": { 5738 - "@radix-ui/react-use-layout-effect": "1.1.1" 5739 - }, 5740 - "peerDependencies": { 5741 - "@types/react": "*", 5742 - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 5743 - }, 5744 - "peerDependenciesMeta": { 5745 - "@types/react": { 5746 - "optional": true 5747 - } 5748 - } 5749 - }, 5750 - "node_modules/@radix-ui/react-label": { 5751 - "version": "2.1.6", 5752 - "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.6.tgz", 5753 - "integrity": "sha512-S/hv1mTlgcPX2gCTJrWuTjSXf7ER3Zf7zWGtOprxhIIY93Qin3n5VgNA0Ez9AgrK/lEtlYgzLd4f5x6AVar4Yw==", 5754 - "dependencies": { 5755 - "@radix-ui/react-primitive": "2.1.2" 5756 - }, 5757 - "peerDependencies": { 5758 - "@types/react": "*", 5759 - "@types/react-dom": "*", 5760 - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", 5761 - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 5762 - }, 5763 - "peerDependenciesMeta": { 5764 - "@types/react": { 5765 - "optional": true 5766 - }, 5767 - "@types/react-dom": { 5768 - "optional": true 5769 - } 5770 - } 5771 - }, 5772 - "node_modules/@radix-ui/react-label/node_modules/@radix-ui/react-primitive": { 5773 - "version": "2.1.2", 5774 - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.2.tgz", 5775 - "integrity": "sha512-uHa+l/lKfxuDD2zjN/0peM/RhhSmRjr5YWdk/37EnSv1nJ88uvG85DPexSm8HdFQROd2VdERJ6ynXbkCFi+APw==", 5776 - "dependencies": { 5777 - "@radix-ui/react-slot": "1.2.2" 5778 - }, 5779 - "peerDependencies": { 5780 - "@types/react": "*", 5781 - "@types/react-dom": "*", 5782 - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", 5783 - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 5784 - }, 5785 - "peerDependenciesMeta": { 5786 - "@types/react": { 5787 - "optional": true 5788 - }, 5789 - "@types/react-dom": { 5790 - "optional": true 5791 - } 5792 - } 5793 - }, 5794 - "node_modules/@radix-ui/react-popper": { 5795 - "version": "1.2.6", 5796 - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.6.tgz", 5797 - "integrity": "sha512-7iqXaOWIjDBfIG7aq8CUEeCSsQMLFdn7VEE8TaFz704DtEzpPHR7w/uuzRflvKgltqSAImgcmxQ7fFX3X7wasg==", 5798 - "dependencies": { 5799 - "@floating-ui/react-dom": "^2.0.0", 5800 - "@radix-ui/react-arrow": "1.1.6", 5801 - "@radix-ui/react-compose-refs": "1.1.2", 5802 - "@radix-ui/react-context": "1.1.2", 5803 - "@radix-ui/react-primitive": "2.1.2", 5804 - "@radix-ui/react-use-callback-ref": "1.1.1", 5805 - "@radix-ui/react-use-layout-effect": "1.1.1", 5806 - "@radix-ui/react-use-rect": "1.1.1", 5807 - "@radix-ui/react-use-size": "1.1.1", 5808 - "@radix-ui/rect": "1.1.1" 5809 - }, 5810 - "peerDependencies": { 5811 - "@types/react": "*", 5812 - "@types/react-dom": "*", 5813 - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", 5814 - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 5815 - }, 5816 - "peerDependenciesMeta": { 5817 - "@types/react": { 5818 - "optional": true 5819 - }, 5820 - "@types/react-dom": { 5821 - "optional": true 5822 - } 5823 - } 5824 - }, 5825 - "node_modules/@radix-ui/react-popper/node_modules/@radix-ui/react-primitive": { 5826 - "version": "2.1.2", 5827 - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.2.tgz", 5828 - "integrity": "sha512-uHa+l/lKfxuDD2zjN/0peM/RhhSmRjr5YWdk/37EnSv1nJ88uvG85DPexSm8HdFQROd2VdERJ6ynXbkCFi+APw==", 5829 - "dependencies": { 5830 - "@radix-ui/react-slot": "1.2.2" 5831 - }, 5832 - "peerDependencies": { 5833 - "@types/react": "*", 5834 - "@types/react-dom": "*", 5835 - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", 5836 - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 5837 - }, 5838 - "peerDependenciesMeta": { 5839 - "@types/react": { 5840 - "optional": true 5841 - }, 5842 - "@types/react-dom": { 5843 - "optional": true 5844 - } 5845 - } 5846 - }, 5847 - "node_modules/@radix-ui/react-portal": { 5848 - "version": "1.1.8", 5849 - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.8.tgz", 5850 - "integrity": "sha512-hQsTUIn7p7fxCPvao/q6wpbxmCwgLrlz+nOrJgC+RwfZqWY/WN+UMqkXzrtKbPrF82P43eCTl3ekeKuyAQbFeg==", 5851 - "dependencies": { 5852 - "@radix-ui/react-primitive": "2.1.2", 5853 - "@radix-ui/react-use-layout-effect": "1.1.1" 5854 - }, 5855 - "peerDependencies": { 5856 - "@types/react": "*", 5857 - "@types/react-dom": "*", 5858 - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", 5859 - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 5860 - }, 5861 - "peerDependenciesMeta": { 5862 - "@types/react": { 5863 - "optional": true 5864 - }, 5865 - "@types/react-dom": { 5866 - "optional": true 5867 - } 5868 - } 5869 - }, 5870 - "node_modules/@radix-ui/react-portal/node_modules/@radix-ui/react-primitive": { 5871 - "version": "2.1.2", 5872 - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.2.tgz", 5873 - "integrity": "sha512-uHa+l/lKfxuDD2zjN/0peM/RhhSmRjr5YWdk/37EnSv1nJ88uvG85DPexSm8HdFQROd2VdERJ6ynXbkCFi+APw==", 5874 - "dependencies": { 5875 - "@radix-ui/react-slot": "1.2.2" 5876 - }, 5877 - "peerDependencies": { 5878 - "@types/react": "*", 5879 - "@types/react-dom": "*", 5880 - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", 5881 - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 5882 - }, 5883 - "peerDependenciesMeta": { 5884 - "@types/react": { 5885 - "optional": true 5886 - }, 5887 - "@types/react-dom": { 5888 - "optional": true 5889 - } 5890 - } 5891 - }, 5892 - "node_modules/@radix-ui/react-presence": { 5893 - "version": "1.1.4", 5894 - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.4.tgz", 5895 - "integrity": "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==", 5896 - "dependencies": { 5897 - "@radix-ui/react-compose-refs": "1.1.2", 5898 - "@radix-ui/react-use-layout-effect": "1.1.1" 5899 - }, 5900 - "peerDependencies": { 5901 - "@types/react": "*", 5902 - "@types/react-dom": "*", 5903 - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", 5904 - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 5905 - }, 5906 - "peerDependenciesMeta": { 5907 - "@types/react": { 5908 - "optional": true 5909 - }, 5910 - "@types/react-dom": { 5911 - "optional": true 5912 - } 5913 - } 5914 - }, 5915 - "node_modules/@radix-ui/react-primitive": { 5916 - "version": "2.1.3", 5917 - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", 5918 - "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", 5919 - "dependencies": { 5920 - "@radix-ui/react-slot": "1.2.3" 5921 - }, 5922 - "peerDependencies": { 5923 - "@types/react": "*", 5924 - "@types/react-dom": "*", 5925 - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", 5926 - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 5927 - }, 5928 - "peerDependenciesMeta": { 5929 - "@types/react": { 5930 - "optional": true 5931 - }, 5932 - "@types/react-dom": { 5933 - "optional": true 5934 - } 5935 - } 5936 - }, 5937 - "node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": { 5938 - "version": "1.2.3", 5939 - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", 5940 - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", 5941 - "dependencies": { 5942 - "@radix-ui/react-compose-refs": "1.1.2" 5943 - }, 5944 - "peerDependencies": { 5945 - "@types/react": "*", 5946 - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 5947 - }, 5948 - "peerDependenciesMeta": { 5949 - "@types/react": { 5950 - "optional": true 5951 - } 5952 - } 5953 - }, 5954 - "node_modules/@radix-ui/react-select": { 5955 - "version": "2.2.4", 5956 - "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.4.tgz", 5957 - "integrity": "sha512-/OOm58Gil4Ev5zT8LyVzqfBcij4dTHYdeyuF5lMHZ2bIp0Lk9oETocYiJ5QC0dHekEQnK6L/FNJCceeb4AkZ6Q==", 5958 - "dependencies": { 5959 - "@radix-ui/number": "1.1.1", 5960 - "@radix-ui/primitive": "1.1.2", 5961 - "@radix-ui/react-collection": "1.1.6", 5962 - "@radix-ui/react-compose-refs": "1.1.2", 5963 - "@radix-ui/react-context": "1.1.2", 5964 - "@radix-ui/react-direction": "1.1.1", 5965 - "@radix-ui/react-dismissable-layer": "1.1.9", 5966 - "@radix-ui/react-focus-guards": "1.1.2", 5967 - "@radix-ui/react-focus-scope": "1.1.6", 5968 - "@radix-ui/react-id": "1.1.1", 5969 - "@radix-ui/react-popper": "1.2.6", 5970 - "@radix-ui/react-portal": "1.1.8", 5971 - "@radix-ui/react-primitive": "2.1.2", 5972 - "@radix-ui/react-slot": "1.2.2", 5973 - "@radix-ui/react-use-callback-ref": "1.1.1", 5974 - "@radix-ui/react-use-controllable-state": "1.2.2", 5975 - "@radix-ui/react-use-layout-effect": "1.1.1", 5976 - "@radix-ui/react-use-previous": "1.1.1", 5977 - "@radix-ui/react-visually-hidden": "1.2.2", 5978 - "aria-hidden": "^1.2.4", 5979 - "react-remove-scroll": "^2.6.3" 5980 - }, 5981 - "peerDependencies": { 5982 - "@types/react": "*", 5983 - "@types/react-dom": "*", 5984 - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", 5985 - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 5986 - }, 5987 - "peerDependenciesMeta": { 5988 - "@types/react": { 5989 - "optional": true 5990 - }, 5991 - "@types/react-dom": { 5992 - "optional": true 5993 - } 5994 - } 5995 - }, 5996 - "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-primitive": { 5997 - "version": "2.1.2", 5998 - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.2.tgz", 5999 - "integrity": "sha512-uHa+l/lKfxuDD2zjN/0peM/RhhSmRjr5YWdk/37EnSv1nJ88uvG85DPexSm8HdFQROd2VdERJ6ynXbkCFi+APw==", 6000 - "dependencies": { 6001 - "@radix-ui/react-slot": "1.2.2" 6002 - }, 6003 - "peerDependencies": { 6004 - "@types/react": "*", 6005 - "@types/react-dom": "*", 6006 - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", 6007 - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 6008 - }, 6009 - "peerDependenciesMeta": { 6010 - "@types/react": { 6011 - "optional": true 6012 - }, 6013 - "@types/react-dom": { 6014 - "optional": true 6015 - } 6016 - } 6017 - }, 6018 - "node_modules/@radix-ui/react-slot": { 6019 - "version": "1.2.2", 6020 - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.2.tgz", 6021 - "integrity": "sha512-y7TBO4xN4Y94FvcWIOIh18fM4R1A8S4q1jhoz4PNzOoHsFcN8pogcFmZrTYAm4F9VRUrWP/Mw7xSKybIeRI+CQ==", 6022 - "dependencies": { 6023 - "@radix-ui/react-compose-refs": "1.1.2" 6024 - }, 6025 - "peerDependencies": { 6026 - "@types/react": "*", 6027 - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 6028 - }, 6029 - "peerDependenciesMeta": { 6030 - "@types/react": { 6031 - "optional": true 6032 - } 6033 - } 6034 - }, 6035 - "node_modules/@radix-ui/react-use-callback-ref": { 6036 - "version": "1.1.1", 6037 - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", 6038 - "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", 6039 - "peerDependencies": { 6040 - "@types/react": "*", 6041 - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 6042 - }, 6043 - "peerDependenciesMeta": { 6044 - "@types/react": { 6045 - "optional": true 6046 - } 6047 - } 6048 - }, 6049 - "node_modules/@radix-ui/react-use-controllable-state": { 6050 - "version": "1.2.2", 6051 - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", 6052 - "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", 6053 - "dependencies": { 6054 - "@radix-ui/react-use-effect-event": "0.0.2", 6055 - "@radix-ui/react-use-layout-effect": "1.1.1" 6056 - }, 6057 - "peerDependencies": { 6058 - "@types/react": "*", 6059 - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 6060 - }, 6061 - "peerDependenciesMeta": { 6062 - "@types/react": { 6063 - "optional": true 6064 - } 6065 - } 6066 - }, 6067 - "node_modules/@radix-ui/react-use-effect-event": { 6068 - "version": "0.0.2", 6069 - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", 6070 - "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", 6071 - "dependencies": { 6072 - "@radix-ui/react-use-layout-effect": "1.1.1" 6073 - }, 6074 - "peerDependencies": { 6075 - "@types/react": "*", 6076 - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 6077 - }, 6078 - "peerDependenciesMeta": { 6079 - "@types/react": { 6080 - "optional": true 6081 - } 6082 - } 6083 - }, 6084 - "node_modules/@radix-ui/react-use-escape-keydown": { 6085 - "version": "1.1.1", 6086 - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", 6087 - "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", 6088 - "dependencies": { 6089 - "@radix-ui/react-use-callback-ref": "1.1.1" 6090 - }, 6091 - "peerDependencies": { 6092 - "@types/react": "*", 6093 - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 6094 - }, 6095 - "peerDependenciesMeta": { 6096 - "@types/react": { 6097 - "optional": true 6098 - } 6099 - } 6100 - }, 6101 - "node_modules/@radix-ui/react-use-layout-effect": { 6102 - "version": "1.1.1", 6103 - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", 6104 - "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", 6105 - "peerDependencies": { 6106 - "@types/react": "*", 6107 - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 6108 - }, 6109 - "peerDependenciesMeta": { 6110 - "@types/react": { 6111 - "optional": true 6112 - } 6113 - } 6114 - }, 6115 - "node_modules/@radix-ui/react-use-previous": { 6116 - "version": "1.1.1", 6117 - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz", 6118 - "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==", 6119 - "peerDependencies": { 6120 - "@types/react": "*", 6121 - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 6122 - }, 6123 - "peerDependenciesMeta": { 6124 - "@types/react": { 6125 - "optional": true 6126 - } 6127 - } 6128 - }, 6129 - "node_modules/@radix-ui/react-use-rect": { 6130 - "version": "1.1.1", 6131 - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz", 6132 - "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==", 6133 - "dependencies": { 6134 - "@radix-ui/rect": "1.1.1" 6135 - }, 6136 - "peerDependencies": { 6137 - "@types/react": "*", 6138 - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 6139 - }, 6140 - "peerDependenciesMeta": { 6141 - "@types/react": { 6142 - "optional": true 6143 - } 6144 - } 6145 - }, 6146 - "node_modules/@radix-ui/react-use-size": { 6147 - "version": "1.1.1", 6148 - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", 6149 - "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", 6150 - "dependencies": { 6151 - "@radix-ui/react-use-layout-effect": "1.1.1" 6152 - }, 6153 - "peerDependencies": { 6154 - "@types/react": "*", 6155 - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 6156 - }, 6157 - "peerDependenciesMeta": { 6158 - "@types/react": { 6159 - "optional": true 6160 - } 6161 - } 6162 - }, 6163 - "node_modules/@radix-ui/react-visually-hidden": { 6164 - "version": "1.2.2", 6165 - "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.2.tgz", 6166 - "integrity": "sha512-ORCmRUbNiZIv6uV5mhFrhsIKw4UX/N3syZtyqvry61tbGm4JlgQuSn0hk5TwCARsCjkcnuRkSdCE3xfb+ADHew==", 6167 - "dependencies": { 6168 - "@radix-ui/react-primitive": "2.1.2" 6169 - }, 6170 - "peerDependencies": { 6171 - "@types/react": "*", 6172 - "@types/react-dom": "*", 6173 - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", 6174 - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 6175 - }, 6176 - "peerDependenciesMeta": { 6177 - "@types/react": { 6178 - "optional": true 6179 - }, 6180 - "@types/react-dom": { 6181 - "optional": true 6182 - } 6183 - } 6184 - }, 6185 - "node_modules/@radix-ui/react-visually-hidden/node_modules/@radix-ui/react-primitive": { 6186 - "version": "2.1.2", 6187 - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.2.tgz", 6188 - "integrity": "sha512-uHa+l/lKfxuDD2zjN/0peM/RhhSmRjr5YWdk/37EnSv1nJ88uvG85DPexSm8HdFQROd2VdERJ6ynXbkCFi+APw==", 6189 - "dependencies": { 6190 - "@radix-ui/react-slot": "1.2.2" 6191 - }, 6192 - "peerDependencies": { 6193 - "@types/react": "*", 6194 - "@types/react-dom": "*", 6195 - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", 6196 - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 6197 - }, 6198 - "peerDependenciesMeta": { 6199 - "@types/react": { 6200 - "optional": true 6201 - }, 6202 - "@types/react-dom": { 6203 - "optional": true 6204 - } 6205 - } 6206 - }, 6207 - "node_modules/@radix-ui/rect": { 6208 - "version": "1.1.1", 6209 - "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz", 6210 - "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==" 6211 - }, 6212 5567 "node_modules/@rtsao/scc": { 6213 5568 "version": "1.1.0", 6214 5569 "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", ··· 6879 6234 "version": "19.1.6", 6880 6235 "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.6.tgz", 6881 6236 "integrity": "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==", 6882 - "devOptional": true, 6237 + "dev": true, 6883 6238 "peerDependencies": { 6884 6239 "@types/react": "^19.0.0" 6885 6240 } ··· 7523 6878 "version": "5.0.1", 7524 6879 "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 7525 6880 "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 6881 + "dev": true, 7526 6882 "engines": { 7527 6883 "node": ">=8" 7528 6884 } ··· 7531 6887 "version": "4.3.0", 7532 6888 "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 7533 6889 "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 6890 + "dev": true, 7534 6891 "dependencies": { 7535 6892 "color-convert": "^2.0.1" 7536 6893 }, ··· 7544 6901 "node_modules/any-promise": { 7545 6902 "version": "1.3.0", 7546 6903 "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", 7547 - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" 6904 + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", 6905 + "dev": true 7548 6906 }, 7549 6907 "node_modules/anymatch": { 7550 6908 "version": "3.1.3", 7551 6909 "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", 7552 6910 "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", 6911 + "dev": true, 7553 6912 "dependencies": { 7554 6913 "normalize-path": "^3.0.0", 7555 6914 "picomatch": "^2.0.4" ··· 7557 6916 "engines": { 7558 6917 "node": ">= 8" 7559 6918 } 7560 - }, 7561 - "node_modules/arg": { 7562 - "version": "5.0.2", 7563 - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", 7564 - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" 7565 6919 }, 7566 6920 "node_modules/argparse": { 7567 6921 "version": "2.0.1", 7568 6922 "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 7569 6923 "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", 7570 6924 "dev": true 7571 - }, 7572 - "node_modules/aria-hidden": { 7573 - "version": "1.2.4", 7574 - "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz", 7575 - "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==", 7576 - "dependencies": { 7577 - "tslib": "^2.0.0" 7578 - }, 7579 - "engines": { 7580 - "node": ">=10" 7581 - } 7582 6925 }, 7583 6926 "node_modules/aria-query": { 7584 6927 "version": "5.3.2", ··· 7763 7106 "node": ">= 0.4" 7764 7107 } 7765 7108 }, 7109 + "node_modules/attr-accept": { 7110 + "version": "2.2.5", 7111 + "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.5.tgz", 7112 + "integrity": "sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ==", 7113 + "engines": { 7114 + "node": ">=4" 7115 + } 7116 + }, 7766 7117 "node_modules/autoprefixer": { 7767 7118 "version": "10.4.21", 7768 7119 "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", ··· 7836 7187 "node_modules/balanced-match": { 7837 7188 "version": "1.0.2", 7838 7189 "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 7839 - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" 7190 + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 7191 + "dev": true 7840 7192 }, 7841 7193 "node_modules/base-x": { 7842 7194 "version": "3.0.11", ··· 7871 7223 "version": "2.3.0", 7872 7224 "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", 7873 7225 "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", 7226 + "dev": true, 7874 7227 "engines": { 7875 7228 "node": ">=8" 7876 7229 }, ··· 7904 7257 "version": "3.0.3", 7905 7258 "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", 7906 7259 "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", 7260 + "dev": true, 7907 7261 "dependencies": { 7908 7262 "fill-range": "^7.1.1" 7909 7263 }, ··· 8090 7444 "version": "2.0.1", 8091 7445 "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", 8092 7446 "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", 7447 + "dev": true, 8093 7448 "engines": { 8094 7449 "node": ">= 6" 8095 7450 } ··· 8145 7500 "version": "3.6.0", 8146 7501 "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", 8147 7502 "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", 7503 + "dev": true, 8148 7504 "dependencies": { 8149 7505 "anymatch": "~3.1.2", 8150 7506 "braces": "~3.0.2", ··· 8168 7524 "version": "5.1.2", 8169 7525 "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 8170 7526 "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 7527 + "dev": true, 8171 7528 "dependencies": { 8172 7529 "is-glob": "^4.0.1" 8173 7530 }, ··· 8184 7541 "node": ">=6.0" 8185 7542 } 8186 7543 }, 8187 - "node_modules/class-variance-authority": { 8188 - "version": "0.7.1", 8189 - "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", 8190 - "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", 8191 - "dependencies": { 8192 - "clsx": "^2.1.1" 8193 - }, 8194 - "funding": { 8195 - "url": "https://polar.sh/cva" 8196 - } 8197 - }, 8198 7544 "node_modules/cli-width": { 8199 7545 "version": "4.1.0", 8200 7546 "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", ··· 8265 7611 "version": "2.0.1", 8266 7612 "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 8267 7613 "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 7614 + "devOptional": true, 8268 7615 "dependencies": { 8269 7616 "color-name": "~1.1.4" 8270 7617 }, ··· 8275 7622 "node_modules/color-name": { 8276 7623 "version": "1.1.4", 8277 7624 "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 8278 - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" 7625 + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 7626 + "devOptional": true 8279 7627 }, 8280 7628 "node_modules/color-string": { 8281 7629 "version": "1.9.1", ··· 8291 7639 "version": "4.1.1", 8292 7640 "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", 8293 7641 "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", 7642 + "dev": true, 8294 7643 "engines": { 8295 7644 "node": ">= 6" 8296 7645 } ··· 8367 7716 "version": "7.0.6", 8368 7717 "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", 8369 7718 "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", 7719 + "dev": true, 8370 7720 "dependencies": { 8371 7721 "path-key": "^3.1.0", 8372 7722 "shebang-command": "^2.0.0", ··· 8499 7849 "version": "3.0.0", 8500 7850 "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", 8501 7851 "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", 7852 + "dev": true, 8502 7853 "bin": { 8503 7854 "cssesc": "bin/cssesc" 8504 7855 }, ··· 8548 7899 "node_modules/csstype": { 8549 7900 "version": "3.1.3", 8550 7901 "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", 8551 - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", 8552 - "devOptional": true 7902 + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" 8553 7903 }, 8554 7904 "node_modules/damerau-levenshtein": { 8555 7905 "version": "1.0.8", ··· 8616 7966 "type": "github", 8617 7967 "url": "https://github.com/sponsors/kossnocorp" 8618 7968 } 7969 + }, 7970 + "node_modules/dayjs": { 7971 + "version": "1.11.13", 7972 + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", 7973 + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==" 8619 7974 }, 8620 7975 "node_modules/debug": { 8621 7976 "version": "4.4.1", ··· 8745 8100 "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", 8746 8101 "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" 8747 8102 }, 8748 - "node_modules/didyoumean": { 8749 - "version": "1.2.2", 8750 - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", 8751 - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" 8752 - }, 8753 8103 "node_modules/dir-glob": { 8754 8104 "version": "3.0.1", 8755 8105 "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", ··· 8762 8112 "node": ">=8" 8763 8113 } 8764 8114 }, 8765 - "node_modules/dlv": { 8766 - "version": "1.1.3", 8767 - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", 8768 - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" 8769 - }, 8770 8115 "node_modules/doctrine": { 8771 8116 "version": "3.0.0", 8772 8117 "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", ··· 8777 8122 }, 8778 8123 "engines": { 8779 8124 "node": ">=6.0.0" 8125 + } 8126 + }, 8127 + "node_modules/dom-helpers": { 8128 + "version": "5.2.1", 8129 + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", 8130 + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", 8131 + "dependencies": { 8132 + "@babel/runtime": "^7.8.7", 8133 + "csstype": "^3.0.2" 8780 8134 } 8781 8135 }, 8782 8136 "node_modules/dom-serializer": { ··· 8887 8241 "node_modules/eastasianwidth": { 8888 8242 "version": "0.2.0", 8889 8243 "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", 8890 - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" 8244 + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", 8245 + "dev": true 8891 8246 }, 8892 8247 "node_modules/electron-to-chromium": { 8893 8248 "version": "1.5.152", ··· 8898 8253 "node_modules/emoji-regex": { 8899 8254 "version": "9.2.2", 8900 8255 "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", 8901 - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" 8256 + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", 8257 + "dev": true 8902 8258 }, 8903 8259 "node_modules/entities": { 8904 8260 "version": "4.5.0", ··· 9676 9032 "node_modules/fast-deep-equal": { 9677 9033 "version": "3.1.3", 9678 9034 "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 9679 - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", 9680 - "dev": true 9035 + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" 9681 9036 }, 9682 9037 "node_modules/fast-glob": { 9683 9038 "version": "3.3.3", 9684 9039 "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", 9685 9040 "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", 9041 + "dev": true, 9686 9042 "dependencies": { 9687 9043 "@nodelib/fs.stat": "^2.0.2", 9688 9044 "@nodelib/fs.walk": "^1.2.3", ··· 9698 9054 "version": "5.1.2", 9699 9055 "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 9700 9056 "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 9057 + "dev": true, 9701 9058 "dependencies": { 9702 9059 "is-glob": "^4.0.1" 9703 9060 }, ··· 9721 9078 "version": "1.19.1", 9722 9079 "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", 9723 9080 "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", 9081 + "dev": true, 9724 9082 "dependencies": { 9725 9083 "reusify": "^1.0.4" 9726 9084 } ··· 9743 9101 "node": "^10.12.0 || >=12.0.0" 9744 9102 } 9745 9103 }, 9104 + "node_modules/file-selector": { 9105 + "version": "2.1.2", 9106 + "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-2.1.2.tgz", 9107 + "integrity": "sha512-QgXo+mXTe8ljeqUFaX3QVHc5osSItJ/Km+xpocx0aSqWGMSCf6qYs/VnzZgS864Pjn5iceMRFigeAV7AfTlaig==", 9108 + "dependencies": { 9109 + "tslib": "^2.7.0" 9110 + }, 9111 + "engines": { 9112 + "node": ">= 12" 9113 + } 9114 + }, 9746 9115 "node_modules/fill-range": { 9747 9116 "version": "7.1.1", 9748 9117 "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", 9749 9118 "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", 9119 + "dev": true, 9750 9120 "dependencies": { 9751 9121 "to-regex-range": "^5.0.1" 9752 9122 }, ··· 9809 9179 "version": "3.3.1", 9810 9180 "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", 9811 9181 "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", 9182 + "dev": true, 9812 9183 "dependencies": { 9813 9184 "cross-spawn": "^7.0.6", 9814 9185 "signal-exit": "^4.0.1" ··· 9866 9237 "version": "2.3.3", 9867 9238 "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", 9868 9239 "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 9240 + "dev": true, 9869 9241 "hasInstallScript": true, 9870 9242 "optional": true, 9871 9243 "os": [ ··· 9879 9251 "version": "1.1.2", 9880 9252 "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", 9881 9253 "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", 9254 + "dev": true, 9882 9255 "funding": { 9883 9256 "url": "https://github.com/sponsors/ljharb" 9884 9257 } ··· 10027 9400 "version": "10.3.10", 10028 9401 "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", 10029 9402 "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", 9403 + "dev": true, 10030 9404 "dependencies": { 10031 9405 "foreground-child": "^3.1.0", 10032 9406 "jackspeak": "^2.3.5", ··· 10048 9422 "version": "6.0.2", 10049 9423 "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", 10050 9424 "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", 9425 + "dev": true, 10051 9426 "dependencies": { 10052 9427 "is-glob": "^4.0.3" 10053 9428 }, ··· 10059 9434 "version": "2.0.1", 10060 9435 "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", 10061 9436 "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", 9437 + "dev": true, 10062 9438 "dependencies": { 10063 9439 "balanced-match": "^1.0.0" 10064 9440 } ··· 10067 9443 "version": "9.0.5", 10068 9444 "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", 10069 9445 "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", 9446 + "dev": true, 10070 9447 "dependencies": { 10071 9448 "brace-expansion": "^2.0.1" 10072 9449 }, ··· 10286 9663 "version": "2.0.2", 10287 9664 "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", 10288 9665 "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", 9666 + "dev": true, 10289 9667 "dependencies": { 10290 9668 "function-bind": "^1.1.2" 10291 9669 }, ··· 10633 10011 "version": "2.1.0", 10634 10012 "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 10635 10013 "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 10014 + "dev": true, 10636 10015 "dependencies": { 10637 10016 "binary-extensions": "^2.0.0" 10638 10017 }, ··· 10681 10060 "version": "2.16.1", 10682 10061 "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", 10683 10062 "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", 10063 + "dev": true, 10684 10064 "dependencies": { 10685 10065 "hasown": "^2.0.2" 10686 10066 }, ··· 10728 10108 "version": "2.1.1", 10729 10109 "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 10730 10110 "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 10111 + "dev": true, 10731 10112 "engines": { 10732 10113 "node": ">=0.10.0" 10733 10114 } ··· 10751 10132 "version": "3.0.0", 10752 10133 "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 10753 10134 "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 10135 + "dev": true, 10754 10136 "engines": { 10755 10137 "node": ">=8" 10756 10138 } ··· 10777 10159 "version": "4.0.3", 10778 10160 "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 10779 10161 "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 10162 + "dev": true, 10780 10163 "dependencies": { 10781 10164 "is-extglob": "^2.1.1" 10782 10165 }, ··· 10806 10189 "version": "7.0.0", 10807 10190 "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 10808 10191 "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 10192 + "dev": true, 10809 10193 "engines": { 10810 10194 "node": ">=0.12.0" 10811 10195 } ··· 11019 10403 "node_modules/isexe": { 11020 10404 "version": "2.0.0", 11021 10405 "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 11022 - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" 10406 + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", 10407 + "dev": true 11023 10408 }, 11024 10409 "node_modules/iterator.prototype": { 11025 10410 "version": "1.1.5", ··· 11042 10427 "version": "2.3.6", 11043 10428 "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", 11044 10429 "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", 10430 + "dev": true, 11045 10431 "dependencies": { 11046 10432 "@isaacs/cliui": "^8.0.2" 11047 10433 }, ··· 11055 10441 "@pkgjs/parseargs": "^0.11.0" 11056 10442 } 11057 10443 }, 11058 - "node_modules/jiti": { 11059 - "version": "1.21.7", 11060 - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", 11061 - "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", 11062 - "bin": { 11063 - "jiti": "bin/jiti.js" 11064 - } 11065 - }, 11066 10444 "node_modules/joycon": { 11067 10445 "version": "3.1.1", 11068 10446 "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", ··· 11075 10453 "node_modules/js-tokens": { 11076 10454 "version": "4.0.0", 11077 10455 "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 11078 - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 11079 - "dev": true 10456 + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" 11080 10457 }, 11081 10458 "node_modules/js-yaml": { 11082 10459 "version": "4.1.0", ··· 11185 10562 "dev": true, 11186 10563 "dependencies": { 11187 10564 "json-buffer": "3.0.1" 10565 + } 10566 + }, 10567 + "node_modules/klona": { 10568 + "version": "2.0.6", 10569 + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", 10570 + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", 10571 + "engines": { 10572 + "node": ">= 8" 11188 10573 } 11189 10574 }, 11190 10575 "node_modules/ky": { ··· 11510 10895 "version": "3.1.3", 11511 10896 "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", 11512 10897 "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", 10898 + "dev": true, 11513 10899 "engines": { 11514 10900 "node": ">=14" 11515 10901 }, ··· 11520 10906 "node_modules/lines-and-columns": { 11521 10907 "version": "1.2.4", 11522 10908 "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", 11523 - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" 10909 + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", 10910 + "dev": true 11524 10911 }, 11525 10912 "node_modules/lmdb": { 11526 10913 "version": "2.7.11", ··· 11608 10995 "version": "1.4.0", 11609 10996 "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 11610 10997 "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 11611 - "dev": true, 11612 10998 "dependencies": { 11613 10999 "js-tokens": "^3.0.0 || ^4.0.0" 11614 11000 }, ··· 11631 11017 "node_modules/lru-cache": { 11632 11018 "version": "10.4.3", 11633 11019 "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", 11634 - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" 11020 + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", 11021 + "dev": true 11635 11022 }, 11636 11023 "node_modules/magic-string": { 11637 11024 "version": "0.30.17", ··· 11701 11088 "version": "1.4.1", 11702 11089 "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", 11703 11090 "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", 11091 + "dev": true, 11704 11092 "engines": { 11705 11093 "node": ">= 8" 11706 11094 } ··· 11709 11097 "version": "4.0.8", 11710 11098 "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", 11711 11099 "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", 11100 + "dev": true, 11712 11101 "dependencies": { 11713 11102 "braces": "^3.0.3", 11714 11103 "picomatch": "^2.3.1" ··· 11775 11164 "version": "7.1.2", 11776 11165 "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", 11777 11166 "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", 11167 + "dev": true, 11778 11168 "engines": { 11779 11169 "node": ">=16 || 14 >=14.17" 11780 11170 } ··· 11860 11250 "version": "2.7.0", 11861 11251 "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", 11862 11252 "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", 11253 + "dev": true, 11863 11254 "dependencies": { 11864 11255 "any-promise": "^1.0.0", 11865 11256 "object-assign": "^4.0.1", ··· 12442 11833 "version": "3.0.0", 12443 11834 "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 12444 11835 "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 11836 + "dev": true, 12445 11837 "engines": { 12446 11838 "node": ">=0.10.0" 12447 11839 } ··· 12505 11897 "node": ">=0.10.0" 12506 11898 } 12507 11899 }, 12508 - "node_modules/object-hash": { 12509 - "version": "3.0.0", 12510 - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", 12511 - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", 12512 - "engines": { 12513 - "node": ">= 6" 12514 - } 12515 - }, 12516 11900 "node_modules/object-inspect": { 12517 11901 "version": "1.13.4", 12518 11902 "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", ··· 12810 12194 "version": "3.1.1", 12811 12195 "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 12812 12196 "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 12197 + "dev": true, 12813 12198 "engines": { 12814 12199 "node": ">=8" 12815 12200 } ··· 12817 12202 "node_modules/path-parse": { 12818 12203 "version": "1.0.7", 12819 12204 "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 12820 - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" 12205 + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 12206 + "dev": true 12821 12207 }, 12822 12208 "node_modules/path-scurry": { 12823 12209 "version": "1.11.1", 12824 12210 "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", 12825 12211 "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", 12212 + "dev": true, 12826 12213 "dependencies": { 12827 12214 "lru-cache": "^10.2.0", 12828 12215 "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" ··· 12872 12259 "version": "2.3.1", 12873 12260 "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 12874 12261 "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 12262 + "dev": true, 12875 12263 "engines": { 12876 12264 "node": ">=8.6" 12877 12265 }, ··· 12879 12267 "url": "https://github.com/sponsors/jonschlinkert" 12880 12268 } 12881 12269 }, 12882 - "node_modules/pify": { 12883 - "version": "2.3.0", 12884 - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", 12885 - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", 12886 - "engines": { 12887 - "node": ">=0.10.0" 12888 - } 12889 - }, 12890 12270 "node_modules/pirates": { 12891 12271 "version": "4.0.7", 12892 12272 "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", 12893 12273 "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", 12274 + "dev": true, 12894 12275 "engines": { 12895 12276 "node": ">= 6" 12896 12277 } ··· 13016 12397 } 13017 12398 }, 13018 12399 "node_modules/postcss": { 13019 - "version": "8.5.3", 13020 - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", 13021 - "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", 12400 + "version": "8.5.6", 12401 + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", 12402 + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", 12403 + "dev": true, 13022 12404 "funding": [ 13023 12405 { 13024 12406 "type": "opencollective", ··· 13034 12416 } 13035 12417 ], 13036 12418 "dependencies": { 13037 - "nanoid": "^3.3.8", 12419 + "nanoid": "^3.3.11", 13038 12420 "picocolors": "^1.1.1", 13039 12421 "source-map-js": "^1.2.1" 13040 12422 }, ··· 13042 12424 "node": "^10 || ^12 || >=14" 13043 12425 } 13044 12426 }, 13045 - "node_modules/postcss-import": { 13046 - "version": "15.1.0", 13047 - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", 13048 - "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", 13049 - "dependencies": { 13050 - "postcss-value-parser": "^4.0.0", 13051 - "read-cache": "^1.0.0", 13052 - "resolve": "^1.1.7" 13053 - }, 13054 - "engines": { 13055 - "node": ">=14.0.0" 13056 - }, 13057 - "peerDependencies": { 13058 - "postcss": "^8.0.0" 13059 - } 13060 - }, 13061 12427 "node_modules/postcss-js": { 13062 12428 "version": "4.0.1", 13063 12429 "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", 13064 12430 "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", 12431 + "dev": true, 13065 12432 "dependencies": { 13066 12433 "camelcase-css": "^2.0.1" 13067 12434 }, ··· 13080 12447 "version": "4.0.2", 13081 12448 "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", 13082 12449 "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", 12450 + "dev": true, 13083 12451 "funding": [ 13084 12452 { 13085 12453 "type": "opencollective", ··· 13110 12478 } 13111 12479 } 13112 12480 }, 13113 - "node_modules/postcss-nested": { 13114 - "version": "6.2.0", 13115 - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", 13116 - "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", 12481 + "node_modules/postcss-mixins": { 12482 + "version": "12.0.0", 12483 + "resolved": "https://registry.npmjs.org/postcss-mixins/-/postcss-mixins-12.0.0.tgz", 12484 + "integrity": "sha512-br7vXwoA5niiQAW3BLgd66xoGie/JJ1O4k7uLDbb+fbdYFXouxAjppIkBZDpPtSIzx63WeVXRGEYStSYa5kQmw==", 12485 + "dev": true, 13117 12486 "funding": [ 13118 12487 { 13119 12488 "type": "opencollective", ··· 13125 12494 } 13126 12495 ], 13127 12496 "dependencies": { 13128 - "postcss-selector-parser": "^6.1.1" 12497 + "postcss-js": "^4.0.1", 12498 + "postcss-simple-vars": "^7.0.1", 12499 + "sugarss": "^5.0.0", 12500 + "tinyglobby": "^0.2.14" 13129 12501 }, 13130 12502 "engines": { 13131 - "node": ">=12.0" 12503 + "node": "^20.0 || ^22.0 || >=24.0" 13132 12504 }, 13133 12505 "peerDependencies": { 13134 12506 "postcss": "^8.2.14" 13135 12507 } 13136 12508 }, 13137 - "node_modules/postcss-selector-parser": { 13138 - "version": "6.1.2", 13139 - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", 13140 - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", 12509 + "node_modules/postcss-preset-mantine": { 12510 + "version": "1.18.0", 12511 + "resolved": "https://registry.npmjs.org/postcss-preset-mantine/-/postcss-preset-mantine-1.18.0.tgz", 12512 + "integrity": "sha512-sP6/s1oC7cOtBdl4mw/IRKmKvYTuzpRrH/vT6v9enMU/EQEQ31eQnHcWtFghOXLH87AAthjL/Q75rLmin1oZoA==", 12513 + "dev": true, 12514 + "dependencies": { 12515 + "postcss-mixins": "^12.0.0", 12516 + "postcss-nested": "^7.0.2" 12517 + }, 12518 + "peerDependencies": { 12519 + "postcss": ">=8.0.0" 12520 + } 12521 + }, 12522 + "node_modules/postcss-preset-mantine/node_modules/postcss-nested": { 12523 + "version": "7.0.2", 12524 + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-7.0.2.tgz", 12525 + "integrity": "sha512-5osppouFc0VR9/VYzYxO03VaDa3e8F23Kfd6/9qcZTUI8P58GIYlArOET2Wq0ywSl2o2PjELhYOFI4W7l5QHKw==", 12526 + "dev": true, 12527 + "funding": [ 12528 + { 12529 + "type": "opencollective", 12530 + "url": "https://opencollective.com/postcss/" 12531 + }, 12532 + { 12533 + "type": "github", 12534 + "url": "https://github.com/sponsors/ai" 12535 + } 12536 + ], 12537 + "dependencies": { 12538 + "postcss-selector-parser": "^7.0.0" 12539 + }, 12540 + "engines": { 12541 + "node": ">=18.0" 12542 + }, 12543 + "peerDependencies": { 12544 + "postcss": "^8.2.14" 12545 + } 12546 + }, 12547 + "node_modules/postcss-preset-mantine/node_modules/postcss-selector-parser": { 12548 + "version": "7.1.0", 12549 + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", 12550 + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", 12551 + "dev": true, 13141 12552 "dependencies": { 13142 12553 "cssesc": "^3.0.0", 13143 12554 "util-deprecate": "^1.0.2" ··· 13146 12557 "node": ">=4" 13147 12558 } 13148 12559 }, 12560 + "node_modules/postcss-simple-vars": { 12561 + "version": "7.0.1", 12562 + "resolved": "https://registry.npmjs.org/postcss-simple-vars/-/postcss-simple-vars-7.0.1.tgz", 12563 + "integrity": "sha512-5GLLXaS8qmzHMOjVxqkk1TZPf1jMqesiI7qLhnlyERalG0sMbHIbJqrcnrpmZdKCLglHnRHoEBB61RtGTsj++A==", 12564 + "dev": true, 12565 + "engines": { 12566 + "node": ">=14.0" 12567 + }, 12568 + "funding": { 12569 + "type": "opencollective", 12570 + "url": "https://opencollective.com/postcss/" 12571 + }, 12572 + "peerDependencies": { 12573 + "postcss": "^8.2.1" 12574 + } 12575 + }, 13149 12576 "node_modules/postcss-value-parser": { 13150 12577 "version": "4.2.0", 13151 12578 "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", 13152 - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" 12579 + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", 12580 + "dev": true 13153 12581 }, 13154 12582 "node_modules/posthtml": { 13155 12583 "version": "0.16.6", ··· 13222 12650 "version": "15.8.1", 13223 12651 "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", 13224 12652 "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", 13225 - "dev": true, 13226 12653 "dependencies": { 13227 12654 "loose-envify": "^1.4.0", 13228 12655 "object-assign": "^4.1.1", ··· 13255 12682 "version": "1.2.3", 13256 12683 "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", 13257 12684 "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", 12685 + "dev": true, 13258 12686 "funding": [ 13259 12687 { 13260 12688 "type": "github", ··· 13325 12753 "react": "^19.1.0" 13326 12754 } 13327 12755 }, 12756 + "node_modules/react-dropzone": { 12757 + "version": "14.3.8", 12758 + "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-14.3.8.tgz", 12759 + "integrity": "sha512-sBgODnq+lcA4P296DY4wacOZz3JFpD99fp+hb//iBO2HHnyeZU3FwWyXJ6salNpqQdsZrgMrotuko/BdJMV8Ug==", 12760 + "dependencies": { 12761 + "attr-accept": "^2.2.4", 12762 + "file-selector": "^2.1.0", 12763 + "prop-types": "^15.8.1" 12764 + }, 12765 + "engines": { 12766 + "node": ">= 10.13" 12767 + }, 12768 + "peerDependencies": { 12769 + "react": ">= 16.8 || 18.0.0" 12770 + } 12771 + }, 13328 12772 "node_modules/react-error-overlay": { 13329 12773 "version": "6.0.9", 13330 12774 "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz", ··· 13342 12786 "node_modules/react-is": { 13343 12787 "version": "16.13.1", 13344 12788 "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", 13345 - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", 13346 - "dev": true 12789 + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" 12790 + }, 12791 + "node_modules/react-number-format": { 12792 + "version": "5.4.4", 12793 + "resolved": "https://registry.npmjs.org/react-number-format/-/react-number-format-5.4.4.tgz", 12794 + "integrity": "sha512-wOmoNZoOpvMminhifQYiYSTCLUDOiUbBunrMrMjA+dV52sY+vck1S4UhR6PkgnoCquvvMSeJjErXZ4qSaWCliA==", 12795 + "peerDependencies": { 12796 + "react": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", 12797 + "react-dom": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" 12798 + } 13347 12799 }, 13348 12800 "node_modules/react-refresh": { 13349 12801 "version": "0.9.0", ··· 13420 12872 } 13421 12873 } 13422 12874 }, 13423 - "node_modules/read-cache": { 13424 - "version": "1.0.0", 13425 - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", 13426 - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", 12875 + "node_modules/react-textarea-autosize": { 12876 + "version": "8.5.9", 12877 + "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.9.tgz", 12878 + "integrity": "sha512-U1DGlIQN5AwgjTyOEnI1oCcMuEr1pv1qOtklB2l4nyMGbHzWrI0eFsYK0zos2YWqAolJyG0IWJaqWmWj5ETh0A==", 12879 + "dependencies": { 12880 + "@babel/runtime": "^7.20.13", 12881 + "use-composed-ref": "^1.3.0", 12882 + "use-latest": "^1.2.1" 12883 + }, 12884 + "engines": { 12885 + "node": ">=10" 12886 + }, 12887 + "peerDependencies": { 12888 + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" 12889 + } 12890 + }, 12891 + "node_modules/react-transition-group": { 12892 + "version": "4.4.5", 12893 + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", 12894 + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", 13427 12895 "dependencies": { 13428 - "pify": "^2.3.0" 12896 + "@babel/runtime": "^7.5.5", 12897 + "dom-helpers": "^5.0.1", 12898 + "loose-envify": "^1.4.0", 12899 + "prop-types": "^15.6.2" 12900 + }, 12901 + "peerDependencies": { 12902 + "react": ">=16.6.0", 12903 + "react-dom": ">=16.6.0" 13429 12904 } 13430 12905 }, 13431 12906 "node_modules/readdirp": { 13432 12907 "version": "3.6.0", 13433 12908 "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 13434 12909 "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 12910 + "dev": true, 13435 12911 "dependencies": { 13436 12912 "picomatch": "^2.2.1" 13437 12913 }, ··· 13518 12994 "version": "1.22.10", 13519 12995 "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", 13520 12996 "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", 12997 + "dev": true, 13521 12998 "dependencies": { 13522 12999 "is-core-module": "^2.16.0", 13523 13000 "path-parse": "^1.0.7", ··· 13576 13053 "version": "1.1.0", 13577 13054 "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", 13578 13055 "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", 13056 + "dev": true, 13579 13057 "engines": { 13580 13058 "iojs": ">=1.0.0", 13581 13059 "node": ">=0.10.0" ··· 13647 13125 "version": "1.2.0", 13648 13126 "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", 13649 13127 "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", 13128 + "dev": true, 13650 13129 "funding": [ 13651 13130 { 13652 13131 "type": "github", ··· 13922 13401 "version": "2.0.0", 13923 13402 "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 13924 13403 "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 13404 + "dev": true, 13925 13405 "dependencies": { 13926 13406 "shebang-regex": "^3.0.0" 13927 13407 }, ··· 13933 13413 "version": "3.0.0", 13934 13414 "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 13935 13415 "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 13416 + "dev": true, 13936 13417 "engines": { 13937 13418 "node": ">=8" 13938 13419 } ··· 14013 13494 "version": "4.1.0", 14014 13495 "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", 14015 13496 "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", 13497 + "dev": true, 14016 13498 "engines": { 14017 13499 "node": ">=14" 14018 13500 }, ··· 14092 13574 "version": "5.1.2", 14093 13575 "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", 14094 13576 "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", 13577 + "dev": true, 14095 13578 "dependencies": { 14096 13579 "eastasianwidth": "^0.2.0", 14097 13580 "emoji-regex": "^9.2.2", ··· 14109 13592 "version": "4.2.3", 14110 13593 "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 14111 13594 "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 13595 + "dev": true, 14112 13596 "dependencies": { 14113 13597 "emoji-regex": "^8.0.0", 14114 13598 "is-fullwidth-code-point": "^3.0.0", ··· 14121 13605 "node_modules/string-width-cjs/node_modules/emoji-regex": { 14122 13606 "version": "8.0.0", 14123 13607 "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 14124 - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" 13608 + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 13609 + "dev": true 14125 13610 }, 14126 13611 "node_modules/string-width/node_modules/ansi-regex": { 14127 13612 "version": "6.1.0", 14128 13613 "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", 14129 13614 "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", 13615 + "dev": true, 14130 13616 "engines": { 14131 13617 "node": ">=12" 14132 13618 }, ··· 14138 13624 "version": "7.1.0", 14139 13625 "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", 14140 13626 "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", 13627 + "dev": true, 14141 13628 "dependencies": { 14142 13629 "ansi-regex": "^6.0.1" 14143 13630 }, ··· 14259 13746 "version": "6.0.1", 14260 13747 "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 14261 13748 "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 13749 + "dev": true, 14262 13750 "dependencies": { 14263 13751 "ansi-regex": "^5.0.1" 14264 13752 }, ··· 14271 13759 "version": "6.0.1", 14272 13760 "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 14273 13761 "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 13762 + "dev": true, 14274 13763 "dependencies": { 14275 13764 "ansi-regex": "^5.0.1" 14276 13765 }, ··· 14334 13823 "version": "3.35.0", 14335 13824 "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", 14336 13825 "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", 13826 + "dev": true, 14337 13827 "dependencies": { 14338 13828 "@jridgewell/gen-mapping": "^0.3.2", 14339 13829 "commander": "^4.0.0", ··· 14351 13841 "node": ">=16 || 14 >=14.17" 14352 13842 } 14353 13843 }, 13844 + "node_modules/sugarss": { 13845 + "version": "5.0.0", 13846 + "resolved": "https://registry.npmjs.org/sugarss/-/sugarss-5.0.0.tgz", 13847 + "integrity": "sha512-3//knMoF9btXcxHTbMRckIYjkEzSZ6pZjiaZ3wM6OIpUtQ06Uwqc0XgAr6jf+U74cLLTV/BEgmHWoeXPC+NhdQ==", 13848 + "dev": true, 13849 + "funding": [ 13850 + { 13851 + "type": "opencollective", 13852 + "url": "https://opencollective.com/postcss/" 13853 + }, 13854 + { 13855 + "type": "github", 13856 + "url": "https://github.com/sponsors/ai" 13857 + } 13858 + ], 13859 + "engines": { 13860 + "node": ">=18.0" 13861 + }, 13862 + "peerDependencies": { 13863 + "postcss": "^8.3.3" 13864 + } 13865 + }, 14354 13866 "node_modules/supports-color": { 14355 13867 "version": "7.2.0", 14356 13868 "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", ··· 14367 13879 "version": "1.0.0", 14368 13880 "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", 14369 13881 "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", 13882 + "dev": true, 14370 13883 "engines": { 14371 13884 "node": ">= 0.4" 14372 13885 }, ··· 14460 13973 "node": ">= 10" 14461 13974 } 14462 13975 }, 14463 - "node_modules/tailwind-merge": { 14464 - "version": "2.6.0", 14465 - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.0.tgz", 14466 - "integrity": "sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==", 14467 - "funding": { 14468 - "type": "github", 14469 - "url": "https://github.com/sponsors/dcastil" 14470 - } 14471 - }, 14472 - "node_modules/tailwindcss": { 14473 - "version": "3.4.17", 14474 - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", 14475 - "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", 14476 - "dependencies": { 14477 - "@alloc/quick-lru": "^5.2.0", 14478 - "arg": "^5.0.2", 14479 - "chokidar": "^3.6.0", 14480 - "didyoumean": "^1.2.2", 14481 - "dlv": "^1.1.3", 14482 - "fast-glob": "^3.3.2", 14483 - "glob-parent": "^6.0.2", 14484 - "is-glob": "^4.0.3", 14485 - "jiti": "^1.21.6", 14486 - "lilconfig": "^3.1.3", 14487 - "micromatch": "^4.0.8", 14488 - "normalize-path": "^3.0.0", 14489 - "object-hash": "^3.0.0", 14490 - "picocolors": "^1.1.1", 14491 - "postcss": "^8.4.47", 14492 - "postcss-import": "^15.1.0", 14493 - "postcss-js": "^4.0.1", 14494 - "postcss-load-config": "^4.0.2", 14495 - "postcss-nested": "^6.2.0", 14496 - "postcss-selector-parser": "^6.1.2", 14497 - "resolve": "^1.22.8", 14498 - "sucrase": "^3.35.0" 14499 - }, 14500 - "bin": { 14501 - "tailwind": "lib/cli.js", 14502 - "tailwindcss": "lib/cli.js" 14503 - }, 14504 - "engines": { 14505 - "node": ">=14.0.0" 14506 - } 14507 - }, 14508 - "node_modules/tailwindcss-animate": { 14509 - "version": "1.0.7", 14510 - "resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz", 14511 - "integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==", 14512 - "peerDependencies": { 14513 - "tailwindcss": ">=3.0.0 || insiders" 14514 - } 13976 + "node_modules/tabbable": { 13977 + "version": "6.2.0", 13978 + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", 13979 + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==" 14515 13980 }, 14516 13981 "node_modules/temp-dir": { 14517 13982 "version": "3.0.0", ··· 14574 14039 "version": "3.3.1", 14575 14040 "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", 14576 14041 "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", 14042 + "dev": true, 14577 14043 "dependencies": { 14578 14044 "any-promise": "^1.0.0" 14579 14045 } ··· 14582 14048 "version": "1.6.0", 14583 14049 "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", 14584 14050 "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", 14051 + "dev": true, 14585 14052 "dependencies": { 14586 14053 "thenify": ">= 3.1.0 < 4" 14587 14054 }, ··· 14590 14057 } 14591 14058 }, 14592 14059 "node_modules/tinyglobby": { 14593 - "version": "0.2.13", 14594 - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", 14595 - "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", 14060 + "version": "0.2.14", 14061 + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", 14062 + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", 14596 14063 "dev": true, 14597 14064 "dependencies": { 14598 14065 "fdir": "^6.4.4", ··· 14647 14114 "version": "5.0.1", 14648 14115 "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 14649 14116 "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 14117 + "dev": true, 14650 14118 "dependencies": { 14651 14119 "is-number": "^7.0.0" 14652 14120 }, ··· 14693 14161 "node_modules/ts-interface-checker": { 14694 14162 "version": "0.1.13", 14695 14163 "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", 14696 - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" 14164 + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", 14165 + "dev": true 14697 14166 }, 14698 14167 "node_modules/tsconfig-paths": { 14699 14168 "version": "3.15.0", ··· 15028 14497 } 15029 14498 } 15030 14499 }, 14500 + "node_modules/use-composed-ref": { 14501 + "version": "1.4.0", 14502 + "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.4.0.tgz", 14503 + "integrity": "sha512-djviaxuOOh7wkj0paeO1Q/4wMZ8Zrnag5H6yBvzN7AKKe8beOaED9SF5/ByLqsku8NP4zQqsvM2u3ew/tJK8/w==", 14504 + "peerDependencies": { 14505 + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" 14506 + }, 14507 + "peerDependenciesMeta": { 14508 + "@types/react": { 14509 + "optional": true 14510 + } 14511 + } 14512 + }, 14513 + "node_modules/use-isomorphic-layout-effect": { 14514 + "version": "1.2.1", 14515 + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.1.tgz", 14516 + "integrity": "sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==", 14517 + "peerDependencies": { 14518 + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" 14519 + }, 14520 + "peerDependenciesMeta": { 14521 + "@types/react": { 14522 + "optional": true 14523 + } 14524 + } 14525 + }, 14526 + "node_modules/use-latest": { 14527 + "version": "1.3.0", 14528 + "resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.3.0.tgz", 14529 + "integrity": "sha512-mhg3xdm9NaM8q+gLT8KryJPnRFOz1/5XPBhmDEVZK1webPzDjrPk7f/mbpeLqTgB9msytYWANxgALOCJKnLvcQ==", 14530 + "dependencies": { 14531 + "use-isomorphic-layout-effect": "^1.1.1" 14532 + }, 14533 + "peerDependencies": { 14534 + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" 14535 + }, 14536 + "peerDependenciesMeta": { 14537 + "@types/react": { 14538 + "optional": true 14539 + } 14540 + } 14541 + }, 15031 14542 "node_modules/use-sidecar": { 15032 14543 "version": "1.1.3", 15033 14544 "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", ··· 15052 14563 "node_modules/util-deprecate": { 15053 14564 "version": "1.0.2", 15054 14565 "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 15055 - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" 14566 + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", 14567 + "dev": true 15056 14568 }, 15057 14569 "node_modules/utility-types": { 15058 14570 "version": "3.11.0", ··· 15103 14615 "version": "2.0.2", 15104 14616 "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 15105 14617 "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 14618 + "dev": true, 15106 14619 "dependencies": { 15107 14620 "isexe": "^2.0.0" 15108 14621 }, ··· 15211 14724 "version": "8.1.0", 15212 14725 "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", 15213 14726 "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", 14727 + "dev": true, 15214 14728 "dependencies": { 15215 14729 "ansi-styles": "^6.1.0", 15216 14730 "string-width": "^5.0.1", ··· 15228 14742 "version": "7.0.0", 15229 14743 "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 15230 14744 "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 14745 + "dev": true, 15231 14746 "dependencies": { 15232 14747 "ansi-styles": "^4.0.0", 15233 14748 "string-width": "^4.1.0", ··· 15243 14758 "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { 15244 14759 "version": "8.0.0", 15245 14760 "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 15246 - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" 14761 + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 14762 + "dev": true 15247 14763 }, 15248 14764 "node_modules/wrap-ansi-cjs/node_modules/string-width": { 15249 14765 "version": "4.2.3", 15250 14766 "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 15251 14767 "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 14768 + "dev": true, 15252 14769 "dependencies": { 15253 14770 "emoji-regex": "^8.0.0", 15254 14771 "is-fullwidth-code-point": "^3.0.0", ··· 15262 14779 "version": "6.1.0", 15263 14780 "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", 15264 14781 "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", 14782 + "dev": true, 15265 14783 "engines": { 15266 14784 "node": ">=12" 15267 14785 }, ··· 15273 14791 "version": "6.2.1", 15274 14792 "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", 15275 14793 "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", 14794 + "dev": true, 15276 14795 "engines": { 15277 14796 "node": ">=12" 15278 14797 }, ··· 15284 14803 "version": "7.1.0", 15285 14804 "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", 15286 14805 "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", 14806 + "dev": true, 15287 14807 "dependencies": { 15288 14808 "ansi-regex": "^6.0.1" 15289 14809 }, ··· 15316 14836 "version": "2.7.1", 15317 14837 "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", 15318 14838 "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", 14839 + "dev": true, 15319 14840 "bin": { 15320 14841 "yaml": "bin.mjs" 15321 14842 },
+11 -11
src/webapp/package.json
··· 20 20 "lint": "next lint" 21 21 }, 22 22 "dependencies": { 23 - "@radix-ui/react-checkbox": "^1.3.2", 24 - "@radix-ui/react-label": "^2.1.6", 25 - "@radix-ui/react-select": "^2.2.4", 26 - "@radix-ui/react-slot": "^1.0.2", 27 - "class-variance-authority": "^0.7.1", 28 - "clsx": "^2.1.1", 23 + "@mantine/core": "^8.1.3", 24 + "@mantine/dates": "^8.1.3", 25 + "@mantine/dropzone": "^8.1.3", 26 + "@mantine/form": "^8.1.3", 27 + "@mantine/hooks": "^8.1.3", 28 + "@mantine/notifications": "^8.1.3", 29 29 "date-fns": "^4.1.0", 30 + "dayjs": "^1.11.13", 30 31 "next": "15.4.1", 31 32 "react": "19.1.0", 32 33 "react-dom": "19.1.0", 33 - "react-icons": "^5.5.0", 34 - "tailwind-merge": "^2.6.0", 35 - "tailwindcss-animate": "^1.0.7" 34 + "react-icons": "^5.5.0" 36 35 }, 37 36 "devDependencies": { 38 37 "@types/chrome": "^0.0.332", ··· 43 42 "eslint": "^8.55.0", 44 43 "eslint-config-next": "15.4.1", 45 44 "plasmo": "^0.90.5", 46 - "postcss": "^8.4.32", 47 - "tailwindcss": "^3.3.6", 45 + "postcss": "^8.5.6", 46 + "postcss-preset-mantine": "^1.18.0", 47 + "postcss-simple-vars": "^7.0.1", 48 48 "typescript": "^5.3.3" 49 49 }, 50 50 "overrides": {
+18 -7
src/webapp/popup.tsx
··· 1 - import { ExtensionAuthProvider, useExtensionAuth } from "./hooks/useExtensionAuth"; 1 + import { 2 + ExtensionAuthProvider, 3 + useExtensionAuth, 4 + } from "./hooks/useExtensionAuth"; 2 5 import { SignInPage } from "./components/extension/SignInPage"; 3 6 import { SaveCardPage } from "./components/extension/SaveCardPage"; 4 - import "./app/globals.css"; 7 + import { Card, MantineProvider, ScrollArea } from "@mantine/core"; 8 + import "@mantine/core/styles.css"; 9 + import { theme } from "@/styles/theme"; 5 10 6 11 function PopupContent() { 7 12 const { isAuthenticated } = useExtensionAuth(); 8 - 13 + 9 14 if (!isAuthenticated) { 10 15 return <SignInPage />; 11 16 } 12 - 17 + 13 18 return <SaveCardPage />; 14 19 } 15 20 16 21 function IndexPopup() { 17 22 return ( 18 - <ExtensionAuthProvider> 19 - <PopupContent /> 20 - </ExtensionAuthProvider> 23 + <MantineProvider theme={theme}> 24 + <ExtensionAuthProvider> 25 + <ScrollArea.Autosize w={400} mah={600}> 26 + <Card> 27 + <PopupContent /> 28 + </Card> 29 + </ScrollArea.Autosize> 30 + </ExtensionAuthProvider> 31 + </MantineProvider> 21 32 ); 22 33 } 23 34
+14
src/webapp/postcss.config.cjs
··· 1 + module.exports = { 2 + plugins: { 3 + "postcss-preset-mantine": {}, 4 + "postcss-simple-vars": { 5 + variables: { 6 + "mantine-breakpoint-xs": "36em", 7 + "mantine-breakpoint-sm": "48em", 8 + "mantine-breakpoint-md": "62em", 9 + "mantine-breakpoint-lg": "75em", 10 + "mantine-breakpoint-xl": "88em", 11 + }, 12 + }, 13 + }, 14 + };
-6
src/webapp/postcss.config.js
··· 1 - module.exports = { 2 - plugins: { 3 - tailwindcss: {}, 4 - autoprefixer: {}, 5 - }, 6 - }
+10
src/webapp/styles/theme.tsx
··· 1 + "use client"; 2 + 3 + import { createTheme } from "@mantine/core"; 4 + 5 + export const theme = createTheme({ 6 + primaryColor: "dark", 7 + fontFamily: 8 + "Archivo, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji", 9 + defaultRadius: "md", 10 + });
-76
src/webapp/tailwind.config.js
··· 1 - /** @type {import('tailwindcss').Config} */ 2 - module.exports = { 3 - darkMode: ["class"], 4 - content: [ 5 - './pages/**/*.{ts,tsx}', 6 - './components/**/*.{ts,tsx}', 7 - './app/**/*.{ts,tsx}', 8 - './src/**/*.{ts,tsx}', 9 - ], 10 - theme: { 11 - container: { 12 - center: true, 13 - padding: "2rem", 14 - screens: { 15 - "2xl": "1400px", 16 - }, 17 - }, 18 - extend: { 19 - colors: { 20 - border: "hsl(var(--border))", 21 - input: "hsl(var(--input))", 22 - ring: "hsl(var(--ring))", 23 - background: "hsl(var(--background))", 24 - foreground: "hsl(var(--foreground))", 25 - primary: { 26 - DEFAULT: "hsl(var(--primary))", 27 - foreground: "hsl(var(--primary-foreground))", 28 - }, 29 - secondary: { 30 - DEFAULT: "hsl(var(--secondary))", 31 - foreground: "hsl(var(--secondary-foreground))", 32 - }, 33 - destructive: { 34 - DEFAULT: "hsl(var(--destructive))", 35 - foreground: "hsl(var(--destructive-foreground))", 36 - }, 37 - muted: { 38 - DEFAULT: "hsl(var(--muted))", 39 - foreground: "hsl(var(--muted-foreground))", 40 - }, 41 - accent: { 42 - DEFAULT: "hsl(var(--accent))", 43 - foreground: "hsl(var(--accent-foreground))", 44 - }, 45 - popover: { 46 - DEFAULT: "hsl(var(--popover))", 47 - foreground: "hsl(var(--popover-foreground))", 48 - }, 49 - card: { 50 - DEFAULT: "hsl(var(--card))", 51 - foreground: "hsl(var(--card-foreground))", 52 - }, 53 - }, 54 - borderRadius: { 55 - lg: "var(--radius)", 56 - md: "calc(var(--radius) - 2px)", 57 - sm: "calc(var(--radius) - 4px)", 58 - }, 59 - keyframes: { 60 - "accordion-down": { 61 - from: { height: 0 }, 62 - to: { height: "var(--radix-accordion-content-height)" }, 63 - }, 64 - "accordion-up": { 65 - from: { height: "var(--radix-accordion-content-height)" }, 66 - to: { height: 0 }, 67 - }, 68 - }, 69 - animation: { 70 - "accordion-down": "accordion-down 0.2s ease-out", 71 - "accordion-up": "accordion-up 0.2s ease-out", 72 - }, 73 - }, 74 - }, 75 - plugins: [require("tailwindcss-animate")], 76 - }