spicebook/frontend/src/components/NotebookCard.tsx
Ryan Malloy 43789bdf24 Homepage redesign: show-don't-tell with animated scope hero
Restructure the homepage to lead with visuals instead of text:

- Hero: split layout with animated Tektronix 465 oscilloscope showing
  RC step response (CSS+SVG, zero JS) that links to the notebook
- Pipeline strip: 3-step Write → Simulate → Visualize with code/terminal
  previews and inline waveform SVG
- Featured notebooks: 3 curated circuits (RC, 555, common emitter) with
  pre-rendered waveform thumbnails
- Gallery cards: decorative graticule header strip, color-coded by engine
- Footer: updated copy with clearer call to action

All new sections are server-rendered Astro components. Total new client
JavaScript: zero bytes.
2026-02-20 15:16:53 -07:00

69 lines
2.0 KiB
TypeScript

import { LayoutGrid } from 'lucide-react';
import { Badge } from './ui/Badge';
import type { NotebookSummary } from '../lib/types';
const engineVariant: Record<string, 'blue' | 'amber'> = {
ngspice: 'blue',
ltspice: 'amber',
};
function formatDate(iso: string): string {
try {
return new Date(iso).toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric',
});
} catch {
return iso;
}
}
interface NotebookCardProps {
notebook: NotebookSummary;
}
export function NotebookCard({ notebook }: NotebookCardProps) {
const { id, title, engine, tags, cell_count, modified } = notebook;
return (
<a
href={`/notebook/${encodeURIComponent(id)}`}
className="group block rounded-lg border border-slate-800 bg-slate-900/50 overflow-hidden hover:border-blue-500/40 hover:bg-slate-800/70 transition-all"
>
<div className="notebook-card-strip" data-engine={engine} />
<div className="p-5">
<div className="flex items-start justify-between mb-2">
<h3 className="text-base font-semibold text-slate-100 group-hover:text-blue-400 transition-colors line-clamp-1">
{title}
</h3>
<Badge variant={engineVariant[engine] ?? 'default'} className="shrink-0 ml-2">
{engine}
</Badge>
</div>
<div className="flex items-center gap-3 text-xs text-slate-500 mb-3">
<span className="inline-flex items-center gap-1">
<LayoutGrid className="w-3 h-3" />
{cell_count} cell{cell_count !== 1 ? 's' : ''}
</span>
<span>{formatDate(modified)}</span>
</div>
{tags.length > 0 && (
<div className="flex flex-wrap gap-1.5">
{tags.map((tag) => (
<span
key={tag}
className="text-xs bg-slate-700/50 text-slate-400 px-2 py-0.5 rounded"
>
{tag}
</span>
))}
</div>
)}
</div>
</a>
);
}