diff --git a/src/components/layout/index.ts b/src/components/layout/index.ts
index 1003f4754a..b80030e309 100644
--- a/src/components/layout/index.ts
+++ b/src/components/layout/index.ts
@@ -1,7 +1,10 @@
-export { Footer } from './Footer';
export { Section } from './Section';
export { LocaleSwitcher } from './LocaleSwitcher';
-export { ThemeProvider } from './ThemeProvider';
export { ThemeSwitcher } from './ThemeSwitcher';
+export { Logo, HoppingLogo } from './Logo';
+export { NavbarHeader } from './NavbarHeader';
+export { NavbarMenu } from './NavbarMenu';
+
+export { ThemeProvider } from './ThemeProvider';
+export { AuthProvider } from './AuthProvider';
export { ReactQueryProvider } from './ReactQueryProvider';
-export { Logo } from './Logo';
diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx
index 28691b2c76..29f98ef9ee 100644
--- a/src/components/ui/button.tsx
+++ b/src/components/ui/button.tsx
@@ -5,7 +5,7 @@ import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@/lib/utils';
const buttonVariants = cva(
- "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive cursor-pointer",
{
variants: {
variant: {
diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx
index 4898dfa6de..526bd07269 100644
--- a/src/components/ui/card.tsx
+++ b/src/components/ui/card.tsx
@@ -8,6 +8,7 @@ function Card({ className, ...props }: React.ComponentProps<'div'>) {
data-slot="card"
className={cn(
'bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm',
+ 'dark:border-0',
className,
)}
{...props}
diff --git a/src/components/ui/ecosystem/button.tsx b/src/components/ui/ecosystem/button.tsx
new file mode 100644
index 0000000000..ca0dc196e0
--- /dev/null
+++ b/src/components/ui/ecosystem/button.tsx
@@ -0,0 +1,121 @@
+import * as React from 'react';
+import { Slot, Slottable } from '@radix-ui/react-slot';
+import { cva, type VariantProps } from 'class-variance-authority';
+import { cn } from '@/lib/utils';
+
+const buttonVariants = cva(
+ 'inline-flex items-center justify-center gap-2 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 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
+ {
+ variants: {
+ variant: {
+ default: 'bg-primary text-primary-foreground hover:bg-primary/90',
+ destructive:
+ 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
+ outline:
+ 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
+ secondary:
+ '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',
+ },
+ effect: {
+ expandIcon: 'group gap-0 relative',
+ ringHover:
+ 'transition-all duration-300 hover:ring-2 hover:ring-primary/90 hover:ring-offset-2',
+ shine:
+ 'before:animate-shine relative overflow-hidden before:absolute before:inset-0 before:rounded-[inherit] before:bg-[linear-gradient(45deg,transparent_25%,rgba(255,255,255,0.5)_50%,transparent_75%,transparent_100%)] before:bg-[length:250%_250%,100%_100%] before:bg-no-repeat background-position_0s_ease',
+ shineHover:
+ 'relative overflow-hidden before:absolute before:inset-0 before:rounded-[inherit] before:bg-[linear-gradient(45deg,transparent_25%,rgba(255,255,255,0.5)_50%,transparent_75%,transparent_100%)] before:bg-[length:250%_250%,100%_100%] before:bg-[position:200%_0,0_0] before:bg-no-repeat before:transition-[background-position_0s_ease] hover:before:bg-[position:-100%_0,0_0] before:duration-1000',
+ gooeyRight:
+ 'relative z-0 overflow-hidden transition-all duration-500 before:absolute before:inset-0 before:-z-10 before:translate-x-[150%] before:translate-y-[150%] before:scale-[2.5] before:rounded-[100%] before:bg-gradient-to-r from-white/40 before:transition-transform before:duration-1000 hover:before:translate-x-[0%] hover:before:translate-y-[0%]',
+ gooeyLeft:
+ 'relative z-0 overflow-hidden transition-all duration-500 after:absolute after:inset-0 after:-z-10 after:translate-x-[-150%] after:translate-y-[150%] after:scale-[2.5] after:rounded-[100%] after:bg-gradient-to-l from-white/40 after:transition-transform after:duration-1000 hover:after:translate-x-[0%] hover:after:translate-y-[0%]',
+ underline:
+ 'relative !no-underline after:absolute after:bg-primary after:bottom-2 after:h-[1px] after:w-2/3 after:origin-bottom-left after:scale-x-100 hover:after:origin-bottom-right hover:after:scale-x-0 after:transition-transform after:ease-in-out after:duration-300',
+ hoverUnderline:
+ 'relative !no-underline after:absolute after:bg-primary after:bottom-2 after:h-[1px] after:w-2/3 after:origin-bottom-right after:scale-x-0 hover:after:origin-bottom-left hover:after:scale-x-100 after:transition-transform after:ease-in-out after:duration-300',
+ gradientSlideShow:
+ 'bg-[size:400%] bg-[linear-gradient(-45deg,var(--gradient-lime),var(--gradient-ocean),var(--gradient-wine),var(--gradient-rust))] animate-gradient-flow',
+ },
+ size: {
+ default: 'h-10 px-4 py-2',
+ sm: 'h-9 rounded-md px-3',
+ lg: 'h-11 rounded-md px-8',
+ icon: 'h-10 w-10',
+ },
+ },
+ defaultVariants: {
+ variant: 'default',
+ size: 'default',
+ },
+ },
+);
+
+interface IconProps {
+ icon: React.ElementType;
+ iconPlacement: 'left' | 'right';
+}
+
+interface IconRefProps {
+ icon?: never;
+ iconPlacement?: undefined;
+}
+
+export interface ButtonProps
+ extends React.ButtonHTMLAttributes
,
+ VariantProps {
+ asChild?: boolean;
+}
+
+export type ButtonIconProps = IconProps | IconRefProps;
+
+const Button = React.forwardRef<
+ HTMLButtonElement,
+ ButtonProps & ButtonIconProps
+>(
+ (
+ {
+ className,
+ variant,
+ effect,
+ size,
+ icon: Icon,
+ iconPlacement,
+ asChild = false,
+ ...props
+ },
+ ref,
+ ) => {
+ const Comp = asChild ? Slot : 'button';
+ return (
+
+ {Icon &&
+ iconPlacement === 'left' &&
+ (effect === 'expandIcon' ? (
+
+
+
+ ) : (
+
+ ))}
+ {props.children}
+ {Icon &&
+ iconPlacement === 'right' &&
+ (effect === 'expandIcon' ? (
+
+
+
+ ) : (
+
+ ))}
+
+ );
+ },
+);
+Button.displayName = 'Button';
+
+export { Button, buttonVariants };
diff --git a/src/components/ui/ecosystem/index.ts b/src/components/ui/ecosystem/index.ts
new file mode 100644
index 0000000000..e1c2216c79
--- /dev/null
+++ b/src/components/ui/ecosystem/index.ts
@@ -0,0 +1,2 @@
+export * from './button';
+export * from './tabs';
diff --git a/src/components/ui/ecosystem/tabs.tsx b/src/components/ui/ecosystem/tabs.tsx
new file mode 100644
index 0000000000..4b24bc7578
--- /dev/null
+++ b/src/components/ui/ecosystem/tabs.tsx
@@ -0,0 +1,124 @@
+'use client';
+
+import * as React from 'react';
+import { useEffect, useRef, useState } from 'react';
+import * as TabsPrimitive from '@radix-ui/react-tabs';
+
+import { cn } from '@/lib/utils';
+
+const Tabs = TabsPrimitive.Root;
+
+const TabsList = React.forwardRef<
+ React.ComponentRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => {
+ const [indicatorStyle, setIndicatorStyle] = useState({
+ left: 0,
+ top: 0,
+ width: 0,
+ height: 0,
+ });
+
+ const tabsListRef = useRef(null);
+
+ const updateIndicator = React.useCallback(() => {
+ if (!tabsListRef.current) {
+ return;
+ }
+
+ const activeTab = tabsListRef.current.querySelector(
+ '[data-state="active"]',
+ );
+
+ if (!activeTab) {
+ return;
+ }
+
+ const activeRect = activeTab.getBoundingClientRect();
+ const tabsRect = tabsListRef.current.getBoundingClientRect();
+
+ requestAnimationFrame(() => {
+ setIndicatorStyle({
+ left: activeRect.left - tabsRect.left,
+ top: activeRect.top - tabsRect.top,
+ width: activeRect.width,
+ height: activeRect.height,
+ });
+ });
+ }, []);
+
+ useEffect(() => {
+ const timeoutId = setTimeout(updateIndicator, 0);
+
+ window.addEventListener('resize', updateIndicator);
+ const observer = new MutationObserver(updateIndicator);
+
+ if (tabsListRef.current) {
+ observer.observe(tabsListRef.current, {
+ attributes: true,
+ childList: true,
+ subtree: true,
+ });
+ }
+
+ return () => {
+ clearTimeout(timeoutId);
+ window.removeEventListener('resize', updateIndicator);
+ observer.disconnect();
+ };
+ }, [updateIndicator]);
+
+ return (
+
+ );
+});
+TabsList.displayName = TabsPrimitive.List.displayName;
+
+const TabsTrigger = React.forwardRef<
+ React.ComponentRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
+
+const TabsContent = React.forwardRef<
+ React.ComponentRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+TabsContent.displayName = TabsPrimitive.Content.displayName;
+
+export { Tabs, TabsContent, TabsList, TabsTrigger };
diff --git a/src/components/ui/skeleton.tsx b/src/components/ui/skeleton.tsx
new file mode 100644
index 0000000000..02535dc4f5
--- /dev/null
+++ b/src/components/ui/skeleton.tsx
@@ -0,0 +1,13 @@
+import { cn } from '@/lib/utils';
+
+function Skeleton({ className, ...props }: React.ComponentProps<'div'>) {
+ return (
+
+ );
+}
+
+export { Skeleton };
diff --git a/src/components/ui/tabs.tsx b/src/components/ui/tabs.tsx
index 1c71bcfc18..3512331a65 100644
--- a/src/components/ui/tabs.tsx
+++ b/src/components/ui/tabs.tsx
@@ -1,124 +1,66 @@
'use client';
import * as React from 'react';
-import { useEffect, useRef, useState } from 'react';
import * as TabsPrimitive from '@radix-ui/react-tabs';
import { cn } from '@/lib/utils';
-const Tabs = TabsPrimitive.Root;
-
-const TabsList = React.forwardRef<
- React.ComponentRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => {
- const [indicatorStyle, setIndicatorStyle] = useState({
- left: 0,
- top: 0,
- width: 0,
- height: 0,
- });
-
- const tabsListRef = useRef(null);
-
- const updateIndicator = React.useCallback(() => {
- if (!tabsListRef.current) {
- return;
- }
-
- const activeTab = tabsListRef.current.querySelector(
- '[data-state="active"]',
- );
-
- if (!activeTab) {
- return;
- }
-
- const activeRect = activeTab.getBoundingClientRect();
- const tabsRect = tabsListRef.current.getBoundingClientRect();
-
- requestAnimationFrame(() => {
- setIndicatorStyle({
- left: activeRect.left - tabsRect.left,
- top: activeRect.top - tabsRect.top,
- width: activeRect.width,
- height: activeRect.height,
- });
- });
- }, []);
-
- useEffect(() => {
- const timeoutId = setTimeout(updateIndicator, 0);
-
- window.addEventListener('resize', updateIndicator);
- const observer = new MutationObserver(updateIndicator);
-
- if (tabsListRef.current) {
- observer.observe(tabsListRef.current, {
- attributes: true,
- childList: true,
- subtree: true,
- });
- }
-
- return () => {
- clearTimeout(timeoutId);
- window.removeEventListener('resize', updateIndicator);
- observer.disconnect();
- };
- }, [updateIndicator]);
-
+function Tabs({
+ className,
+ ...props
+}: React.ComponentProps) {
return (
-
+
);
-});
-TabsList.displayName = TabsPrimitive.List.displayName;
+}
-const TabsTrigger = React.forwardRef<
- React.ComponentRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-));
-TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
+function TabsList({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ );
+}
-const TabsContent = React.forwardRef<
- React.ComponentRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-));
-TabsContent.displayName = TabsPrimitive.Content.displayName;
+function TabsTrigger({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ );
+}
-export { Tabs, TabsContent, TabsList, TabsTrigger };
+function TabsContent({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ );
+}
+
+export { Tabs, TabsList, TabsTrigger, TabsContent };
diff --git a/src/components/usage/UsageCard.tsx b/src/components/usage/UsageCard.tsx
new file mode 100644
index 0000000000..cf6e908493
--- /dev/null
+++ b/src/components/usage/UsageCard.tsx
@@ -0,0 +1,128 @@
+'use client';
+
+import React, { useState } from 'react';
+import { usePathname } from 'next/navigation';
+import Link from 'next/link';
+import { useTranslations } from 'next-intl';
+import { useAuth } from '@clerk/nextjs';
+import { useQuery } from '@tanstack/react-query';
+import { ArrowRightIcon } from 'lucide-react';
+
+import { TelemetryEventName } from '@roo-code/types';
+
+import { type TimePeriod, timePeriods } from '@/schemas';
+import { getUsage } from '@/actions/analytics';
+
+import {
+ Button,
+ Card,
+ CardHeader,
+ CardTitle,
+ CardDescription,
+ CardContent,
+} from '@/components/ui';
+import { Button as EnhancedButton } from '@/components/ui/ecosystem';
+
+export const UsageCard = () => {
+ const t = useTranslations('DashboardIndex');
+ const { orgId } = useAuth();
+ const [timePeriod, setTimePeriod] = useState(7);
+
+ const path = usePathname();
+
+ const usage = useQuery({
+ queryKey: ['usage', orgId, timePeriod],
+ queryFn: () => getUsage({ orgId, timePeriod }),
+ });
+
+ return (
+
+
+ {t('analytics_title')}
+ {t('analytics_description')}
+ {path !== '/usage' && (
+
+
+ See all usage
+
+
+ )}
+
+
+
+
+ {timePeriods.map((period) => (
+
+ ))}
+
+
+
+
+ {t('analytics_active_developers')}
+
+
+ {usage.data?.[TelemetryEventName.LLM_COMPLETION]?.userCount ??
+ '-'}
+
+
+
+
+ {t('analytics_tasks_started')}
+
+
+ {usage.data?.[TelemetryEventName.TASK_CREATED]?.eventCount ??
+ '-'}
+
+
+
+
+ {t('analytics_tasks_completed')}
+
+
+ {usage.data?.[TelemetryEventName.TASK_COMPLETED]?.eventCount ??
+ '-'}
+
+
+
+
+ {t('analytics_tokens_consumed')}
+
+
+ {usage.data?.[TelemetryEventName.LLM_COMPLETION]?.inputTokens ??
+ '-'}
+
+
+
+
+ {t('analytics_llm_costs')}
+
+
+ {usage.data?.[TelemetryEventName.LLM_COMPLETION]?.cost?.toFixed(
+ 2,
+ ) ?? '-'}
+
+
+
+
+
+
+ );
+};
diff --git a/src/db/schema.ts b/src/db/schema.ts
index d86b56a98b..f2f120cb05 100644
--- a/src/db/schema.ts
+++ b/src/db/schema.ts
@@ -23,9 +23,7 @@ export const auditLogs = pgTable('audit_logs', {
export enum AuditLogTargetType {
PROVIDER_WHITELIST = 1,
DEFAULT_PARAMETERS = 2,
- MEMBER_CHANGE = 3, // TODO: Currently no logs of this type are collected
+ MEMBER_CHANGE = 3, // TODO: Currently no logs of this type are collected.
}
-export type AuditLogType = Omit & {
- targetType: AuditLogTargetType;
-};
+export type AuditLogType = typeof auditLogs.$inferSelect;
diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json
index 9bc4c09ad2..0ca4ead310 100644
--- a/src/i18n/locales/en.json
+++ b/src/i18n/locales/en.json
@@ -27,8 +27,7 @@
"analytics_tokens_consumed": "Tokens Consumed",
"analytics_costs": "Costs (USD)",
"analytics_llm_costs": "LLM Model Costs (USD)",
- "analytics_active_developers": "Active Developers",
- "analytics_view_details": "View detailed analytics"
+ "analytics_active_developers": "Active Developers"
},
"UserProfile": {
"title_bar": "User Profile",
diff --git a/src/middleware.ts b/src/middleware.ts
index 6beb90019b..f60e8c5a69 100644
--- a/src/middleware.ts
+++ b/src/middleware.ts
@@ -1,15 +1,18 @@
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';
-const isProtectedRoute = createRouteMatcher([
- '/dashboard(.*)',
- '/onboarding(.*)',
- '/api(.*)',
+const isUnprotectedRoute = createRouteMatcher([
+ '/sign-in(.*)',
+ '/sign-up(.*)',
+ '/',
]);
export default clerkMiddleware(
async (auth, req) => {
- if (isProtectedRoute(req)) {
+ if (!isUnprotectedRoute(req)) {
+ console.log(`clerkMiddleware: PROTECTED -> ${req.nextUrl.pathname}`);
await auth.protect();
+ } else {
+ console.log(`clerkMiddleware: UNPROTECTED -> ${req.nextUrl.pathname}`);
}
},
{ debug: false },
diff --git a/src/styles/globals.css b/src/styles/globals.css
deleted file mode 100644
index 07ad200cf5..0000000000
--- a/src/styles/globals.css
+++ /dev/null
@@ -1,119 +0,0 @@
-@import 'tailwindcss';
-
-@custom-variant dark (&:is(.dark *));
-
-@theme inline {
- --radius-sm: calc(var(--radius) - 4px);
- --radius-md: calc(var(--radius) - 2px);
- --radius-lg: var(--radius);
- --radius-xl: calc(var(--radius) + 4px);
- --color-background: var(--background);
- --color-foreground: var(--foreground);
- --color-card: var(--card);
- --color-card-foreground: var(--card-foreground);
- --color-popover: var(--popover);
- --color-popover-foreground: var(--popover-foreground);
- --color-primary: var(--primary);
- --color-primary-foreground: var(--primary-foreground);
- --color-secondary: var(--secondary);
- --color-secondary-foreground: var(--secondary-foreground);
- --color-muted: var(--muted);
- --color-muted-foreground: var(--muted-foreground);
- --color-accent: var(--accent);
- --color-accent-foreground: var(--accent-foreground);
- --color-destructive: var(--destructive);
- --color-border: var(--border);
- --color-input: var(--input);
- --color-ring: var(--ring);
- --color-chart-1: var(--chart-1);
- --color-chart-2: var(--chart-2);
- --color-chart-3: var(--chart-3);
- --color-chart-4: var(--chart-4);
- --color-chart-5: var(--chart-5);
- --color-sidebar: var(--sidebar);
- --color-sidebar-foreground: var(--sidebar-foreground);
- --color-sidebar-primary: var(--sidebar-primary);
- --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
- --color-sidebar-accent: var(--sidebar-accent);
- --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
- --color-sidebar-border: var(--sidebar-border);
- --color-sidebar-ring: var(--sidebar-ring);
-}
-
-:root {
- --radius: 0.625rem;
- --background: oklch(1 0 0);
- --foreground: oklch(0.145 0 0);
- --card: oklch(1 0 0);
- --card-foreground: oklch(0.145 0 0);
- --popover: oklch(1 0 0);
- --popover-foreground: oklch(0.145 0 0);
- --primary: oklch(0.205 0 0);
- --primary-foreground: oklch(0.985 0 0);
- --secondary: oklch(0.97 0 0);
- --secondary-foreground: oklch(0.205 0 0);
- --muted: oklch(0.97 0 0);
- --muted-foreground: oklch(0.556 0 0);
- --accent: oklch(0.97 0 0);
- --accent-foreground: oklch(0.205 0 0);
- --destructive: oklch(0.577 0.245 27.325);
- --border: oklch(0.922 0 0);
- --input: oklch(0.922 0 0);
- --ring: oklch(0.708 0 0);
- --chart-1: oklch(0.646 0.222 41.116);
- --chart-2: oklch(0.6 0.118 184.704);
- --chart-3: oklch(0.398 0.07 227.392);
- --chart-4: oklch(0.828 0.189 84.429);
- --chart-5: oklch(0.769 0.188 70.08);
- --sidebar: oklch(0.985 0 0);
- --sidebar-foreground: oklch(0.145 0 0);
- --sidebar-primary: oklch(0.205 0 0);
- --sidebar-primary-foreground: oklch(0.985 0 0);
- --sidebar-accent: oklch(0.97 0 0);
- --sidebar-accent-foreground: oklch(0.205 0 0);
- --sidebar-border: oklch(0.922 0 0);
- --sidebar-ring: oklch(0.708 0 0);
-}
-
-.dark {
- --background: oklch(0.145 0 0);
- --foreground: oklch(0.985 0 0);
- --card: oklch(0.205 0 0);
- --card-foreground: oklch(0.985 0 0);
- --popover: oklch(0.205 0 0);
- --popover-foreground: oklch(0.985 0 0);
- --primary: oklch(0.922 0 0);
- --primary-foreground: oklch(0.205 0 0);
- --secondary: oklch(0.269 0 0);
- --secondary-foreground: oklch(0.985 0 0);
- --muted: oklch(0.269 0 0);
- --muted-foreground: oklch(0.708 0 0);
- --accent: oklch(0.269 0 0);
- --accent-foreground: oklch(0.985 0 0);
- --destructive: oklch(0.704 0.191 22.216);
- --border: oklch(1 0 0 / 10%);
- --input: oklch(1 0 0 / 15%);
- --ring: oklch(0.556 0 0);
- --chart-1: oklch(0.488 0.243 264.376);
- --chart-2: oklch(0.696 0.17 162.48);
- --chart-3: oklch(0.769 0.188 70.08);
- --chart-4: oklch(0.627 0.265 303.9);
- --chart-5: oklch(0.645 0.246 16.439);
- --sidebar: oklch(0.205 0 0);
- --sidebar-foreground: oklch(0.985 0 0);
- --sidebar-primary: oklch(0.488 0.243 264.376);
- --sidebar-primary-foreground: oklch(0.985 0 0);
- --sidebar-accent: oklch(0.269 0 0);
- --sidebar-accent-foreground: oklch(0.985 0 0);
- --sidebar-border: oklch(1 0 0 / 10%);
- --sidebar-ring: oklch(0.556 0 0);
-}
-
-@layer base {
- * {
- @apply border-border outline-ring/50;
- }
- body {
- @apply bg-background text-foreground;
- }
-}