diff --git a/app/apple-icon.tsx b/app/apple-icon.tsx new file mode 100644 index 0000000..f1926a0 --- /dev/null +++ b/app/apple-icon.tsx @@ -0,0 +1,54 @@ +import { ImageResponse } from 'next/og' + +export const size = { + width: 180, + height: 180, +} +export const contentType = 'image/png' + +export default function AppleIcon() { + return new ImageResponse( + ( +
+
+ + + + + +
+
+ ), + { + ...size, + }, + ) +} diff --git a/app/globals.css b/app/globals.css index d9f33b7..6232fcb 100644 --- a/app/globals.css +++ b/app/globals.css @@ -3,45 +3,45 @@ @custom-variant dark (&:is(.dark *)); -/* Codeplans.ai - Dark theme inspired by Vercel dashboard */ +/* Codeplans.ai - Light theme (mirrors the dark palette below) */ :root { - --background: oklch(0.07 0 0); - --foreground: oklch(0.95 0 0); - --card: oklch(0.11 0 0); - --card-foreground: oklch(0.95 0 0); - --popover: oklch(0.11 0 0); - --popover-foreground: oklch(0.95 0 0); - --primary: oklch(0.95 0 0); - --primary-foreground: oklch(0.07 0 0); - --secondary: oklch(0.18 0 0); - --secondary-foreground: oklch(0.95 0 0); - --muted: oklch(0.15 0 0); - --muted-foreground: oklch(0.6 0 0); - --accent: oklch(0.7 0.15 145); - --accent-foreground: oklch(0.07 0 0); + --background: oklch(0.985 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.6 0.15 145); + --accent-foreground: oklch(0.985 0 0); --destructive: oklch(0.55 0.22 25); - --destructive-foreground: oklch(0.95 0 0); - --border: oklch(0.22 0 0); - --input: oklch(0.15 0 0); - --ring: oklch(0.7 0.15 145); - --chart-1: oklch(0.7 0.15 145); - --chart-2: oklch(0.65 0.2 250); - --chart-3: oklch(0.75 0.15 55); - --chart-4: oklch(0.6 0.2 300); - --chart-5: oklch(0.65 0.18 25); + --destructive-foreground: oklch(0.985 0 0); + --border: oklch(0.9 0 0); + --input: oklch(0.9 0 0); + --ring: oklch(0.6 0.15 145); + --chart-1: oklch(0.55 0.15 145); + --chart-2: oklch(0.5 0.2 250); + --chart-3: oklch(0.6 0.15 55); + --chart-4: oklch(0.5 0.2 300); + --chart-5: oklch(0.5 0.18 25); --radius: 0.5rem; - --sidebar: oklch(0.09 0 0); - --sidebar-foreground: oklch(0.95 0 0); - --sidebar-primary: oklch(0.7 0.15 145); - --sidebar-primary-foreground: oklch(0.07 0 0); - --sidebar-accent: oklch(0.15 0 0); - --sidebar-accent-foreground: oklch(0.95 0 0); - --sidebar-border: oklch(0.22 0 0); - --sidebar-ring: oklch(0.7 0.15 145); - --success: oklch(0.7 0.15 145); - --success-foreground: oklch(0.07 0 0); - --warning: oklch(0.75 0.15 55); - --warning-foreground: oklch(0.07 0 0); + --sidebar: oklch(0.97 0 0); + --sidebar-foreground: oklch(0.145 0 0); + --sidebar-primary: oklch(0.6 0.15 145); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.94 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0.9 0 0); + --sidebar-ring: oklch(0.6 0.15 145); + --success: oklch(0.55 0.15 145); + --success-foreground: oklch(0.985 0 0); + --warning: oklch(0.6 0.15 55); + --warning-foreground: oklch(0.985 0 0); } .dark { diff --git a/app/layout.tsx b/app/layout.tsx index 40d8daf..2ccc599 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,6 +1,7 @@ import type { Metadata } from 'next' import { Geist, Geist_Mono } from 'next/font/google' import { Analytics } from '@vercel/analytics/next' +import { ThemeProvider } from '@/components/theme-provider' import './globals.css' const _geist = Geist({ subsets: ["latin"] }); @@ -9,23 +10,6 @@ const _geistMono = Geist_Mono({ subsets: ["latin"] }); export const metadata: Metadata = { title: 'CodePlans — Engineering Planning', description: 'Coordinate and track changes across your software architecture. Products, assets, code plans, and tasks in one place.', - icons: { - icon: [ - { - url: '/icon-light-32x32.png', - media: '(prefers-color-scheme: light)', - }, - { - url: '/icon-dark-32x32.png', - media: '(prefers-color-scheme: dark)', - }, - { - url: '/icon.svg', - type: 'image/svg+xml', - }, - ], - apple: '/apple-icon.png', - }, } export default function RootLayout({ @@ -34,9 +18,11 @@ export default function RootLayout({ children: React.ReactNode }>) { return ( - + - {children} + + {children} + diff --git a/components/app-shell.tsx b/components/app-shell.tsx index 7aae5f5..6d8906a 100644 --- a/components/app-shell.tsx +++ b/components/app-shell.tsx @@ -15,6 +15,7 @@ import { } from '@/components/ui/dropdown-menu' import { Avatar, AvatarFallback } from '@/components/ui/avatar' import { Badge } from '@/components/ui/badge' +import { ThemeToggle } from '@/components/theme-toggle' import { LayoutDashboard, UserCircle2, @@ -265,6 +266,8 @@ export function AppShell({ children, user, orgName, products, selectedProductId,
+ + diff --git a/components/theme-toggle.tsx b/components/theme-toggle.tsx new file mode 100644 index 0000000..8f9062f --- /dev/null +++ b/components/theme-toggle.tsx @@ -0,0 +1,54 @@ +'use client' + +import { useEffect, useState } from 'react' +import { useTheme } from 'next-themes' +import { Button } from '@/components/ui/button' +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from '@/components/ui/dropdown-menu' +import { Sun, Moon, Monitor } from 'lucide-react' +import { cn } from '@/lib/utils' + +const OPTIONS = [ + { value: 'light', label: 'Light', icon: Sun }, + { value: 'dark', label: 'Dark', icon: Moon }, + { value: 'system', label: 'System', icon: Monitor }, +] as const + +export function ThemeToggle() { + const { theme, setTheme } = useTheme() + // Avoid rendering an icon that doesn't match the persisted theme before hydration + const [mounted, setMounted] = useState(false) + useEffect(() => setMounted(true), []) + + const current = OPTIONS.find((o) => o.value === theme) ?? OPTIONS[2] + const CurrentIcon = current.icon + + return ( + + + + + + {OPTIONS.map((option) => { + const Icon = option.icon + return ( + setTheme(option.value)} + className={cn('gap-2', theme === option.value && 'font-medium')} + > + + {option.label} + + ) + })} + + + ) +} diff --git a/components/ui/switch.tsx b/components/ui/switch.tsx index 3c4cfa3..70d5118 100644 --- a/components/ui/switch.tsx +++ b/components/ui/switch.tsx @@ -13,7 +13,7 @@ function Switch({ ", "homepage": "https://codeplans.ai", diff --git a/public/apple-icon.png b/public/apple-icon.png deleted file mode 100644 index f9418b4..0000000 Binary files a/public/apple-icon.png and /dev/null differ diff --git a/public/icon-dark-32x32.png b/public/icon-dark-32x32.png deleted file mode 100644 index 12c825a..0000000 Binary files a/public/icon-dark-32x32.png and /dev/null differ diff --git a/public/icon-light-32x32.png b/public/icon-light-32x32.png deleted file mode 100644 index a3462cc..0000000 Binary files a/public/icon-light-32x32.png and /dev/null differ diff --git a/public/icon.svg b/public/icon.svg deleted file mode 100644 index 5c11e6c..0000000 --- a/public/icon.svg +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file