124 lines
3.9 KiB
TypeScript
124 lines
3.9 KiB
TypeScript
import Link from "next/link";
|
|
import { Metadata } from "next";
|
|
import { Card, CardHeader, CardTitle } from "@/components/ui/card";
|
|
import { Button } from "@/components/ui/button";
|
|
import {
|
|
Gauge,
|
|
Ruler,
|
|
Timer,
|
|
Focus,
|
|
MoveRight,
|
|
ChevronRight,
|
|
} from "lucide-react";
|
|
|
|
export const metadata: Metadata = {
|
|
title: "Laser Toolkit",
|
|
description: "Quick utilities for scaling settings and converting resolution units.",
|
|
};
|
|
|
|
type Tool = {
|
|
slug: string;
|
|
title: string;
|
|
description: string;
|
|
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
|
|
};
|
|
|
|
const TOOLS: Tool[] = [
|
|
{
|
|
slug: "power-lens-scaler",
|
|
title: "Power & Lens Scaler",
|
|
description: "Scale speed, power, and frequency when wattage or lens field size changes.",
|
|
icon: Gauge,
|
|
},
|
|
{
|
|
slug: "dpi-lpi-dpcm",
|
|
title: "DPI ▸ LPI ▸ DPCM",
|
|
description: "Convert between DPI, LPI, and DPCM. Bidirectional. Assumes LPI≈DPI for raster rows (common workflow).",
|
|
icon: Ruler,
|
|
},
|
|
|
|
// NEW
|
|
{
|
|
slug: "pulse-overlap",
|
|
title: "Pulse Overlap",
|
|
description:
|
|
"Given speed (mm/s), frequency (kHz) and spot size (µm), compute pulse spacing, overlap %, and pulses/mm.",
|
|
icon: MoveRight,
|
|
},
|
|
{
|
|
slug: "hatch-overlap",
|
|
title: "Hatch Overlap",
|
|
description:
|
|
"Given spot size (µm) and hatch gap (µm) or LPI, compute hatch overlap %. Great for vector fills.",
|
|
icon: Ruler,
|
|
},
|
|
{
|
|
slug: "job-time-estimator",
|
|
title: "Job Time Estimator",
|
|
description:
|
|
"Quick estimate for raster or vector jobs. Uses dimensions, DPI/LPI or path length, speed, passes, and a small overhead factor.",
|
|
icon: Timer,
|
|
},
|
|
{
|
|
slug: "beam-spot-size",
|
|
title: "Beam Spot Size",
|
|
description:
|
|
"Approximate diffraction-limited spot size from wavelength, focal length, beam diameter, and M².",
|
|
icon: Focus,
|
|
},
|
|
];
|
|
|
|
export default function ToolkitSplash() {
|
|
return (
|
|
<div className="mx-auto w-full max-w-6xl px-4 py-8">
|
|
{/* Header */}
|
|
<div className="mb-6 flex items-start justify-between gap-4">
|
|
<div>
|
|
<h1 className="text-2xl font-semibold tracking-tight">Laser Toolkit</h1>
|
|
<p className="mt-1 text-sm text-muted-foreground">
|
|
Handy calculators and converters for daily laser work —{" "}
|
|
<span className="italic">hover for details</span>.
|
|
</p>
|
|
</div>
|
|
|
|
<Button asChild variant="outline">
|
|
<Link href="/">Back to Main Menu</Link>
|
|
</Button>
|
|
</div>
|
|
|
|
{/* Grid of tools */}
|
|
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
{TOOLS.map((tool) => (
|
|
<Link key={tool.slug} href={`/laser-toolkit/${tool.slug}`} className="group">
|
|
<Card className="relative overflow-hidden transition-shadow hover:shadow-md">
|
|
<CardHeader className="p-4">
|
|
<div className="flex items-start gap-3">
|
|
<div className="rounded-xl border bg-card p-2">
|
|
<tool.icon className="h-5 w-5" />
|
|
</div>
|
|
<div className="flex-1 min-w-0">
|
|
<CardTitle className="text-base">{tool.title}</CardTitle>
|
|
|
|
{/* Full description on hover (no truncation) */}
|
|
<p
|
|
className="
|
|
max-h-0 overflow-hidden text-xs text-muted-foreground opacity-0
|
|
transition-all duration-200
|
|
group-hover:max-h-96 group-hover:opacity-100
|
|
mt-1 whitespace-pre-wrap
|
|
"
|
|
>
|
|
{tool.description}
|
|
</p>
|
|
</div>
|
|
<ChevronRight className="mt-1 h-4 w-4 text-muted-foreground transition-transform group-hover:translate-x-0.5" />
|
|
</div>
|
|
</CardHeader>
|
|
</Card>
|
|
</Link>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|