Skip to content

Commit c7c65b2

Browse files
mvp
1 parent 340ce58 commit c7c65b2

15 files changed

Lines changed: 5649 additions & 154 deletions

File tree

.claude/settings.local.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"permissions": {
3+
"allow": [
4+
"WebFetch(domain:tinywow.com)"
5+
],
6+
"deny": []
7+
}
8+
}

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "serp-blocks"]
2+
path = serp-blocks
3+
url = https://github.com/devinschumacher/serp-blocks.git

app/layout.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ const inter = Inter({
88
});
99

1010
export const metadata: Metadata = {
11-
title: "Serp Apps",
12-
description: "Collection of useful applications",
11+
title: "SerpTools - Free Online Tools for Everyday Tasks",
12+
description: "A collection of free, easy-to-use online tools including CSV combiner, JSON converter, character counter, and more. No signup required.",
1313
};
1414

1515
export default function RootLayout({

app/page.tsx

Lines changed: 227 additions & 152 deletions
Large diffs are not rendered by default.
Lines changed: 295 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
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

Comments
 (0)