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:
@@ -5,10 +5,12 @@ 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 = () => (
|
||||
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<TooltipProvider>
|
||||
<Toaster />
|
||||
@@ -22,6 +24,7 @@ const App = () => (
|
||||
</BrowserRouter>
|
||||
</TooltipProvider>
|
||||
</QueryClientProvider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
|
||||
export default App;
|
||||
|
8
src/components/theme-provider.tsx
Normal file
8
src/components/theme-provider.tsx
Normal 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>;
|
||||
}
|
@@ -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>
|
||||
)}
|
||||
|
@@ -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>
|
||||
|
Reference in New Issue
Block a user