-
{commitChanges}
+
+
{commitChanges}
);
diff --git a/components/loader.tsx b/components/loader.tsx
index e0cf5f7..98ee9a5 100644
--- a/components/loader.tsx
+++ b/components/loader.tsx
@@ -1,7 +1,7 @@
import React from "react";
const Loader = () => {
- return
;
+ return
;
};
export default Loader;
diff --git a/components/md-components.tsx b/components/md-components.tsx
index f21a3ef..d3739fb 100644
--- a/components/md-components.tsx
+++ b/components/md-components.tsx
@@ -6,7 +6,7 @@ import { cn } from "@/lib/utils";
import { NpmCommands } from "@/types/unist";
import { Style } from "@/components/md/styles";
import { Event } from "@/lib/events";
-import { CopyButton, CopyNpmCommandButton } from "@/components/md/copy-button";
+import { CopyButton } from "@/components/md/copy-button";
import { CodeBlockWrapper } from "@/components/md/code-block-wrapper";
import Image from "next/image";
import { StyleWrapper } from "@/components/md/style-wrapper";
@@ -17,16 +17,12 @@ import { coldarkDark } from "react-syntax-highlighter/dist/cjs/styles/prism";
import { remark } from "remark";
import rehypeKatex from "rehype-katex";
-const customStyle = {
- backgroundColor: "#18181b",
-};
-
const components = {
h1: ({ className, ...props }: React.HTMLAttributes
) => (
@@ -35,7 +31,7 @@ const components = {
@@ -44,7 +40,7 @@ const components = {
@@ -53,7 +49,7 @@ const components = {
@@ -62,7 +58,7 @@ const components = {
@@ -71,7 +67,7 @@ const components = {
@@ -129,7 +125,7 @@ const components = {
|
@@ -138,7 +134,7 @@ const components = {
|
@@ -166,8 +162,8 @@ const components = {
@@ -180,21 +176,6 @@ const components = {
className={cn("absolute right-4 top-4", __withMeta__ && "top-16")}
/>
)}
-
- {__npmCommand__ &&
- __yarnCommand__ &&
- __pnpmCommand__ &&
- __bunCommand__ && (
-
- )}
);
},
@@ -203,24 +184,22 @@ const components = {
const match = /language-(\w+)/.exec(className || "");
const language = match ? match[1] : null;
- const modifier = match ? match[2] : null;
const value = String(children).replace(/\n$/, "");
+ const customStyle = {
+ backgroundColor: "var(--primary-foreground) / 0.05",
+ };
+
return match ? (
-
+
{value}
@@ -230,8 +209,8 @@ const components = {
) : (
diff --git a/components/md/copy-button.tsx b/components/md/copy-button.tsx
index b50feb5..61c7b77 100644
--- a/components/md/copy-button.tsx
+++ b/components/md/copy-button.tsx
@@ -1,18 +1,10 @@
"use client";
import * as React from "react";
-import { DropdownMenuTriggerProps } from "@radix-ui/react-dropdown-menu";
import { CheckIcon, CopyIcon } from "@radix-ui/react-icons";
import { Event, trackEvent } from "@/lib/events";
import { cn } from "@/lib/utils";
-import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuTrigger,
-} from "@/components/ui/dropdown-menu";
-import { NpmCommands } from "@/types/unist";
import { Button } from "../ui/button";
interface CopyButtonProps extends React.HTMLAttributes {
@@ -48,7 +40,7 @@ export function CopyButton({
size="icon"
variant="ghost"
className={cn(
- "relative z-10 h-6 w-6 text-zinc-50 hover:bg-zinc-700 hover:text-zinc-50",
+ "relative z-10 h-6 w-6 bg-card border",
className,
)}
onClick={() => {
@@ -69,142 +61,10 @@ export function CopyButton({
>
Copy
{hasCopied ? (
-
+
) : (
)}
);
}
-
-interface CopyWithClassNamesProps extends DropdownMenuTriggerProps {
- value: string;
- classNames: string;
- className?: string;
-}
-
-export function CopyWithClassNames({
- value,
- classNames,
- className,
- ...props
-}: CopyWithClassNamesProps) {
- const [hasCopied, setHasCopied] = React.useState(false);
-
- React.useEffect(() => {
- setTimeout(() => {
- setHasCopied(false);
- }, 2000);
- }, [hasCopied]);
-
- const copyToClipboard = React.useCallback((value: string) => {
- copyToClipboardWithMeta(value);
- setHasCopied(true);
- }, []);
-
- return (
-
-
-
-
-
- copyToClipboard(value)}>
- Component
-
- copyToClipboard(classNames)}>
- Classname
-
-
-
- );
-}
-
-interface CopyNpmCommandButtonProps extends DropdownMenuTriggerProps {
- commands: Required;
-}
-
-export function CopyNpmCommandButton({
- commands,
- className,
- ...props
-}: CopyNpmCommandButtonProps) {
- const [hasCopied, setHasCopied] = React.useState(false);
-
- React.useEffect(() => {
- setTimeout(() => {
- setHasCopied(false);
- }, 2000);
- }, [hasCopied]);
-
- const copyCommand = React.useCallback(
- (value: string, pm: "npm" | "pnpm" | "yarn" | "bun") => {
- copyToClipboardWithMeta(value, {
- name: "copy_npm_command",
- properties: {
- command: value,
- pm,
- },
- });
- setHasCopied(true);
- },
- [],
- );
-
- return (
-
-
-
-
-
- copyCommand(commands.__npmCommand__, "npm")}
- >
- npm
-
- copyCommand(commands.__yarnCommand__, "yarn")}
- >
- yarn
-
- copyCommand(commands.__pnpmCommand__, "pnpm")}
- >
- pnpm
-
- copyCommand(commands.__bunCommand__, "bun")}
- >
- bun
-
-
-
- );
-}
diff --git a/components/notificationBanner.tsx b/components/notificationBanner.tsx
new file mode 100644
index 0000000..7b8ae3e
--- /dev/null
+++ b/components/notificationBanner.tsx
@@ -0,0 +1,85 @@
+"use client";
+
+import React, { useState, useEffect } from "react";
+import { Button } from "@/components/ui/button";
+import {
+ Dialog,
+ DialogClose,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "@/components/ui/dialog";
+
+const NotificationPopup = ({
+ onClose,
+ onOpenChange,
+ showNotification,
+}: {
+ onClose: () => void;
+ onOpenChange(open: boolean): void;
+ showNotification: boolean;
+}) => {
+ return (
+
+ );
+};
+
+const NotificationBanner = () => {
+ const [showNotification, setShowNotification] = useState(false);
+
+ useEffect(() => {
+ const hasVisitedBefore = localStorage.getItem("hasVisitedBefore");
+ if (!hasVisitedBefore) {
+ setShowNotification(true);
+ }
+ }, [showNotification]);
+
+ const handleCloseNotification = () => {
+ setShowNotification(false);
+ localStorage.setItem("hasVisitedBefore", "true");
+ };
+
+ const onOpenChange = (open: boolean) => {
+ if (!localStorage.getItem("hasVisitedBefore")) {
+ setShowNotification(true);
+ }
+ setShowNotification(false);
+ };
+
+ return (
+
+ {showNotification && (
+
+ )}
+
+ );
+};
+
+export default NotificationBanner;
diff --git a/components/ui/dialog.tsx b/components/ui/dialog.tsx
new file mode 100644
index 0000000..88adf87
--- /dev/null
+++ b/components/ui/dialog.tsx
@@ -0,0 +1,116 @@
+"use client";
+
+import * as React from "react";
+import * as DialogPrimitive from "@radix-ui/react-dialog";
+import { cn } from "@/lib/utils";
+
+const Dialog = DialogPrimitive.Root;
+
+const DialogTrigger = DialogPrimitive.Trigger;
+
+const DialogPortal = DialogPrimitive.Portal;
+
+const DialogClose = DialogPrimitive.Close;
+
+const DialogOverlay = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
+
+const DialogContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+
+ {children}
+
+
+));
+DialogContent.displayName = DialogPrimitive.Content.displayName;
+
+const DialogHeader = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => (
+
+);
+DialogHeader.displayName = "DialogHeader";
+
+const DialogFooter = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => (
+
+);
+DialogFooter.displayName = "DialogFooter";
+
+const DialogTitle = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+DialogTitle.displayName = DialogPrimitive.Title.displayName;
+
+const DialogDescription = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+DialogDescription.displayName = DialogPrimitive.Description.displayName;
+
+export {
+ Dialog,
+ DialogPortal,
+ DialogOverlay,
+ DialogTrigger,
+ DialogClose,
+ DialogContent,
+ DialogHeader,
+ DialogFooter,
+ DialogTitle,
+ DialogDescription,
+};
diff --git a/components/ui/spinner.tsx b/components/ui/spinner.tsx
new file mode 100644
index 0000000..e7ce46e
--- /dev/null
+++ b/components/ui/spinner.tsx
@@ -0,0 +1,38 @@
+"use client";
+
+import React from "react";
+
+import { cn } from "@/lib/utils";
+
+const Spinner = React.forwardRef<
+ HTMLDivElement,
+ React.ComponentPropsWithoutRef<"div">
+>(({ className, ...props }, ref) => {
+ const computeDelay = (i: number) => `${-1.2 + i * 0.1}s`;
+ const computeRotation = (i: number) => `${i * 30}deg`;
+ return (
+
+
+ {[...Array(12)].map((_, i) => (
+
+ ))}
+
+
+ );
+});
+Spinner.displayName = "Spinner";
+
+export { Spinner };
diff --git a/package.json b/package.json
index e2e20ba..0977f9c 100644
--- a/package.json
+++ b/package.json
@@ -22,6 +22,7 @@
"dependencies": {
"@google/generative-ai": "^0.10.0",
"@radix-ui/react-collapsible": "^1.0.3",
+ "@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.0.2",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f617183..7168af3 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -11,6 +11,9 @@ dependencies:
'@radix-ui/react-collapsible':
specifier: ^1.0.3
version: 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1)
+ '@radix-ui/react-dialog':
+ specifier: ^1.0.5
+ version: 1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1)
'@radix-ui/react-dropdown-menu':
specifier: ^2.0.6
version: 2.0.6(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1)
@@ -482,6 +485,40 @@ packages:
react: 18.3.1
dev: false
+ /@radix-ui/react-dialog@1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.24.5
+ '@radix-ui/primitive': 1.0.1
+ '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.1)(react@18.3.1)
+ '@radix-ui/react-context': 1.0.1(@types/react@18.3.1)(react@18.3.1)
+ '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1)
+ '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.3.1)(react@18.3.1)
+ '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1)
+ '@radix-ui/react-id': 1.0.1(@types/react@18.3.1)(react@18.3.1)
+ '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1)
+ '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1)
+ '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1)
+ '@radix-ui/react-slot': 1.0.2(@types/react@18.3.1)(react@18.3.1)
+ '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.3.1)(react@18.3.1)
+ '@types/react': 18.3.1
+ '@types/react-dom': 18.3.0
+ aria-hidden: 1.2.4
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ react-remove-scroll: 2.5.5(@types/react@18.3.1)(react@18.3.1)
+ dev: false
+
/@radix-ui/react-direction@1.0.1(@types/react@18.3.1)(react@18.3.1):
resolution: {integrity: sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==}
peerDependencies:
diff --git a/tailwind.config.ts b/tailwind.config.ts
index 41668a3..2429b09 100644
--- a/tailwind.config.ts
+++ b/tailwind.config.ts
@@ -67,10 +67,15 @@ const config = {
from: { height: "var(--radix-accordion-content-height)" },
to: { height: "0" },
},
+ spinner: {
+ from: { opacity: "1" },
+ to: { opacity: "0.15" },
+ },
},
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
+ spinner: "spinner 1.2s linear infinite",
},
},
},