1+ "use client" ;
2+
3+ import { useState , useEffect } from "react" ;
4+ import { Navbar } from "@/components/Navbar" ;
5+ import { Button } from "@/components/ui/button" ;
6+ import { Card , CardContent , CardDescription , CardHeader , CardTitle } from "@/components/ui/card" ;
7+ import { Badge } from "@/components/ui/badge" ;
8+ import { Progress } from "@/components/ui/progress" ;
9+ import {
10+ Type ,
11+ FileText ,
12+ Hash ,
13+ AlignLeft ,
14+ Space ,
15+ Copy ,
16+ Trash2 ,
17+ Download ,
18+ BarChart3 ,
19+ Sparkles ,
20+ Clock ,
21+ Book
22+ } from "lucide-react" ;
23+
24+ interface TextStats {
25+ characters : number ;
26+ charactersNoSpaces : number ;
27+ words : number ;
28+ sentences : number ;
29+ paragraphs : number ;
30+ readingTime : number ;
31+ }
32+
33+ export default function CharacterCounterPage ( ) {
34+ const [ text , setText ] = useState ( '' ) ;
35+ const [ stats , setStats ] = useState < TextStats > ( {
36+ characters : 0 ,
37+ charactersNoSpaces : 0 ,
38+ words : 0 ,
39+ sentences : 0 ,
40+ paragraphs : 0 ,
41+ readingTime : 0
42+ } ) ;
43+
44+ useEffect ( ( ) => {
45+ // Calculate stats whenever text changes
46+ const characters = text . length ;
47+ const charactersNoSpaces = text . replace ( / \s / g, '' ) . length ;
48+ const words = text . trim ( ) ? text . trim ( ) . split ( / \s + / ) . length : 0 ;
49+ const sentences = text . split ( / [ . ! ? ] + / ) . filter ( s => s . trim ( ) ) . length ;
50+ const paragraphs = text . split ( / \n \n + / ) . filter ( p => p . trim ( ) ) . length ;
51+ const readingTime = Math . ceil ( words / 200 ) ; // Average reading speed
52+
53+ setStats ( {
54+ characters,
55+ charactersNoSpaces,
56+ words,
57+ sentences,
58+ paragraphs,
59+ readingTime
60+ } ) ;
61+ } , [ text ] ) ;
62+
63+ const clearText = ( ) => setText ( '' ) ;
64+
65+ const copyText = ( ) => {
66+ navigator . clipboard . writeText ( text ) ;
67+ } ;
68+
69+ const maxCharacters = 5000 ; // For progress bar demonstration
70+ const characterProgress = ( stats . characters / maxCharacters ) * 100 ;
71+
72+ return (
73+ < >
74+ < Navbar />
75+ < main className = "min-h-screen bg-background" >
76+ { /* Hero Section */ }
77+ < section className = "relative overflow-hidden border-b" >
78+ < div className = "absolute inset-0" >
79+ < div className = "absolute inset-0 bg-gradient-to-br from-blue-50 via-transparent to-purple-50 dark:from-blue-950/20 dark:to-purple-950/20" />
80+ < div className = "absolute right-0 top-0 h-72 w-72 bg-blue-400/10 blur-3xl" />
81+ < div className = "absolute bottom-0 left-0 h-72 w-72 bg-purple-400/10 blur-3xl" />
82+ </ div >
83+
84+ < div className = "container relative py-12" >
85+ < div className = "mx-auto max-w-4xl text-center" >
86+ < div className = "mb-6 inline-flex h-20 w-20 items-center justify-center rounded-3xl bg-gradient-to-br from-blue-500 to-purple-600 shadow-2xl" >
87+ < Type className = "h-10 w-10 text-white" />
88+ </ div >
89+
90+ < Badge className = "mb-4" variant = "secondary" >
91+ < Sparkles className = "mr-1 h-3 w-3" />
92+ Real-time Analysis
93+ </ Badge >
94+
95+ < h1 className = "mb-4 text-4xl font-bold tracking-tight sm:text-5xl" >
96+ Character Counter
97+ </ h1 >
98+ < p className = "mx-auto max-w-2xl text-lg text-muted-foreground" >
99+ Count characters, words, sentences, and more. Get instant insights
100+ about your text with our powerful analysis tool.
101+ </ p >
102+ </ div >
103+ </ div >
104+ </ section >
105+
106+ < div className = "container py-8" >
107+ < div className = "mx-auto max-w-6xl" >
108+ { /* Main Content */ }
109+ < div className = "grid gap-8 lg:grid-cols-3" >
110+ { /* Text Input Section */ }
111+ < div className = "lg:col-span-2" >
112+ < Card className = "h-full" >
113+ < CardHeader >
114+ < div className = "flex items-center justify-between" >
115+ < div >
116+ < CardTitle > Enter Your Text</ CardTitle >
117+ < CardDescription >
118+ Type or paste your text below for instant analysis
119+ </ CardDescription >
120+ </ div >
121+ < div className = "flex gap-2" >
122+ < Button
123+ variant = "ghost"
124+ size = "icon"
125+ onClick = { copyText }
126+ disabled = { ! text }
127+ >
128+ < Copy className = "h-4 w-4" />
129+ </ Button >
130+ < Button
131+ variant = "ghost"
132+ size = "icon"
133+ onClick = { clearText }
134+ disabled = { ! text }
135+ >
136+ < Trash2 className = "h-4 w-4" />
137+ </ Button >
138+ </ div >
139+ </ div >
140+ </ CardHeader >
141+ < CardContent >
142+ < div className = "space-y-4" >
143+ < textarea
144+ className = "min-h-[400px] w-full rounded-lg border bg-background p-4 text-base focus:outline-none focus:ring-2 focus:ring-primary"
145+ placeholder = "Start typing or paste your text here..."
146+ value = { text }
147+ onChange = { ( e ) => setText ( e . target . value ) }
148+ />
149+
150+ { /* Character Progress Bar */ }
151+ < div className = "space-y-2" >
152+ < div className = "flex items-center justify-between text-sm" >
153+ < span className = "text-muted-foreground" > Character Limit</ span >
154+ < span className = { stats . characters > maxCharacters ? "text-red-500" : "text-muted-foreground" } >
155+ { stats . characters . toLocaleString ( ) } / { maxCharacters . toLocaleString ( ) }
156+ </ span >
157+ </ div >
158+ < Progress value = { characterProgress > 100 ? 100 : characterProgress } className = "h-2" />
159+ </ div >
160+ </ div >
161+ </ CardContent >
162+ </ Card >
163+ </ div >
164+
165+ { /* Stats Section */ }
166+ < div className = "space-y-6" >
167+ { /* Primary Stats */ }
168+ < Card className = "overflow-hidden" >
169+ < div className = "bg-gradient-to-br from-primary/10 to-primary/5 p-6" >
170+ < div className = "flex items-center justify-between" >
171+ < div >
172+ < p className = "text-sm font-medium text-muted-foreground" > Total Characters</ p >
173+ < p className = "text-3xl font-bold" > { stats . characters . toLocaleString ( ) } </ p >
174+ </ div >
175+ < div className = "flex h-12 w-12 items-center justify-center rounded-full bg-primary/10" >
176+ < Hash className = "h-6 w-6 text-primary" />
177+ </ div >
178+ </ div >
179+ </ div >
180+ </ Card >
181+
182+ { /* Secondary Stats Grid */ }
183+ < div className = "grid gap-4" >
184+ < Card >
185+ < CardContent className = "flex items-center justify-between p-6" >
186+ < div >
187+ < p className = "text-sm text-muted-foreground" > Characters (no spaces)</ p >
188+ < p className = "text-2xl font-semibold" > { stats . charactersNoSpaces . toLocaleString ( ) } </ p >
189+ </ div >
190+ < Space className = "h-5 w-5 text-muted-foreground" />
191+ </ CardContent >
192+ </ Card >
193+
194+ < Card >
195+ < CardContent className = "flex items-center justify-between p-6" >
196+ < div >
197+ < p className = "text-sm text-muted-foreground" > Words</ p >
198+ < p className = "text-2xl font-semibold" > { stats . words . toLocaleString ( ) } </ p >
199+ </ div >
200+ < FileText className = "h-5 w-5 text-muted-foreground" />
201+ </ CardContent >
202+ </ Card >
203+
204+ < Card >
205+ < CardContent className = "flex items-center justify-between p-6" >
206+ < div >
207+ < p className = "text-sm text-muted-foreground" > Sentences</ p >
208+ < p className = "text-2xl font-semibold" > { stats . sentences } </ p >
209+ </ div >
210+ < AlignLeft className = "h-5 w-5 text-muted-foreground" />
211+ </ CardContent >
212+ </ Card >
213+
214+ < Card >
215+ < CardContent className = "flex items-center justify-between p-6" >
216+ < div >
217+ < p className = "text-sm text-muted-foreground" > Paragraphs</ p >
218+ < p className = "text-2xl font-semibold" > { stats . paragraphs } </ p >
219+ </ div >
220+ < Book className = "h-5 w-5 text-muted-foreground" />
221+ </ CardContent >
222+ </ Card >
223+
224+ < Card >
225+ < CardContent className = "flex items-center justify-between p-6" >
226+ < div >
227+ < p className = "text-sm text-muted-foreground" > Reading Time</ p >
228+ < p className = "text-2xl font-semibold" > { stats . readingTime } min</ p >
229+ </ div >
230+ < Clock className = "h-5 w-5 text-muted-foreground" />
231+ </ CardContent >
232+ </ Card >
233+ </ div >
234+
235+ { /* Export Button */ }
236+ < Button className = "w-full" variant = "outline" >
237+ < Download className = "mr-2 h-4 w-4" />
238+ Export Stats
239+ </ Button >
240+ </ div >
241+ </ div >
242+
243+ { /* Features Section */ }
244+ < div className = "mt-12 grid gap-6 md:grid-cols-3" >
245+ < Card >
246+ < CardHeader >
247+ < div className = "mb-4 flex h-12 w-12 items-center justify-center rounded-lg bg-blue-500/10" >
248+ < BarChart3 className = "h-6 w-6 text-blue-500" />
249+ </ div >
250+ < CardTitle > Real-time Analysis</ CardTitle >
251+ </ CardHeader >
252+ < CardContent >
253+ < p className = "text-sm text-muted-foreground" >
254+ Get instant feedback as you type. Watch your stats update in real-time
255+ with every character.
256+ </ p >
257+ </ CardContent >
258+ </ Card >
259+
260+ < Card >
261+ < CardHeader >
262+ < div className = "mb-4 flex h-12 w-12 items-center justify-center rounded-lg bg-green-500/10" >
263+ < FileText className = "h-6 w-6 text-green-500" />
264+ </ div >
265+ < CardTitle > Comprehensive Stats</ CardTitle >
266+ </ CardHeader >
267+ < CardContent >
268+ < p className = "text-sm text-muted-foreground" >
269+ Track characters, words, sentences, paragraphs, and estimated reading
270+ time all in one place.
271+ </ p >
272+ </ CardContent >
273+ </ Card >
274+
275+ < Card >
276+ < CardHeader >
277+ < div className = "mb-4 flex h-12 w-12 items-center justify-center rounded-lg bg-purple-500/10" >
278+ < Sparkles className = "h-6 w-6 text-purple-500" />
279+ </ div >
280+ < CardTitle > Smart Features</ CardTitle >
281+ </ CardHeader >
282+ < CardContent >
283+ < p className = "text-sm text-muted-foreground" >
284+ Visual progress indicators, one-click copy and clear, and export
285+ functionality for your convenience.
286+ </ p >
287+ </ CardContent >
288+ </ Card >
289+ </ div >
290+ </ div >
291+ </ div >
292+ </ main >
293+ </ >
294+ ) ;
295+ }
0 commit comments