feat(App.tsx): integrate theme provider for dark/light mode switching and update UI elements to use new theme classes

This commit is contained in:
2025-08-11 13:04:01 +02:00
parent 511022a076
commit e5e4677a5e
4 changed files with 78 additions and 54 deletions

View File

@@ -5,23 +5,26 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { BrowserRouter, Routes, Route } from "react-router-dom"; import { BrowserRouter, Routes, Route } from "react-router-dom";
import Index from "./pages/Index"; import Index from "./pages/Index";
import NotFound from "./pages/NotFound"; import NotFound from "./pages/NotFound";
import { ThemeProvider } from "@/components/theme-provider";
const queryClient = new QueryClient(); const queryClient = new QueryClient();
const App = () => ( const App = () => (
<QueryClientProvider client={queryClient}> <ThemeProvider attribute="class" defaultTheme="system" enableSystem>
<TooltipProvider> <QueryClientProvider client={queryClient}>
<Toaster /> <TooltipProvider>
<Sonner /> <Toaster />
<BrowserRouter> <Sonner />
<Routes> <BrowserRouter>
<Route path="/" element={<Index />} /> <Routes>
{/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */} <Route path="/" element={<Index />} />
<Route path="*" element={<NotFound />} /> {/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */}
</Routes> <Route path="*" element={<NotFound />} />
</BrowserRouter> </Routes>
</TooltipProvider> </BrowserRouter>
</QueryClientProvider> </TooltipProvider>
</QueryClientProvider>
</ThemeProvider>
); );
export default App; export default App;

View File

@@ -0,0 +1,8 @@
"use client";
import { ThemeProvider as NextThemesProvider } from "next-themes";
import type { ThemeProviderProps } from "next-themes/dist/types";
export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
}

View File

@@ -1,5 +1,5 @@
import { useState, useEffect, useCallback, useRef } from 'react'; import { useState, useEffect, useCallback, useRef } from 'react';
import { Trash2 } from 'lucide-react'; import { Trash2, Moon, Sun } from 'lucide-react';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Textarea } from '@/components/ui/textarea'; import { Textarea } from '@/components/ui/textarea';
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
@@ -9,6 +9,8 @@ import { Slider } from '@/components/ui/slider';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { toast } from '@/hooks/use-toast'; import { toast } from '@/hooks/use-toast';
import { Window } from '@tauri-apps/api/window'; import { Window } from '@tauri-apps/api/window';
import { Switch } from '@/components/ui/switch';
import { useTheme } from 'next-themes';
interface Note { interface Note {
id: string; id: string;
@@ -71,6 +73,8 @@ const Index = () => {
const [gotoDateInput, setGotoDateInput] = useState(''); const [gotoDateInput, setGotoDateInput] = useState('');
const [cacheMode, setCacheMode] = useState<'global' | 'scoped'>('global'); const [cacheMode, setCacheMode] = useState<'global' | 'scoped'>('global');
const { resolvedTheme, setTheme } = useTheme();
const previousNoteRef = useRef<HTMLDivElement>(null); const previousNoteRef = useRef<HTMLDivElement>(null);
const currentNoteRef = useRef<HTMLTextAreaElement>(null); const currentNoteRef = useRef<HTMLTextAreaElement>(null);
const scratchRef = useRef<HTMLTextAreaElement>(null); const scratchRef = useRef<HTMLTextAreaElement>(null);
@@ -1185,12 +1189,21 @@ const Index = () => {
}; };
return ( return (
<div className={`min-h-screen bg-gradient-to-br from-slate-900 to-stone-900 flex flex-col ${getTextClass('base')}`}> <div className={`min-h-screen bg-background text-foreground flex flex-col ${getTextClass('base')}`}>
{/* Header */} {/* Header */}
<header className="bg-slate-800 border-b border-slate-700 p-4 shadow-sm"> <header className="bg-card text-card-foreground border-b border-border p-4 shadow-sm">
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
<h1 className={`${getTextClass('5xl')} font-bold text-slate-100`}>Journal</h1> <h1 className={`${getTextClass('5xl')} font-bold`}>Journal</h1>
<div className="flex gap-2 items-center"> <div className="flex gap-3 items-center">
<div className="hidden sm:flex items-center gap-2">
<Sun className="h-4 w-4" />
<Switch
checked={resolvedTheme === 'dark'}
onCheckedChange={(checked) => setTheme(checked ? 'dark' : 'light')}
aria-label="Toggle theme"
/>
<Moon className="h-4 w-4" />
</div>
<Select value={fontSize} onValueChange={saveFontSizeSetting}> <Select value={fontSize} onValueChange={saveFontSizeSetting}>
<SelectTrigger className="w-32"> <SelectTrigger className="w-32">
<SelectValue placeholder="Font Size" /> <SelectValue placeholder="Font Size" />
@@ -1217,15 +1230,15 @@ const Index = () => {
{/* Previous Note - Top Half */} {/* Previous Note - Top Half */}
<div <div
ref={previousNoteRef} ref={previousNoteRef}
className="flex-1 bg-slate-800 rounded-lg border border-slate-700 shadow-sm p-6 overflow-auto cursor-pointer select-none" className="flex-1 bg-card rounded-lg border border-border shadow-sm p-6 overflow-auto cursor-pointer select-none"
> >
<div className={`${getTextClass('2xl')} text-slate-400 mb-3`}> <div className={`${getTextClass('2xl')} text-muted-foreground mb-3`}>
Previous Entry {currentNoteIndex > 0 && `(${currentNoteIndex + 1} of ${noteCache.length})`} Previous Entry {currentNoteIndex > 0 && `(${currentNoteIndex + 1} of ${noteCache.length})`}
<span className={`ml-2 ${getTextClass('xl')} text-slate-500`}>Scroll to navigate</span> <span className={`ml-2 ${getTextClass('xl')} text-muted-foreground`}>Scroll to navigate</span>
</div> </div>
{previousNote ? ( {previousNote ? (
<div className="space-y-4 h-[calc(100%-2rem)]"> <div className="space-y-4 h-[calc(100%-2rem)]">
<div className={`${getTextClass('xl')} text-slate-500`}> <div className={`${getTextClass('xl')} text-muted-foreground`}>
{formatDate(previousNote.epochTime)} {formatDate(previousNote.epochTime)}
</div> </div>
<Textarea <Textarea
@@ -1239,20 +1252,20 @@ const Index = () => {
} }
}} }}
onBlur={handlePreviousNoteBlur} onBlur={handlePreviousNoteBlur}
className={`h-[calc(100%-3rem)] border-0 resize-none focus:ring-0 text-slate-200 bg-transparent ${getTextClass('2xl')}`} className={`h-[calc(100%-3rem)] border-0 resize-none focus:ring-0 bg-transparent ${getTextClass('2xl')}`}
placeholder="Previous entry content..." placeholder="Previous entry content..."
/> />
</div> </div>
) : ( ) : (
<div className={`text-slate-500 text-center py-12 ${getTextClass('2xl')}`}> <div className={`text-muted-foreground text-center py-12 ${getTextClass('2xl')}`}>
{isLoading ? 'Loading notes...' : 'No previous entries found'} {isLoading ? 'Loading notes...' : 'No previous entries found'}
</div> </div>
)} )}
</div> </div>
{/* Current Note - Bottom Half */} {/* Current Note - Bottom Half */}
<div className="flex-1 bg-slate-800 rounded-lg border border-slate-700 shadow-sm p-6 overflow-hidden"> <div className="flex-1 bg-card rounded-lg border border-border shadow-sm p-6 overflow-hidden">
<div className={`${getTextClass('2xl')} text-slate-400 mb-3`}>Current Entry</div> <div className={`${getTextClass('2xl')} text-muted-foreground mb-3`}>Current Entry</div>
<Textarea <Textarea
ref={currentNoteRef} ref={currentNoteRef}
value={currentNote} value={currentNote}
@@ -1260,20 +1273,20 @@ const Index = () => {
setCurrentNote(e.target.value); setCurrentNote(e.target.value);
}} }}
onBlur={handleCurrentNoteBlur} onBlur={handleCurrentNoteBlur}
className={`h-[calc(100%-2rem)] border-0 resize-none focus:ring-0 text-slate-200 bg-transparent ${getTextClass('2xl')}`} className={`h-[calc(100%-2rem)] border-0 resize-none focus:ring-0 bg-transparent ${getTextClass('2xl')}`}
placeholder="Start writing your thoughts..." placeholder="Start writing your thoughts..."
/> />
</div> </div>
</div> </div>
{/* Right Panel - Scratch Pad - 30% */} {/* Right Panel - Scratch Pad - 30% */}
<div className="flex-[3] bg-slate-800 rounded-lg border border-slate-700 shadow-sm p-6 h-[calc(100vh-8rem)] overflow-hidden"> <div className="flex-[3] bg-card rounded-lg border border-border shadow-sm p-6 h-[calc(100vh-8rem)] overflow-hidden">
<div className={`${getTextClass('2xl')} text-slate-400 mb-3`}>Scratch Pad</div> <div className={`${getTextClass('2xl')} text-muted-foreground mb-3`}>Scratch Pad</div>
<Textarea <Textarea
ref={scratchRef} ref={scratchRef}
value={scratchPad} value={scratchPad}
onChange={(e) => setScratchPad(e.target.value)} onChange={(e) => setScratchPad(e.target.value)}
className={`h-[calc(100%-2rem)] border-0 resize-none focus:ring-0 text-slate-200 bg-transparent ${getTextClass('2xl')}`} className={`h-[calc(100%-2rem)] border-0 resize-none focus:ring-0 bg-transparent ${getTextClass('2xl')}`}
placeholder="Quick notes, ideas, temporary thoughts..." placeholder="Quick notes, ideas, temporary thoughts..."
/> />
</div> </div>
@@ -1281,9 +1294,9 @@ const Index = () => {
{/* Search Modal */} {/* Search Modal */}
<Dialog open={isSearchOpen} onOpenChange={setIsSearchOpen}> <Dialog open={isSearchOpen} onOpenChange={setIsSearchOpen}>
<DialogContent className="max-w-4xl max-h-[80vh] overflow-hidden bg-slate-800 border-slate-700"> <DialogContent className="max-w-4xl max-h-[80vh] overflow-hidden bg-popover border-border">
<DialogHeader> <DialogHeader>
<DialogTitle className={`${getTextClass('4xl')} text-slate-100`}>Search Notes</DialogTitle> <DialogTitle className={`${getTextClass('4xl')}`}>Search Notes</DialogTitle>
</DialogHeader> </DialogHeader>
<div className="space-y-4"> <div className="space-y-4">
<Input <Input
@@ -1292,31 +1305,31 @@ const Index = () => {
placeholder="Search your notes..." placeholder="Search your notes..."
value={searchQuery} value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)} onChange={(e) => setSearchQuery(e.target.value)}
className={`${getTextClass('2xl')} bg-slate-700 border-slate-600 text-slate-200`} className={`${getTextClass('2xl')} bg-secondary border-input text-secondary-foreground`}
/> />
<div className="overflow-auto max-h-[50vh] space-y-4"> <div className="overflow-auto max-h-[50vh] space-y-4">
{searchResults.length > 0 ? ( {searchResults.length > 0 ? (
searchResults.map((note) => ( searchResults.map((note) => (
<div <div
key={note.id} key={note.id}
className="p-4 border border-slate-700 rounded-lg hover:bg-slate-700 cursor-pointer" className="p-4 border border-border rounded-lg hover:bg-accent cursor-pointer"
onClick={() => handleSearchResultClick(note)} onClick={() => handleSearchResultClick(note)}
> >
<div className={`${getTextClass('xl')} text-slate-400 mb-2`}> <div className={`${getTextClass('xl')} text-muted-foreground mb-2`}>
{formatDate(note.epochTime)} {formatDate(note.epochTime)}
</div> </div>
<div <div
className={`${getTextClass('2xl')} text-slate-200 whitespace-pre-wrap`} className={`${getTextClass('2xl')} whitespace-pre-wrap`}
dangerouslySetInnerHTML={{ __html: note.snippet || note.content }} dangerouslySetInnerHTML={{ __html: note.snippet || note.content }}
/> />
</div> </div>
)) ))
) : searchQuery ? ( ) : searchQuery ? (
<div className={`text-center py-8 text-slate-400 ${getTextClass('2xl')}`}> <div className={`text-center py-8 text-muted-foreground ${getTextClass('2xl')}`}>
No results found for "{searchQuery}" No results found for "{searchQuery}"
</div> </div>
) : ( ) : (
<div className={`text-center py-8 text-slate-400 ${getTextClass('2xl')}`}> <div className={`text-center py-8 text-muted-foreground ${getTextClass('2xl')}`}>
Start typing to search your notes... Start typing to search your notes...
</div> </div>
)} )}
@@ -1327,9 +1340,9 @@ const Index = () => {
{/* Go To Date Modal */} {/* Go To Date Modal */}
<Dialog open={isGotoOpen} onOpenChange={setIsGotoOpen}> <Dialog open={isGotoOpen} onOpenChange={setIsGotoOpen}>
<DialogContent className="max-w-xl bg-slate-800 border-slate-700"> <DialogContent className="max-w-xl bg-popover border-border">
<DialogHeader> <DialogHeader>
<DialogTitle className={`${getTextClass('4xl')} text-slate-100`}>Go to date</DialogTitle> <DialogTitle className={`${getTextClass('4xl')}`}>Go to date</DialogTitle>
</DialogHeader> </DialogHeader>
<div className="space-y-4"> <div className="space-y-4">
<Input <Input
@@ -1343,7 +1356,7 @@ const Index = () => {
goToClosestNoteToDate(gotoDateInput); goToClosestNoteToDate(gotoDateInput);
} }
}} }}
className={`${getTextClass('2xl')} bg-slate-700 border-slate-600 text-slate-200`} className={`${getTextClass('2xl')} bg-secondary border-input text-secondary-foreground`}
/> />
<div className="flex justify-end gap-2"> <div className="flex justify-end gap-2">
<Button <Button
@@ -1366,14 +1379,14 @@ const Index = () => {
{/* Cleanup Modal */} {/* Cleanup Modal */}
<Dialog open={isCleanupOpen} onOpenChange={setIsCleanupOpen}> <Dialog open={isCleanupOpen} onOpenChange={setIsCleanupOpen}>
<DialogContent className="max-w-6xl max-h-[90vh] overflow-hidden bg-slate-800 border-slate-700"> <DialogContent className="max-w-6xl max-h-[90vh] overflow-hidden bg-popover border-border">
<DialogHeader> <DialogHeader>
<DialogTitle className={`${getTextClass('4xl')} text-slate-100 mb-6`}>Cleanup Notes</DialogTitle> <DialogTitle className={`${getTextClass('4xl')} mb-6`}>Cleanup Notes</DialogTitle>
</DialogHeader> </DialogHeader>
<div className="space-y-8"> <div className="space-y-8">
<div className="space-y-6"> <div className="space-y-6">
<div> <div>
<label className={`${getTextClass('2xl')} font-medium text-slate-200 mb-4 block`}> <label className={`${getTextClass('2xl')} font-medium mb-4 block`}>
Character Repetition Threshold: {(cleanupThreshold[0] * 100).toFixed(0)}% Character Repetition Threshold: {(cleanupThreshold[0] * 100).toFixed(0)}%
</label> </label>
<Slider <Slider
@@ -1384,13 +1397,13 @@ const Index = () => {
step={0.05} step={0.05}
className="w-full h-8" className="w-full h-8"
/> />
<p className={`${getTextClass('xl')} text-slate-400 mt-2`}> <p className={`${getTextClass('xl')} text-muted-foreground mt-2`}>
Notes where a single character makes up more than this percentage will be flagged Notes where a single character makes up more than this percentage will be flagged
</p> </p>
</div> </div>
<div> <div>
<label className={`${getTextClass('2xl')} font-medium text-slate-200 mb-4 block`}> <label className={`${getTextClass('2xl')} font-medium mb-4 block`}>
Minimum Letter Count: {minLetterCount[0]} Minimum Letter Count: {minLetterCount[0]}
</label> </label>
<Slider <Slider
@@ -1401,7 +1414,7 @@ const Index = () => {
step={1} step={1}
className="w-full h-8" className="w-full h-8"
/> />
<p className={`${getTextClass('xl')} text-slate-400 mt-2`}> <p className={`${getTextClass('xl')} text-muted-foreground mt-2`}>
Notes with fewer letters than this will be flagged as potentially accidental Notes with fewer letters than this will be flagged as potentially accidental
</p> </p>
</div> </div>
@@ -1416,17 +1429,17 @@ const Index = () => {
problematicNotes.map((note) => ( problematicNotes.map((note) => (
<div <div
key={note.id} key={note.id}
className="p-6 border border-orange-900 rounded-lg bg-orange-950" className="p-6 border border-destructive/50 rounded-lg bg-destructive/10"
> >
<div className="flex justify-between items-start mb-4"> <div className="flex justify-between items-start mb-4">
<div className={`${getTextClass('2xl')} text-slate-400`}> <div className={`${getTextClass('2xl')} text-muted-foreground`}>
{formatDate(note.epochTime)} {formatDate(note.epochTime)}
</div> </div>
<Badge variant="destructive" className={`${getTextClass('2xl')} px-4 py-2`}> <Badge variant="destructive" className={`${getTextClass('2xl')} px-4 py-2`}>
{note.problemReason} {note.problemReason}
</Badge> </Badge>
</div> </div>
<div className={`${getTextClass('2xl')} text-slate-200 mb-4`}> <div className={`${getTextClass('2xl')} mb-4`}>
{note.content.substring(0, 200)} {note.content.substring(0, 200)}
{note.content.length > 200 && '...'} {note.content.length > 200 && '...'}
</div> </div>
@@ -1441,7 +1454,7 @@ const Index = () => {
</div> </div>
)) ))
) : ( ) : (
<div className={`text-center py-12 text-slate-400 ${getTextClass('2xl')}`}> <div className={`text-center py-12 text-muted-foreground ${getTextClass('2xl')}`}>
No problematic notes found with current settings No problematic notes found with current settings
</div> </div>
)} )}

View File

@@ -12,11 +12,11 @@ const NotFound = () => {
}, [location.pathname]); }, [location.pathname]);
return ( return (
<div className="min-h-screen flex items-center justify-center bg-gray-100"> <div className="min-h-screen flex items-center justify-center bg-background text-foreground">
<div className="text-center"> <div className="text-center">
<h1 className="text-4xl font-bold mb-4">404</h1> <h1 className="text-4xl font-bold mb-4">404</h1>
<p className="text-xl text-gray-600 mb-4">Oops! Page not found</p> <p className="text-xl text-muted-foreground mb-4">Oops! Page not found</p>
<a href="/" className="text-blue-500 hover:text-blue-700 underline"> <a href="/" className="text-primary underline">
Return to Home Return to Home
</a> </a>
</div> </div>