diff --git a/.env.local b/.env.local index 6cd5bf1..d7e8761 100644 --- a/.env.local +++ b/.env.local @@ -1 +1 @@ -NEXT_PUBLIC_COMMIT_SHA=579eb74 +NEXT_PUBLIC_COMMIT_SHA=f25b4dc diff --git a/app/components/FullScreenPlayer.tsx b/app/components/FullScreenPlayer.tsx index 96ca8a7..4e0c2cf 100644 --- a/app/components/FullScreenPlayer.tsx +++ b/app/components/FullScreenPlayer.tsx @@ -21,14 +21,7 @@ import { FaListUl } from "react-icons/fa6"; import { Heart } from 'lucide-react'; -import { Card, CardContent } from '@/components/ui/card'; import { ScrollArea } from '@/components/ui/scroll-area'; -import { - ContextMenu, - ContextMenuContent, - ContextMenuItem, - ContextMenuTrigger, -} from "@/components/ui/context-menu"; interface LyricLine { time: number; @@ -294,10 +287,9 @@ export const FullScreenPlayer: React.FC = ({ isOpen, onCl {/* Overlay for better contrast */}
-
- {/* Header */} -
-

+
+ {/* Floating Header */} +
{onOpenQueue && (
{/* Main Content */} -
+
{/* Left Side - Album Art and Controls */}
{/* Album Art */} @@ -453,7 +445,7 @@ export const FullScreenPlayer: React.FC = ({ isOpen, onCl
-
+
{lyrics.map((line, index) => (
= ({ isOpen, onCl onClick={() => handleLyricClick(line.time)} className={`text-sm sm:text-base lg:text-base leading-relaxed transition-all duration-300 break-words cursor-pointer hover:text-foreground ${ index === currentLyricIndex - ? 'text-foreground font-bold text-lg sm:text-xl lg:text-2xl' + ? 'text-foreground font-bold text-2xl' : index < currentLyricIndex ? 'text-foreground/60' : 'text-foreground/40' @@ -470,8 +462,8 @@ export const FullScreenPlayer: React.FC = ({ isOpen, onCl wordWrap: 'break-word', overflowWrap: 'break-word', hyphens: 'auto', - paddingBottom: '6px', - paddingLeft: '16px' + paddingBottom: '4px', + paddingLeft: '8px' }} title={`Click to jump to ${formatTime(line.time)}`} > diff --git a/app/components/ThemeProvider.tsx b/app/components/ThemeProvider.tsx index 77f685c..7ab4814 100644 --- a/app/components/ThemeProvider.tsx +++ b/app/components/ThemeProvider.tsx @@ -2,12 +2,14 @@ import React, { createContext, useContext, useEffect, useState } from 'react'; - -type Theme = 'blue' | 'violet' | 'red' | 'rose' | 'orange' | 'green' | 'yellow'; +type Theme = 'default' | 'blue' | 'violet' | 'red' | 'rose' | 'orange' | 'green' | 'yellow'; +type Mode = 'light' | 'dark' | 'system'; interface ThemeContextType { theme: Theme; + mode: Mode; setTheme: (theme: Theme) => void; + setMode: (mode: Mode) => void; } const ThemeContext = createContext(undefined); @@ -25,18 +27,25 @@ interface ThemeProviderProps { } export const ThemeProvider: React.FC = ({ children }) => { - const [theme, setTheme] = useState('blue'); + const [theme, setTheme] = useState('default'); + const [mode, setMode] = useState('system'); const [mounted, setMounted] = useState(false); // Load theme settings from localStorage on component mount useEffect(() => { setMounted(true); const savedTheme = localStorage.getItem('theme'); - const validThemes: Theme[] = ['blue', 'violet', 'red', 'rose', 'orange', 'green', 'yellow']; + const savedMode = localStorage.getItem('theme-mode'); + const validThemes: Theme[] = ['default', 'blue', 'violet', 'red', 'rose', 'orange', 'green', 'yellow']; + const validModes: Mode[] = ['light', 'dark', 'system']; if (savedTheme && validThemes.includes(savedTheme as Theme)) { setTheme(savedTheme as Theme); } + + if (savedMode && validModes.includes(savedMode as Mode)) { + setMode(savedMode as Mode); + } }, []); // Apply theme changes @@ -46,35 +55,54 @@ export const ThemeProvider: React.FC = ({ children }) => { const root = document.documentElement; // Remove existing theme classes - root.classList.remove('theme-blue', 'theme-violet', 'theme-red', 'theme-rose', 'theme-orange', 'theme-green', 'theme-yellow', 'dark'); + root.classList.remove('theme-default', 'theme-blue', 'theme-violet', 'theme-red', 'theme-rose', 'theme-orange', 'theme-green', 'theme-yellow', 'dark'); // Add new theme class root.classList.add(`theme-${theme}`); - // Always follow system preference for dark mode - const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); - const applySystemTheme = () => { - root.classList.toggle('dark', mediaQuery.matches); + // Apply dark/light mode + const applyMode = () => { + if (mode === 'dark') { + root.classList.add('dark'); + } else if (mode === 'light') { + root.classList.remove('dark'); + } else { // system + const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); + root.classList.toggle('dark', mediaQuery.matches); + } }; - applySystemTheme(); - mediaQuery.addEventListener('change', applySystemTheme); + applyMode(); - // Save theme to localStorage + // Listen for system preference changes only if mode is 'system' + let mediaQuery: MediaQueryList | null = null; + if (mode === 'system') { + mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); + mediaQuery.addEventListener('change', applyMode); + } + + // Save settings to localStorage localStorage.setItem('theme', theme); + localStorage.setItem('theme-mode', mode); // Cleanup listener - return () => mediaQuery.removeEventListener('change', applySystemTheme); - }, [theme, mounted]); + return () => { + if (mediaQuery) { + mediaQuery.removeEventListener('change', applyMode); + } + }; + }, [theme, mode, mounted]); return ( -
+
{children}
diff --git a/app/components/album-artwork.tsx b/app/components/album-artwork.tsx index 163eefe..373ca55 100644 --- a/app/components/album-artwork.tsx +++ b/app/components/album-artwork.tsx @@ -112,7 +112,7 @@ export function AlbumArtwork({
- handleClick()}> + handleClick()}>
{album.coverArt && api ? ( { artist: Artist size?: number imageOnly?: boolean + responsive?: boolean } export function ArtistIcon({ artist, size = 150, imageOnly = false, + responsive = false, className, ...props }: ArtistIconProps) { @@ -54,9 +56,9 @@ export function ArtistIcon({ starItem(artist.id, 'artist'); } }; - // Get cover art URL with proper fallback + // Get cover art URL with proper fallback - use higher resolution for better quality const artistImageUrl = artist.coverArt && api - ? api.getCoverArtUrl(artist.coverArt, 200) + ? api.getCoverArtUrl(artist.coverArt, 320) : '/default-user.jpg'; // If imageOnly is true, return just the image without context menu or text @@ -79,22 +81,33 @@ export function ArtistIcon({ ); } + // Determine if we should use responsive layout + const isResponsive = responsive; + return (
- handleClick()}> + handleClick()}>
{artist.name}
@@ -105,19 +118,6 @@ export function ArtistIcon({

- {/*
- {artist.name} -
*/}
diff --git a/app/favorites/page.tsx b/app/favorites/page.tsx index d208c67..4e969d3 100644 --- a/app/favorites/page.tsx +++ b/app/favorites/page.tsx @@ -3,7 +3,6 @@ import React, { useState, useEffect } from 'react'; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Button } from "@/components/ui/button"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { useNavidrome } from "@/app/components/NavidromeContext"; import { AlbumArtwork } from "@/app/components/album-artwork"; import { ArtistIcon } from "@/app/components/artist-icon"; @@ -167,33 +166,14 @@ const FavoritesPage = () => { ) : (
{favoriteAlbums.map((album) => ( - -
- {album.coverArt && api ? ( - {album.name} - ) : ( -
- -
- )} -
- handlePlayAlbum(album)}/> -
-
- -

{album.name}

-

{album.artist}

-

- {album.songCount} songs • {Math.floor(album.duration / 60)} min -

-
-
+ ))}
)} @@ -271,23 +251,7 @@ const FavoritesPage = () => { ) : (
{favoriteArtists.map((artist) => ( - - -
- {artist.name} -
-

{artist.name}

-

- {artist.albumCount} albums -

-
-
+ ))}
)} diff --git a/app/globals.css b/app/globals.css index 8db518c..d0ebb2b 100644 --- a/app/globals.css +++ b/app/globals.css @@ -3,40 +3,49 @@ @custom-variant dark (@media (prefers-color-scheme: dark)); @theme { - --color-background: hsl(var(--background)); - --color-foreground: hsl(var(--foreground)); + --color-background: var(--background); + --color-foreground: var(--foreground); - --color-card: hsl(var(--card)); - --color-card-foreground: hsl(var(--card-foreground)); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); - --color-popover: hsl(var(--popover)); - --color-popover-foreground: hsl(var(--popover-foreground)); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); - --color-primary: hsl(var(--primary)); - --color-primary-foreground: hsl(var(--primary-foreground)); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); - --color-secondary: hsl(var(--secondary)); - --color-secondary-foreground: hsl(var(--secondary-foreground)); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); - --color-muted: hsl(var(--muted)); - --color-muted-foreground: hsl(var(--muted-foreground)); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); - --color-accent: hsl(var(--accent)); - --color-accent-foreground: hsl(var(--accent-foreground)); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); - --color-destructive: hsl(var(--destructive)); - --color-destructive-foreground: hsl(var(--destructive-foreground)); + --color-destructive: var(--destructive); + --color-destructive-foreground: var(--destructive-foreground); - --color-border: hsl(var(--border)); - --color-input: hsl(var(--input)); - --color-ring: hsl(var(--ring)); - --color-hover: hsl(var(--hover)); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --color-hover: var(--hover); - --color-chart-1: hsl(var(--chart-1)); - --color-chart-2: hsl(var(--chart-2)); - --color-chart-3: hsl(var(--chart-3)); - --color-chart-4: hsl(var(--chart-4)); - --color-chart-5: hsl(var(--chart-5)); + --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); --radius-lg: var(--radius); --radius-md: calc(var(--radius) - 2px); @@ -61,16 +70,18 @@ } } -@utility text-balance { - text-wrap: balance; -} +@layer utilities { + .text-balance { + text-wrap: balance; + } -@utility animate-scroll { - animation: scroll 8s linear infinite; -} + .animate-scroll { + animation: scroll 8s linear infinite; + } -@utility animate-infinite-scroll { - animation: infiniteScroll 10s linear infinite; + .animate-infinite-scroll { + animation: infiniteScroll 10s linear infinite; + } } @layer utilities { @@ -103,257 +114,648 @@ @layer base { :root { - --background: 240 10% 3.9%; - --foreground: 0 0% 98%; - --card: 240 10% 3.9%; - --card-foreground: 0 0% 98%; - --popover: 240 10% 3.9%; - --popover-foreground: 0 0% 98%; - --primary: 217.2 91.2% 59.8%; - --primary-foreground: 222.2 47.4% 11.2%; - --secondary: 217.2 32.6% 17.5%; - --secondary-foreground: 0 0% 98%; - --muted: 217.2 32.6% 17.5%; - --muted-foreground: 215 20.2% 65.1%; - --accent: 217.2 32.6% 17.5%; - --accent-foreground: 0 0% 98%; - --destructive: 0 62.8% 30.6%; - --destructive-foreground: 0 0% 98%; - --border: 217.2 32.6% 17.5%; - --input: 217.2 32.6% 17.5%; - --ring: 224.3 76.3% 48%; - --chart-1: 220 70% 50%; - --chart-2: 160 60% 45%; - --chart-3: 30 80% 55%; - --chart-4: 280 65% 60%; - --chart-5: 340 75% 55%; - --hover: 240 27% 11%; --radius: 0.5rem; + --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); + --hover: oklch(0.97 0 0); + --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); + --hover: oklch(0.269 0 0); + --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); + } + + /* Default/White Theme */ + .theme-default { + --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); + --hover: oklch(0.97 0 0); + --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); + } + + .theme-default.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); + --hover: oklch(0.269 0 0); + --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); + } + + /* Blue Theme */ + .theme-blue { + --background: oklch(1 0 0); + --foreground: oklch(0.141 0.005 285.823); + --card: oklch(1 0 0); + --card-foreground: oklch(0.141 0.005 285.823); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.141 0.005 285.823); + --primary: oklch(0.623 0.214 259.815); + --primary-foreground: oklch(0.97 0.014 254.604); + --secondary: oklch(0.967 0.001 286.375); + --secondary-foreground: oklch(0.21 0.006 285.885); + --muted: oklch(0.967 0.001 286.375); + --muted-foreground: oklch(0.552 0.016 285.938); + --accent: oklch(0.967 0.001 286.375); + --accent-foreground: oklch(0.21 0.006 285.885); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.92 0.004 286.32); + --input: oklch(0.92 0.004 286.32); + --ring: oklch(0.623 0.214 259.815); + --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); + --hover: oklch(0.967 0.001 286.375); + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.141 0.005 285.823); + --sidebar-primary: oklch(0.623 0.214 259.815); + --sidebar-primary-foreground: oklch(0.97 0.014 254.604); + --sidebar-accent: oklch(0.967 0.001 286.375); + --sidebar-accent-foreground: oklch(0.21 0.006 285.885); + --sidebar-border: oklch(0.92 0.004 286.32); + --sidebar-ring: oklch(0.623 0.214 259.815); } - /* Blue Theme Dark */ .theme-blue.dark { - --background: 240 10% 3.9%; - --foreground: 0 0% 98%; - --card: 240 10% 3.9%; - --card-foreground: 0 0% 98%; - --popover: 240 10% 3.9%; - --popover-foreground: 0 0% 98%; - --primary: 217.2 91.2% 59.8%; - --primary-foreground: 222.2 47.4% 11.2%; - --secondary: 217.2 32.6% 17.5%; - --secondary-foreground: 0 0% 98%; - --muted: 217.2 32.6% 17.5%; - --muted-foreground: 215 20.2% 65.1%; - --accent: 217.2 32.6% 17.5%; - --accent-foreground: 0 0% 98%; - --destructive: 0 62.8% 30.6%; - --destructive-foreground: 0 0% 98%; - --border: 217.2 32.6% 17.5%; - --input: 217.2 32.6% 17.5%; - --ring: 224.3 76.3% 48%; - --hover: 240 27% 11%; - --radius: 0.5rem; + --background: oklch(0.141 0.005 285.823); + --foreground: oklch(0.985 0 0); + --card: oklch(0.21 0.006 285.885); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.21 0.006 285.885); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.546 0.245 262.881); + --primary-foreground: oklch(0.379 0.146 265.522); + --secondary: oklch(0.274 0.006 286.033); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.274 0.006 286.033); + --muted-foreground: oklch(0.705 0.015 286.067); + --accent: oklch(0.274 0.006 286.033); + --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.488 0.243 264.376); + --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); + --hover: oklch(0.274 0.006 286.033); + --sidebar: oklch(0.21 0.006 285.885); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.546 0.245 262.881); + --sidebar-primary-foreground: oklch(0.379 0.146 265.522); + --sidebar-accent: oklch(0.274 0.006 286.033); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.488 0.243 264.376); + } + + /* Violet Theme */ + .theme-violet { + --background: oklch(1 0 0); + --foreground: oklch(0.141 0.005 285.823); + --card: oklch(1 0 0); + --card-foreground: oklch(0.141 0.005 285.823); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.141 0.005 285.823); + --primary: oklch(0.606 0.25 292.717); + --primary-foreground: oklch(0.969 0.016 293.756); + --secondary: oklch(0.967 0.001 286.375); + --secondary-foreground: oklch(0.21 0.006 285.885); + --muted: oklch(0.967 0.001 286.375); + --muted-foreground: oklch(0.552 0.016 285.938); + --accent: oklch(0.967 0.001 286.375); + --accent-foreground: oklch(0.21 0.006 285.885); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.92 0.004 286.32); + --input: oklch(0.92 0.004 286.32); + --ring: oklch(0.606 0.25 292.717); + --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); + --hover: oklch(0.967 0.001 286.375); + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.141 0.005 285.823); + --sidebar-primary: oklch(0.606 0.25 292.717); + --sidebar-primary-foreground: oklch(0.969 0.016 293.756); + --sidebar-accent: oklch(0.967 0.001 286.375); + --sidebar-accent-foreground: oklch(0.21 0.006 285.885); + --sidebar-border: oklch(0.92 0.004 286.32); + --sidebar-ring: oklch(0.606 0.25 292.717); } - /* Violet Theme Dark */ .theme-violet.dark { - --background: 224 71.4% 4.1%; - --foreground: 210 20% 98%; - --card: 224 71.4% 4.1%; - --card-foreground: 210 20% 98%; - --popover: 224 71.4% 4.1%; - --popover-foreground: 210 20% 98%; - --primary: 263.4 70% 50.4%; - --primary-foreground: 210 20% 98%; - --secondary: 215 27.9% 16.9%; - --secondary-foreground: 210 20% 98%; - --muted: 215 27.9% 16.9%; - --muted-foreground: 217.9 10.6% 64.9%; - --accent: 215 27.9% 16.9%; - --accent-foreground: 210 20% 98%; - --destructive: 0 62.8% 30.6%; - --destructive-foreground: 210 20% 98%; - --border: 215 27.9% 16.9%; - --input: 215 27.9% 16.9%; - --ring: 263.4 70% 50.4%; - --radius: 0.5rem; + --background: oklch(0.141 0.005 285.823); + --foreground: oklch(0.985 0 0); + --card: oklch(0.21 0.006 285.885); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.21 0.006 285.885); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.541 0.281 293.009); + --primary-foreground: oklch(0.969 0.016 293.756); + --secondary: oklch(0.274 0.006 286.033); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.274 0.006 286.033); + --muted-foreground: oklch(0.705 0.015 286.067); + --accent: oklch(0.274 0.006 286.033); + --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.541 0.281 293.009); + --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); + --hover: oklch(0.274 0.006 286.033); + --sidebar: oklch(0.21 0.006 285.885); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.541 0.281 293.009); + --sidebar-primary-foreground: oklch(0.969 0.016 293.756); + --sidebar-accent: oklch(0.274 0.006 286.033); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.541 0.281 293.009); } - .theme-red.dark { - --background: 0 0% 3.9%; - --foreground: 0 0% 98%; - --card: 0 0% 3.9%; - --card-foreground: 0 0% 98%; - --popover: 0 0% 3.9%; - --popover-foreground: 0 0% 98%; - --primary: 0 72.2% 50.6%; - --primary-foreground: 0 85.7% 97.3%; - --secondary: 0 0% 14.9%; - --secondary-foreground: 0 0% 98%; - --muted: 0 0% 14.9%; - --muted-foreground: 0 0% 63.9%; - --accent: 0 0% 14.9%; - --accent-foreground: 0 0% 98%; - --destructive: 0 62.8% 30.6%; - --destructive-foreground: 0 0% 98%; - --border: 0 0% 14.9%; - --input: 0 0% 14.9%; - --ring: 0 72.2% 50.6%; - --chart-1: 220 70% 50%; - --chart-2: 160 60% 45%; - --chart-3: 30 80% 55%; - --chart-4: 280 65% 60%; - --chart-5: 340 75% 55%; + /* Red Theme */ + .theme-red { + --background: oklch(1 0 0); + --foreground: oklch(0.141 0.005 285.823); + --card: oklch(1 0 0); + --card-foreground: oklch(0.141 0.005 285.823); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.141 0.005 285.823); + --primary: oklch(0.637 0.237 25.331); + --primary-foreground: oklch(0.971 0.013 17.38); + --secondary: oklch(0.967 0.001 286.375); + --secondary-foreground: oklch(0.21 0.006 285.885); + --muted: oklch(0.967 0.001 286.375); + --muted-foreground: oklch(0.552 0.016 285.938); + --accent: oklch(0.967 0.001 286.375); + --accent-foreground: oklch(0.21 0.006 285.885); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.92 0.004 286.32); + --input: oklch(0.92 0.004 286.32); + --ring: oklch(0.637 0.237 25.331); + --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); + --hover: oklch(0.967 0.001 286.375); + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.141 0.005 285.823); + --sidebar-primary: oklch(0.637 0.237 25.331); + --sidebar-primary-foreground: oklch(0.971 0.013 17.38); + --sidebar-accent: oklch(0.967 0.001 286.375); + --sidebar-accent-foreground: oklch(0.21 0.006 285.885); + --sidebar-border: oklch(0.92 0.004 286.32); + --sidebar-ring: oklch(0.637 0.237 25.331); + } + + .theme-red.dark { + --background: oklch(0.141 0.005 285.823); + --foreground: oklch(0.985 0 0); + --card: oklch(0.21 0.006 285.885); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.21 0.006 285.885); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.637 0.237 25.331); + --primary-foreground: oklch(0.971 0.013 17.38); + --secondary: oklch(0.274 0.006 286.033); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.274 0.006 286.033); + --muted-foreground: oklch(0.705 0.015 286.067); + --accent: oklch(0.274 0.006 286.033); + --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.637 0.237 25.331); + --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); + --hover: oklch(0.274 0.006 286.033); + --sidebar: oklch(0.21 0.006 285.885); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.637 0.237 25.331); + --sidebar-primary-foreground: oklch(0.971 0.013 17.38); + --sidebar-accent: oklch(0.274 0.006 286.033); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.637 0.237 25.331); + } + + /* Rose Theme */ + .theme-rose { + --background: oklch(1 0 0); + --foreground: oklch(0.141 0.005 285.823); + --card: oklch(1 0 0); + --card-foreground: oklch(0.141 0.005 285.823); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.141 0.005 285.823); + --primary: oklch(0.645 0.246 16.439); + --primary-foreground: oklch(0.969 0.015 12.422); + --secondary: oklch(0.967 0.001 286.375); + --secondary-foreground: oklch(0.21 0.006 285.885); + --muted: oklch(0.967 0.001 286.375); + --muted-foreground: oklch(0.552 0.016 285.938); + --accent: oklch(0.967 0.001 286.375); + --accent-foreground: oklch(0.21 0.006 285.885); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.92 0.004 286.32); + --input: oklch(0.92 0.004 286.32); + --ring: oklch(0.645 0.246 16.439); + --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); + --hover: oklch(0.967 0.001 286.375); + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.141 0.005 285.823); + --sidebar-primary: oklch(0.645 0.246 16.439); + --sidebar-primary-foreground: oklch(0.969 0.015 12.422); + --sidebar-accent: oklch(0.967 0.001 286.375); + --sidebar-accent-foreground: oklch(0.21 0.006 285.885); + --sidebar-border: oklch(0.92 0.004 286.32); + --sidebar-ring: oklch(0.645 0.246 16.439); } .theme-rose.dark { - --background: 20 14.3% 4.1%; - --foreground: 0 0% 95%; - --card: 24 9.8% 10%; - --card-foreground: 0 0% 95%; - --popover: 0 0% 9%; - --popover-foreground: 0 0% 95%; - --primary: 346.8 77.2% 49.8%; - --primary-foreground: 355.7 100% 97.3%; - --secondary: 240 3.7% 15.9%; - --secondary-foreground: 0 0% 98%; - --muted: 0 0% 15%; - --muted-foreground: 240 5% 64.9%; - --accent: 12 6.5% 15.1%; - --accent-foreground: 0 0% 98%; - --destructive: 0 62.8% 30.6%; - --destructive-foreground: 0 85.7% 97.3%; - --border: 240 3.7% 15.9%; - --input: 240 3.7% 15.9%; - --ring: 346.8 77.2% 49.8%; - --chart-1: 220 70% 50%; - --chart-2: 160 60% 45%; - --chart-3: 30 80% 55%; - --chart-4: 280 65% 60%; - --chart-5: 340 75% 55%; + --background: oklch(0.141 0.005 285.823); + --foreground: oklch(0.985 0 0); + --card: oklch(0.21 0.006 285.885); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.21 0.006 285.885); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.645 0.246 16.439); + --primary-foreground: oklch(0.969 0.015 12.422); + --secondary: oklch(0.274 0.006 286.033); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.274 0.006 286.033); + --muted-foreground: oklch(0.705 0.015 286.067); + --accent: oklch(0.274 0.006 286.033); + --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.645 0.246 16.439); + --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); + --hover: oklch(0.274 0.006 286.033); + --sidebar: oklch(0.21 0.006 285.885); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.645 0.246 16.439); + --sidebar-primary-foreground: oklch(0.969 0.015 12.422); + --sidebar-accent: oklch(0.274 0.006 286.033); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.645 0.246 16.439); } - + + /* Orange Theme */ + .theme-orange { + --background: oklch(1 0 0); + --foreground: oklch(0.141 0.005 285.823); + --card: oklch(1 0 0); + --card-foreground: oklch(0.141 0.005 285.823); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.141 0.005 285.823); + --primary: oklch(0.705 0.213 47.604); + --primary-foreground: oklch(0.98 0.016 73.684); + --secondary: oklch(0.967 0.001 286.375); + --secondary-foreground: oklch(0.21 0.006 285.885); + --muted: oklch(0.967 0.001 286.375); + --muted-foreground: oklch(0.552 0.016 285.938); + --accent: oklch(0.967 0.001 286.375); + --accent-foreground: oklch(0.21 0.006 285.885); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.92 0.004 286.32); + --input: oklch(0.92 0.004 286.32); + --ring: oklch(0.705 0.213 47.604); + --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); + --hover: oklch(0.967 0.001 286.375); + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.141 0.005 285.823); + --sidebar-primary: oklch(0.705 0.213 47.604); + --sidebar-primary-foreground: oklch(0.98 0.016 73.684); + --sidebar-accent: oklch(0.967 0.001 286.375); + --sidebar-accent-foreground: oklch(0.21 0.006 285.885); + --sidebar-border: oklch(0.92 0.004 286.32); + --sidebar-ring: oklch(0.705 0.213 47.604); + } + .theme-orange.dark { - --background: 20 14.3% 4.1%; - --foreground: 60 9.1% 97.8%; - --card: 20 14.3% 4.1%; - --card-foreground: 60 9.1% 97.8%; - --popover: 20 14.3% 4.1%; - --popover-foreground: 60 9.1% 97.8%; - --primary: 20.5 90.2% 48.2%; - --primary-foreground: 60 9.1% 97.8%; - --secondary: 12 6.5% 15.1%; - --secondary-foreground: 60 9.1% 97.8%; - --muted: 12 6.5% 15.1%; - --muted-foreground: 24 5.4% 63.9%; - --accent: 12 6.5% 15.1%; - --accent-foreground: 60 9.1% 97.8%; - --destructive: 0 72.2% 50.6%; - --destructive-foreground: 60 9.1% 97.8%; - --border: 12 6.5% 15.1%; - --input: 12 6.5% 15.1%; - --ring: 20.5 90.2% 48.2%; - --chart-1: 220 70% 50%; - --chart-2: 160 60% 45%; - --chart-3: 30 80% 55%; - --chart-4: 280 65% 60%; - --chart-5: 340 75% 55%; + --background: oklch(0.141 0.005 285.823); + --foreground: oklch(0.985 0 0); + --card: oklch(0.21 0.006 285.885); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.21 0.006 285.885); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.646 0.222 41.116); + --primary-foreground: oklch(0.98 0.016 73.684); + --secondary: oklch(0.274 0.006 286.033); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.274 0.006 286.033); + --muted-foreground: oklch(0.705 0.015 286.067); + --accent: oklch(0.274 0.006 286.033); + --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.646 0.222 41.116); + --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); + --hover: oklch(0.274 0.006 286.033); + --sidebar: oklch(0.21 0.006 285.885); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.646 0.222 41.116); + --sidebar-primary-foreground: oklch(0.98 0.016 73.684); + --sidebar-accent: oklch(0.274 0.006 286.033); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.646 0.222 41.116); + } + + /* Green Theme */ + .theme-green { + --background: oklch(1 0 0); + --foreground: oklch(0.141 0.005 285.823); + --card: oklch(1 0 0); + --card-foreground: oklch(0.141 0.005 285.823); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.141 0.005 285.823); + --primary: oklch(0.723 0.219 149.579); + --primary-foreground: oklch(0.982 0.018 155.826); + --secondary: oklch(0.967 0.001 286.375); + --secondary-foreground: oklch(0.21 0.006 285.885); + --muted: oklch(0.967 0.001 286.375); + --muted-foreground: oklch(0.552 0.016 285.938); + --accent: oklch(0.967 0.001 286.375); + --accent-foreground: oklch(0.21 0.006 285.885); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.92 0.004 286.32); + --input: oklch(0.92 0.004 286.32); + --ring: oklch(0.723 0.219 149.579); + --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); + --hover: oklch(0.967 0.001 286.375); + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.141 0.005 285.823); + --sidebar-primary: oklch(0.723 0.219 149.579); + --sidebar-primary-foreground: oklch(0.982 0.018 155.826); + --sidebar-accent: oklch(0.967 0.001 286.375); + --sidebar-accent-foreground: oklch(0.21 0.006 285.885); + --sidebar-border: oklch(0.92 0.004 286.32); + --sidebar-ring: oklch(0.723 0.219 149.579); } .theme-green.dark { - --background: 20 14.3% 4.1%; - --foreground: 0 0% 95%; - --card: 24 9.8% 10%; - --card-foreground: 0 0% 95%; - --popover: 0 0% 9%; - --popover-foreground: 0 0% 95%; - --primary: 142.1 70.6% 45.3%; - --primary-foreground: 144.9 80.4% 10%; - --secondary: 240 3.7% 15.9%; - --secondary-foreground: 0 0% 98%; - --muted: 0 0% 15%; - --muted-foreground: 240 5% 64.9%; - --accent: 12 6.5% 15.1%; - --accent-foreground: 0 0% 98%; - --destructive: 0 62.8% 30.6%; - --destructive-foreground: 0 85.7% 97.3%; - --border: 240 3.7% 15.9%; - --input: 240 3.7% 15.9%; - --ring: 142.4 71.8% 29.2%; - --chart-1: 220 70% 50%; - --chart-2: 160 60% 45%; - --chart-3: 30 80% 55%; - --chart-4: 280 65% 60%; - --chart-5: 340 75% 55%; + --background: oklch(0.141 0.005 285.823); + --foreground: oklch(0.985 0 0); + --card: oklch(0.21 0.006 285.885); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.21 0.006 285.885); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.696 0.17 162.48); + --primary-foreground: oklch(0.393 0.095 152.535); + --secondary: oklch(0.274 0.006 286.033); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.274 0.006 286.033); + --muted-foreground: oklch(0.705 0.015 286.067); + --accent: oklch(0.274 0.006 286.033); + --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.527 0.154 150.069); + --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); + --hover: oklch(0.274 0.006 286.033); + --sidebar: oklch(0.21 0.006 285.885); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.696 0.17 162.48); + --sidebar-primary-foreground: oklch(0.393 0.095 152.535); + --sidebar-accent: oklch(0.274 0.006 286.033); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.527 0.154 150.069); + } + + /* Yellow Theme */ + .theme-yellow { + --background: oklch(1 0 0); + --foreground: oklch(0.141 0.005 285.823); + --card: oklch(1 0 0); + --card-foreground: oklch(0.141 0.005 285.823); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.141 0.005 285.823); + --primary: oklch(0.795 0.184 86.047); + --primary-foreground: oklch(0.421 0.095 57.708); + --secondary: oklch(0.967 0.001 286.375); + --secondary-foreground: oklch(0.21 0.006 285.885); + --muted: oklch(0.967 0.001 286.375); + --muted-foreground: oklch(0.552 0.016 285.938); + --accent: oklch(0.967 0.001 286.375); + --accent-foreground: oklch(0.21 0.006 285.885); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.92 0.004 286.32); + --input: oklch(0.92 0.004 286.32); + --ring: oklch(0.795 0.184 86.047); + --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); + --hover: oklch(0.967 0.001 286.375); + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.141 0.005 285.823); + --sidebar-primary: oklch(0.795 0.184 86.047); + --sidebar-primary-foreground: oklch(0.421 0.095 57.708); + --sidebar-accent: oklch(0.967 0.001 286.375); + --sidebar-accent-foreground: oklch(0.21 0.006 285.885); + --sidebar-border: oklch(0.92 0.004 286.32); + --sidebar-ring: oklch(0.795 0.184 86.047); } .theme-yellow.dark { - --background: 20 14.3% 4.1%; - --foreground: 60 9.1% 97.8%; - --card: 20 14.3% 4.1%; - --card-foreground: 60 9.1% 97.8%; - --popover: 20 14.3% 4.1%; - --popover-foreground: 60 9.1% 97.8%; - --primary: 47.9 95.8% 53.1%; - --primary-foreground: 26 83.3% 14.1%; - --secondary: 12 6.5% 15.1%; - --secondary-foreground: 60 9.1% 97.8%; - --muted: 12 6.5% 15.1%; - --muted-foreground: 24 5.4% 63.9%; - --accent: 12 6.5% 15.1%; - --accent-foreground: 60 9.1% 97.8%; - --destructive: 0 62.8% 30.6%; - --destructive-foreground: 60 9.1% 97.8%; - --border: 12 6.5% 15.1%; - --input: 12 6.5% 15.1%; - --ring: 35.5 91.7% 32.9%; - --chart-1: 220 70% 50%; - --chart-2: 160 60% 45%; - --chart-3: 30 80% 55%; - --chart-4: 280 65% 60%; - --chart-5: 340 75% 55%; + --background: oklch(0.141 0.005 285.823); + --foreground: oklch(0.985 0 0); + --card: oklch(0.21 0.006 285.885); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.21 0.006 285.885); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.795 0.184 86.047); + --primary-foreground: oklch(0.421 0.095 57.708); + --secondary: oklch(0.274 0.006 286.033); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.274 0.006 286.033); + --muted-foreground: oklch(0.705 0.015 286.067); + --accent: oklch(0.274 0.006 286.033); + --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.554 0.135 66.442); + --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); + --hover: oklch(0.274 0.006 286.033); + --sidebar: oklch(0.21 0.006 285.885); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.795 0.184 86.047); + --sidebar-primary-foreground: oklch(0.421 0.095 57.708); + --sidebar-accent: oklch(0.274 0.006 286.033); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.554 0.135 66.442); } } - - - - - - - - - @layer utilities { - /* BackUp */ - .dark { - --background: 240 10% 3.9%; - --foreground: 0 0% 98%; - --card: 240 10% 3.9%; - --card-foreground: 0 0% 98%; - --popover: 240 10% 3.9%; - --popover-foreground: 0 0% 98%; - --primary: 217.2 91.2% 59.8%; - --primary-foreground: 222.2 47.4% 11.2%; - --secondary: 217.2 32.6% 17.5%; - --secondary-foreground: 0 0% 98%; - --muted: 217.2 32.6% 17.5%; - --muted-foreground: 215 20.2% 65.1%; - --accent: 217.2 32.6% 17.5%; - --accent-foreground: 0 0% 98%; - --destructive: 0 62.8% 30.6%; - --destructive-foreground: 0 0% 98%; - --border: 217.2 32.6% 17.5%; - --input: 217.2 32.6% 17.5%; - --ring: 224.3 76.3% 48%; - --chart-1: 220 70% 50%; - --chart-2: 160 60% 45%; - --chart-3: 30 80% 55%; - --chart-4: 280 65% 60%; - --chart-5: 340 75% 55%; - --hover: 240 27% 11%; - --radius: 0.5rem; + body { + font-family: Arial, Helvetica, sans-serif; } :focus-visible { @@ -373,37 +775,75 @@ @layer base { * { - @apply border-border; + border-color: var(--color-border); + outline-color: var(--color-ring); } body { - @apply bg-background text-foreground; + background-color: var(--color-background); + color: var(--color-foreground); } h1 { - @apply text-2xl; - @apply font-black; + font-size: 1.5rem; + font-weight: 900; } h2 { - @apply text-xl; - @apply font-black; + font-size: 1.25rem; + font-weight: 900; } h3 { - @apply text-lg; - @apply font-black; + font-size: 1.125rem; + font-weight: 900; } h4 { - @apply text-base; - @apply font-black; + font-size: 1rem; + font-weight: 900; } h5 { - @apply text-sm; - @apply font-black; + font-size: 0.875rem; + font-weight: 900; } h6 { - @apply text-xs; - @apply font-black; + font-size: 0.75rem; + font-weight: 900; } ul { - @apply list-disc; - @apply ml-9; + list-style-type: disc; + margin-left: 2.25rem; } - } \ No newline at end of file + } + +/* + ---break--- +*/ + +/* + +will delete after the new theme replaces the old one +since the new theme already has the sidebar colors defined + +:root { + --sidebar: hsl(0 0% 98%); + --sidebar-foreground: hsl(240 5.3% 26.1%); + --sidebar-primary: hsl(240 5.9% 10%); + --sidebar-primary-foreground: hsl(0 0% 98%); + --sidebar-accent: hsl(240 4.8% 95.9%); + --sidebar-accent-foreground: hsl(240 5.9% 10%); + --sidebar-border: hsl(220 13% 91%); + --sidebar-ring: hsl(217.2 91.2% 59.8%); +} + + +.dark { + --sidebar: hsl(240 5.9% 10%); + --sidebar-foreground: hsl(240 4.8% 95.9%); + --sidebar-primary: hsl(224.3 76.3% 48%); + --sidebar-primary-foreground: hsl(0 0% 100%); + --sidebar-accent: hsl(240 3.7% 15.9%); + --sidebar-accent-foreground: hsl(240 4.8% 95.9%); + --sidebar-border: hsl(240 3.7% 15.9%); + --sidebar-ring: hsl(217.2 91.2% 59.8%); +} */ + +/* + ---break--- +*/ \ No newline at end of file diff --git a/app/library/artists/page.tsx b/app/library/artists/page.tsx index ef53e4b..e20edc7 100644 --- a/app/library/artists/page.tsx +++ b/app/library/artists/page.tsx @@ -7,13 +7,11 @@ import { Separator } from "@/components/ui/separator"; import { Tabs, TabsContent } from "@/components/ui/tabs"; import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; -import { Card, CardContent } from "@/components/ui/card"; -import { Button } from "@/components/ui/button"; import { ArtistIcon } from '@/app/components/artist-icon'; import { useNavidrome } from '@/app/components/NavidromeContext'; import { Artist } from '@/lib/navidrome'; import Loading from '@/app/components/loading'; -import { Search, Heart } from 'lucide-react'; +import { Search } from 'lucide-react'; import { useRouter } from 'next/navigation'; import Image from 'next/image'; @@ -106,27 +104,7 @@ export default function ArtistPage() {
{filteredArtists.map((artist) => ( - -
handleViewArtist(artist)}> -
- {artist.name} -
-
-
-
- -

{artist.name}

-

- {artist.albumCount} albums -

-
-
+ ))}
diff --git a/app/search/page.tsx b/app/search/page.tsx index 396c123..60082e2 100644 --- a/app/search/page.tsx +++ b/app/search/page.tsx @@ -101,7 +101,7 @@ export default function SearchPage() { }; return ( -
+
{/* Header */}
@@ -136,19 +136,25 @@ export default function SearchPage() { )} {/* Artists */} - {searchResults.artists.length > 0 && ( + {/* {searchResults.artists.length > 0 && (

Artists

{searchResults.artists.map((artist) => ( - + ))}
- )} + )} */} + {/* broken for now */} {/* Albums */} {searchResults.albums.length > 0 && ( diff --git a/app/settings/page.tsx b/app/settings/page.tsx index 6f172bd..946a0b8 100644 --- a/app/settings/page.tsx +++ b/app/settings/page.tsx @@ -14,34 +14,24 @@ import { FaServer, FaUser, FaLock, FaCheck, FaTimes, FaLastfm, FaCog } from 'rea import { Settings, ExternalLink } from 'lucide-react'; const SettingsPage = () => { - const { theme, setTheme } = useTheme(); + const { theme, setTheme, mode, setMode } = useTheme(); const { config, updateConfig, isConnected, testConnection, clearConfig } = useNavidromeConfig(); const { toast } = useToast(); const { isEnabled: isStandaloneLastFmEnabled, getCredentials, getAuthUrl, getSessionKey } = useStandaloneLastFm(); const [formData, setFormData] = useState({ - serverUrl: config.serverUrl, - username: config.username, - password: config.password + serverUrl: '', + username: '', + password: '' }); const [isTesting, setIsTesting] = useState(false); const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); // Last.fm scrobbling settings (Navidrome integration) - const [scrobblingEnabled, setScrobblingEnabled] = useState(() => { - if (typeof window !== 'undefined') { - return localStorage.getItem('lastfm-scrobbling-enabled') === 'true'; - } - return true; - }); + const [scrobblingEnabled, setScrobblingEnabled] = useState(true); // Standalone Last.fm settings - const [standaloneLastFmEnabled, setStandaloneLastFmEnabled] = useState(() => { - if (typeof window !== 'undefined') { - return localStorage.getItem('standalone-lastfm-enabled') === 'true'; - } - return false; - }); + const [standaloneLastFmEnabled, setStandaloneLastFmEnabled] = useState(false); const [lastFmCredentials, setLastFmCredentials] = useState({ apiKey: '', @@ -50,6 +40,9 @@ const SettingsPage = () => { username: '' }); + // Client-side hydration state + const [isClient, setIsClient] = useState(false); + // Check if Navidrome is configured via environment variables const hasEnvConfig = React.useMemo(() => { return !!(process.env.NEXT_PUBLIC_NAVIDROME_URL && @@ -58,25 +51,51 @@ const SettingsPage = () => { }, []); // Sidebar settings - const [sidebarCollapsed, setSidebarCollapsed] = useState(() => { - if (typeof window !== 'undefined') { - return localStorage.getItem('sidebar-collapsed') === 'true'; - } - return false; - }); + const [sidebarCollapsed, setSidebarCollapsed] = useState(false); - // Load Last.fm credentials on mount + // Initialize client-side state after hydration useEffect(() => { - const credentials = getCredentials(); - if (credentials) { - setLastFmCredentials({ - apiKey: credentials.apiKey, - apiSecret: credentials.apiSecret, - sessionKey: credentials.sessionKey || '', - username: credentials.username || '' - }); + setIsClient(true); + + // Initialize form data with config values + setFormData({ + serverUrl: config.serverUrl || '', + username: config.username || '', + password: config.password || '' + }); + + // Load saved preferences from localStorage + const savedScrobbling = localStorage.getItem('lastfm-scrobbling-enabled'); + if (savedScrobbling !== null) { + setScrobblingEnabled(savedScrobbling === 'true'); } - }, [getCredentials]); + + const savedStandaloneLastFm = localStorage.getItem('standalone-lastfm-enabled'); + if (savedStandaloneLastFm !== null) { + setStandaloneLastFmEnabled(savedStandaloneLastFm === 'true'); + } + + const savedSidebarCollapsed = localStorage.getItem('sidebar-collapsed'); + if (savedSidebarCollapsed !== null) { + setSidebarCollapsed(savedSidebarCollapsed === 'true'); + } + + // Load Last.fm credentials + const storedCredentials = localStorage.getItem('lastfm-credentials'); + if (storedCredentials) { + try { + const credentials = JSON.parse(storedCredentials); + setLastFmCredentials({ + apiKey: credentials.apiKey || '', + apiSecret: credentials.apiSecret || '', + sessionKey: credentials.sessionKey || '', + username: credentials.username || '' + }); + } catch (error) { + console.error('Failed to parse stored Last.fm credentials:', error); + } + } + }, [config.serverUrl, config.username, config.password]); const handleInputChange = (field: string, value: string) => { setFormData(prev => ({ ...prev, [field]: value })); @@ -171,7 +190,9 @@ const SettingsPage = () => { const handleScrobblingToggle = (enabled: boolean) => { setScrobblingEnabled(enabled); - localStorage.setItem('lastfm-scrobbling-enabled', enabled.toString()); + if (isClient) { + localStorage.setItem('lastfm-scrobbling-enabled', enabled.toString()); + } toast({ title: enabled ? "Scrobbling Enabled" : "Scrobbling Disabled", description: enabled @@ -182,7 +203,9 @@ const SettingsPage = () => { const handleStandaloneLastFmToggle = (enabled: boolean) => { setStandaloneLastFmEnabled(enabled); - localStorage.setItem('standalone-lastfm-enabled', enabled.toString()); + if (isClient) { + localStorage.setItem('standalone-lastfm-enabled', enabled.toString()); + } toast({ title: enabled ? "Standalone Last.fm Enabled" : "Standalone Last.fm Disabled", description: enabled @@ -193,7 +216,9 @@ const SettingsPage = () => { const handleSidebarToggle = (collapsed: boolean) => { setSidebarCollapsed(collapsed); - localStorage.setItem('sidebar-collapsed', collapsed.toString()); + if (isClient) { + localStorage.setItem('sidebar-collapsed', collapsed.toString()); + } toast({ title: collapsed ? "Sidebar Collapsed" : "Sidebar Expanded", description: collapsed @@ -202,7 +227,9 @@ const SettingsPage = () => { }); // Trigger a custom event to notify the sidebar component - window.dispatchEvent(new CustomEvent('sidebar-toggle', { detail: { collapsed } })); + if (typeof window !== 'undefined') { + window.dispatchEvent(new CustomEvent('sidebar-toggle', { detail: { collapsed } })); + } }; const handleLastFmAuth = () => { @@ -234,7 +261,9 @@ const SettingsPage = () => { return; } - localStorage.setItem('lastfm-credentials', JSON.stringify(lastFmCredentials)); + if (isClient) { + localStorage.setItem('lastfm-credentials', JSON.stringify(lastFmCredentials)); + } toast({ title: "Credentials Saved", description: "Last.fm credentials have been saved locally.", @@ -256,7 +285,9 @@ const SettingsPage = () => { }; setLastFmCredentials(updatedCredentials); - localStorage.setItem('lastfm-credentials', JSON.stringify(updatedCredentials)); + if (isClient) { + localStorage.setItem('lastfm-credentials', JSON.stringify(updatedCredentials)); + } toast({ title: "Last.fm Authentication Complete", @@ -273,11 +304,19 @@ const SettingsPage = () => { return (
-
-
-

Settings

-

Customize your music experience

+ {!isClient ? ( +
+
+

Settings

+

Loading...

+
+ ) : ( +
+
+

Settings

+

Customize your music experience

+
{!hasEnvConfig && ( @@ -619,6 +658,7 @@ const SettingsPage = () => { + Default Blue Violet Red @@ -630,9 +670,23 @@ const SettingsPage = () => {
+
+ + +
+
-

Theme: Choose between blue and violet color schemes

-

Dark Mode: Automatically follows your system preferences

+

Theme: Choose from multiple color schemes including default (white)

+

Display Mode: Choose light, dark, or system (follows your device preferences)

@@ -666,7 +720,8 @@ const SettingsPage = () => {
-
+
+ )}
); }; diff --git a/components/ui/accordion.tsx b/components/ui/accordion.tsx new file mode 100644 index 0000000..4a8cca4 --- /dev/null +++ b/components/ui/accordion.tsx @@ -0,0 +1,66 @@ +"use client" + +import * as React from "react" +import * as AccordionPrimitive from "@radix-ui/react-accordion" +import { ChevronDownIcon } from "lucide-react" + +import { cn } from "@/lib/utils" + +function Accordion({ + ...props +}: React.ComponentProps) { + return +} + +function AccordionItem({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AccordionTrigger({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + svg]:rotate-180", + className + )} + {...props} + > + {children} + + + + ) +} + +function AccordionContent({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + +
{children}
+
+ ) +} + +export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } diff --git a/components/ui/aspect-ratio.tsx b/components/ui/aspect-ratio.tsx new file mode 100644 index 0000000..3df3fd0 --- /dev/null +++ b/components/ui/aspect-ratio.tsx @@ -0,0 +1,11 @@ +"use client" + +import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio" + +function AspectRatio({ + ...props +}: React.ComponentProps) { + return +} + +export { AspectRatio } diff --git a/components/ui/breadcrumb.tsx b/components/ui/breadcrumb.tsx new file mode 100644 index 0000000..eb88f32 --- /dev/null +++ b/components/ui/breadcrumb.tsx @@ -0,0 +1,109 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { ChevronRight, MoreHorizontal } from "lucide-react" + +import { cn } from "@/lib/utils" + +function Breadcrumb({ ...props }: React.ComponentProps<"nav">) { + return