diff --git a/components/ui/dropdown-menu.tsx b/components/ui/dropdown-menu.tsx new file mode 100644 index 0000000..cafe6ba --- /dev/null +++ b/components/ui/dropdown-menu.tsx @@ -0,0 +1,267 @@ +"use client" + +import * as React from "react" +import { Menu as MenuPrimitive } from "@base-ui/react/menu" +import { cn } from "cn" +import { ChevronRightIcon, CheckIcon } from "lucide-react" + +function DropdownMenu({ ...props }: MenuPrimitive.Root.Props) { + return +} + +function DropdownMenuPortal({ ...props }: MenuPrimitive.Portal.Props) { + return +} + +function DropdownMenuTrigger({ ...props }: MenuPrimitive.Trigger.Props) { + return +} + +function DropdownMenuContent({ + align = "start", + alignOffset = 0, + side = "bottom", + sideOffset = 4, + className, + ...props +}: MenuPrimitive.Popup.Props & + Pick< + MenuPrimitive.Positioner.Props, + "align" | "alignOffset" | "side" | "sideOffset" + >) { + return ( + + + + + + ) +} + +function DropdownMenuGroup({ ...props }: MenuPrimitive.Group.Props) { + return +} + +function DropdownMenuLabel({ + className, + inset, + ...props +}: MenuPrimitive.GroupLabel.Props & { + inset?: boolean +}) { + return ( + + ) +} + +function DropdownMenuItem({ + className, + inset, + variant = "default", + ...props +}: MenuPrimitive.Item.Props & { + inset?: boolean + variant?: "default" | "destructive" +}) { + return ( + + ) +} + +function DropdownMenuSub({ ...props }: MenuPrimitive.SubmenuRoot.Props) { + return +} + +function DropdownMenuSubTrigger({ + className, + inset, + children, + ...props +}: MenuPrimitive.SubmenuTrigger.Props & { + inset?: boolean +}) { + return ( + + {children} + + + ) +} + +function DropdownMenuSubContent({ + align = "start", + alignOffset = -3, + side = "right", + sideOffset = 0, + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DropdownMenuCheckboxItem({ + className, + children, + checked, + inset, + ...props +}: MenuPrimitive.CheckboxItem.Props & { + inset?: boolean +}) { + return ( + + + + + + + {children} + + ) +} + +function DropdownMenuRadioGroup({ ...props }: MenuPrimitive.RadioGroup.Props) { + return ( + + ) +} + +function DropdownMenuRadioItem({ + className, + children, + inset, + ...props +}: MenuPrimitive.RadioItem.Props & { + inset?: boolean +}) { + return ( + + + + + + + {children} + + ) +} + +function DropdownMenuSeparator({ + className, + ...props +}: MenuPrimitive.Separator.Props) { + return ( + + ) +} + +function DropdownMenuShortcut({ + className, + ...props +}: React.ComponentProps<"span">) { + return ( + + ) +} + +export { + DropdownMenu, + DropdownMenuPortal, + DropdownMenuTrigger, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuLabel, + DropdownMenuItem, + DropdownMenuCheckboxItem, + DropdownMenuRadioGroup, + DropdownMenuRadioItem, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuSub, + DropdownMenuSubTrigger, + DropdownMenuSubContent, +} diff --git a/package-lock.json b/package-lock.json index ce668a3..df6f5da 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,10 +9,12 @@ "version": "0.1.0", "dependencies": { "@base-ui/react": "^1.8.0", + "@vercel/speed-insights": "^2.0.0", "class-variance-authority": "^0.7.1", "cn": "^0.2.6", "lucide-react": "^1.45.0", "next": "16.3.4", + "next-themes": "^0.4.6", "react": "19.2.8", "react-dom": "19.2.8", "shadcn": "^4.21.0", @@ -2962,6 +2964,44 @@ "win32" ] }, + "node_modules/@vercel/speed-insights": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@vercel/speed-insights/-/speed-insights-2.0.0.tgz", + "integrity": "sha512-jwkNcrTeafWxjmWq4AHBaptSqZiJkYU5adLC9QBSqeim0GcqDMgN5Ievh8OG1rJ6W3A4l1oiP7qr9CWxGuzu3w==", + "license": "Apache-2.0", + "peerDependencies": { + "@sveltejs/kit": "^1 || ^2", + "next": ">= 13", + "nuxt": ">= 3", + "react": "^18 || ^19 || ^19.0.0-rc", + "svelte": ">= 4", + "vue": "^3", + "vue-router": "^4" + }, + "peerDependenciesMeta": { + "@sveltejs/kit": { + "optional": true + }, + "next": { + "optional": true + }, + "nuxt": { + "optional": true + }, + "react": { + "optional": true + }, + "svelte": { + "optional": true + }, + "vue": { + "optional": true + }, + "vue-router": { + "optional": true + } + } + }, "node_modules/accepts": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", @@ -7106,6 +7146,16 @@ } } }, + "node_modules/next-themes": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz", + "integrity": "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" + } + }, "node_modules/next/node_modules/postcss": { "version": "8.5.23", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.23.tgz", diff --git a/package.json b/package.json index 0153b99..91f33a7 100644 --- a/package.json +++ b/package.json @@ -10,10 +10,12 @@ }, "dependencies": { "@base-ui/react": "^1.8.0", + "@vercel/speed-insights": "^2.0.0", "class-variance-authority": "^0.7.1", "cn": "^0.2.6", "lucide-react": "^1.45.0", "next": "16.3.4", + "next-themes": "^0.4.6", "react": "19.2.8", "react-dom": "19.2.8", "shadcn": "^4.21.0", diff --git a/public/moving_clouds_in_mountains.gif b/public/moving_clouds_in_mountains.gif new file mode 100644 index 0000000..c45c725 Binary files /dev/null and b/public/moving_clouds_in_mountains.gif differ diff --git a/public/moving_clouds_in_mountains.mp4 b/public/moving_clouds_in_mountains.mp4 deleted file mode 100644 index 97e0ed1..0000000 Binary files a/public/moving_clouds_in_mountains.mp4 and /dev/null differ diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 27f0074..b40cc7c 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,5 +1,7 @@ import type { Metadata } from "next"; import { Capriola, Inter, JetBrains_Mono } from "next/font/google"; +import { SpeedInsights } from "@vercel/speed-insights/next"; +import { ThemeProvider } from "../components/theme-provider"; import "./globals.css"; const inter = Inter({ @@ -27,9 +29,19 @@ export default function RootLayout({ children }: LayoutProps<"/">) { return ( - {children} + + + {children} + + + ); } diff --git a/src/components/HeroVideo.tsx b/src/components/HeroVideo.tsx index 5367f88..3b8c7e3 100644 --- a/src/components/HeroVideo.tsx +++ b/src/components/HeroVideo.tsx @@ -1,16 +1,16 @@ +import Image from "next/image"; + const HeroVideo = () => { return ( -
- +
+ Hero banner
); } diff --git a/src/components/ProfileHeader.tsx b/src/components/ProfileHeader.tsx index b5d2c7a..e9f9d99 100644 --- a/src/components/ProfileHeader.tsx +++ b/src/components/ProfileHeader.tsx @@ -1,5 +1,6 @@ import Image from "next/image"; import HeroVideo from "./HeroVideo"; +import { ModeToggle } from "./ToggleButton"; const ProfileHeader = () => { return ( @@ -22,11 +23,15 @@ const ProfileHeader = () => { Nitish Kumar -

+

Curious mind, restless hands, too many ideas.

+ +
+ +
diff --git a/src/components/ToggleButton.tsx b/src/components/ToggleButton.tsx new file mode 100644 index 0000000..1e3838d --- /dev/null +++ b/src/components/ToggleButton.tsx @@ -0,0 +1,29 @@ +"use client"; + +import { Moon, Sun } from "lucide-react"; +import { useTheme } from "next-themes"; +import { Button } from "@/components/ui/button"; + +export function ModeToggle() { + const { theme, setTheme } = useTheme(); + + const toggleTheme = () => { + setTheme(theme === "dark" ? "light" : "dark"); + }; + + return ( + + ); +} \ No newline at end of file diff --git a/src/components/theme-provider.tsx b/src/components/theme-provider.tsx new file mode 100644 index 0000000..e018a73 --- /dev/null +++ b/src/components/theme-provider.tsx @@ -0,0 +1,11 @@ +"use client" + +import * as React from "react" +import { ThemeProvider as NextThemesProvider } from "next-themes" + +export function ThemeProvider({ + children, + ...props +}: React.ComponentProps) { + return {children} +} \ No newline at end of file