first commit
Some checks failed
Build and Push Docker Image / build (push) Has been cancelled
Sync to CNB / sync (push) Has been cancelled
Delete old workflow runs / del_runs (push) Has been cancelled
Upstream Sync / Sync latest commits from upstream repo (push) Has been cancelled

This commit is contained in:
2026-01-30 16:57:44 +08:00
commit 3d175d75af
119 changed files with 35834 additions and 0 deletions

115
app/stopwatch/page.tsx Normal file
View File

@@ -0,0 +1,115 @@
"use client";
import { useState, useEffect, useRef } from "react";
import { Watch, Play, Pause, RotateCcw, Timer as TimerIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
export default function StopwatchPage() {
const [time, setTime] = useState(0);
const [running, setRunning] = useState(false);
const [laps, setLaps] = useState<number[]>([]);
const timerRef = useRef<any>(null);
useEffect(() => {
if (running) {
timerRef.current = setInterval(() => {
setTime((prev) => prev + 10);
}, 10);
} else {
clearInterval(timerRef.current);
}
return () => clearInterval(timerRef.current);
}, [running]);
const formatTime = (ms: number) => {
const minutes = Math.floor(ms / 60000);
const seconds = Math.floor((ms % 60000) / 1000);
const milliseconds = Math.floor((ms % 1000) / 10);
return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}.${milliseconds.toString().padStart(2, '0')}`;
};
const handleLap = () => {
setLaps([time, ...laps]);
};
const handleReset = () => {
setRunning(false);
setTime(0);
setLaps([]);
};
return (
<div className="space-y-6 animate-in fade-in slide-in-from-bottom-4 duration-500">
<div className="flex items-center space-x-4 border-b pb-4">
<div className="flex h-12 w-12 items-center justify-center rounded-xl bg-linear-to-br from-blue-500 to-indigo-600 shadow-lg">
<Watch className="h-6 w-6 text-white" />
</div>
<div>
<h1 className="text-2xl font-bold tracking-tight"></h1>
<p className="text-muted-foreground"></p>
</div>
</div>
<div className="grid gap-6 lg:grid-cols-2">
<Card className="flex flex-col items-center justify-center py-12 space-y-8 h-fit">
<div className="text-7xl md:text-8xl font-mono font-bold tracking-tighter tabular-nums text-primary">
{formatTime(time)}
</div>
<div className="flex gap-4">
<Button
size="lg"
variant={running ? "outline" : "default"}
className="w-32 h-14 text-lg rounded-full"
onClick={() => setRunning(!running)}
>
{running ? (
<><Pause className="mr-2 h-5 w-5" /> </>
) : (
<><Play className="mr-2 h-5 w-5" /> </>
)}
</Button>
<Button
size="lg"
variant="secondary"
className="w-32 h-14 text-lg rounded-full"
onClick={running ? handleLap : handleReset}
disabled={time === 0}
>
{running ? (
<><TimerIcon className="mr-2 h-5 w-5" /> </>
) : (
<><RotateCcw className="mr-2 h-5 w-5" /> </>
)}
</Button>
</div>
</Card>
<Card className="h-[400px] flex flex-col">
<CardHeader>
<CardTitle className="text-base flex items-center justify-between">
<span className="text-xs font-normal text-muted-foreground"> {laps.length} </span>
</CardTitle>
</CardHeader>
<CardContent className="flex-1 overflow-y-auto space-y-2 pr-2">
{laps.length === 0 ? (
<div className="h-full flex items-center justify-center text-muted-foreground italic">
</div>
) : (
laps.map((lap, i) => (
<div key={i} className="flex items-center justify-between p-3 rounded-lg bg-muted/30 border border-muted/50 animate-in slide-in-from-top-2 duration-300">
<span className="text-xs font-bold text-muted-foreground">LAP {laps.length - i}</span>
<span className="font-mono font-bold">{formatTime(lap)}</span>
</div>
))
)}
</CardContent>
</Card>
</div>
</div>
);
}