svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3",
+ className
+ )}
+ {...props}
+ />
+ )
+}
+
+export {
+ Avatar,
+ AvatarImage,
+ AvatarFallback,
+ AvatarGroup,
+ AvatarGroupCount,
+ AvatarBadge,
+}
diff --git a/src/components/ui/breadcrumb.tsx b/src/components/ui/breadcrumb.tsx
index 912db6c..de00f9d 100644
--- a/src/components/ui/breadcrumb.tsx
+++ b/src/components/ui/breadcrumb.tsx
@@ -1,107 +1,115 @@
-import * as React from "react";
-import { Slot } from "@radix-ui/react-slot";
-import { cn } from "~/lib/utils";
-import { ChevronRightIcon, DotsHorizontalIcon } from "@radix-ui/react-icons";
+import * as React from "react"
+import { Slot } from "radix-ui"
-const Breadcrumb = React.forwardRef<
- HTMLElement,
- React.ComponentPropsWithoutRef<"nav"> & {
- separator?: React.ReactNode;
- }
->(({ ...props }, ref) =>
);
-Breadcrumb.displayName = "Breadcrumb";
+import { cn } from "~/lib/utils"
+import { ChevronRightIcon, MoreHorizontalIcon } from "lucide-react"
-const BreadcrumbList = React.forwardRef<
- HTMLOListElement,
- React.ComponentPropsWithoutRef<"ol">
->(({ className, ...props }, ref) => (
-
-));
-BreadcrumbList.displayName = "BreadcrumbList";
+function Breadcrumb({ className, ...props }: React.ComponentProps<"nav">) {
+ return (
+
+ )
+}
-const BreadcrumbItem = React.forwardRef<
- HTMLLIElement,
- React.ComponentPropsWithoutRef<"li">
->(({ className, ...props }, ref) => (
-
-));
-BreadcrumbItem.displayName = "BreadcrumbItem";
+function BreadcrumbList({ className, ...props }: React.ComponentProps<"ol">) {
+ return (
+
+ )
+}
-const BreadcrumbLink = React.forwardRef<
- HTMLAnchorElement,
- React.ComponentPropsWithoutRef<"a"> & {
- asChild?: boolean;
- }
->(({ asChild, className, ...props }, ref) => {
- const Comp = asChild ? Slot : "a";
+function BreadcrumbItem({ className, ...props }: React.ComponentProps<"li">) {
+ return (
+
+ )
+}
+
+function BreadcrumbLink({
+ asChild,
+ className,
+ ...props
+}: React.ComponentProps<"a"> & {
+ asChild?: boolean
+}) {
+ const Comp = asChild ? Slot.Root : "a"
return (
- );
-});
-BreadcrumbLink.displayName = "BreadcrumbLink";
+ )
+}
-const BreadcrumbPage = React.forwardRef<
- HTMLSpanElement,
- React.ComponentPropsWithoutRef<"span">
->(({ className, ...props }, ref) => (
-
-));
-BreadcrumbPage.displayName = "BreadcrumbPage";
+function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) {
+ return (
+
+ )
+}
-const BreadcrumbSeparator = ({
+function BreadcrumbSeparator({
children,
className,
...props
-}: React.ComponentProps<"li">) => (
-
svg]:h-3.5 [&>svg]:w-3.5", className)}
- {...props}
- >
- {children ?? }
-
-);
-BreadcrumbSeparator.displayName = "BreadcrumbSeparator";
+}: React.ComponentProps<"li">) {
+ return (
+
svg]:size-3.5", className)}
+ {...props}
+ >
+ {children ?? (
+
+ )}
+
+ )
+}
-const BreadcrumbEllipsis = ({
+function BreadcrumbEllipsis({
className,
...props
-}: React.ComponentProps<"span">) => (
-
-
- More
-
-);
-BreadcrumbEllipsis.displayName = "BreadcrumbElipssis";
+}: React.ComponentProps<"span">) {
+ return (
+
svg]:size-3.5",
+ className
+ )}
+ {...props}
+ >
+
+ More
+
+ )
+}
export {
Breadcrumb,
@@ -111,4 +119,4 @@ export {
BreadcrumbPage,
BreadcrumbSeparator,
BreadcrumbEllipsis,
-};
+}
diff --git a/src/components/ui/dropdown-menu.tsx b/src/components/ui/dropdown-menu.tsx
index d8addfa..19c44f0 100644
--- a/src/components/ui/dropdown-menu.tsx
+++ b/src/components/ui/dropdown-menu.tsx
@@ -1,204 +1,269 @@
-"use client";
+"use client"
-import * as React from "react";
-import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
-import { cn } from "~/lib/utils";
-import {
- CheckIcon,
- ChevronRightIcon,
- DotFilledIcon,
-} from "@radix-ui/react-icons";
+import * as React from "react"
+import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui"
-const DropdownMenu = DropdownMenuPrimitive.Root;
+import { cn } from "~/lib/utils"
+import { CheckIcon, ChevronRightIcon } from "lucide-react"
-const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
+function DropdownMenu({
+ ...props
+}: React.ComponentProps
) {
+ return
+}
-const DropdownMenuGroup = DropdownMenuPrimitive.Group;
+function DropdownMenuPortal({
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
-const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
+function DropdownMenuTrigger({
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
-const DropdownMenuSub = DropdownMenuPrimitive.Sub;
+function DropdownMenuContent({
+ className,
+ align = "start",
+ sideOffset = 4,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+ )
+}
-const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
+function DropdownMenuGroup({
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
-const DropdownMenuSubTrigger = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef & {
- inset?: boolean;
- }
->(({ className, inset, children, ...props }, ref) => (
-
- {children}
-
-
-));
-DropdownMenuSubTrigger.displayName =
- DropdownMenuPrimitive.SubTrigger.displayName;
-
-const DropdownMenuSubContent = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-));
-DropdownMenuSubContent.displayName =
- DropdownMenuPrimitive.SubContent.displayName;
-
-const DropdownMenuContent = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, sideOffset = 4, ...props }, ref) => (
-
- & {
+ inset?: boolean
+ variant?: "default" | "destructive"
+}) {
+ return (
+
-
-));
-DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
+ )
+}
-const DropdownMenuItem = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef & {
- inset?: boolean;
- }
->(({ className, inset, ...props }, ref) => (
- svg]:size-4 [&>svg]:shrink-0",
- inset && "pl-8",
- className,
- )}
- {...props}
- />
-));
-DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
-
-const DropdownMenuCheckboxItem = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, children, checked, ...props }, ref) => (
-
-
-
-
-
-
- {children}
-
-));
-DropdownMenuCheckboxItem.displayName =
- DropdownMenuPrimitive.CheckboxItem.displayName;
-
-const DropdownMenuRadioItem = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, children, ...props }, ref) => (
-
-
-
-
-
-
- {children}
-
-));
-DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
-
-const DropdownMenuLabel = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef & {
- inset?: boolean;
- }
->(({ className, inset, ...props }, ref) => (
-
-));
-DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
-
-const DropdownMenuSeparator = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-));
-DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
-
-const DropdownMenuShortcut = ({
+function DropdownMenuCheckboxItem({
className,
+ children,
+ checked,
+ inset,
...props
-}: React.HTMLAttributes) => {
+}: React.ComponentProps & {
+ inset?: boolean
+}) {
return (
-
+
+
+
+
+
+ {children}
+
+ )
+}
+
+function DropdownMenuRadioGroup({
+ ...props
+}: React.ComponentProps) {
+ return (
+
- );
-};
-DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
+ )
+}
+
+function DropdownMenuRadioItem({
+ className,
+ children,
+ inset,
+ ...props
+}: React.ComponentProps & {
+ inset?: boolean
+}) {
+ return (
+
+
+
+
+
+
+ {children}
+
+ )
+}
+
+function DropdownMenuLabel({
+ className,
+ inset,
+ ...props
+}: React.ComponentProps & {
+ inset?: boolean
+}) {
+ return (
+
+ )
+}
+
+function DropdownMenuSeparator({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function DropdownMenuShortcut({
+ className,
+ ...props
+}: React.ComponentProps<"span">) {
+ return (
+
+ )
+}
+
+function DropdownMenuSub({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function DropdownMenuSubTrigger({
+ className,
+ inset,
+ children,
+ ...props
+}: React.ComponentProps & {
+ inset?: boolean
+}) {
+ return (
+
+ {children}
+
+
+ )
+}
+
+function DropdownMenuSubContent({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
export {
DropdownMenu,
+ DropdownMenuPortal,
DropdownMenuTrigger,
DropdownMenuContent,
+ DropdownMenuGroup,
+ DropdownMenuLabel,
DropdownMenuItem,
DropdownMenuCheckboxItem,
+ DropdownMenuRadioGroup,
DropdownMenuRadioItem,
- DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuShortcut,
- DropdownMenuGroup,
- DropdownMenuPortal,
DropdownMenuSub,
- DropdownMenuSubContent,
DropdownMenuSubTrigger,
- DropdownMenuRadioGroup,
-};
+ DropdownMenuSubContent,
+}
diff --git a/src/components/ui/skeleton.tsx b/src/components/ui/skeleton.tsx
index be9066a..91720f2 100644
--- a/src/components/ui/skeleton.tsx
+++ b/src/components/ui/skeleton.tsx
@@ -1,12 +1,13 @@
-import { cn } from "~/lib/utils";
+import { cn } from "~/lib/utils"
-function Skeleton({
- className,
- ...props
-}: React.HTMLAttributes) {
+function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
return (
-
- );
+
+ )
}
-export { Skeleton };
+export { Skeleton }
diff --git a/src/lib/data.tsx b/src/lib/data.tsx
index 75d7486..25300f3 100644
--- a/src/lib/data.tsx
+++ b/src/lib/data.tsx
@@ -16,6 +16,7 @@ export interface Project {
websiteLink?: string; // For deployed website links
image?: string;
imageAlt?: string;
+ slidesUrl?: string;
featured: boolean;
}
@@ -470,6 +471,9 @@ export const projects: Project[] = [
"The Halo is a mandatory titanium arch on all F1 cars. It saves lives but cuts through the most interesting part of onboard footage. This project removes it cleanly from visor-cam video using two stages. Stage one: classical CV mask detection — Sobel-Y gradient detection for the arch edge, a robust probe-and-fit keel detector with outlier rejection and temporal jump guards, and explicit geometry construction to avoid over-masking. Stage two: two inpainting methods compared side by side — LaMa (Fast Fourier Convolution network, per-frame spatial inpainting) and RAFT optical flow with backward warp and distance-transform blending for temporal coherence across 300 frames at 60fps.",
tags: ["Python", "OpenCV", "Computer Vision", "LaMa", "RAFT", "Jupyter", "Inpainting"],
gitLink: "https://github.com/soconnor0919/f1-halo-removal",
+ image: "/images/f1-halo-removal.png",
+ imageAlt: "Side-by-side comparison of original F1 footage, LaMa spatial inpainting, and RAFT temporal inpainting across three frames",
+ slidesUrl: "/publications/f1-halo-removal.pdf",
featured: true,
},
{
diff --git a/src/lib/utils.ts b/src/lib/utils.ts
index a5ef193..bd0c391 100644
--- a/src/lib/utils.ts
+++ b/src/lib/utils.ts
@@ -1,6 +1,6 @@
-import { clsx, type ClassValue } from "clsx";
-import { twMerge } from "tailwind-merge";
+import { clsx, type ClassValue } from "clsx"
+import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
- return twMerge(clsx(inputs));
+ return twMerge(clsx(inputs))
}