feat: add React frontend with search and mod pages

Add apps/web (Vite + React Router + Tailwind v4 + Zustand) with a search page and a mod page that analyzes saves via @tts/extract. Document the frontend in the README and docs.
This commit is contained in:
2026-08-08 11:22:09 +08:00
parent b32cb53c67
commit 7c812a109c
21 changed files with 1067 additions and 30 deletions
+57
View File
@@ -0,0 +1,57 @@
import { Link } from 'react-router-dom';
import { useSearchStore } from '../stores/searchStore';
export default function SearchResults() {
const { items, page, hasMore, loading, search } = useSearchStore();
if (loading) return null;
if (items.length === 0) {
return <p className="text-sm text-zinc-500">No results yet.</p>;
}
return (
<div className="space-y-4">
<ul className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
{items.map((item) => (
<li key={item.id}>
<Link
to={`/mod/${item.id}`}
className="block overflow-hidden rounded-lg border border-zinc-800 bg-zinc-900 hover:border-zinc-600"
>
{item.previewImageUrl && (
<img
src={item.previewImageUrl}
alt={item.title}
className="h-40 w-full object-cover"
loading="lazy"
/>
)}
<div className="p-3">
<h3 className="truncate font-medium">{item.title}</h3>
<p className="truncate text-sm text-zinc-400">{item.author}</p>
</div>
</Link>
</li>
))}
</ul>
<div className="flex items-center justify-between text-sm">
<button
onClick={() => search(useSearchStore.getState().query, page - 1)}
disabled={page <= 1 || loading}
className="rounded-lg border border-zinc-700 px-3 py-1.5 hover:bg-zinc-800 disabled:opacity-40"
>
Previous
</button>
<span className="text-zinc-400">Page {page}</span>
<button
onClick={() => search(useSearchStore.getState().query, page + 1)}
disabled={!hasMore || loading}
className="rounded-lg border border-zinc-700 px-3 py-1.5 hover:bg-zinc-800 disabled:opacity-40"
>
Next
</button>
</div>
</div>
);
}