some ui polish

This commit is contained in:
estevez.sebastian@gmail.com 2025-07-17 15:08:16 -04:00
parent fab50d30e0
commit 11ae8cf530
14 changed files with 916 additions and 138 deletions

View file

@ -0,0 +1,42 @@
import { Navigation } from "@/components/navigation";
import { ModeToggle } from "@/components/mode-toggle";
interface NavigationLayoutProps {
children: React.ReactNode;
}
export function NavigationLayout({ children }: NavigationLayoutProps) {
return (
<div className="h-full relative">
<div className="hidden h-full md:flex md:w-72 md:flex-col md:fixed md:inset-y-0 z-[80] border-r border-border/40">
<Navigation />
</div>
<main className="md:pl-72">
<div className="flex flex-col min-h-screen">
<header className="sticky top-0 z-40 w-full border-b border-border/40 bg-background">
<div className="container flex h-14 max-w-screen-2xl items-center">
<div className="mr-4 hidden md:flex">
<h1 className="text-lg font-semibold tracking-tight">
GenDB
</h1>
</div>
<div className="flex flex-1 items-center justify-between space-x-2 md:justify-end">
<div className="w-full flex-1 md:w-auto md:flex-none">
{/* Search component could go here */}
</div>
<nav className="flex items-center">
<ModeToggle />
</nav>
</div>
</div>
</header>
<div className="flex-1">
<div className="container py-6 lg:py-8">
{children}
</div>
</div>
</div>
</main>
</div>
);
}

View file

@ -9,6 +9,12 @@ export function Navigation() {
const pathname = usePathname()
const routes = [
{
label: "Ingest",
icon: Settings,
href: "/admin",
active: pathname === "/admin",
},
{
label: "Search",
icon: Search,
@ -21,22 +27,11 @@ export function Navigation() {
href: "/chat",
active: pathname === "/chat",
},
{
label: "Admin",
icon: Settings,
href: "/admin",
active: pathname === "/admin",
},
]
return (
<div className="space-y-4 py-4 flex flex-col h-full bg-card">
<div className="px-3 py-2 flex-1">
<Link href="/" className="flex items-center pl-3 mb-8">
<h1 className="text-2xl font-bold tracking-tight">
GenDB
</h1>
</Link>
<div className="space-y-1">
{routes.map((route) => (
<Link

View file

@ -0,0 +1,36 @@
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const badgeVariants = cva(
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
{
variants: {
variant: {
default:
"border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
secondary:
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
destructive:
"border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
outline: "text-foreground",
},
},
defaultVariants: {
variant: "default",
},
}
)
export interface BadgeProps
extends React.HTMLAttributes<HTMLDivElement>,
VariantProps<typeof badgeVariants> {}
function Badge({ className, variant, ...props }: BadgeProps) {
return (
<div className={cn(badgeVariants({ variant }), className)} {...props} />
)
}
export { Badge, badgeVariants }

View file

@ -10,7 +10,7 @@ const buttonVariants = cva(
variants: {
variant: {
default:
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
"bg-white text-black shadow-xs hover:bg-gray-100",
destructive:
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
outline:

View file

@ -0,0 +1,160 @@
"use client"
import * as React from "react"
import * as SelectPrimitive from "@radix-ui/react-select"
import { Check, ChevronDown, ChevronUp } from "lucide-react"
import { cn } from "@/lib/utils"
const Select = SelectPrimitive.Root
const SelectGroup = SelectPrimitive.Group
const SelectValue = SelectPrimitive.Value
const SelectTrigger = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
<SelectPrimitive.Trigger
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
)}
{...props}
>
{children}
<SelectPrimitive.Icon asChild>
<ChevronDown className="h-4 w-4 opacity-50" />
</SelectPrimitive.Icon>
</SelectPrimitive.Trigger>
))
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
const SelectScrollUpButton = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
>(({ className, ...props }, ref) => (
<SelectPrimitive.ScrollUpButton
ref={ref}
className={cn(
"flex cursor-default items-center justify-center py-1",
className
)}
{...props}
>
<ChevronUp className="h-4 w-4" />
</SelectPrimitive.ScrollUpButton>
))
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
const SelectScrollDownButton = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
>(({ className, ...props }, ref) => (
<SelectPrimitive.ScrollDownButton
ref={ref}
className={cn(
"flex cursor-default items-center justify-center py-1",
className
)}
{...props}
>
<ChevronDown className="h-4 w-4" />
</SelectPrimitive.ScrollDownButton>
))
SelectScrollDownButton.displayName =
SelectPrimitive.ScrollDownButton.displayName
const SelectContent = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
>(({ className, children, position = "popper", ...props }, ref) => (
<SelectPrimitive.Portal>
<SelectPrimitive.Content
ref={ref}
className={cn(
"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
)}
position={position}
{...props}
>
<SelectScrollUpButton />
<SelectPrimitive.Viewport
className={cn(
"p-1",
position === "popper" &&
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
)}
>
{children}
</SelectPrimitive.Viewport>
<SelectScrollDownButton />
</SelectPrimitive.Content>
</SelectPrimitive.Portal>
))
SelectContent.displayName = SelectPrimitive.Content.displayName
const SelectLabel = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
>(({ className, ...props }, ref) => (
<SelectPrimitive.Label
ref={ref}
className={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)}
{...props}
/>
))
SelectLabel.displayName = SelectPrimitive.Label.displayName
const SelectItem = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
>(({ className, children, ...props }, ref) => (
<SelectPrimitive.Item
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
)}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<SelectPrimitive.ItemIndicator>
<Check className="h-4 w-4" />
</SelectPrimitive.ItemIndicator>
</span>
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
</SelectPrimitive.Item>
))
SelectItem.displayName = SelectPrimitive.Item.displayName
const SelectSeparator = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
>(({ className, ...props }, ref) => (
<SelectPrimitive.Separator
ref={ref}
className={cn("-mx-1 my-1 h-px bg-muted", className)}
{...props}
/>
))
SelectSeparator.displayName = SelectPrimitive.Separator.displayName
export {
Select,
SelectGroup,
SelectValue,
SelectTrigger,
SelectContent,
SelectLabel,
SelectItem,
SelectSeparator,
SelectScrollUpButton,
SelectScrollDownButton,
}

View file

@ -0,0 +1,29 @@
"use client"
import * as React from "react"
import * as SwitchPrimitives from "@radix-ui/react-switch"
import { cn } from "@/lib/utils"
const Switch = React.forwardRef<
React.ElementRef<typeof SwitchPrimitives.Root>,
React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
>(({ className, ...props }, ref) => (
<SwitchPrimitives.Root
className={cn(
"peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
className
)}
{...props}
ref={ref}
>
<SwitchPrimitives.Thumb
className={cn(
"pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0"
)}
/>
</SwitchPrimitives.Root>
))
Switch.displayName = SwitchPrimitives.Root.displayName
export { Switch }

View file

@ -10,7 +10,9 @@
"dependencies": {
"@radix-ui/react-label": "^2.1.7",
"@radix-ui/react-navigation-menu": "^1.2.13",
"@radix-ui/react-select": "^2.2.5",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-switch": "^1.2.5",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^0.525.0",
@ -233,6 +235,44 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@floating-ui/core": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.2.tgz",
"integrity": "sha512-wNB5ooIKHQc+Kui96jE/n69rHFWAVoxn5CAzL1Xdd8FG03cgY3MLO+GF9U3W737fYDSgPWA6MReKhBQBop6Pcw==",
"license": "MIT",
"dependencies": {
"@floating-ui/utils": "^0.2.10"
}
},
"node_modules/@floating-ui/dom": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.2.tgz",
"integrity": "sha512-7cfaOQuCS27HD7DX+6ib2OrnW+b4ZBwDNnCcT0uTyidcmyWb03FnQqJybDBoCnpdxwBSfA94UAYlRCt7mV+TbA==",
"license": "MIT",
"dependencies": {
"@floating-ui/core": "^1.7.2",
"@floating-ui/utils": "^0.2.10"
}
},
"node_modules/@floating-ui/react-dom": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.4.tgz",
"integrity": "sha512-JbbpPhp38UmXDDAu60RJmbeme37Jbgsm7NrHGgzYYFKmblzRUh6Pa641dII6LsjwF4XlScDrde2UAzDo/b9KPw==",
"license": "MIT",
"dependencies": {
"@floating-ui/dom": "^1.7.2"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/@floating-ui/utils": {
"version": "0.2.10",
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz",
"integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==",
"license": "MIT"
},
"node_modules/@humanfs/core": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
@ -981,12 +1021,41 @@
"node": ">=14"
}
},
"node_modules/@radix-ui/number": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz",
"integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==",
"license": "MIT"
},
"node_modules/@radix-ui/primitive": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz",
"integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==",
"license": "MIT"
},
"node_modules/@radix-ui/react-arrow": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz",
"integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-primitive": "2.1.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-collection": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz",
@ -1085,6 +1154,46 @@
}
}
},
"node_modules/@radix-ui/react-focus-guards": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.2.tgz",
"integrity": "sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-focus-scope": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz",
"integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-use-callback-ref": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-id": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz",
@ -1162,6 +1271,62 @@
}
}
},
"node_modules/@radix-ui/react-popper": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.7.tgz",
"integrity": "sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ==",
"license": "MIT",
"dependencies": {
"@floating-ui/react-dom": "^2.0.0",
"@radix-ui/react-arrow": "1.1.7",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-use-callback-ref": "1.1.1",
"@radix-ui/react-use-layout-effect": "1.1.1",
"@radix-ui/react-use-rect": "1.1.1",
"@radix-ui/react-use-size": "1.1.1",
"@radix-ui/rect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-portal": {
"version": "1.1.9",
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz",
"integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-use-layout-effect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-presence": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.4.tgz",
@ -1209,6 +1374,49 @@
}
}
},
"node_modules/@radix-ui/react-select": {
"version": "2.2.5",
"resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.5.tgz",
"integrity": "sha512-HnMTdXEVuuyzx63ME0ut4+sEMYW6oouHWNGUZc7ddvUWIcfCva/AMoqEW/3wnEllriMWBa0RHspCYnfCWJQYmA==",
"license": "MIT",
"dependencies": {
"@radix-ui/number": "1.1.1",
"@radix-ui/primitive": "1.1.2",
"@radix-ui/react-collection": "1.1.7",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-direction": "1.1.1",
"@radix-ui/react-dismissable-layer": "1.1.10",
"@radix-ui/react-focus-guards": "1.1.2",
"@radix-ui/react-focus-scope": "1.1.7",
"@radix-ui/react-id": "1.1.1",
"@radix-ui/react-popper": "1.2.7",
"@radix-ui/react-portal": "1.1.9",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-slot": "1.2.3",
"@radix-ui/react-use-callback-ref": "1.1.1",
"@radix-ui/react-use-controllable-state": "1.2.2",
"@radix-ui/react-use-layout-effect": "1.1.1",
"@radix-ui/react-use-previous": "1.1.1",
"@radix-ui/react-visually-hidden": "1.2.3",
"aria-hidden": "^1.2.4",
"react-remove-scroll": "^2.6.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
@ -1227,6 +1435,35 @@
}
}
},
"node_modules/@radix-ui/react-switch": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.2.5.tgz",
"integrity": "sha512-5ijLkak6ZMylXsaImpZ8u4Rlf5grRmoc0p0QeX9VJtlrM4f5m3nCTX8tWga/zOA8PZYIR/t0p2Mnvd7InrJ6yQ==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.2",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-use-controllable-state": "1.2.2",
"@radix-ui/react-use-previous": "1.1.1",
"@radix-ui/react-use-size": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-use-callback-ref": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",
@ -1327,6 +1564,42 @@
}
}
},
"node_modules/@radix-ui/react-use-rect": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz",
"integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==",
"license": "MIT",
"dependencies": {
"@radix-ui/rect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-use-size": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz",
"integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-use-layout-effect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-visually-hidden": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz",
@ -1350,6 +1623,12 @@
}
}
},
"node_modules/@radix-ui/rect": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz",
"integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==",
"license": "MIT"
},
"node_modules/@rtsao/scc": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
@ -2096,6 +2375,18 @@
"dev": true,
"license": "Python-2.0"
},
"node_modules/aria-hidden": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz",
"integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==",
"license": "MIT",
"dependencies": {
"tslib": "^2.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/aria-query": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz",
@ -2835,6 +3126,12 @@
"node": ">=8"
}
},
"node_modules/detect-node-es": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
"integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==",
"license": "MIT"
},
"node_modules/didyoumean": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
@ -3768,6 +4065,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-nonce": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz",
"integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/get-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
@ -5464,6 +5770,75 @@
"dev": true,
"license": "MIT"
},
"node_modules/react-remove-scroll": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.1.tgz",
"integrity": "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==",
"license": "MIT",
"dependencies": {
"react-remove-scroll-bar": "^2.3.7",
"react-style-singleton": "^2.2.3",
"tslib": "^2.1.0",
"use-callback-ref": "^1.3.3",
"use-sidecar": "^1.1.3"
},
"engines": {
"node": ">=10"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/react-remove-scroll-bar": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz",
"integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==",
"license": "MIT",
"dependencies": {
"react-style-singleton": "^2.2.2",
"tslib": "^2.0.0"
},
"engines": {
"node": ">=10"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/react-style-singleton": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz",
"integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==",
"license": "MIT",
"dependencies": {
"get-nonce": "^1.0.0",
"tslib": "^2.0.0"
},
"engines": {
"node": ">=10"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
@ -6678,6 +7053,49 @@
"punycode": "^2.1.0"
}
},
"node_modules/use-callback-ref": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz",
"integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==",
"license": "MIT",
"dependencies": {
"tslib": "^2.0.0"
},
"engines": {
"node": ">=10"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/use-sidecar": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz",
"integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==",
"license": "MIT",
"dependencies": {
"detect-node-es": "^1.1.0",
"tslib": "^2.0.0"
},
"engines": {
"node": ">=10"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",

View file

@ -11,7 +11,9 @@
"dependencies": {
"@radix-ui/react-label": "^2.1.7",
"@radix-ui/react-navigation-menu": "^1.2.13",
"@radix-ui/react-select": "^2.2.5",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-switch": "^1.2.5",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^0.525.0",

View file

@ -84,8 +84,8 @@ export default function AdminPage() {
return (
<div className="space-y-8">
<div>
<h1 className="text-3xl font-bold">Admin Panel</h1>
<p className="text-muted-foreground">Upload files and manage your document database</p>
<h1 className="text-3xl font-bold">Ingest</h1>
<p className="text-muted-foreground">Upload and manage documents in your database</p>
</div>
{uploadStatus && (

View file

@ -86,8 +86,8 @@ export default function ChatPage() {
}
return (
<div className="max-w-4xl mx-auto space-y-8">
<div className="text-center">
<div className="space-y-8">
<div>
<h1 className="text-3xl font-bold tracking-tight">Chat Assistant</h1>
<p className="text-muted-foreground mt-2">Ask questions about your documents and get AI-powered answers</p>
</div>
@ -124,7 +124,7 @@ export default function ChatPage() {
</CardHeader>
<CardContent className="flex-1 flex flex-col gap-4 min-h-0">
{/* Messages Area */}
<div className="flex-1 overflow-y-auto overflow-x-hidden space-y-4 p-4 rounded-lg bg-muted/20 min-h-0">
<div className="flex-1 overflow-y-auto overflow-x-hidden space-y-6 p-4 rounded-lg bg-muted/20 min-h-0">
{messages.length === 0 ? (
<div className="flex items-center justify-center h-full text-muted-foreground">
<div className="text-center">
@ -136,59 +136,60 @@ export default function ChatPage() {
) : (
<>
{messages.map((message, index) => (
<div
key={index}
className={cn(
"flex w-full",
message.role === "user" ? "justify-end" : "justify-start"
<div key={index} className="space-y-2">
{message.role === "user" && (
<div className="space-y-2">
<div className="flex items-center gap-2">
<div className="w-8 h-8 rounded-lg bg-primary/20 flex items-center justify-center">
<User className="h-4 w-4 text-primary" />
</div>
<span className="font-medium text-foreground">User</span>
</div>
<div className="pl-10">
<p className="text-foreground whitespace-pre-wrap break-words">{message.content}</p>
</div>
</div>
)}
>
<div
className={cn(
"flex items-start gap-3 max-w-[80%] min-w-0",
message.role === "user" ? "flex-row-reverse" : "flex-row"
)}
>
<div
className={cn(
"flex h-8 w-8 shrink-0 items-center justify-center rounded-full",
message.role === "user"
? "bg-primary text-primary-foreground"
: "bg-secondary text-secondary-foreground"
)}
>
{message.role === "user" ? (
<User className="h-4 w-4" />
) : (
<Bot className="h-4 w-4" />
)}
{message.role === "assistant" && (
<div className="space-y-2">
<div className="flex items-center gap-2">
<div className="w-8 h-8 rounded-lg bg-accent/20 flex items-center justify-center">
<Bot className="h-4 w-4 text-accent-foreground" />
</div>
<span className="font-medium text-foreground">AI</span>
<span className="text-sm text-muted-foreground">gpt-4.1</span>
</div>
<div className="pl-10">
<div className="rounded-lg bg-card border border-border/40 p-4">
<div className="flex items-center gap-2 mb-2">
<div className="w-2 h-2 bg-green-400 rounded-full"></div>
<span className="text-sm text-green-400 font-medium">Finished</span>
<span className="text-xs text-muted-foreground ml-auto">
{message.timestamp.toLocaleTimeString()}
</span>
</div>
<p className="text-foreground whitespace-pre-wrap break-words">{message.content}</p>
</div>
</div>
</div>
<div
className={cn(
"rounded-lg px-3 py-2 text-sm min-w-0 overflow-hidden",
message.role === "user"
? "bg-primary text-primary-foreground"
: "bg-card border border-border/40"
)}
>
<p className="whitespace-pre-wrap break-words overflow-wrap-anywhere">{message.content}</p>
<p className="text-xs opacity-70 mt-1">
{message.timestamp.toLocaleTimeString()}
</p>
</div>
</div>
)}
</div>
))}
{loading && (
<div className="flex w-full justify-start">
<div className="flex items-start gap-3 max-w-[80%] min-w-0">
<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-secondary text-secondary-foreground">
<Bot className="h-4 w-4" />
<div className="space-y-2">
<div className="flex items-center gap-2">
<div className="w-8 h-8 rounded-lg bg-accent/20 flex items-center justify-center">
<Bot className="h-4 w-4 text-accent-foreground" />
</div>
<div className="rounded-lg px-3 py-2 text-sm bg-card border border-border/40 min-w-0 overflow-hidden">
<div className="flex items-center gap-2">
<Loader2 className="h-4 w-4 animate-spin" />
<span>Thinking...</span>
<span className="font-medium text-foreground">AI</span>
<span className="text-sm text-muted-foreground">gpt-4.1</span>
</div>
<div className="pl-10">
<div className="rounded-lg bg-card border border-border/40 p-4">
<div className="flex items-center gap-2 mb-2">
<Loader2 className="w-4 h-4 animate-spin text-white" />
<span className="text-sm text-white font-medium">Thinking...</span>
</div>
</div>
</div>

View file

@ -32,29 +32,52 @@
}
.dark {
--background: 222.2 84% 4.9%;
/* Main backgrounds - very dark charcoal */
--background: 0 0% 10%;
--foreground: 210 40% 98%;
--card: 222.2 84% 4.9%;
/* Card backgrounds - slightly lighter dark gray */
--card: 0 0% 16%;
--card-foreground: 210 40% 98%;
--popover: 222.2 84% 4.9%;
/* Popover backgrounds */
--popover: 0 0% 16%;
--popover-foreground: 210 40% 98%;
--primary: 217.2 91.2% 59.8%;
--primary-foreground: 222.2 47.4% 11.2%;
--secondary: 217.2 32.6% 17.5%;
/* Primary accent - bright teal/cyan */
--primary: 162 100% 42%;
--primary-foreground: 0 0% 10%;
/* Secondary elements - medium gray */
--secondary: 215 25% 27%;
--secondary-foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 65.1%;
--accent: 217.2 32.6% 17.5%;
--accent-foreground: 210 40% 98%;
--destructive: 0 62.8% 30.6%;
/* Muted elements - darker gray */
--muted: 215 25% 27%;
--muted-foreground: 215 20% 65%;
/* Accent elements - bright blue */
--accent: 0 0% 100%;
--accent-foreground: 0 0% 10%;
/* Destructive/error - red */
--destructive: 0 63% 31%;
--destructive-foreground: 210 40% 98%;
--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--ring: 217.2 91.2% 59.8%;
--chart-1: 220 70% 50%;
--chart-2: 160 60% 45%;
--chart-3: 30 80% 55%;
--chart-4: 280 65% 60%;
/* Borders - subtle gray */
--border: 215 25% 35%;
/* Input backgrounds - darker gray */
--input: 215 25% 27%;
/* Ring/focus colors - teal accent */
--ring: 162 100% 42%;
/* Chart colors - adjusted for dark theme */
--chart-1: 162 100% 42%;
--chart-2: 142 76% 36%;
--chart-3: 217 91% 60%;
--chart-4: 45 93% 58%;
--chart-5: 340 75% 55%;
}
}

View file

@ -37,28 +37,25 @@ export default function RootLayout({
disableTransitionOnChange
>
<div className="h-full relative">
<div className="hidden h-full md:flex md:w-72 md:flex-col md:fixed md:inset-y-0 z-[80] border-r border-border/40">
<header className="sticky top-0 z-50 w-full border-b border-border/40 bg-background">
<div className="flex h-14 items-center px-4">
<div className="flex items-center">
<h1 className="text-lg font-semibold tracking-tight text-white">
GenDB
</h1>
</div>
<div className="flex flex-1 items-center justify-end space-x-2">
<nav className="flex items-center">
<ModeToggle />
</nav>
</div>
</div>
</header>
<div className="hidden md:flex md:w-72 md:flex-col md:fixed md:top-14 md:bottom-0 md:left-0 z-[80] border-r border-border/40">
<Navigation />
</div>
<main className="md:pl-72">
<div className="flex flex-col min-h-screen">
<header className="sticky top-0 z-40 w-full border-b border-border/40 bg-background">
<div className="container flex h-14 max-w-screen-2xl items-center">
<div className="mr-4 hidden md:flex">
<h1 className="text-lg font-semibold tracking-tight">
GenDB
</h1>
</div>
<div className="flex flex-1 items-center justify-between space-x-2 md:justify-end">
<div className="w-full flex-1 md:w-auto md:flex-none">
{/* Search component could go here */}
</div>
<nav className="flex items-center">
<ModeToggle />
</nav>
</div>
</div>
</header>
<div className="flex-1">
<div className="container py-6 lg:py-8">
{children}

View file

@ -5,7 +5,7 @@ import { Button } from "@/components/ui/button"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Search, Loader2, FileText } from "lucide-react"
import { Search, Loader2, FileText, Database, Zap } from "lucide-react"
interface SearchResult {
filename: string
@ -57,92 +57,131 @@ export default function SearchPage() {
}
return (
<div className="max-w-4xl mx-auto space-y-8">
<div className="text-center">
<h1 className="text-3xl font-bold tracking-tight">Document Search</h1>
<p className="text-muted-foreground mt-2">Search through your indexed documents</p>
<div className="space-y-8">
{/* Hero Section */}
<div className="space-y-4">
<div className="mb-4">
<h1 className="text-4xl font-bold tracking-tight text-white">
Search
</h1>
</div>
<p className="text-xl text-muted-foreground">
Find documents using semantic search
</p>
<p className="text-sm text-muted-foreground max-w-2xl">
Enter your search query to find relevant documents using AI-powered semantic search across your document collection.
</p>
</div>
<Card className="max-w-2xl w-full mx-auto">
{/* Search Interface */}
<Card className="max-w-4xl w-full mx-auto bg-card/50 backdrop-blur-sm border-border/50">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Search className="h-5 w-5" />
Search Documents
</CardTitle>
<CardDescription>
Enter your search query to find relevant documents
Enter your search query to find relevant documents using semantic search
</CardDescription>
</CardHeader>
<CardContent>
<CardContent className="space-y-6">
<form onSubmit={handleSearch} className="space-y-4">
<div className="space-y-2">
<Label htmlFor="search-query">Search Query</Label>
<div className="space-y-3">
<Label htmlFor="search-query" className="font-medium">
Search Query
</Label>
<Input
id="search-query"
type="text"
placeholder="Enter your search terms..."
placeholder="e.g., 'financial reports from Q4' or 'user authentication setup'"
value={query}
onChange={(e) => setQuery(e.target.value)}
className="text-base"
className="h-12 bg-background/50 border-border/50 focus:border-blue-400/50 focus:ring-blue-400/20"
/>
</div>
<Button
type="submit"
disabled={!query.trim() || loading}
className="w-full"
className="w-full h-12 transition-all duration-200"
>
{loading ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
<Loader2 className="mr-3 h-5 w-5 animate-spin" />
Searching...
</>
) : (
<>
<Search className="mr-2 h-4 w-4" />
Search
<Search className="mr-3 h-5 w-5" />
Search Documents
</>
)}
</Button>
</form>
{/* Always render the results area, but show it empty if no search has been performed */}
{/* Results Section */}
<div className="mt-8">
{searchPerformed ? (
<div className="space-y-4">
<div className="space-y-6">
<div className="flex items-center justify-between">
<h2 className="text-xl font-semibold">Search Results</h2>
<span className="text-sm text-muted-foreground">
{results.length} result{results.length !== 1 ? 's' : ''} found
</span>
<h2 className="text-2xl font-semibold flex items-center gap-2">
<Zap className="h-6 w-6 text-yellow-400" />
Search Results
</h2>
<div className="flex items-center gap-2">
<div className="h-2 w-2 bg-green-400 rounded-full animate-pulse"></div>
<span className="text-sm text-muted-foreground">
{results.length} result{results.length !== 1 ? 's' : ''} found
</span>
</div>
</div>
{results.length === 0 ? (
<Card>
<CardContent className="pt-6">
<p className="text-center text-muted-foreground">
No documents found matching your search query.
</p>
<Card className="bg-muted/20 border-dashed border-muted-foreground/30">
<CardContent className="pt-8 pb-8">
<div className="text-center space-y-3">
<div className="mx-auto w-16 h-16 bg-muted/30 rounded-full flex items-center justify-center">
<Search className="h-8 w-8 text-muted-foreground/50" />
</div>
<p className="text-lg font-medium text-muted-foreground">
No documents found
</p>
<p className="text-sm text-muted-foreground/70 max-w-md mx-auto">
Try adjusting your search terms or check if documents have been indexed.
</p>
</div>
</CardContent>
</Card>
) : (
<div className="space-y-4">
{results.map((result, index) => (
<Card key={index}>
<Card key={index} className="bg-card/50 backdrop-blur-sm border-border/50 hover:bg-card/70 transition-all duration-200 hover:shadow-lg hover:shadow-blue-500/10">
<CardHeader className="pb-3">
<div className="flex items-center justify-between">
<CardTitle className="text-lg flex items-center gap-2">
<FileText className="h-4 w-4" />
{result.filename}
<CardTitle className="text-lg flex items-center gap-3">
<div className="p-2 rounded-lg bg-blue-500/20 border border-blue-500/30">
<FileText className="h-4 w-4 text-blue-400" />
</div>
<span className="truncate">{result.filename}</span>
</CardTitle>
<div className="text-sm text-muted-foreground">
Score: {result.score.toFixed(2)}
<div className="flex items-center gap-2">
<div className="px-2 py-1 rounded-md bg-green-500/20 border border-green-500/30">
<span className="text-xs font-medium text-green-400">
{result.score.toFixed(2)}
</span>
</div>
</div>
</div>
<CardDescription>
Type: {result.mimetype} Page {result.page}
<CardDescription className="flex items-center gap-4 text-sm">
<span className="px-2 py-1 rounded bg-muted/50 text-muted-foreground">
{result.mimetype}
</span>
<span className="text-muted-foreground">
Page {result.page}
</span>
</CardDescription>
</CardHeader>
<CardContent>
<div className="border-l-2 border-primary pl-4">
<p className="text-sm leading-relaxed">
<div className="border-l-2 border-blue-400/50 pl-4 py-2 bg-muted/20 rounded-r-lg">
<p className="text-sm leading-relaxed text-foreground/90">
{result.text}
</p>
</div>
@ -153,7 +192,11 @@ export default function SearchPage() {
)}
</div>
) : (
<div style={{ minHeight: 120 }} />
<div className="h-32 flex items-center justify-center">
<p className="text-muted-foreground/50 text-sm">
Enter a search query above to get started
</p>
</div>
)}
</div>
</CardContent>

View file

@ -52,6 +52,38 @@ const config: Config = {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))",
},
// Custom colors from the screenshot
teal: {
50: "#f0fdfa",
100: "#ccfbf1",
200: "#99f6e4",
300: "#5eead4",
400: "#2dd4bf",
500: "#14b8a6",
600: "#0d9488",
700: "#0f766e",
800: "#115e59",
900: "#134e4a",
DEFAULT: "#00D4AA", // Primary teal from screenshot
},
success: {
DEFAULT: "#10B981", // Success green from screenshot
foreground: "#ffffff",
},
// Additional grays matching the screenshot
gray: {
850: "#1a1a1a", // Very dark background
800: "#2a2a2a", // Card background
750: "#333333", // Slightly lighter card
700: "#374151", // Input background
600: "#4b5563", // Border color
500: "#6b7280", // Muted text
400: "#9ca3af", // Secondary text
300: "#d1d5db",
200: "#e5e7eb",
100: "#f3f4f6",
50: "#f8f9fa", // Primary text
},
},
borderRadius: {
lg: "var(--radius)",