mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-10-06 02:47:56 +00:00
Add themes
This commit is contained in:
parent
aee48a55a0
commit
a988fd0542
11 changed files with 121 additions and 53 deletions
|
|
@ -43,6 +43,7 @@
|
|||
"lucide-react": "^0.509.0",
|
||||
"next": "^15.3.2",
|
||||
"next-intl": "^4.1.0",
|
||||
"next-themes": "^0.4.6",
|
||||
"pg": "^8.13.0",
|
||||
"pino": "^9.5.0",
|
||||
"pino-pretty": "^13.0.0",
|
||||
|
|
|
|||
14
pnpm-lock.yaml
generated
14
pnpm-lock.yaml
generated
|
|
@ -65,6 +65,9 @@ importers:
|
|||
next-intl:
|
||||
specifier: ^4.1.0
|
||||
version: 4.1.0(next@15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(typescript@5.8.3)
|
||||
next-themes:
|
||||
specifier: ^0.4.6
|
||||
version: 0.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
pg:
|
||||
specifier: ^8.13.0
|
||||
version: 8.15.6
|
||||
|
|
@ -6449,6 +6452,12 @@ packages:
|
|||
typescript:
|
||||
optional: true
|
||||
|
||||
next-themes@0.4.6:
|
||||
resolution: {integrity: sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==}
|
||||
peerDependencies:
|
||||
react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc
|
||||
|
||||
next@15.3.2:
|
||||
resolution: {integrity: sha512-CA3BatMyHkxZ48sgOCLdVHjFU36N7TF1HhqAHLFOkV6buwZnvMI84Cug8xD56B9mCuKrqXnLn94417GrZ/jjCQ==}
|
||||
engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
|
||||
|
|
@ -15704,6 +15713,11 @@ snapshots:
|
|||
optionalDependencies:
|
||||
typescript: 5.8.3
|
||||
|
||||
next-themes@0.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
|
||||
dependencies:
|
||||
react: 19.1.0
|
||||
react-dom: 19.1.0(react@19.1.0)
|
||||
|
||||
next@15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
|
||||
dependencies:
|
||||
'@next/env': 15.3.2
|
||||
|
|
|
|||
|
|
@ -4,12 +4,11 @@ import type { Metadata } from 'next';
|
|||
import { NextIntlClientProvider } from 'next-intl';
|
||||
import { getLocale } from 'next-intl/server';
|
||||
|
||||
import { ThemeProvider } from '@/components/ThemeProvider';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
icons: [
|
||||
{
|
||||
rel: 'apple-touch-icon',
|
||||
url: '/apple-touch-icon.png',
|
||||
},
|
||||
{ rel: 'apple-touch-icon', url: '/apple-touch-icon.png' },
|
||||
{
|
||||
rel: 'icon',
|
||||
type: 'image/png',
|
||||
|
|
@ -22,14 +21,15 @@ export const metadata: Metadata = {
|
|||
sizes: '16x16',
|
||||
url: '/favicon-16x16.png',
|
||||
},
|
||||
{
|
||||
rel: 'icon',
|
||||
url: '/favicon.ico',
|
||||
},
|
||||
{ rel: 'icon', url: '/favicon.ico' },
|
||||
],
|
||||
};
|
||||
|
||||
export default async function RootLayout(props: { children: React.ReactNode }) {
|
||||
export default async function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
const locale = await getLocale();
|
||||
|
||||
return (
|
||||
|
|
@ -38,7 +38,16 @@ export default async function RootLayout(props: { children: React.ReactNode }) {
|
|||
className="bg-background text-foreground antialiased"
|
||||
suppressHydrationWarning
|
||||
>
|
||||
<NextIntlClientProvider>{props.children}</NextIntlClientProvider>
|
||||
<NextIntlClientProvider>
|
||||
<ThemeProvider
|
||||
attribute="class"
|
||||
defaultTheme="system"
|
||||
enableSystem
|
||||
disableTransitionOnChange
|
||||
>
|
||||
{children}
|
||||
</ThemeProvider>
|
||||
</NextIntlClientProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -6,14 +6,15 @@ import { useLocale } from 'next-intl';
|
|||
|
||||
import { Locales, isLocale } from '@/types/locale';
|
||||
import { setLocale } from '@/actions/locale';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Button,
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuRadioGroup,
|
||||
DropdownMenuRadioItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
} from '@/components/ui';
|
||||
import { Globe } from 'lucide-react';
|
||||
|
||||
export const LocaleSwitcher = () => {
|
||||
const router = useRouter();
|
||||
|
|
@ -32,24 +33,8 @@ export const LocaleSwitcher = () => {
|
|||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
className="p-2 focus-visible:ring-offset-0"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
aria-label="lang-switcher"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="size-6 stroke-current stroke-2"
|
||||
fill="none"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" />
|
||||
<path d="M3 12a9 9 0 1 0 18 0 9 9 0 0 0-18 0M3.6 9h16.8M3.6 15h16.8" />
|
||||
<path d="M11.5 3a17 17 0 0 0 0 18M12.5 3a17 17 0 0 1 0 18" />
|
||||
</svg>
|
||||
<Button variant="ghost" size="icon">
|
||||
<Globe />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
|
|
|
|||
11
src/components/ThemeProvider.tsx
Normal file
11
src/components/ThemeProvider.tsx
Normal file
|
|
@ -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<typeof NextThemesProvider>) {
|
||||
return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
|
||||
}
|
||||
38
src/components/ThemeSwitcher.tsx
Normal file
38
src/components/ThemeSwitcher.tsx
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
'use client';
|
||||
|
||||
import { Moon, Sun } from 'lucide-react';
|
||||
import { useTheme } from 'next-themes';
|
||||
|
||||
import {
|
||||
Button,
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui';
|
||||
|
||||
export function ThemeSwitcher() {
|
||||
const { setTheme } = useTheme();
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" size="icon">
|
||||
<Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
|
||||
<Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem onClick={() => setTheme('light')}>
|
||||
Light
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => setTheme('dark')}>
|
||||
Dark
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => setTheme('system')}>
|
||||
System
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { type ForwardedRef, forwardRef } from 'react';
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Button } from '@/components/ui';
|
||||
|
||||
type ToggleMenuButtonProps = {
|
||||
onClick?: () => void;
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ import {
|
|||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
Separator,
|
||||
} from '@/components/ui';
|
||||
import { Logo } from '@/components/templates/Logo';
|
||||
|
||||
type DashboardHeaderProps = {
|
||||
|
|
|
|||
|
|
@ -6,11 +6,17 @@ import { ToggleMenuButton } from '@/components/ToggleMenuButton';
|
|||
import { useMenu } from '@/hooks/useMenu';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
export const CenteredMenu = (props: {
|
||||
type CenteredMenuProps = {
|
||||
logo: React.ReactNode;
|
||||
children: React.ReactNode;
|
||||
rightMenu: React.ReactNode;
|
||||
}) => {
|
||||
children?: React.ReactNode;
|
||||
};
|
||||
|
||||
export const CenteredMenu = ({
|
||||
logo,
|
||||
rightMenu,
|
||||
children,
|
||||
}: CenteredMenuProps) => {
|
||||
const { showMenu, handleToggleMenu } = useMenu();
|
||||
|
||||
const navClass = cn('max-lg:w-full max-lg:bg-secondary max-lg:p-5', {
|
||||
|
|
@ -19,18 +25,17 @@ export const CenteredMenu = (props: {
|
|||
|
||||
return (
|
||||
<div className="flex flex-wrap items-center justify-between">
|
||||
<Link href="/">{props.logo}</Link>
|
||||
|
||||
<Link href="/">{logo}</Link>
|
||||
<div className="lg:hidden [&_button:hover]:opacity-100 [&_button]:opacity-60">
|
||||
<ToggleMenuButton onClick={handleToggleMenu} />
|
||||
</div>
|
||||
|
||||
<nav className={cn('rounded-t max-lg:mt-2', navClass)}>
|
||||
<ul className="flex gap-x-6 gap-y-1 text-lg font-medium max-lg:flex-col [&_a:hover]:opacity-100 [&_a]:opacity-60 max-lg:[&_a]:inline-block max-lg:[&_a]:w-full">
|
||||
{props.children}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
{children && (
|
||||
<nav className={cn('rounded-t max-lg:mt-2', navClass)}>
|
||||
<ul className="flex gap-x-6 gap-y-1 text-lg font-medium max-lg:flex-col [&_a:hover]:opacity-100 [&_a]:opacity-60 max-lg:[&_a]:inline-block max-lg:[&_a]:w-full">
|
||||
{children}
|
||||
</ul>
|
||||
</nav>
|
||||
)}
|
||||
<div
|
||||
className={cn(
|
||||
'rounded-b max-lg:border-t max-lg:border-border',
|
||||
|
|
@ -38,7 +43,7 @@ export const CenteredMenu = (props: {
|
|||
)}
|
||||
>
|
||||
<ul className="flex flex-row items-center gap-x-1.5 text-lg font-medium [&_li[data-fade]:hover]:opacity-100 [&_li[data-fade]]:opacity-60">
|
||||
{props.rightMenu}
|
||||
{rightMenu}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import Link from 'next/link';
|
||||
import { useTranslations } from 'next-intl';
|
||||
|
||||
import { Button } from '@/components/ui';
|
||||
import { LocaleSwitcher } from '@/components/LocaleSwitcher';
|
||||
import { buttonVariants } from '@/components/ui/button';
|
||||
import { ThemeSwitcher } from '@/components/ThemeSwitcher';
|
||||
import { CenteredMenu } from '@/components/landing/CenteredMenu';
|
||||
import { Section } from '@/components/landing/Section';
|
||||
|
||||
|
|
@ -20,19 +21,20 @@ export const Navbar = () => {
|
|||
<li data-fade>
|
||||
<LocaleSwitcher />
|
||||
</li>
|
||||
<li data-fade>
|
||||
<ThemeSwitcher />
|
||||
</li>
|
||||
<li className="ml-1 mr-2.5" data-fade>
|
||||
<Link href="/sign-in">{t('sign_in')}</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link className={buttonVariants()} href="/sign-up">
|
||||
{t('sign_up')}
|
||||
</Link>
|
||||
<Button asChild>
|
||||
<Link href="/sign-up">{t('sign_up')}</Link>
|
||||
</Button>
|
||||
</li>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<div />
|
||||
</CenteredMenu>
|
||||
/>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
3
src/components/ui/index.ts
Normal file
3
src/components/ui/index.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export * from './button';
|
||||
export * from './dropdown-menu';
|
||||
export * from './separator';
|
||||
Loading…
Add table
Reference in a new issue