🖥️ wip: Config screen

This commit is contained in:
mbaharip
2024-04-22 21:22:15 +07:00
parent c5434fe9c4
commit 3afe51581b
34 changed files with 1698 additions and 626 deletions
+4 -1
View File
@@ -45,7 +45,7 @@
"clsx": "^2.1.0",
"cmdk": "^1.0.0",
"date-fns": "^3.6.0",
"embla-carousel-react": "^8.0.1",
"embla-carousel-react": "^8.0.2",
"googleapis": "^118.0.0",
"input-otp": "^1.2.3",
"jsonwebtoken": "^9.0.0",
@@ -55,6 +55,7 @@
"next-themes": "^0.3.0",
"nextjs-toploader": "^1.6.11",
"react": "^18",
"react-colorful": "^5.6.1",
"react-day-picker": "^8.10.0",
"react-dom": "^18",
"react-h5-audio-player": "^3.9.1",
@@ -69,6 +70,7 @@
"rehype-katex": "^6.0.3",
"rehype-prism-plus": "^1.6.3",
"rehype-raw": "6.1.1",
"remark-breaks": "^4.0.0",
"remark-gfm": "^3.0.1",
"remark-math": "^5.1.1",
"remark-slug": "^7.0.1",
@@ -76,6 +78,7 @@
"sonner": "^1.4.41",
"tailwind-merge": "^2.2.2",
"tailwindcss-animate": "^1.0.7",
"use-debouncy": "^5.0.1",
"vaul": "^0.9.0",
"zod": "^3.22.4"
},
+11 -3
View File
@@ -6,6 +6,7 @@ import ReactMarkdown from "react-markdown";
import rehypeKatex from "rehype-katex";
import rehypePrism from "rehype-prism-plus";
import rehypeRaw from "rehype-raw";
import remarkBreaks from "remark-breaks";
import remarkGfm from "remark-gfm";
import remarkMath from "remark-math";
import remarkSlug from "remark-slug";
@@ -19,10 +20,11 @@ import "./highlight.css";
type Props = {
content: string;
view: "markdown" | "raw";
className?: string;
};
export default function Markdown({ content, view }: Props) {
export default function Markdown({ content, view, className }: Props) {
return (
<div className='flex flex-col p-3'>
<div className={cn("flex flex-col p-3", className)}>
<div
className={cn(
"markdown w-full rounded-[var(--radius)]",
@@ -42,7 +44,13 @@ export default function Markdown({ content, view }: Props) {
<ReactMarkdown
className='w-full'
disallowedElements={["script"]}
remarkPlugins={[remarkGfm, remarkMath, remarkSlug, remarkToc]}
remarkPlugins={[
remarkGfm,
remarkMath,
remarkSlug,
remarkToc,
remarkBreaks,
]}
rehypePlugins={[
rehypeKatex,
rehypeRaw,
+54 -1
View File
@@ -149,9 +149,34 @@ export default function Navbar() {
<Separator
orientation='vertical'
className='mx-3'
className='mx-3 my-auto h-6'
/>
{config.showDeployGuide && (
<Tooltip>
<TooltipTrigger asChild>
<Link
href={"/deploy"}
className={cn(
"flex flex-col items-center justify-center",
"opacity-80",
"hover:opacity-100",
"cursor-pointer",
"p-1.5",
)}
>
<Icon
name={"Book"}
size={20}
/>
</Link>
</TooltipTrigger>
<TooltipContent side='bottom'>
<p>Deploy Guide</p>
</TooltipContent>
</Tooltip>
)}
<DropdownMenu
modal={false}
open={themeOpen}
@@ -268,6 +293,11 @@ export default function Navbar() {
</DropdownMenu>
)}
<Separator
orientation='vertical'
className='mx-3 my-auto h-6'
/>
<Dialog>
<Tooltip>
<TooltipTrigger asChild>
@@ -457,6 +487,29 @@ export default function Navbar() {
</Link>
</Button>
))}
{config.showDeployGuide && (
<Button
variant={"ghost"}
size={"sm"}
asChild
>
<Link
href={"/deploy"}
className='flex w-full items-center justify-between gap-3'
onClick={() => setOpen(false)}
>
<div className='flex items-center gap-3'>
<Icon
name={"Book"}
className='text-foreground'
size={18}
/>
Deploy Guide
</div>
</Link>
</Button>
)}
</div>
<div className='flex flex-col'>
+473
View File
@@ -0,0 +1,473 @@
"use client";
import { ChangeEvent, useMemo, useRef, useState } from "react";
import { HslColor, HslColorPicker } from "react-colorful";
import toast from "react-hot-toast";
import { z } from "zod";
import { Schema_Config } from "~/schema";
import { cn } from "~/utils";
import { GenerateAESKey, VerifyAESKey } from "~/app/actions";
import Icon from "~/components/Icon";
import { Button } from "~/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "~/components/ui/dropdown-menu";
import { Input } from "~/components/ui/input";
import { Label } from "~/components/ui/label";
import { Separator } from "~/components/ui/separator";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "~/components/ui/tooltip";
import config from "~/config/gIndex.config";
export const getting_started = `Welcome to the deployment guide! This guide will help you to deploy the application to Vercel or similar services.
If you are new to this project, you can follow along from the beginning.
But if you've already deployed the app before and want to upgrade from v1, you can skip to the [Migrating from v1](#migrating) section.
You can also use this guide to [configure the app](#config) and [customize the theme](#theme).
_**Note:** This guide assumes you have a basic understanding of how to deploy a Next.js app on Vercel or other platforms._`;
export const new_user_guide = `Prerequisites:
- A basic understanding of Vercel (or similar services)
- Google Cloud Platform account
### Fork or Clone the repository
It's pretty obvious, but you need to fork the repository to your account.
You can [click here](https://github.com/mbahArip/next-gdrive-index/fork) to fork the repository.
You can choose any repository name, description, and visibility.
But if you want to run it locally, you can clone the repository instead.
### Create a Google Cloud Platform project and enable Google Drive API
We need an access to Google Drive API to get the files from Google Drive.
So you need to create a project in Google Cloud Platform and enable Google Drive API to get your own credentials.
1. Go to [Google Cloud Platform](https://console.cloud.google.com/)
2. Click the \`New Project\` button
3. Enter a project name, and click the \`Create\` button
4. After the project is created, click the \`Enable APIs and Services\` button
5. Search for Google Drive API, and click the \`Enable\` button
### Create a Service Account and get the credentials
After enabling the Google Drive API, we need to create a service account to get the credentials.
The credentials will be used to authenticate the application to access the Google Drive API and get the files.
1. On [APIs & Services](https://console.cloud.google.com/apis/dashboard) page, click the \`Credentials\` menu on the sidebar
2. Click the \`Create Credentials\` button, and choose \`Service account\`
3. Enter your service account name and description, and then click the \`Done\` button
4. You will see the service account you just created on \`Service Account\` table, click the name of the service account to open the details
5. Go to \`Keys\` tab, then click the \`Add Key\` button and choose the \`Create new key\`
6. Pick \`JSON\` as the key type and click the \`Create\` button
7. The JSON file will be downloaded to your computer, and **keep it safe**. We will use it later on the configuration
_**Note:** The JSON file contains sensitive information, don't share it with anyone_
### Create shared folder in Google Drive
Since the service account can't access your Root folder, you need to create a new folder, and share it with the service account.
This folder will be used as the root folder for the application.
1. Go to [Google Drive](https://drive.google.com/)
2. Click the \`New\` button, and choose \`Folder\` to create a new folder, you can name it anything you want
3. Right-click the folder you just created, and choose \`Share\`
4. Enter the email address of the service account you just created (you can find it on the JSON file, or on the service account details page)
5. To allow download files larger than deployment limit, you need to enable \`Link sharing\` and set it to \`Anyone with the link\`
6. Copy the folder ID from the URL, it's the part after \`/folders/\` in the URL (e.g: https://drive.google.com/drive/u/0/folders/ \`<folder_id>\` )
### Configuring the app and Customizing the theme
Now we need to configure the app to use the credentials and folder ID we just created.
You can follow the [App Configuration](#config) and [Customize Theme](#theme) sections to configure the app and customize the theme.
_**Note:** You can skip the theme customization, but you **NEED** to configure the app_
### Deploy the app
On this guide we will use Vercel to deploy the app, but you can use other platforms like Netlify, Heroku, etc.
But don't forget to adjust the \`fileSizeLimit\` on the [configuration](#config) if you use other platforms.
> Before deploying, make sure you have pushed the changes to your repository
1. Go to [Vercel](https://vercel.com/)
2. Click on the \`Add new\` button, and choose \`Project\`
3. Choose the repository you just forked
4. On the \`Environment Variables\` section, copy the whole content from \`.env.local\` you just downloaded from [configuration](#config) section, and paste it on the key fields. It will automatically add all the environment variables needed
5. Click the \`Deploy\` button
6. Wait for the deployment to finish, and open project
7. Go to \`Settings\` tab, and click the \`Functions\` menu, and select your \`Function Region\` to the nearest region to your location for optimal speed
8. Go to \`Deployment\` tab, click the 3 dots on the right side of the latest \`Production\` deployment, and click the \`Redeploy\` button to apply the changes
For other platforms, you can check their own documentation for Next.js deployment guide.
### Done! 🎉
Congratulations! You have successfully deployed the app.`;
export const migration_guide = `If you've already deployed the app before and want to upgrade from v1, you can follow this guide to migrate the app to the latest version.
### Update your environment and configuration
If you still have the \`.env.local file\`, you can go to the [configuration](#config) section, and load the file to update the environment variables.
If you don't have it, go to your deployment platform and copy the environment variables from the platform to the [configuration](#config) section.
You can also load the old \`gindex.config.ts\` file to the [configuration](#config) section to set the default configuration.
### Update the repository
First, you need to update the repository to the latest version.
If you open your forked repository, you will see a notification that the repository is behind the original repository.
You can sync the repository by clicking the \`Sync fork\` button.
After the repository is updated, you can replace the \`gindex.config.ts\` file with the new one.
### Update deployment
Now go to your Vercel project page (or other platforms).
Go to the \`Settings\` tab, and click the \`Environment Variables\` menu.
You can delete all the old environment variables, and copy the new environment variables from the updated \`.env.local\` file.
Now you can redeploy the app to apply the changes.
`;
type Environment =
| "GD_SERVICE_B64"
| "ENCRYPTION_KEY"
| "SITE_PASSWORD"
| "NEXT_PUBLIC_DOMAIN";
type EnvironmentInput = {
key: Environment;
title: string;
description?: string;
value: string;
onChange: (e: ChangeEvent<HTMLInputElement>) => void;
validation?: (value: string) => void;
action?: {
label: string;
onClick: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
};
};
export function Configuration() {
const [configuration, setConfiguration] =
useState<z.input<typeof Schema_Config>>(config);
const [environment, setEnvironment] = useState<Record<Environment, string>>({
GD_SERVICE_B64: "",
ENCRYPTION_KEY: "",
SITE_PASSWORD: "",
NEXT_PUBLIC_DOMAIN: "",
});
const [btnState, setBtnState] = useState<
Record<string, "idle" | "loading" | "success">
>({
ENCRYPTION_KEY: "idle",
});
const [error, setError] = useState<Record<string, string>>({});
const fileConfigRef = useRef<HTMLInputElement>(null);
const fileEnvRef = useRef<HTMLInputElement>(null);
const envInputs = useMemo<EnvironmentInput[]>(
() => [
{
key: "ENCRYPTION_KEY",
title: "Encryption Key",
description:
"The encryption key used to encrypt all the sensitive data, must be 16 characters",
value: environment.ENCRYPTION_KEY,
onChange: (e) => {
setEnvironment((prev) => ({
...prev,
ENCRYPTION_KEY: e.target.value,
}));
},
validation(value) {
if (!value) return;
if (value.length !== 16)
throw new Error("Encryption key must be 16 characters");
},
action: {
label: "Generate",
onClick: generateEncryption,
},
},
],
[environment],
);
async function generateEncryption(
e: React.MouseEvent<HTMLButtonElement, MouseEvent>,
) {
e.preventDefault();
setBtnState((prev) => ({ ...prev, ENCRYPTION_KEY: "loading" }));
try {
const keyStr = await GenerateAESKey();
setEnvironment((prev) => ({
...prev,
ENCRYPTION_KEY: keyStr,
}));
const valid = await VerifyAESKey("This is a test", keyStr);
setError((prev) => ({
...prev,
encryption: valid ? "" : "Invalid encryption key",
}));
} catch (error) {
const e = error as Error;
console.error(error);
toast.error(e.message);
} finally {
setBtnState((prev) => ({ ...prev, ENCRYPTION_KEY: "idle" }));
}
}
return (
<Card>
<CardHeader className='pb-0'>
<div className='flex w-full items-center justify-between gap-3'>
<CardTitle
className='text-3xl'
id='config'
>
App Configuration
</CardTitle>
<input
ref={fileConfigRef}
type='file'
hidden
accept='.ts'
onChange={(e) => {
const selectedFile = e.target.files?.[0];
const expectedFileName = "gindex.config.ts";
if (selectedFile?.name !== expectedFileName) {
toast.error(
`Invalid file, expected ${expectedFileName} but got ${selectedFile?.name}`,
);
e.target.value = "";
return;
}
}}
/>
<input
ref={fileEnvRef}
type='file'
hidden
accept='.env, .env.local, .env.development, .env.production'
onChange={(e) => {
const selectedFile = e.target.files?.[0];
}}
/>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
size={"sm"}
variant={"outline"}
>
Load Config
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align='end'>
<DropdownMenuItem asChild>
<div
className='flex w-full items-center justify-between gap-6'
onClick={() => fileConfigRef.current?.click()}
>
Load config file
<Icon
name={"Upload"}
size={"1rem"}
/>
</div>
</DropdownMenuItem>
<DropdownMenuItem asChild>
<div
className='flex w-full items-center justify-between gap-6'
onClick={() => fileEnvRef.current?.click()}
>
Load environment file
<Icon
name={"Upload"}
size={"1rem"}
/>
</div>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
<Separator />
</CardHeader>
<CardContent>
<form className='flex w-full flex-col gap-3 py-3'>
<h4>Environment Config</h4>
<Separator />
<div
slot='Inputs'
className='flex flex-col gap-3'
>
{envInputs.map((input) => (
<div
key={`input-${input}`}
className='flex flex-col gap-1.5'
>
<div
slot='input-label'
className='flex h-fit items-center gap-1.5'
>
<Label htmlFor={input.key}>{input.title}</Label>
{input.description && (
<Tooltip>
<TooltipTrigger>
<Icon
name={"Info"}
size={"1rem"}
className='text-muted-foreground'
/>
</TooltipTrigger>
<TooltipContent>
<p>{input.description}</p>
</TooltipContent>
</Tooltip>
)}
</div>
<div
slot='input-value'
className='col-span-3 flex w-full flex-col gap-1.5'
>
<div className='flex w-full gap-1.5'>
<Input
id={input.key}
name={input.key}
value={input.value}
onChange={(e) => {
input.validation?.(e.target.value);
input.onChange(e);
}}
onBlur={async () => {
try {
setError((prev) => ({
...prev,
[input.key]: "",
}));
if (!input.value) return;
input.validation?.(input.value);
} catch (error) {
const e = error as Error;
setError((prev) => ({
...prev,
[input.key]: e.message,
}));
}
}}
/>
{input.action && (
<Button
variant={"secondary"}
onClick={input.action.onClick}
disabled={btnState[input.key] === "loading"}
>
<div className='relative flex w-full items-center'>
<span
className={cn(
"relative transition-all duration-300 ease-in-out",
)}
>
{input.action.label}
</span>
<Icon
name={"LoaderCircle"}
className={cn(
"animate-spin transition-all",
btnState[input.key] === "loading"
? "ml-1.5 size-4 opacity-100"
: "ml-0 size-0 opacity-0",
)}
/>
</div>
</Button>
)}
</div>
<span
className={cn(
"block text-sm text-destructive",
error[input.key]
? "opacity-100"
: "select-none opacity-0",
)}
>
{error[input.key]}
</span>
</div>
</div>
))}
</div>
</form>
</CardContent>
</Card>
);
}
export function CustomizeTheme() {
const ColorLabel = ({
label,
value,
onChange,
}: {
label: string;
value: HslColor;
onChange: (newColor: HslColor) => void;
}) => {
return (
<div className='flex items-center gap-3'>
<span>{label}:</span>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<div className='cursor-pointer rounded-sm border border-border bg-foreground p-0.5'>
<div
className={`size-4`}
style={{
backgroundColor: `hsl(${value.h}, ${value.s}%, ${value.l}%)`,
}}
/>
</div>
</DropdownMenuTrigger>
<DropdownMenuContent>
<HslColorPicker
color={value}
onChange={onChange}
/>
</DropdownMenuContent>
</DropdownMenu>
</div>
);
};
const [colors, setColors] = useState({
primary: { h: 200, s: 50, l: 50 },
secondary: { h: 200, s: 50, l: 50 },
});
return (
<Card>
<CardHeader className='pb-0'>
<CardTitle
className='text-3xl'
id='theme'
>
Customize Theme
</CardTitle>
<Separator />
</CardHeader>
<CardContent>
<Button>Not Preview</Button>
<div className='preview select-none'>
<Button>Button</Button>
</div>
<ColorLabel
label='Primary'
value={{ h: 200, s: 50, l: 50 }}
onChange={(e) => {
setColors((prev) => ({ ...prev, primary: e }));
}}
/>
</CardContent>
</Card>
);
}
+293
View File
@@ -0,0 +1,293 @@
"use client";
import { icons } from "lucide-react";
import { useMemo, useState } from "react";
import { cn } from "~/utils";
import Markdown from "~/app/@markdown";
import Icon from "~/components/Icon";
import { Button } from "~/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "~/components/ui/dropdown-menu";
import { Separator } from "~/components/ui/separator";
import useMediaQuery from "~/hooks/useMediaQuery";
import {
Configuration,
CustomizeTheme,
getting_started,
migration_guide,
new_user_guide,
} from "./docs";
type Section = "start" | "new-user" | "migrating" | "config" | "theme";
type SectionItem = {
id: Section;
title: string;
icon: keyof typeof icons;
};
export default function DeployGuidePage() {
const sectionMenu = useMemo<SectionItem[]>(
() => [
{
id: "start",
title: "Top of page",
icon: "ChevronUp",
},
{
id: "new-user",
title: "New User Guide",
icon: "UserPlus",
},
{
id: "migrating",
title: "Migrating from v1",
icon: "GitBranch",
},
{
id: "config",
title: "App Configuration",
icon: "Settings",
},
{
id: "theme",
title: "Theme Customization",
icon: "PaintRoller",
},
],
[],
);
const [sectionOpen, setSectionOpen] = useState<boolean>(false);
const isDesktop = useMediaQuery("(min-width: 768px)");
return (
<div
className={cn(
"relative mx-auto w-full max-w-screen-desktop",
"gap-3 p-3",
"flex-grow-0",
"flex flex-col",
)}
>
<div
id='fab'
className={cn("fixed bottom-6 right-6 z-10")}
>
<DropdownMenu
open={sectionOpen}
onOpenChange={setSectionOpen}
>
<DropdownMenuTrigger asChild>
<Button
size={"icon"}
variant={"outline"}
>
<Icon
name='SquareMenu'
size={"1.25rem"}
/>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
align='end'
side='left'
>
{sectionMenu.map((item) => (
<DropdownMenuItem
key={item.id}
asChild
>
<div
// href={`#${item.id}`}
className='flex w-full items-center justify-between gap-6'
onClick={() => {
const target = document.getElementById(item.id);
if (target) {
target.scrollIntoView({ behavior: "smooth" });
}
}}
>
{item.title}
<Icon
name={item.icon}
size={"1rem"}
/>
</div>
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
{/* {isDesktop ? (
<DropdownMenu
open={sectionOpen}
onOpenChange={setSectionOpen}
>
<DropdownMenuTrigger asChild>
<Button
size={"icon"}
variant={"outline"}
>
<Icon
name='SquareMenu'
size={"1.25rem"}
/>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
align='end'
side='left'
>
{sectionMenu.map((item) => (
<DropdownMenuItem
key={item.id}
asChild
>
<Button
variant={"ghost"}
asChild
className='w-full'
>
<Link
href={`#${item.id}`}
className='flex w-full items-center justify-between gap-6'
>
{item.title}
<Icon
name={item.icon}
size={"1rem"}
/>
</Link>
</Button>
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
) : (
<Drawer
open={sectionOpen}
onOpenChange={setSectionOpen}
>
<DrawerTrigger asChild>
<Button
size={"icon"}
variant={"outline"}
>
<Icon
name='SquareMenu'
size={"1.25rem"}
/>
</Button>
</DrawerTrigger>
<DrawerContent>
<DrawerHeader className='text-left'>
<DrawerTitle>Sections</DrawerTitle>
<DrawerDescription>Jump to a section</DrawerDescription>
</DrawerHeader>
<div className='grid gap-1.5 px-4'>
{sectionMenu.map((item) => (
<DrawerClose
asChild
key={item.id}
>
<Button
variant='ghost'
className='w-full'
asChild
>
<Link
href={`#${item.id}`}
className='flex w-full items-center justify-between gap-6'
>
{item.title}
<Icon
name={item.icon}
size='1rem'
/>
</Link>
</Button>
</DrawerClose>
))}
</div>
<DrawerFooter>
<DrawerClose asChild>
<Button variant='secondary'>Close</Button>
</DrawerClose>
</DrawerFooter>
</DrawerContent>
</Drawer>
)} */}
</div>
<Card>
<CardHeader className='pb-0'>
<CardTitle
className='text-3xl'
id='start'
>
Deployment Guide
</CardTitle>
<Separator />
</CardHeader>
<CardContent>
<Markdown
content={getting_started}
view='markdown'
className='px-0'
/>
</CardContent>
</Card>
<Card>
<CardHeader className='pb-0'>
<CardTitle
className='text-3xl'
id='new-user'
>
New User Guide
</CardTitle>
<Separator />
</CardHeader>
<CardContent>
<Markdown
content={new_user_guide}
view='markdown'
className='px-0'
/>
</CardContent>
</Card>
<Card>
<CardHeader className='pb-0'>
<CardTitle
className='text-3xl'
id='migrating'
>
Migrating from v1
</CardTitle>
<Separator />
</CardHeader>
<CardContent>
<Markdown
content={migration_guide}
view='markdown'
className='px-0'
/>
</CardContent>
</Card>
<Separator className='my-6' />
<Configuration />
<CustomizeTheme />
</div>
);
}
+9
View File
@@ -11,6 +11,8 @@ import { decryptData } from "~/utils/encryptionHelper/hash";
import gdrive from "~/utils/gdriveInstance";
import { getFileType } from "~/utils/previewHelper";
import config from "~/config/gIndex.config";
import FileBrowser from "../@explorer";
import Header from "../@header";
import HeaderButton from "../@header.button";
@@ -25,6 +27,7 @@ import {
GetFiles,
GetReadme,
} from "../actions";
import DeployGuidePage from "./deploy";
export const revalidate = 300;
export const dynamic = "force-dynamic";
@@ -39,6 +42,9 @@ export async function generateMetadata(
{ params: { rest } }: Props,
parent: ResolvedMetadata,
): Promise<Metadata> {
if (rest[0] === "deploy" && config.showDeployGuide)
return { title: "Deploy Guide" };
const paths = await CheckPaths(rest);
if (!paths.success) return { title: "Not Found" };
@@ -71,6 +77,9 @@ export async function generateMetadata(
}
export default async function RestPage({ params: { rest } }: Props) {
if (rest[0] === "deploy" && config.showDeployGuide)
return <DeployGuidePage />;
const paths = await CheckPaths(rest);
if (!paths.success) notFound();
const unlocked = await CheckPassword(paths.data);
+23
View File
@@ -1,5 +1,6 @@
"use server";
import crypto from "crypto";
import { revalidatePath } from "next/cache";
import { cookies } from "next/headers";
import { z } from "zod";
@@ -699,3 +700,25 @@ export async function CheckDownloadToken(token: string): Promise<{
};
}
}
export async function GenerateAESKey(): Promise<string> {
try {
const key = crypto.randomBytes(8).toString("hex");
return key;
} catch (error) {
const e = error as Error;
console.error(e.message);
throw new Error(e.message);
}
}
export async function VerifyAESKey(
data: string,
key: string,
): Promise<boolean> {
try {
const encrypt = await encryptData(data, key);
const decrypt = await decryptData(encrypt, key);
return !!encrypt && !!decrypt;
} catch (error) {
return false;
}
}
+9 -1
View File
@@ -1,5 +1,5 @@
import { Metadata } from "next";
import { JetBrains_Mono, Source_Sans_3 } from "next/font/google";
import { JetBrains_Mono, Outfit, Source_Sans_3 } from "next/font/google";
import { cn } from "~/utils";
import config from "~/config/gIndex.config";
@@ -19,6 +19,13 @@ const sourceSans3 = Source_Sans_3({
subsets: ["latin", "latin-ext"],
variable: "--font-source-sans-3",
});
const outfit = Outfit({
weight: ["300", "400", "600", "700"],
style: ["normal"],
display: "auto",
subsets: ["latin", "latin-ext"],
variable: "--font-outfit",
});
const jetbrainsMono = JetBrains_Mono({
weight: ["300", "400", "600", "700"],
style: ["normal", "italic"],
@@ -94,6 +101,7 @@ export default async function RootLayout({
"h-full bg-background font-sans text-foreground",
jetbrainsMono.variable,
sourceSans3.variable,
outfit.variable,
)}
>
<ThemeProvider
+3 -3
View File
@@ -51,11 +51,11 @@
}
.markdown ul {
@apply my-1.5 list-disc leading-loose;
@apply my-3 !list-outside list-disc leading-loose;
}
.markdown ol {
@apply my-1.5 list-decimal leading-loose;
@apply my-3 !list-inside list-decimal leading-loose;
}
.markdown li {
@@ -98,7 +98,7 @@
}
.markdown blockquote {
@apply my-2 rounded-r-[var(--radius)] border-l-4 border-l-primary bg-muted py-4 pl-2 text-foreground;
@apply my-2 rounded-r-[var(--radius)] border-l-4 border-l-primary/50 bg-muted/25 py-1.5 pl-3 text-foreground;
}
.markdown hr {
+37 -45
View File
@@ -1,21 +1,16 @@
import { Slot } from "@radix-ui/react-slot";
import { ChevronRight, MoreHorizontal } from "lucide-react";
import * as React from "react";
import { cn } from "~/utils";
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { ChevronRight, MoreHorizontal } from "lucide-react"
import { cn } from "~/utils"
const Breadcrumb = React.forwardRef<
HTMLElement,
React.ComponentPropsWithoutRef<"nav"> & {
separator?: React.ReactNode;
separator?: React.ReactNode
}
>(({ ...props }, ref) => (
<nav
ref={ref}
aria-label='breadcrumb'
{...props}
/>
));
Breadcrumb.displayName = "Breadcrumb";
>(({ ...props }, ref) => <nav ref={ref} aria-label="breadcrumb" {...props} />)
Breadcrumb.displayName = "Breadcrumb"
const BreadcrumbList = React.forwardRef<
HTMLOListElement,
@@ -24,13 +19,13 @@ const BreadcrumbList = React.forwardRef<
<ol
ref={ref}
className={cn(
"flex flex-wrap items-center gap-1.5 break-words text-sm text-zinc-500 dark:text-zinc-400 sm:gap-2.5",
className,
"flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5",
className
)}
{...props}
/>
));
BreadcrumbList.displayName = "BreadcrumbList";
))
BreadcrumbList.displayName = "BreadcrumbList"
const BreadcrumbItem = React.forwardRef<
HTMLLIElement,
@@ -41,29 +36,26 @@ const BreadcrumbItem = React.forwardRef<
className={cn("inline-flex items-center gap-1.5", className)}
{...props}
/>
));
BreadcrumbItem.displayName = "BreadcrumbItem";
))
BreadcrumbItem.displayName = "BreadcrumbItem"
const BreadcrumbLink = React.forwardRef<
HTMLAnchorElement,
React.ComponentPropsWithoutRef<"a"> & {
asChild?: boolean;
asChild?: boolean
}
>(({ asChild, className, ...props }, ref) => {
const Comp = asChild ? Slot : "a";
const Comp = asChild ? Slot : "a"
return (
<Comp
ref={ref}
className={cn(
"transition-colors hover:text-zinc-950 dark:hover:text-zinc-50",
className,
)}
className={cn("transition-colors hover:text-foreground", className)}
{...props}
/>
);
});
BreadcrumbLink.displayName = "BreadcrumbLink";
)
})
BreadcrumbLink.displayName = "BreadcrumbLink"
const BreadcrumbPage = React.forwardRef<
HTMLSpanElement,
@@ -71,14 +63,14 @@ const BreadcrumbPage = React.forwardRef<
>(({ className, ...props }, ref) => (
<span
ref={ref}
role='link'
aria-disabled='true'
aria-current='page'
className={cn("font-normal text-zinc-950 dark:text-zinc-50", className)}
role="link"
aria-disabled="true"
aria-current="page"
className={cn("font-normal text-foreground", className)}
{...props}
/>
));
BreadcrumbPage.displayName = "BreadcrumbPage";
))
BreadcrumbPage.displayName = "BreadcrumbPage"
const BreadcrumbSeparator = ({
children,
@@ -86,31 +78,31 @@ const BreadcrumbSeparator = ({
...props
}: React.ComponentProps<"li">) => (
<li
role='presentation'
aria-hidden='true'
role="presentation"
aria-hidden="true"
className={cn("[&>svg]:size-3.5", className)}
{...props}
>
{children ?? <ChevronRight />}
</li>
);
BreadcrumbSeparator.displayName = "BreadcrumbSeparator";
)
BreadcrumbSeparator.displayName = "BreadcrumbSeparator"
const BreadcrumbEllipsis = ({
className,
...props
}: React.ComponentProps<"span">) => (
<span
role='presentation'
aria-hidden='true'
role="presentation"
aria-hidden="true"
className={cn("flex h-9 w-9 items-center justify-center", className)}
{...props}
>
<MoreHorizontal className='h-4 w-4' />
<span className='sr-only'>More</span>
<MoreHorizontal className="h-4 w-4" />
<span className="sr-only">More</span>
</span>
);
BreadcrumbEllipsis.displayName = "BreadcrumbElipssis";
)
BreadcrumbEllipsis.displayName = "BreadcrumbElipssis"
export {
Breadcrumb,
@@ -120,4 +112,4 @@ export {
BreadcrumbPage,
BreadcrumbSeparator,
BreadcrumbEllipsis,
};
}
+21 -22
View File
@@ -1,24 +1,23 @@
import { Slot } from "@radix-ui/react-slot";
import { type VariantProps, cva } from "class-variance-authority";
import * as React from "react";
import { cn } from "~/utils";
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "~/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-white transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-950 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 dark:ring-offset-zinc-950 dark:focus-visible:ring-zinc-300",
"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",
{
variants: {
variant: {
default:
"bg-zinc-900 text-zinc-50 hover:bg-zinc-900/90 dark:bg-zinc-50 dark:text-zinc-900 dark:hover:bg-zinc-50/90",
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive:
"bg-red-500 text-zinc-50 hover:bg-red-500/90 dark:bg-red-900 dark:text-zinc-50 dark:hover:bg-red-900/90",
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline:
"border border-zinc-200 bg-white hover:bg-zinc-100 hover:text-zinc-900 dark:border-zinc-800 dark:bg-zinc-950 dark:hover:bg-zinc-800 dark:hover:text-zinc-50",
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-zinc-100 text-zinc-900 hover:bg-zinc-100/80 dark:bg-zinc-800 dark:text-zinc-50 dark:hover:bg-zinc-800/80",
ghost:
"hover:bg-zinc-100 hover:text-zinc-900 dark:hover:bg-zinc-800 dark:hover:text-zinc-50",
link: "text-zinc-900 underline-offset-4 hover:underline dark:text-zinc-50",
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-10 px-4 py-2",
@@ -31,27 +30,27 @@ const buttonVariants = cva(
variant: "default",
size: "default",
},
},
);
}
)
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean;
asChild?: boolean
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button";
const Comp = asChild ? Slot : "button"
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
);
},
);
Button.displayName = "Button";
)
}
)
Button.displayName = "Button"
export { Button, buttonVariants };
export { Button, buttonVariants }
+19 -29
View File
@@ -1,5 +1,6 @@
import * as React from "react";
import { cn } from "~/utils";
import * as React from "react"
import { cn } from "~/utils"
const Card = React.forwardRef<
HTMLDivElement,
@@ -9,12 +10,12 @@ const Card = React.forwardRef<
ref={ref}
className={cn(
"rounded-lg border bg-card text-card-foreground shadow-sm",
className,
className
)}
{...props}
/>
));
Card.displayName = "Card";
))
Card.displayName = "Card"
const CardHeader = React.forwardRef<
HTMLDivElement,
@@ -25,8 +26,8 @@ const CardHeader = React.forwardRef<
className={cn("flex flex-col space-y-1.5 p-6", className)}
{...props}
/>
));
CardHeader.displayName = "CardHeader";
))
CardHeader.displayName = "CardHeader"
const CardTitle = React.forwardRef<
HTMLParagraphElement,
@@ -36,12 +37,12 @@ const CardTitle = React.forwardRef<
ref={ref}
className={cn(
"text-2xl font-semibold leading-none tracking-tight",
className,
className
)}
{...props}
/>
));
CardTitle.displayName = "CardTitle";
))
CardTitle.displayName = "CardTitle"
const CardDescription = React.forwardRef<
HTMLParagraphElement,
@@ -52,20 +53,16 @@ const CardDescription = React.forwardRef<
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
));
CardDescription.displayName = "CardDescription";
))
CardDescription.displayName = "CardDescription"
const CardContent = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("p-6 pt-0", className)}
{...props}
/>
));
CardContent.displayName = "CardContent";
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
))
CardContent.displayName = "CardContent"
const CardFooter = React.forwardRef<
HTMLDivElement,
@@ -76,14 +73,7 @@ const CardFooter = React.forwardRef<
className={cn("flex items-center p-6 pt-0", className)}
{...props}
/>
));
CardFooter.displayName = "CardFooter";
))
CardFooter.displayName = "CardFooter"
export {
Card,
CardHeader,
CardFooter,
CardTitle,
CardDescription,
CardContent,
};
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
+89 -92
View File
@@ -1,45 +1,45 @@
"use client";
"use client"
import * as React from "react"
import useEmblaCarousel, {
type UseEmblaCarouselType,
} from "embla-carousel-react";
import { ArrowLeft, ArrowRight } from "lucide-react";
import * as React from "react";
import { cn } from "~/utils";
} from "embla-carousel-react"
import { ArrowLeft, ArrowRight } from "lucide-react"
import { Button } from "~/components/ui/button";
import { cn } from "~/utils"
import { Button } from "~/components/ui/button"
type CarouselApi = UseEmblaCarouselType[1];
type UseCarouselParameters = Parameters<typeof useEmblaCarousel>;
type CarouselOptions = UseCarouselParameters[0];
type CarouselPlugin = UseCarouselParameters[1];
type CarouselApi = UseEmblaCarouselType[1]
type UseCarouselParameters = Parameters<typeof useEmblaCarousel>
type CarouselOptions = UseCarouselParameters[0]
type CarouselPlugin = UseCarouselParameters[1]
type CarouselProps = {
opts?: CarouselOptions;
plugins?: CarouselPlugin;
orientation?: "horizontal" | "vertical";
setApi?: (api: CarouselApi) => void;
};
opts?: CarouselOptions
plugins?: CarouselPlugin
orientation?: "horizontal" | "vertical"
setApi?: (api: CarouselApi) => void
}
type CarouselContextProps = {
carouselRef: ReturnType<typeof useEmblaCarousel>[0];
api: ReturnType<typeof useEmblaCarousel>[1];
scrollPrev: () => void;
scrollNext: () => void;
canScrollPrev: boolean;
canScrollNext: boolean;
} & CarouselProps;
carouselRef: ReturnType<typeof useEmblaCarousel>[0]
api: ReturnType<typeof useEmblaCarousel>[1]
scrollPrev: () => void
scrollNext: () => void
canScrollPrev: boolean
canScrollNext: boolean
} & CarouselProps
const CarouselContext = React.createContext<CarouselContextProps | null>(null);
const CarouselContext = React.createContext<CarouselContextProps | null>(null)
function useCarousel() {
const context = React.useContext(CarouselContext);
const context = React.useContext(CarouselContext)
if (!context) {
throw new Error("useCarousel must be used within a <Carousel />");
throw new Error("useCarousel must be used within a <Carousel />")
}
return context;
return context
}
const Carousel = React.forwardRef<
@@ -56,69 +56,69 @@ const Carousel = React.forwardRef<
children,
...props
},
ref,
ref
) => {
const [carouselRef, api] = useEmblaCarousel(
{
...opts,
axis: orientation === "horizontal" ? "x" : "y",
},
plugins,
);
const [canScrollPrev, setCanScrollPrev] = React.useState(false);
const [canScrollNext, setCanScrollNext] = React.useState(false);
plugins
)
const [canScrollPrev, setCanScrollPrev] = React.useState(false)
const [canScrollNext, setCanScrollNext] = React.useState(false)
const onSelect = React.useCallback((api: CarouselApi) => {
if (!api) {
return;
return
}
setCanScrollPrev(api.canScrollPrev());
setCanScrollNext(api.canScrollNext());
}, []);
setCanScrollPrev(api.canScrollPrev())
setCanScrollNext(api.canScrollNext())
}, [])
const scrollPrev = React.useCallback(() => {
api?.scrollPrev();
}, [api]);
api?.scrollPrev()
}, [api])
const scrollNext = React.useCallback(() => {
api?.scrollNext();
}, [api]);
api?.scrollNext()
}, [api])
const handleKeyDown = React.useCallback(
(event: React.KeyboardEvent<HTMLDivElement>) => {
if (event.key === "ArrowLeft") {
event.preventDefault();
scrollPrev();
event.preventDefault()
scrollPrev()
} else if (event.key === "ArrowRight") {
event.preventDefault();
scrollNext();
event.preventDefault()
scrollNext()
}
},
[scrollPrev, scrollNext],
);
[scrollPrev, scrollNext]
)
React.useEffect(() => {
if (!api || !setApi) {
return;
return
}
setApi(api);
}, [api, setApi]);
setApi(api)
}, [api, setApi])
React.useEffect(() => {
if (!api) {
return;
return
}
onSelect(api);
api.on("reInit", onSelect);
api.on("select", onSelect);
onSelect(api)
api.on("reInit", onSelect)
api.on("select", onSelect)
return () => {
api?.off("select", onSelect);
};
}, [api, onSelect]);
api?.off("select", onSelect)
}
}, [api, onSelect])
return (
<CarouselContext.Provider
@@ -138,70 +138,67 @@ const Carousel = React.forwardRef<
ref={ref}
onKeyDownCapture={handleKeyDown}
className={cn("relative", className)}
role='region'
aria-roledescription='carousel'
role="region"
aria-roledescription="carousel"
{...props}
>
{children}
</div>
</CarouselContext.Provider>
);
},
);
Carousel.displayName = "Carousel";
)
}
)
Carousel.displayName = "Carousel"
const CarouselContent = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => {
const { carouselRef, orientation } = useCarousel();
const { carouselRef, orientation } = useCarousel()
return (
<div
ref={carouselRef}
className='overflow-hidden'
>
<div ref={carouselRef} className="overflow-hidden">
<div
ref={ref}
className={cn(
"flex",
orientation === "horizontal" ? "-ml-4" : "-mt-4 flex-col",
className,
className
)}
{...props}
/>
</div>
);
});
CarouselContent.displayName = "CarouselContent";
)
})
CarouselContent.displayName = "CarouselContent"
const CarouselItem = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => {
const { orientation } = useCarousel();
const { orientation } = useCarousel()
return (
<div
ref={ref}
role='group'
aria-roledescription='slide'
role="group"
aria-roledescription="slide"
className={cn(
"min-w-0 shrink-0 grow-0 basis-full",
orientation === "horizontal" ? "pl-4" : "pt-4",
className,
className
)}
{...props}
/>
);
});
CarouselItem.displayName = "CarouselItem";
)
})
CarouselItem.displayName = "CarouselItem"
const CarouselPrevious = React.forwardRef<
HTMLButtonElement,
React.ComponentProps<typeof Button>
>(({ className, variant = "outline", size = "icon", ...props }, ref) => {
const { orientation, scrollPrev, canScrollPrev } = useCarousel();
const { orientation, scrollPrev, canScrollPrev } = useCarousel()
return (
<Button
@@ -213,24 +210,24 @@ const CarouselPrevious = React.forwardRef<
orientation === "horizontal"
? "-left-12 top-1/2 -translate-y-1/2"
: "-top-12 left-1/2 -translate-x-1/2 rotate-90",
className,
className
)}
disabled={!canScrollPrev}
onClick={scrollPrev}
{...props}
>
<ArrowLeft className='h-4 w-4' />
<span className='sr-only'>Previous slide</span>
<ArrowLeft className="h-4 w-4" />
<span className="sr-only">Previous slide</span>
</Button>
);
});
CarouselPrevious.displayName = "CarouselPrevious";
)
})
CarouselPrevious.displayName = "CarouselPrevious"
const CarouselNext = React.forwardRef<
HTMLButtonElement,
React.ComponentProps<typeof Button>
>(({ className, variant = "outline", size = "icon", ...props }, ref) => {
const { orientation, scrollNext, canScrollNext } = useCarousel();
const { orientation, scrollNext, canScrollNext } = useCarousel()
return (
<Button
@@ -242,18 +239,18 @@ const CarouselNext = React.forwardRef<
orientation === "horizontal"
? "-right-12 top-1/2 -translate-y-1/2"
: "-bottom-12 left-1/2 -translate-x-1/2 rotate-90",
className,
className
)}
disabled={!canScrollNext}
onClick={scrollNext}
{...props}
>
<ArrowRight className='h-4 w-4' />
<span className='sr-only'>Next slide</span>
<ArrowRight className="h-4 w-4" />
<span className="sr-only">Next slide</span>
</Button>
);
});
CarouselNext.displayName = "CarouselNext";
)
})
CarouselNext.displayName = "CarouselNext"
export {
type CarouselApi,
@@ -262,4 +259,4 @@ export {
CarouselItem,
CarouselPrevious,
CarouselNext,
};
}
+31 -30
View File
@@ -1,17 +1,18 @@
"use client";
"use client"
import * as DialogPrimitive from "@radix-ui/react-dialog";
import { X } from "lucide-react";
import * as React from "react";
import { cn } from "~/utils";
import * as React from "react"
import * as DialogPrimitive from "@radix-ui/react-dialog"
import { X } from "lucide-react"
const Dialog = DialogPrimitive.Root;
import { cn } from "~/utils"
const DialogTrigger = DialogPrimitive.Trigger;
const Dialog = DialogPrimitive.Root
const DialogPortal = DialogPrimitive.Portal;
const DialogTrigger = DialogPrimitive.Trigger
const DialogClose = DialogPrimitive.Close;
const DialogPortal = DialogPrimitive.Portal
const DialogClose = DialogPrimitive.Close
const DialogOverlay = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Overlay>,
@@ -21,12 +22,12 @@ const DialogOverlay = React.forwardRef<
ref={ref}
className={cn(
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className,
className
)}
{...props}
/>
));
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
))
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
const DialogContent = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Content>,
@@ -38,19 +39,19 @@ const DialogContent = React.forwardRef<
ref={ref}
className={cn(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 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-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
className,
className
)}
{...props}
>
{children}
<DialogPrimitive.Close className='absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground'>
<X className='h-4 w-4' />
<span className='sr-only'>Close</span>
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
</DialogPrimitive.Content>
</DialogPortal>
));
DialogContent.displayName = DialogPrimitive.Content.displayName;
))
DialogContent.displayName = DialogPrimitive.Content.displayName
const DialogHeader = ({
className,
@@ -59,12 +60,12 @@ const DialogHeader = ({
<div
className={cn(
"flex flex-col space-y-1.5 text-center sm:text-left",
className,
className
)}
{...props}
/>
);
DialogHeader.displayName = "DialogHeader";
)
DialogHeader.displayName = "DialogHeader"
const DialogFooter = ({
className,
@@ -73,12 +74,12 @@ const DialogFooter = ({
<div
className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
className,
className
)}
{...props}
/>
);
DialogFooter.displayName = "DialogFooter";
)
DialogFooter.displayName = "DialogFooter"
const DialogTitle = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Title>,
@@ -88,12 +89,12 @@ const DialogTitle = React.forwardRef<
ref={ref}
className={cn(
"text-lg font-semibold leading-none tracking-tight",
className,
className
)}
{...props}
/>
));
DialogTitle.displayName = DialogPrimitive.Title.displayName;
))
DialogTitle.displayName = DialogPrimitive.Title.displayName
const DialogDescription = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Description>,
@@ -104,8 +105,8 @@ const DialogDescription = React.forwardRef<
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
));
DialogDescription.displayName = DialogPrimitive.Description.displayName;
))
DialogDescription.displayName = DialogPrimitive.Description.displayName
export {
Dialog,
@@ -118,4 +119,4 @@ export {
DialogFooter,
DialogTitle,
DialogDescription,
};
}
+26 -25
View File
@@ -1,8 +1,9 @@
"use client";
"use client"
import * as React from "react";
import { Drawer as DrawerPrimitive } from "vaul";
import { cn } from "~/utils";
import * as React from "react"
import { Drawer as DrawerPrimitive } from "vaul"
import { cn } from "~/utils"
const Drawer = ({
shouldScaleBackground = true,
@@ -12,14 +13,14 @@ const Drawer = ({
shouldScaleBackground={shouldScaleBackground}
{...props}
/>
);
Drawer.displayName = "Drawer";
)
Drawer.displayName = "Drawer"
const DrawerTrigger = DrawerPrimitive.Trigger;
const DrawerTrigger = DrawerPrimitive.Trigger
const DrawerPortal = DrawerPrimitive.Portal;
const DrawerPortal = DrawerPrimitive.Portal
const DrawerClose = DrawerPrimitive.Close;
const DrawerClose = DrawerPrimitive.Close
const DrawerOverlay = React.forwardRef<
React.ElementRef<typeof DrawerPrimitive.Overlay>,
@@ -30,8 +31,8 @@ const DrawerOverlay = React.forwardRef<
className={cn("fixed inset-0 z-50 bg-black/80", className)}
{...props}
/>
));
DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName;
))
DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName
const DrawerContent = React.forwardRef<
React.ElementRef<typeof DrawerPrimitive.Content>,
@@ -43,16 +44,16 @@ const DrawerContent = React.forwardRef<
ref={ref}
className={cn(
"fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-[10px] border bg-background",
className,
className
)}
{...props}
>
<div className='mx-auto mt-4 h-2 w-[100px] rounded-full bg-muted' />
<div className="mx-auto mt-4 h-2 w-[100px] rounded-full bg-muted" />
{children}
</DrawerPrimitive.Content>
</DrawerPortal>
));
DrawerContent.displayName = "DrawerContent";
))
DrawerContent.displayName = "DrawerContent"
const DrawerHeader = ({
className,
@@ -62,8 +63,8 @@ const DrawerHeader = ({
className={cn("grid gap-1.5 p-4 text-center sm:text-left", className)}
{...props}
/>
);
DrawerHeader.displayName = "DrawerHeader";
)
DrawerHeader.displayName = "DrawerHeader"
const DrawerFooter = ({
className,
@@ -73,8 +74,8 @@ const DrawerFooter = ({
className={cn("mt-auto flex flex-col gap-2 p-4", className)}
{...props}
/>
);
DrawerFooter.displayName = "DrawerFooter";
)
DrawerFooter.displayName = "DrawerFooter"
const DrawerTitle = React.forwardRef<
React.ElementRef<typeof DrawerPrimitive.Title>,
@@ -84,12 +85,12 @@ const DrawerTitle = React.forwardRef<
ref={ref}
className={cn(
"text-lg font-semibold leading-none tracking-tight",
className,
className
)}
{...props}
/>
));
DrawerTitle.displayName = DrawerPrimitive.Title.displayName;
))
DrawerTitle.displayName = DrawerPrimitive.Title.displayName
const DrawerDescription = React.forwardRef<
React.ElementRef<typeof DrawerPrimitive.Description>,
@@ -100,8 +101,8 @@ const DrawerDescription = React.forwardRef<
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
));
DrawerDescription.displayName = DrawerPrimitive.Description.displayName;
))
DrawerDescription.displayName = DrawerPrimitive.Description.displayName
export {
Drawer,
@@ -114,4 +115,4 @@ export {
DrawerFooter,
DrawerTitle,
DrawerDescription,
};
}
+47 -46
View File
@@ -1,26 +1,27 @@
"use client";
"use client"
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
import { Check, ChevronRight, Circle } from "lucide-react";
import * as React from "react";
import { cn } from "~/utils";
import * as React from "react"
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
import { Check, ChevronRight, Circle } from "lucide-react"
const DropdownMenu = DropdownMenuPrimitive.Root;
import { cn } from "~/utils"
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
const DropdownMenu = DropdownMenuPrimitive.Root
const DropdownMenuGroup = DropdownMenuPrimitive.Group;
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
const DropdownMenuGroup = DropdownMenuPrimitive.Group
const DropdownMenuSub = DropdownMenuPrimitive.Sub;
const DropdownMenuPortal = DropdownMenuPrimitive.Portal
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
const DropdownMenuSub = DropdownMenuPrimitive.Sub
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
const DropdownMenuSubTrigger = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
inset?: boolean;
inset?: boolean
}
>(({ className, inset, children, ...props }, ref) => (
<DropdownMenuPrimitive.SubTrigger
@@ -28,16 +29,16 @@ const DropdownMenuSubTrigger = React.forwardRef<
className={cn(
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent",
inset && "pl-8",
className,
className
)}
{...props}
>
{children}
<ChevronRight className='ml-auto h-4 w-4' />
<ChevronRight className="ml-auto h-4 w-4" />
</DropdownMenuPrimitive.SubTrigger>
));
))
DropdownMenuSubTrigger.displayName =
DropdownMenuPrimitive.SubTrigger.displayName;
DropdownMenuPrimitive.SubTrigger.displayName
const DropdownMenuSubContent = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
@@ -47,13 +48,13 @@ const DropdownMenuSubContent = React.forwardRef<
ref={ref}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg 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",
className,
className
)}
{...props}
/>
));
))
DropdownMenuSubContent.displayName =
DropdownMenuPrimitive.SubContent.displayName;
DropdownMenuPrimitive.SubContent.displayName
const DropdownMenuContent = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
@@ -65,18 +66,18 @@ const DropdownMenuContent = React.forwardRef<
sideOffset={sideOffset}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 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",
className,
className
)}
{...props}
/>
</DropdownMenuPrimitive.Portal>
));
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
))
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName
const DropdownMenuItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
inset?: boolean;
inset?: boolean
}
>(({ className, inset, ...props }, ref) => (
<DropdownMenuPrimitive.Item
@@ -84,12 +85,12 @@ const DropdownMenuItem = React.forwardRef<
className={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
inset && "pl-8",
className,
className
)}
{...props}
/>
));
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
))
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName
const DropdownMenuCheckboxItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
@@ -99,21 +100,21 @@ const DropdownMenuCheckboxItem = React.forwardRef<
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className,
className
)}
checked={checked}
{...props}
>
<span className='absolute left-2 flex h-3.5 w-3.5 items-center justify-center'>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<Check className='h-4 w-4' />
<Check className="h-4 w-4" />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.CheckboxItem>
));
))
DropdownMenuCheckboxItem.displayName =
DropdownMenuPrimitive.CheckboxItem.displayName;
DropdownMenuPrimitive.CheckboxItem.displayName
const DropdownMenuRadioItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
@@ -123,24 +124,24 @@ const DropdownMenuRadioItem = React.forwardRef<
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className,
className
)}
{...props}
>
<span className='absolute left-2 flex h-3.5 w-3.5 items-center justify-center'>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<Circle className='h-2 w-2 fill-current' />
<Circle className="h-2 w-2 fill-current" />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.RadioItem>
));
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
))
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName
const DropdownMenuLabel = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
inset?: boolean;
inset?: boolean
}
>(({ className, inset, ...props }, ref) => (
<DropdownMenuPrimitive.Label
@@ -148,12 +149,12 @@ const DropdownMenuLabel = React.forwardRef<
className={cn(
"px-2 py-1.5 text-sm font-semibold",
inset && "pl-8",
className,
className
)}
{...props}
/>
));
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
))
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName
const DropdownMenuSeparator = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
@@ -164,8 +165,8 @@ const DropdownMenuSeparator = React.forwardRef<
className={cn("-mx-1 my-1 h-px bg-muted", className)}
{...props}
/>
));
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
))
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName
const DropdownMenuShortcut = ({
className,
@@ -176,9 +177,9 @@ const DropdownMenuShortcut = ({
className={cn("ml-auto text-xs tracking-widest opacity-60", className)}
{...props}
/>
);
};
DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
)
}
DropdownMenuShortcut.displayName = "DropdownMenuShortcut"
export {
DropdownMenu,
@@ -196,4 +197,4 @@ export {
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuRadioGroup,
};
}
+9 -8
View File
@@ -1,5 +1,6 @@
import * as React from "react";
import { cn } from "~/utils";
import * as React from "react"
import { cn } from "~/utils"
export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> {}
@@ -11,14 +12,14 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
type={type}
className={cn(
"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",
className,
className
)}
ref={ref}
{...props}
/>
);
},
);
Input.displayName = "Input";
)
}
)
Input.displayName = "Input"
export { Input };
export { Input }
+11 -10
View File
@@ -1,13 +1,14 @@
"use client";
"use client"
import * as LabelPrimitive from "@radix-ui/react-label";
import { type VariantProps, cva } from "class-variance-authority";
import * as React from "react";
import { cn } from "~/utils";
import * as React from "react"
import * as LabelPrimitive from "@radix-ui/react-label"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "~/utils"
const labelVariants = cva(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
);
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
)
const Label = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
@@ -19,7 +20,7 @@ const Label = React.forwardRef<
className={cn(labelVariants(), className)}
{...props}
/>
));
Label.displayName = LabelPrimitive.Root.displayName;
))
Label.displayName = LabelPrimitive.Root.displayName
export { Label };
export { Label }
+55 -54
View File
@@ -1,19 +1,20 @@
"use client";
"use client"
import * as MenubarPrimitive from "@radix-ui/react-menubar";
import { Check, ChevronRight, Circle } from "lucide-react";
import * as React from "react";
import { cn } from "~/utils";
import * as React from "react"
import * as MenubarPrimitive from "@radix-ui/react-menubar"
import { Check, ChevronRight, Circle } from "lucide-react"
const MenubarMenu = MenubarPrimitive.Menu;
import { cn } from "~/utils"
const MenubarGroup = MenubarPrimitive.Group;
const MenubarMenu = MenubarPrimitive.Menu
const MenubarPortal = MenubarPrimitive.Portal;
const MenubarGroup = MenubarPrimitive.Group
const MenubarSub = MenubarPrimitive.Sub;
const MenubarPortal = MenubarPrimitive.Portal
const MenubarRadioGroup = MenubarPrimitive.RadioGroup;
const MenubarSub = MenubarPrimitive.Sub
const MenubarRadioGroup = MenubarPrimitive.RadioGroup
const Menubar = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.Root>,
@@ -23,12 +24,12 @@ const Menubar = React.forwardRef<
ref={ref}
className={cn(
"flex h-10 items-center space-x-1 rounded-md border bg-background p-1",
className,
className
)}
{...props}
/>
));
Menubar.displayName = MenubarPrimitive.Root.displayName;
))
Menubar.displayName = MenubarPrimitive.Root.displayName
const MenubarTrigger = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.Trigger>,
@@ -38,17 +39,17 @@ const MenubarTrigger = React.forwardRef<
ref={ref}
className={cn(
"flex cursor-default select-none items-center rounded-sm px-3 py-1.5 text-sm font-medium outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
className,
className
)}
{...props}
/>
));
MenubarTrigger.displayName = MenubarPrimitive.Trigger.displayName;
))
MenubarTrigger.displayName = MenubarPrimitive.Trigger.displayName
const MenubarSubTrigger = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.SubTrigger>,
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.SubTrigger> & {
inset?: boolean;
inset?: boolean
}
>(({ className, inset, children, ...props }, ref) => (
<MenubarPrimitive.SubTrigger
@@ -56,15 +57,15 @@ const MenubarSubTrigger = React.forwardRef<
className={cn(
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
inset && "pl-8",
className,
className
)}
{...props}
>
{children}
<ChevronRight className='ml-auto h-4 w-4' />
<ChevronRight className="ml-auto h-4 w-4" />
</MenubarPrimitive.SubTrigger>
));
MenubarSubTrigger.displayName = MenubarPrimitive.SubTrigger.displayName;
))
MenubarSubTrigger.displayName = MenubarPrimitive.SubTrigger.displayName
const MenubarSubContent = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.SubContent>,
@@ -74,12 +75,12 @@ const MenubarSubContent = React.forwardRef<
ref={ref}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground 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",
className,
className
)}
{...props}
/>
));
MenubarSubContent.displayName = MenubarPrimitive.SubContent.displayName;
))
MenubarSubContent.displayName = MenubarPrimitive.SubContent.displayName
const MenubarContent = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.Content>,
@@ -87,7 +88,7 @@ const MenubarContent = React.forwardRef<
>(
(
{ className, align = "start", alignOffset = -4, sideOffset = 8, ...props },
ref,
ref
) => (
<MenubarPrimitive.Portal>
<MenubarPrimitive.Content
@@ -97,19 +98,19 @@ const MenubarContent = React.forwardRef<
sideOffset={sideOffset}
className={cn(
"z-50 min-w-[12rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in 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",
className,
className
)}
{...props}
/>
</MenubarPrimitive.Portal>
),
);
MenubarContent.displayName = MenubarPrimitive.Content.displayName;
)
)
MenubarContent.displayName = MenubarPrimitive.Content.displayName
const MenubarItem = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Item> & {
inset?: boolean;
inset?: boolean
}
>(({ className, inset, ...props }, ref) => (
<MenubarPrimitive.Item
@@ -117,12 +118,12 @@ const MenubarItem = React.forwardRef<
className={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
inset && "pl-8",
className,
className
)}
{...props}
/>
));
MenubarItem.displayName = MenubarPrimitive.Item.displayName;
))
MenubarItem.displayName = MenubarPrimitive.Item.displayName
const MenubarCheckboxItem = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.CheckboxItem>,
@@ -132,20 +133,20 @@ const MenubarCheckboxItem = React.forwardRef<
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className,
className
)}
checked={checked}
{...props}
>
<span className='absolute left-2 flex h-3.5 w-3.5 items-center justify-center'>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<MenubarPrimitive.ItemIndicator>
<Check className='h-4 w-4' />
<Check className="h-4 w-4" />
</MenubarPrimitive.ItemIndicator>
</span>
{children}
</MenubarPrimitive.CheckboxItem>
));
MenubarCheckboxItem.displayName = MenubarPrimitive.CheckboxItem.displayName;
))
MenubarCheckboxItem.displayName = MenubarPrimitive.CheckboxItem.displayName
const MenubarRadioItem = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.RadioItem>,
@@ -155,24 +156,24 @@ const MenubarRadioItem = React.forwardRef<
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className,
className
)}
{...props}
>
<span className='absolute left-2 flex h-3.5 w-3.5 items-center justify-center'>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<MenubarPrimitive.ItemIndicator>
<Circle className='h-2 w-2 fill-current' />
<Circle className="h-2 w-2 fill-current" />
</MenubarPrimitive.ItemIndicator>
</span>
{children}
</MenubarPrimitive.RadioItem>
));
MenubarRadioItem.displayName = MenubarPrimitive.RadioItem.displayName;
))
MenubarRadioItem.displayName = MenubarPrimitive.RadioItem.displayName
const MenubarLabel = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Label> & {
inset?: boolean;
inset?: boolean
}
>(({ className, inset, ...props }, ref) => (
<MenubarPrimitive.Label
@@ -180,12 +181,12 @@ const MenubarLabel = React.forwardRef<
className={cn(
"px-2 py-1.5 text-sm font-semibold",
inset && "pl-8",
className,
className
)}
{...props}
/>
));
MenubarLabel.displayName = MenubarPrimitive.Label.displayName;
))
MenubarLabel.displayName = MenubarPrimitive.Label.displayName
const MenubarSeparator = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.Separator>,
@@ -196,8 +197,8 @@ const MenubarSeparator = React.forwardRef<
className={cn("-mx-1 my-1 h-px bg-muted", className)}
{...props}
/>
));
MenubarSeparator.displayName = MenubarPrimitive.Separator.displayName;
))
MenubarSeparator.displayName = MenubarPrimitive.Separator.displayName
const MenubarShortcut = ({
className,
@@ -207,13 +208,13 @@ const MenubarShortcut = ({
<span
className={cn(
"ml-auto text-xs tracking-widest text-muted-foreground",
className,
className
)}
{...props}
/>
);
};
MenubarShortcut.displayname = "MenubarShortcut";
)
}
MenubarShortcut.displayname = "MenubarShortcut"
export {
Menubar,
@@ -232,4 +233,4 @@ export {
MenubarGroup,
MenubarSub,
MenubarShortcut,
};
}
+35 -34
View File
@@ -1,15 +1,16 @@
"use client";
"use client"
import * as SelectPrimitive from "@radix-ui/react-select";
import { Check, ChevronDown, ChevronUp } from "lucide-react";
import * as React from "react";
import { cn } from "~/utils";
import * as React from "react"
import * as SelectPrimitive from "@radix-ui/react-select"
import { Check, ChevronDown, ChevronUp } from "lucide-react"
const Select = SelectPrimitive.Root;
import { cn } from "~/utils"
const SelectGroup = SelectPrimitive.Group;
const Select = SelectPrimitive.Root
const SelectValue = SelectPrimitive.Value;
const SelectGroup = SelectPrimitive.Group
const SelectValue = SelectPrimitive.Value
const SelectTrigger = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Trigger>,
@@ -19,17 +20,17 @@ const SelectTrigger = React.forwardRef<
ref={ref}
className={cn(
"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",
className,
className
)}
{...props}
>
{children}
<SelectPrimitive.Icon asChild>
<ChevronDown className='h-4 w-4 opacity-50' />
<ChevronDown className="h-4 w-4 opacity-50" />
</SelectPrimitive.Icon>
</SelectPrimitive.Trigger>
));
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
))
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
const SelectScrollUpButton = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
@@ -39,14 +40,14 @@ const SelectScrollUpButton = React.forwardRef<
ref={ref}
className={cn(
"flex cursor-default items-center justify-center py-1",
className,
className
)}
{...props}
>
<ChevronUp className='h-4 w-4' />
<ChevronUp className="h-4 w-4" />
</SelectPrimitive.ScrollUpButton>
));
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
))
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
const SelectScrollDownButton = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
@@ -56,15 +57,15 @@ const SelectScrollDownButton = React.forwardRef<
ref={ref}
className={cn(
"flex cursor-default items-center justify-center py-1",
className,
className
)}
{...props}
>
<ChevronDown className='h-4 w-4' />
<ChevronDown className="h-4 w-4" />
</SelectPrimitive.ScrollDownButton>
));
))
SelectScrollDownButton.displayName =
SelectPrimitive.ScrollDownButton.displayName;
SelectPrimitive.ScrollDownButton.displayName
const SelectContent = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Content>,
@@ -77,7 +78,7 @@ const SelectContent = React.forwardRef<
"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",
position === "popper" &&
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
className,
className
)}
position={position}
{...props}
@@ -87,7 +88,7 @@ const SelectContent = React.forwardRef<
className={cn(
"p-1",
position === "popper" &&
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]",
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
)}
>
{children}
@@ -95,8 +96,8 @@ const SelectContent = React.forwardRef<
<SelectScrollDownButton />
</SelectPrimitive.Content>
</SelectPrimitive.Portal>
));
SelectContent.displayName = SelectPrimitive.Content.displayName;
))
SelectContent.displayName = SelectPrimitive.Content.displayName
const SelectLabel = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Label>,
@@ -107,8 +108,8 @@ const SelectLabel = React.forwardRef<
className={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)}
{...props}
/>
));
SelectLabel.displayName = SelectPrimitive.Label.displayName;
))
SelectLabel.displayName = SelectPrimitive.Label.displayName
const SelectItem = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Item>,
@@ -118,20 +119,20 @@ const SelectItem = React.forwardRef<
ref={ref}
className={cn(
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className,
className
)}
{...props}
>
<span className='absolute left-2 flex h-3.5 w-3.5 items-center justify-center'>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<SelectPrimitive.ItemIndicator>
<Check className='h-4 w-4' />
<Check className="h-4 w-4" />
</SelectPrimitive.ItemIndicator>
</span>
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
</SelectPrimitive.Item>
));
SelectItem.displayName = SelectPrimitive.Item.displayName;
))
SelectItem.displayName = SelectPrimitive.Item.displayName
const SelectSeparator = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Separator>,
@@ -142,8 +143,8 @@ const SelectSeparator = React.forwardRef<
className={cn("-mx-1 my-1 h-px bg-muted", className)}
{...props}
/>
));
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
))
SelectSeparator.displayName = SelectPrimitive.Separator.displayName
export {
Select,
@@ -156,4 +157,4 @@ export {
SelectSeparator,
SelectScrollUpButton,
SelectScrollDownButton,
};
}
+11 -10
View File
@@ -1,8 +1,9 @@
"use client";
"use client"
import * as SeparatorPrimitive from "@radix-ui/react-separator";
import * as React from "react";
import { cn } from "~/utils";
import * as React from "react"
import * as SeparatorPrimitive from "@radix-ui/react-separator"
import { cn } from "~/utils"
const Separator = React.forwardRef<
React.ElementRef<typeof SeparatorPrimitive.Root>,
@@ -10,7 +11,7 @@ const Separator = React.forwardRef<
>(
(
{ className, orientation = "horizontal", decorative = true, ...props },
ref,
ref
) => (
<SeparatorPrimitive.Root
ref={ref}
@@ -19,12 +20,12 @@ const Separator = React.forwardRef<
className={cn(
"shrink-0 bg-border",
orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
className,
className
)}
{...props}
/>
),
);
Separator.displayName = SeparatorPrimitive.Root.displayName;
)
)
Separator.displayName = SeparatorPrimitive.Root.displayName
export { Separator };
export { Separator }
+32 -31
View File
@@ -1,18 +1,19 @@
"use client";
"use client"
import * as SheetPrimitive from "@radix-ui/react-dialog";
import { type VariantProps, cva } from "class-variance-authority";
import { X } from "lucide-react";
import * as React from "react";
import { cn } from "~/utils";
import * as React from "react"
import * as SheetPrimitive from "@radix-ui/react-dialog"
import { cva, type VariantProps } from "class-variance-authority"
import { X } from "lucide-react"
const Sheet = SheetPrimitive.Root;
import { cn } from "~/utils"
const SheetTrigger = SheetPrimitive.Trigger;
const Sheet = SheetPrimitive.Root
const SheetClose = SheetPrimitive.Close;
const SheetTrigger = SheetPrimitive.Trigger
const SheetPortal = SheetPrimitive.Portal;
const SheetClose = SheetPrimitive.Close
const SheetPortal = SheetPrimitive.Portal
const SheetOverlay = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Overlay>,
@@ -21,13 +22,13 @@ const SheetOverlay = React.forwardRef<
<SheetPrimitive.Overlay
className={cn(
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className,
className
)}
{...props}
ref={ref}
/>
));
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
))
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName
const sheetVariants = cva(
"fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
@@ -45,8 +46,8 @@ const sheetVariants = cva(
defaultVariants: {
side: "right",
},
},
);
}
)
interface SheetContentProps
extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
@@ -64,14 +65,14 @@ const SheetContent = React.forwardRef<
{...props}
>
{children}
<SheetPrimitive.Close className='absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary'>
<X className='h-4 w-4' />
<span className='sr-only'>Close</span>
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
</SheetPrimitive.Close>
</SheetPrimitive.Content>
</SheetPortal>
));
SheetContent.displayName = SheetPrimitive.Content.displayName;
))
SheetContent.displayName = SheetPrimitive.Content.displayName
const SheetHeader = ({
className,
@@ -80,12 +81,12 @@ const SheetHeader = ({
<div
className={cn(
"flex flex-col space-y-2 text-center sm:text-left",
className,
className
)}
{...props}
/>
);
SheetHeader.displayName = "SheetHeader";
)
SheetHeader.displayName = "SheetHeader"
const SheetFooter = ({
className,
@@ -94,12 +95,12 @@ const SheetFooter = ({
<div
className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
className,
className
)}
{...props}
/>
);
SheetFooter.displayName = "SheetFooter";
)
SheetFooter.displayName = "SheetFooter"
const SheetTitle = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Title>,
@@ -110,8 +111,8 @@ const SheetTitle = React.forwardRef<
className={cn("text-lg font-semibold text-foreground", className)}
{...props}
/>
));
SheetTitle.displayName = SheetPrimitive.Title.displayName;
))
SheetTitle.displayName = SheetPrimitive.Title.displayName
const SheetDescription = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Description>,
@@ -122,8 +123,8 @@ const SheetDescription = React.forwardRef<
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
));
SheetDescription.displayName = SheetPrimitive.Description.displayName;
))
SheetDescription.displayName = SheetPrimitive.Description.displayName
export {
Sheet,
@@ -136,4 +137,4 @@ export {
SheetFooter,
SheetTitle,
SheetDescription,
};
}
+3 -3
View File
@@ -1,4 +1,4 @@
import { cn } from "~/utils";
import { cn } from "~/utils"
function Skeleton({
className,
@@ -9,7 +9,7 @@ function Skeleton({
className={cn("animate-pulse rounded-md bg-muted", className)}
{...props}
/>
);
)
}
export { Skeleton };
export { Skeleton }
+25 -28
View File
@@ -1,31 +1,28 @@
import * as React from "react";
import { cn } from "~/utils";
import * as React from "react"
import { cn } from "~/utils"
const Table = React.forwardRef<
HTMLTableElement,
React.HTMLAttributes<HTMLTableElement>
>(({ className, ...props }, ref) => (
<div className='relative w-full overflow-auto'>
<div className="relative w-full overflow-auto">
<table
ref={ref}
className={cn("w-full caption-bottom text-sm", className)}
{...props}
/>
</div>
));
Table.displayName = "Table";
))
Table.displayName = "Table"
const TableHeader = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<thead
ref={ref}
className={cn("[&_tr]:border-b", className)}
{...props}
/>
));
TableHeader.displayName = "TableHeader";
<thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
))
TableHeader.displayName = "TableHeader"
const TableBody = React.forwardRef<
HTMLTableSectionElement,
@@ -36,8 +33,8 @@ const TableBody = React.forwardRef<
className={cn("[&_tr:last-child]:border-0", className)}
{...props}
/>
));
TableBody.displayName = "TableBody";
))
TableBody.displayName = "TableBody"
const TableFooter = React.forwardRef<
HTMLTableSectionElement,
@@ -47,12 +44,12 @@ const TableFooter = React.forwardRef<
ref={ref}
className={cn(
"border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
className,
className
)}
{...props}
/>
));
TableFooter.displayName = "TableFooter";
))
TableFooter.displayName = "TableFooter"
const TableRow = React.forwardRef<
HTMLTableRowElement,
@@ -62,12 +59,12 @@ const TableRow = React.forwardRef<
ref={ref}
className={cn(
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
className,
className
)}
{...props}
/>
));
TableRow.displayName = "TableRow";
))
TableRow.displayName = "TableRow"
const TableHead = React.forwardRef<
HTMLTableCellElement,
@@ -77,12 +74,12 @@ const TableHead = React.forwardRef<
ref={ref}
className={cn(
"h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
className,
className
)}
{...props}
/>
));
TableHead.displayName = "TableHead";
))
TableHead.displayName = "TableHead"
const TableCell = React.forwardRef<
HTMLTableCellElement,
@@ -93,8 +90,8 @@ const TableCell = React.forwardRef<
className={cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className)}
{...props}
/>
));
TableCell.displayName = "TableCell";
))
TableCell.displayName = "TableCell"
const TableCaption = React.forwardRef<
HTMLTableCaptionElement,
@@ -105,8 +102,8 @@ const TableCaption = React.forwardRef<
className={cn("mt-4 text-sm text-muted-foreground", className)}
{...props}
/>
));
TableCaption.displayName = "TableCaption";
))
TableCaption.displayName = "TableCaption"
export {
Table,
@@ -117,4 +114,4 @@ export {
TableRow,
TableCell,
TableCaption,
};
}
+9 -8
View File
@@ -1,5 +1,6 @@
import * as React from "react";
import { cn } from "~/utils";
import * as React from "react"
import { cn } from "~/utils"
export interface TextareaProps
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
@@ -10,14 +11,14 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
<textarea
className={cn(
"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",
className,
className
)}
ref={ref}
{...props}
/>
);
},
);
Textarea.displayName = "Textarea";
)
}
)
Textarea.displayName = "Textarea"
export { Textarea };
export { Textarea }
+31 -30
View File
@@ -1,12 +1,13 @@
"use client";
"use client"
import * as ToastPrimitives from "@radix-ui/react-toast";
import { type VariantProps, cva } from "class-variance-authority";
import { X } from "lucide-react";
import * as React from "react";
import { cn } from "~/utils";
import * as React from "react"
import * as ToastPrimitives from "@radix-ui/react-toast"
import { cva, type VariantProps } from "class-variance-authority"
import { X } from "lucide-react"
const ToastProvider = ToastPrimitives.Provider;
import { cn } from "~/utils"
const ToastProvider = ToastPrimitives.Provider
const ToastViewport = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Viewport>,
@@ -16,12 +17,12 @@ const ToastViewport = React.forwardRef<
ref={ref}
className={cn(
"fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]",
className,
className
)}
{...props}
/>
));
ToastViewport.displayName = ToastPrimitives.Viewport.displayName;
))
ToastViewport.displayName = ToastPrimitives.Viewport.displayName
const toastVariants = cva(
"group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
@@ -36,8 +37,8 @@ const toastVariants = cva(
defaultVariants: {
variant: "default",
},
},
);
}
)
const Toast = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Root>,
@@ -50,9 +51,9 @@ const Toast = React.forwardRef<
className={cn(toastVariants({ variant }), className)}
{...props}
/>
);
});
Toast.displayName = ToastPrimitives.Root.displayName;
)
})
Toast.displayName = ToastPrimitives.Root.displayName
const ToastAction = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Action>,
@@ -62,12 +63,12 @@ const ToastAction = React.forwardRef<
ref={ref}
className={cn(
"inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive",
className,
className
)}
{...props}
/>
));
ToastAction.displayName = ToastPrimitives.Action.displayName;
))
ToastAction.displayName = ToastPrimitives.Action.displayName
const ToastClose = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Close>,
@@ -77,15 +78,15 @@ const ToastClose = React.forwardRef<
ref={ref}
className={cn(
"absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600",
className,
className
)}
toast-close=''
toast-close=""
{...props}
>
<X className='h-4 w-4' />
<X className="h-4 w-4" />
</ToastPrimitives.Close>
));
ToastClose.displayName = ToastPrimitives.Close.displayName;
))
ToastClose.displayName = ToastPrimitives.Close.displayName
const ToastTitle = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Title>,
@@ -96,8 +97,8 @@ const ToastTitle = React.forwardRef<
className={cn("text-sm font-semibold", className)}
{...props}
/>
));
ToastTitle.displayName = ToastPrimitives.Title.displayName;
))
ToastTitle.displayName = ToastPrimitives.Title.displayName
const ToastDescription = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Description>,
@@ -108,12 +109,12 @@ const ToastDescription = React.forwardRef<
className={cn("text-sm opacity-90", className)}
{...props}
/>
));
ToastDescription.displayName = ToastPrimitives.Description.displayName;
))
ToastDescription.displayName = ToastPrimitives.Description.displayName
type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>;
type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>
type ToastActionElement = React.ReactElement<typeof ToastAction>;
type ToastActionElement = React.ReactElement<typeof ToastAction>
export {
type ToastProps,
@@ -125,4 +126,4 @@ export {
ToastDescription,
ToastClose,
ToastAction,
};
}
+8 -11
View File
@@ -1,4 +1,4 @@
"use client";
"use client"
import {
Toast,
@@ -7,21 +7,18 @@ import {
ToastProvider,
ToastTitle,
ToastViewport,
} from "~/components/ui/toast";
import { useToast } from "~/components/ui/use-toast";
} from "~/components/ui/toast"
import { useToast } from "~/components/ui/use-toast"
export function Toaster() {
const { toasts } = useToast();
const { toasts } = useToast()
return (
<ToastProvider>
{toasts.map(function ({ id, title, description, action, ...props }) {
return (
<Toast
key={id}
{...props}
>
<div className='grid gap-1'>
<Toast key={id} {...props}>
<div className="grid gap-1">
{title && <ToastTitle>{title}</ToastTitle>}
{description && (
<ToastDescription>{description}</ToastDescription>
@@ -30,9 +27,9 @@ export function Toaster() {
{action}
<ToastClose />
</Toast>
);
)
})}
<ToastViewport />
</ToastProvider>
);
)
}
+12 -11
View File
@@ -1,14 +1,15 @@
"use client";
"use client"
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
import * as React from "react";
import { cn } from "~/utils";
import * as React from "react"
import * as TooltipPrimitive from "@radix-ui/react-tooltip"
const TooltipProvider = TooltipPrimitive.Provider;
import { cn } from "~/utils"
const Tooltip = TooltipPrimitive.Root;
const TooltipProvider = TooltipPrimitive.Provider
const TooltipTrigger = TooltipPrimitive.Trigger;
const Tooltip = TooltipPrimitive.Root
const TooltipTrigger = TooltipPrimitive.Trigger
const TooltipContent = React.forwardRef<
React.ElementRef<typeof TooltipPrimitive.Content>,
@@ -19,11 +20,11 @@ const TooltipContent = React.forwardRef<
sideOffset={sideOffset}
className={cn(
"z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-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",
className,
className
)}
{...props}
/>
));
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
))
TooltipContent.displayName = TooltipPrimitive.Content.displayName
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
+72 -69
View File
@@ -1,75 +1,78 @@
"use client";
"use client"
// Inspired by react-hot-toast library
import * as React from "react";
import * as React from "react"
import type { ToastActionElement, ToastProps } from "~/components/ui/toast";
import type {
ToastActionElement,
ToastProps,
} from "~/components/ui/toast"
const TOAST_LIMIT = 3;
const TOAST_REMOVE_DELAY = 1000000;
const TOAST_LIMIT = 1
const TOAST_REMOVE_DELAY = 1000000
type ToasterToast = ToastProps & {
id: string;
title?: React.ReactNode;
description?: React.ReactNode;
action?: ToastActionElement;
};
id: string
title?: React.ReactNode
description?: React.ReactNode
action?: ToastActionElement
}
const actionTypes = {
ADD_TOAST: "ADD_TOAST",
UPDATE_TOAST: "UPDATE_TOAST",
DISMISS_TOAST: "DISMISS_TOAST",
REMOVE_TOAST: "REMOVE_TOAST",
} as const;
} as const
let count = 0;
let count = 0
function genId() {
count = (count + 1) % Number.MAX_SAFE_INTEGER;
return count.toString();
count = (count + 1) % Number.MAX_SAFE_INTEGER
return count.toString()
}
type ActionType = typeof actionTypes;
type ActionType = typeof actionTypes
type Action =
| {
type: ActionType["ADD_TOAST"];
toast: ToasterToast;
type: ActionType["ADD_TOAST"]
toast: ToasterToast
}
| {
type: ActionType["UPDATE_TOAST"];
toast: Partial<ToasterToast>;
type: ActionType["UPDATE_TOAST"]
toast: Partial<ToasterToast>
}
| {
type: ActionType["DISMISS_TOAST"];
toastId?: ToasterToast["id"];
type: ActionType["DISMISS_TOAST"]
toastId?: ToasterToast["id"]
}
| {
type: ActionType["REMOVE_TOAST"];
toastId?: ToasterToast["id"];
};
type: ActionType["REMOVE_TOAST"]
toastId?: ToasterToast["id"]
}
interface State {
toasts: ToasterToast[];
toasts: ToasterToast[]
}
const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>();
const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()
const addToRemoveQueue = (toastId: string) => {
if (toastTimeouts.has(toastId)) {
return;
return
}
const timeout = setTimeout(() => {
toastTimeouts.delete(toastId);
toastTimeouts.delete(toastId)
dispatch({
type: "REMOVE_TOAST",
toastId: toastId,
});
}, TOAST_REMOVE_DELAY);
})
}, TOAST_REMOVE_DELAY)
toastTimeouts.set(toastId, timeout);
};
toastTimeouts.set(toastId, timeout)
}
export const reducer = (state: State, action: Action): State => {
switch (action.type) {
@@ -77,27 +80,27 @@ export const reducer = (state: State, action: Action): State => {
return {
...state,
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
};
}
case "UPDATE_TOAST":
return {
...state,
toasts: state.toasts.map((t) =>
t.id === action.toast.id ? { ...t, ...action.toast } : t,
t.id === action.toast.id ? { ...t, ...action.toast } : t
),
};
}
case "DISMISS_TOAST": {
const { toastId } = action;
const { toastId } = action
// ! Side effects ! - This could be extracted into a dismissToast() action,
// but I'll keep it here for simplicity
if (toastId) {
addToRemoveQueue(toastId);
addToRemoveQueue(toastId)
} else {
state.toasts.forEach((toast) => {
addToRemoveQueue(toast.id);
});
addToRemoveQueue(toast.id)
})
}
return {
@@ -108,46 +111,46 @@ export const reducer = (state: State, action: Action): State => {
...t,
open: false,
}
: t,
: t
),
};
}
}
case "REMOVE_TOAST":
if (action.toastId === undefined) {
return {
...state,
toasts: [],
};
}
}
return {
...state,
toasts: state.toasts.filter((t) => t.id !== action.toastId),
};
}
}
};
const listeners: Array<(state: State) => void> = [];
let memoryState: State = { toasts: [] };
function dispatch(action: Action) {
memoryState = reducer(memoryState, action);
listeners.forEach((listener) => {
listener(memoryState);
});
}
type Toast = Omit<ToasterToast, "id">;
const listeners: Array<(state: State) => void> = []
let memoryState: State = { toasts: [] }
function dispatch(action: Action) {
memoryState = reducer(memoryState, action)
listeners.forEach((listener) => {
listener(memoryState)
})
}
type Toast = Omit<ToasterToast, "id">
function toast({ ...props }: Toast) {
const id = genId();
const id = genId()
const update = (props: ToasterToast) =>
dispatch({
type: "UPDATE_TOAST",
toast: { ...props, id },
});
const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id });
})
const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id })
dispatch({
type: "ADD_TOAST",
@@ -156,36 +159,36 @@ function toast({ ...props }: Toast) {
id,
open: true,
onOpenChange: (open) => {
if (!open) dismiss();
if (!open) dismiss()
},
},
});
})
return {
id: id,
dismiss,
update,
};
}
}
function useToast() {
const [state, setState] = React.useState<State>(memoryState);
const [state, setState] = React.useState<State>(memoryState)
React.useEffect(() => {
listeners.push(setState);
listeners.push(setState)
return () => {
const index = listeners.indexOf(setState);
const index = listeners.indexOf(setState)
if (index > -1) {
listeners.splice(index, 1);
listeners.splice(index, 1)
}
};
}, [state]);
}
}, [state])
return {
...state,
toast,
dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
};
}
}
export { toast, useToast };
export { useToast, toast }
+12
View File
@@ -22,6 +22,18 @@ const config: z.input<typeof Schema_Config> = {
process.env.NEXT_PUBLIC_DOMAIN || process.env.NEXT_PUBLIC_VERCEL_URL
}`,
/**
* Allow access to the deploy guide
* Will use the `/deploy` route, might be overlap with file / folder name
*
* Set this to false on final deployment
*
* I'm using this to show the deploy guide on my own demo deployment
*
* @default false
*/
showDeployGuide: true,
/**
* DEPRECATED
* Since in 2.0 we're using server side data fetching, this is not needed anymore.
+51
View File
@@ -34,10 +34,60 @@ export const Schema_File = z.object({
.nullable(),
});
export const Schema_Old_Config = z.object({
version: z.literal("1.0.0"),
basePath: z.string(),
masterKey: z.string(),
cacheControl: z.string(),
apiConfig: z.object({
rootFolder: z.string(),
isTeamDrive: z.boolean(),
defaultQuery: z.array(z.string()),
defaultField: z.string(),
defaultOrder: z.string(),
itemsPerPage: z.number().positive(),
searchRsult: z.number().positive(),
specialFile: z.object({
password: z.string(),
readme: z.string(),
banner: z.string(),
}),
hiddenFiles: z.array(z.string()),
allowDownloadProtectedFile: z.boolean(),
temporaryTokenDuration: z.number().positive(),
maxFileSize: z.number().positive(),
}),
siteConfig: z.object({
siteName: z.string(),
siteDescription: z.string(),
siteIcon: z.string(),
favIcon: z.string(),
twitterHandle: z.string().optional().default("@__mbaharip__"),
defaultAccentColor: z.string(),
privateIndex: z.boolean().optional().default(false),
navbarItems: z.array(
z.object({
icon: z.string(),
name: z.string(),
href: z.string(),
external: z.boolean().optional().default(false),
}),
),
}),
});
export const Schema_Config = z.object({
version: z.string(),
basePath: z.string(),
cacheControl: z.string(),
showDeployGuide: z.boolean(),
apiConfig: z.object({
rootFolder: z.string(),
@@ -122,3 +172,4 @@ export const Schema_Config = z.object({
),
}),
});
+12 -4
View File
@@ -16,9 +16,13 @@ const generateKey = () => {
const key = generateKey();
const iv = Buffer.from(key);
export async function encryptData(data: string): Promise<string> {
export async function encryptData(
data: string,
encryptKey: string = key,
ivKey: Buffer = iv,
): Promise<string> {
try {
const cipher = crypto.createCipheriv("aes-128-cbc", key, iv);
const cipher = crypto.createCipheriv("aes-128-cbc", encryptKey, ivKey);
return Buffer.concat([
cipher.update(data, "utf-8"),
cipher.final(),
@@ -30,9 +34,13 @@ export async function encryptData(data: string): Promise<string> {
}
}
export async function decryptData(hash: string): Promise<string> {
export async function decryptData(
hash: string,
encryptKey: string = key,
ivKey: Buffer = iv,
): Promise<string> {
try {
const decipher = crypto.createDecipheriv("aes-128-cbc", key, iv);
const decipher = crypto.createDecipheriv("aes-128-cbc", encryptKey, ivKey);
return Buffer.concat([
decipher.update(hash, "hex"),
+1 -1
View File
@@ -20,7 +20,7 @@ const config: Config = {
},
extend: {
fontFamily: {
sans: ["var(--font-source-sans-3)", ...tw.fontFamily.sans],
sans: ["var(--font-outfit)", ...tw.fontFamily.sans],
mono: ["var(--font-jetbrains-mono)", ...tw.fontFamily.mono],
},
colors: {
+160 -16
View File
@@ -1894,6 +1894,15 @@ __metadata:
languageName: node
linkType: hard
"@types/mdast@npm:^4.0.0":
version: 4.0.3
resolution: "@types/mdast@npm:4.0.3"
dependencies:
"@types/unist": "npm:*"
checksum: 10c0/e6994404f5ce58073aa6c1a37ceac3060326470a464e2d751580a9f89e2dbca3a2a6222b849bdaaa5bffbe89033c50a886d17e49fca3b040a4ffcf970e387a0c
languageName: node
linkType: hard
"@types/mime-types@npm:^2.1.1":
version: 2.1.4
resolution: "@types/mime-types@npm:2.1.4"
@@ -2006,6 +2015,13 @@ __metadata:
languageName: node
linkType: hard
"@types/unist@npm:*, @types/unist@npm:^3.0.0":
version: 3.0.2
resolution: "@types/unist@npm:3.0.2"
checksum: 10c0/39f220ce184a773c55c18a127062bfc4d0d30c987250cd59bab544d97be6cfec93717a49ef96e81f024b575718f798d4d329eb81c452fc57d6d051af8b043ebf
languageName: node
linkType: hard
"@types/unist@npm:^2, @types/unist@npm:^2.0.0":
version: 2.0.10
resolution: "@types/unist@npm:2.0.10"
@@ -3059,6 +3075,15 @@ __metadata:
languageName: node
linkType: hard
"devlop@npm:^1.0.0":
version: 1.1.0
resolution: "devlop@npm:1.1.0"
dependencies:
dequal: "npm:^2.0.0"
checksum: 10c0/e0928ab8f94c59417a2b8389c45c55ce0a02d9ac7fd74ef62d01ba48060129e1d594501b77de01f3eeafc7cb00773819b0df74d96251cf20b31c5b3071f45c0e
languageName: node
linkType: hard
"didyoumean@npm:^1.2.2":
version: 1.2.2
resolution: "didyoumean@npm:1.2.2"
@@ -3130,31 +3155,31 @@ __metadata:
languageName: node
linkType: hard
"embla-carousel-react@npm:^8.0.1":
version: 8.0.1
resolution: "embla-carousel-react@npm:8.0.1"
"embla-carousel-react@npm:^8.0.2":
version: 8.0.2
resolution: "embla-carousel-react@npm:8.0.2"
dependencies:
embla-carousel: "npm:8.0.1"
embla-carousel-reactive-utils: "npm:8.0.1"
embla-carousel: "npm:8.0.2"
embla-carousel-reactive-utils: "npm:8.0.2"
peerDependencies:
react: ^16.8.0 || ^17.0.1 || ^18.0.0
checksum: 10c0/a16af76be911133f00ff38491b0ed12f09571949234b22bfe83cbd2d8b0d3cf43b666ffc4fc6aaf17e04691495fdad69fc1bc33db3eeec9ba7f67fe8db056a25
checksum: 10c0/7e45266d4251a960515a3283e65454b61e54d6f400f2dee7f56a3ac50532f7ce4735d926494cb1570d81de4bf299c3d1417ab4e64467cda139a7f60b49583757
languageName: node
linkType: hard
"embla-carousel-reactive-utils@npm:8.0.1":
version: 8.0.1
resolution: "embla-carousel-reactive-utils@npm:8.0.1"
"embla-carousel-reactive-utils@npm:8.0.2":
version: 8.0.2
resolution: "embla-carousel-reactive-utils@npm:8.0.2"
peerDependencies:
embla-carousel: 8.0.1
checksum: 10c0/c511dbbcd869f11f102e826aea600f1668f8097792b4e185678f44240466fe6a224b68833c408bd9eb6c9b26e40321b6f18f70f45bd19df3cd403d964e1fba95
embla-carousel: 8.0.2
checksum: 10c0/e7e81916971008642700af0b96a59117324214c020759a53feb1d96edb34649329018777dc00b87c631c9d04efd544945f5914dc3cbd5289766a6cdcea253f4e
languageName: node
linkType: hard
"embla-carousel@npm:8.0.1":
version: 8.0.1
resolution: "embla-carousel@npm:8.0.1"
checksum: 10c0/9ce30759a77e75ff4ce490102c429794fd46f03bbcc4a6af4ecefbe55a5de5289b7ac0f7607d1774a9b23c241d5781bf3d45459590768b15679a9da5b56ef6df
"embla-carousel@npm:8.0.2":
version: 8.0.2
resolution: "embla-carousel@npm:8.0.2"
checksum: 10c0/e63ce4e387c0e227ce211a1131f81c74dbe6c4bb32a5d072e0c1e30774305ca9a30fc86ebb042707bf8d6ec8cb57575628dfa1b18b36dd3206d4774cd34b73bc
languageName: node
linkType: hard
@@ -5350,6 +5375,18 @@ __metadata:
languageName: node
linkType: hard
"mdast-util-find-and-replace@npm:^3.0.0":
version: 3.0.1
resolution: "mdast-util-find-and-replace@npm:3.0.1"
dependencies:
"@types/mdast": "npm:^4.0.0"
escape-string-regexp: "npm:^5.0.0"
unist-util-is: "npm:^6.0.0"
unist-util-visit-parents: "npm:^6.0.0"
checksum: 10c0/1faca98c4ee10a919f23b8cc6d818e5bb6953216a71dfd35f51066ed5d51ef86e5063b43dcfdc6061cd946e016a9f0d44a1dccadd58452cf4ed14e39377f00cb
languageName: node
linkType: hard
"mdast-util-from-markdown@npm:^1.0.0":
version: 1.3.1
resolution: "mdast-util-from-markdown@npm:1.3.1"
@@ -5451,6 +5488,16 @@ __metadata:
languageName: node
linkType: hard
"mdast-util-newline-to-break@npm:^2.0.0":
version: 2.0.0
resolution: "mdast-util-newline-to-break@npm:2.0.0"
dependencies:
"@types/mdast": "npm:^4.0.0"
mdast-util-find-and-replace: "npm:^3.0.0"
checksum: 10c0/756a5660b0a821e0d6d6a0b2d9b13ac32e41cc028c485a91bccf6300977e2557236c6cc93dbd55c68b785f1ed6eae69209a4ffe182533cd1cdfda369021bebd2
languageName: node
linkType: hard
"mdast-util-phrasing@npm:^3.0.0":
version: 3.0.1
resolution: "mdast-util-phrasing@npm:3.0.1"
@@ -6169,7 +6216,7 @@ __metadata:
clsx: "npm:^2.1.0"
cmdk: "npm:^1.0.0"
date-fns: "npm:^3.6.0"
embla-carousel-react: "npm:^8.0.1"
embla-carousel-react: "npm:^8.0.2"
encoding: "npm:^0.1.13"
eslint: "npm:8.38.0"
eslint-config-next: "npm:^14.1.4"
@@ -6185,6 +6232,7 @@ __metadata:
prettier: "npm:3.0.0"
prettier-plugin-tailwindcss: "npm:0.5.12"
react: "npm:^18"
react-colorful: "npm:^5.6.1"
react-day-picker: "npm:^8.10.0"
react-dom: "npm:^18"
react-h5-audio-player: "npm:^3.9.1"
@@ -6199,6 +6247,7 @@ __metadata:
rehype-katex: "npm:^6.0.3"
rehype-prism-plus: "npm:^1.6.3"
rehype-raw: "npm:6.1.1"
remark-breaks: "npm:^4.0.0"
remark-gfm: "npm:^3.0.1"
remark-math: "npm:^5.1.1"
remark-slug: "npm:^7.0.1"
@@ -6208,6 +6257,7 @@ __metadata:
tailwindcss: "npm:^3.4.1"
tailwindcss-animate: "npm:^1.0.7"
typescript: "npm:^5"
use-debouncy: "npm:^5.0.1"
vaul: "npm:^0.9.0"
zod: "npm:^3.22.4"
languageName: unknown
@@ -6950,6 +7000,16 @@ __metadata:
languageName: node
linkType: hard
"react-colorful@npm:^5.6.1":
version: 5.6.1
resolution: "react-colorful@npm:5.6.1"
peerDependencies:
react: ">=16.8.0"
react-dom: ">=16.8.0"
checksum: 10c0/48eb73cf71e10841c2a61b6b06ab81da9fffa9876134c239bfdebcf348ce2a47e56b146338e35dfb03512c85966bfc9a53844fc56bc50154e71f8daee59ff6f0
languageName: node
linkType: hard
"react-day-picker@npm:^8.10.0":
version: 8.10.0
resolution: "react-day-picker@npm:8.10.0"
@@ -7329,6 +7389,17 @@ __metadata:
languageName: node
linkType: hard
"remark-breaks@npm:^4.0.0":
version: 4.0.0
resolution: "remark-breaks@npm:4.0.0"
dependencies:
"@types/mdast": "npm:^4.0.0"
mdast-util-newline-to-break: "npm:^2.0.0"
unified: "npm:^11.0.0"
checksum: 10c0/d7b319a7993b54c5d574e9255080c5de68cfa24f993873b0ee296af13f478521c41d4b7ae0fc14b4607ea70c8f6967e998ab7a467de13139141e66a1a34cb6be
languageName: node
linkType: hard
"remark-gfm@npm:^3.0.1":
version: 3.0.1
resolution: "remark-gfm@npm:3.0.1"
@@ -8312,6 +8383,21 @@ __metadata:
languageName: node
linkType: hard
"unified@npm:^11.0.0":
version: 11.0.4
resolution: "unified@npm:11.0.4"
dependencies:
"@types/unist": "npm:^3.0.0"
bail: "npm:^2.0.0"
devlop: "npm:^1.0.0"
extend: "npm:^3.0.0"
is-plain-obj: "npm:^4.0.0"
trough: "npm:^2.0.0"
vfile: "npm:^6.0.0"
checksum: 10c0/b550cdc994d54c84e2e098eb02cfa53535cbc140c148aa3296f235cb43082b499d239110f342fa65eb37ad919472a93cc62f062a83541485a69498084cc87ba1
languageName: node
linkType: hard
"unique-filename@npm:^3.0.0":
version: 3.0.0
resolution: "unique-filename@npm:3.0.0"
@@ -8367,6 +8453,15 @@ __metadata:
languageName: node
linkType: hard
"unist-util-is@npm:^6.0.0":
version: 6.0.0
resolution: "unist-util-is@npm:6.0.0"
dependencies:
"@types/unist": "npm:^3.0.0"
checksum: 10c0/9419352181eaa1da35eca9490634a6df70d2217815bb5938a04af3a662c12c5607a2f1014197ec9c426fbef18834f6371bfdb6f033040fa8aa3e965300d70e7e
languageName: node
linkType: hard
"unist-util-position@npm:^4.0.0":
version: 4.0.4
resolution: "unist-util-position@npm:4.0.4"
@@ -8395,6 +8490,15 @@ __metadata:
languageName: node
linkType: hard
"unist-util-stringify-position@npm:^4.0.0":
version: 4.0.0
resolution: "unist-util-stringify-position@npm:4.0.0"
dependencies:
"@types/unist": "npm:^3.0.0"
checksum: 10c0/dfe1dbe79ba31f589108cb35e523f14029b6675d741a79dea7e5f3d098785045d556d5650ec6a8338af11e9e78d2a30df12b1ee86529cded1098da3f17ee999e
languageName: node
linkType: hard
"unist-util-visit-parents@npm:^5.0.0, unist-util-visit-parents@npm:^5.1.1":
version: 5.1.3
resolution: "unist-util-visit-parents@npm:5.1.3"
@@ -8405,6 +8509,16 @@ __metadata:
languageName: node
linkType: hard
"unist-util-visit-parents@npm:^6.0.0":
version: 6.0.1
resolution: "unist-util-visit-parents@npm:6.0.1"
dependencies:
"@types/unist": "npm:^3.0.0"
unist-util-is: "npm:^6.0.0"
checksum: 10c0/51b1a5b0aa23c97d3e03e7288f0cdf136974df2217d0999d3de573c05001ef04cccd246f51d2ebdfb9e8b0ed2704451ad90ba85ae3f3177cf9772cef67f56206
languageName: node
linkType: hard
"unist-util-visit@npm:^4.0.0":
version: 4.1.2
resolution: "unist-util-visit@npm:4.1.2"
@@ -8461,6 +8575,15 @@ __metadata:
languageName: node
linkType: hard
"use-debouncy@npm:^5.0.1":
version: 5.0.1
resolution: "use-debouncy@npm:5.0.1"
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
checksum: 10c0/d5d2b5f330c161ed6de4045a6f9499b459366c7f6902ed4200c7ee5f88491969e48b5dee180d2a1028a744b360cfdf59ff66b5620a01dc52f01ba106d2577695
languageName: node
linkType: hard
"use-sidecar@npm:^1.1.2":
version: 1.1.2
resolution: "use-sidecar@npm:1.1.2"
@@ -8539,6 +8662,16 @@ __metadata:
languageName: node
linkType: hard
"vfile-message@npm:^4.0.0":
version: 4.0.2
resolution: "vfile-message@npm:4.0.2"
dependencies:
"@types/unist": "npm:^3.0.0"
unist-util-stringify-position: "npm:^4.0.0"
checksum: 10c0/07671d239a075f888b78f318bc1d54de02799db4e9dce322474e67c35d75ac4a5ac0aaf37b18801d91c9f8152974ea39678aa72d7198758b07f3ba04fb7d7514
languageName: node
linkType: hard
"vfile@npm:^5.0.0":
version: 5.3.7
resolution: "vfile@npm:5.3.7"
@@ -8551,6 +8684,17 @@ __metadata:
languageName: node
linkType: hard
"vfile@npm:^6.0.0":
version: 6.0.1
resolution: "vfile@npm:6.0.1"
dependencies:
"@types/unist": "npm:^3.0.0"
unist-util-stringify-position: "npm:^4.0.0"
vfile-message: "npm:^4.0.0"
checksum: 10c0/443bda43e5ad3b73c5976e987dba2b2d761439867ba7d5d7c5f4b01d3c1cb1b976f5f0e6b2399a00dc9b4eaec611bd9984ce9ce8a75a72e60aed518b10a902d2
languageName: node
linkType: hard
"warning@npm:^4.0.2":
version: 4.0.3
resolution: "warning@npm:4.0.3"