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 Index from "./pages/Index";
import NotFound from "./pages/NotFound";
import { ThemeProvider } from "@/components/theme-provider";
const queryClient = new QueryClient();
const App = () => (
<QueryClientProvider client={queryClient}>
<TooltipProvider>
<Toaster />
<Sonner />
<BrowserRouter>
<Routes>
<Route path="/" element={<Index />} />
{/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */}
<Route path="*" element={<NotFound />} />
</Routes>
</BrowserRouter>
</TooltipProvider>
</QueryClientProvider>
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
<QueryClientProvider client={queryClient}>
<TooltipProvider>
<Toaster />
<Sonner />
<BrowserRouter>
<Routes>
<Route path="/" element={<Index />} />
{/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */}
<Route path="*" element={<NotFound />} />
</Routes>
</BrowserRouter>
</TooltipProvider>
</QueryClientProvider>
</ThemeProvider>
);
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 { Trash2 } from 'lucide-react';
import { Trash2, Moon, Sun } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Textarea } from '@/components/ui/textarea';
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 { toast } from '@/hooks/use-toast';
import { Window } from '@tauri-apps/api/window';
import { Switch } from '@/components/ui/switch';
import { useTheme } from 'next-themes';
interface Note {
id: string;
@@ -71,6 +73,8 @@ const Index = () => {
const [gotoDateInput, setGotoDateInput] = useState('');
const [cacheMode, setCacheMode] = useState<'global' | 'scoped'>('global');
const { resolvedTheme, setTheme } = useTheme();
const previousNoteRef = useRef<HTMLDivElement>(null);
const currentNoteRef = useRef<HTMLTextAreaElement>(null);
const scratchRef = useRef<HTMLTextAreaElement>(null);
@@ -1185,12 +1189,21 @@ const Index = () => {
};
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 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">
<h1 className={`${getTextClass('5xl')} font-bold text-slate-100`}>Journal</h1>
<div className="flex gap-2 items-center">
<h1 className={`${getTextClass('5xl')} font-bold`}>Journal</h1>
<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}>
<SelectTrigger className="w-32">
<SelectValue placeholder="Font Size" />
@@ -1217,15 +1230,15 @@ const Index = () => {
{/* Previous Note - Top Half */}
<div
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})`}
<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>
{previousNote ? (
<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)}
</div>
<Textarea
@@ -1239,20 +1252,20 @@ const Index = () => {
}
}}
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..."
/>
</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'}
</div>
)}
</div>
{/* 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={`${getTextClass('2xl')} text-slate-400 mb-3`}>Current Entry</div>
<div className="flex-1 bg-card rounded-lg border border-border shadow-sm p-6 overflow-hidden">
<div className={`${getTextClass('2xl')} text-muted-foreground mb-3`}>Current Entry</div>
<Textarea
ref={currentNoteRef}
value={currentNote}
@@ -1260,20 +1273,20 @@ const Index = () => {
setCurrentNote(e.target.value);
}}
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..."
/>
</div>
</div>
{/* 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={`${getTextClass('2xl')} text-slate-400 mb-3`}>Scratch Pad</div>
<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-muted-foreground mb-3`}>Scratch Pad</div>
<Textarea
ref={scratchRef}
value={scratchPad}
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..."
/>
</div>
@@ -1281,9 +1294,9 @@ const Index = () => {
{/* Search Modal */}
<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>
<DialogTitle className={`${getTextClass('4xl')} text-slate-100`}>Search Notes</DialogTitle>
<DialogTitle className={`${getTextClass('4xl')}`}>Search Notes</DialogTitle>
</DialogHeader>
<div className="space-y-4">
<Input
@@ -1292,31 +1305,31 @@ const Index = () => {
placeholder="Search your notes..."
value={searchQuery}
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">
{searchResults.length > 0 ? (
searchResults.map((note) => (
<div
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)}
>
<div className={`${getTextClass('xl')} text-slate-400 mb-2`}>
<div className={`${getTextClass('xl')} text-muted-foreground mb-2`}>
{formatDate(note.epochTime)}
</div>
<div
className={`${getTextClass('2xl')} text-slate-200 whitespace-pre-wrap`}
className={`${getTextClass('2xl')} whitespace-pre-wrap`}
dangerouslySetInnerHTML={{ __html: note.snippet || note.content }}
/>
</div>
))
) : 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}"
</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...
</div>
)}
@@ -1327,9 +1340,9 @@ const Index = () => {
{/* Go To Date Modal */}
<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>
<DialogTitle className={`${getTextClass('4xl')} text-slate-100`}>Go to date</DialogTitle>
<DialogTitle className={`${getTextClass('4xl')}`}>Go to date</DialogTitle>
</DialogHeader>
<div className="space-y-4">
<Input
@@ -1343,7 +1356,7 @@ const Index = () => {
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">
<Button
@@ -1366,14 +1379,14 @@ const Index = () => {
{/* Cleanup Modal */}
<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>
<DialogTitle className={`${getTextClass('4xl')} text-slate-100 mb-6`}>Cleanup Notes</DialogTitle>
<DialogTitle className={`${getTextClass('4xl')} mb-6`}>Cleanup Notes</DialogTitle>
</DialogHeader>
<div className="space-y-8">
<div className="space-y-6">
<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)}%
</label>
<Slider
@@ -1384,13 +1397,13 @@ const Index = () => {
step={0.05}
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
</p>
</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]}
</label>
<Slider
@@ -1401,7 +1414,7 @@ const Index = () => {
step={1}
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
</p>
</div>
@@ -1416,17 +1429,17 @@ const Index = () => {
problematicNotes.map((note) => (
<div
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={`${getTextClass('2xl')} text-slate-400`}>
<div className={`${getTextClass('2xl')} text-muted-foreground`}>
{formatDate(note.epochTime)}
</div>
<Badge variant="destructive" className={`${getTextClass('2xl')} px-4 py-2`}>
{note.problemReason}
</Badge>
</div>
<div className={`${getTextClass('2xl')} text-slate-200 mb-4`}>
<div className={`${getTextClass('2xl')} mb-4`}>
{note.content.substring(0, 200)}
{note.content.length > 200 && '...'}
</div>
@@ -1441,7 +1454,7 @@ const Index = () => {
</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
</div>
)}

View File

@@ -12,11 +12,11 @@ const NotFound = () => {
}, [location.pathname]);
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">
<h1 className="text-4xl font-bold mb-4">404</h1>
<p className="text-xl text-gray-600 mb-4">Oops! Page not found</p>
<a href="/" className="text-blue-500 hover:text-blue-700 underline">
<p className="text-xl text-muted-foreground mb-4">Oops! Page not found</p>
<a href="/" className="text-primary underline">
Return to Home
</a>
</div>