Initial commit

This commit is contained in:
admin
2026-05-26 22:46:00 +02:00
commit 7e2c2ff89c
256 changed files with 51523 additions and 0 deletions
@@ -0,0 +1,132 @@
"use client";
import { useState, useTransition } from "react";
import Link from "next/link";
import { Ruler, Loader2, AlertTriangle, ExternalLink } from "lucide-react";
import { scanUndersizedCovers, type UndersizedCover } from "@/app/actions/maintenance";
import { thumbUrl } from "@/lib/assetUrls";
import { useSettingsPanel } from "./SettingsPanelProvider";
type State =
| { kind: "idle" }
| { kind: "scanning" }
| { kind: "result"; rows: UndersizedCover[] };
function fmtBytes(n: number): string {
if (n < 1024) return `${n} B`;
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
return `${(n / 1024 / 1024).toFixed(2)} MB`;
}
export function UndersizedCoversButton() {
const [state, setState] = useState<State>({ kind: "idle" });
const [pending, start] = useTransition();
const { close: closeSettings } = useSettingsPanel();
const scan = () => {
setState({ kind: "scanning" });
start(async () => {
const rows = await scanUndersizedCovers();
setState({ kind: "result", rows });
});
};
return (
<div className="py-2">
<div className="flex items-start justify-between gap-4">
<div className="min-w-0">
<div className="text-sm font-medium">Find Undersized Covers</div>
<div className="text-xs text-[var(--color-fg-muted)] mt-0.5">
Scan top-level covers smaller than standard JAV size (default
floor is <code className="font-mono">750×500</code>; real covers are
usually <code className="font-mono">800×538</code>). Catches
thumbnails or web previews accidentally imported as covers.
</div>
{state.kind === "result" && state.rows.length === 0 && (
<div className="text-xs text-[var(--color-mint)] mt-2">
No undersized covers all top-level covers meet the size threshold.
</div>
)}
{state.kind === "result" && state.rows.length > 0 && (
<div className="text-xs text-[var(--color-coral)] mt-2 flex items-center gap-1.5">
<AlertTriangle className="w-3.5 h-3.5" />
{state.rows.length} undersized cover{state.rows.length === 1 ? "" : "s"} found.
</div>
)}
</div>
<div className="flex-shrink-0 flex items-center gap-2">
{state.kind === "idle" && (
<button
onClick={scan}
disabled={pending}
className="inline-flex items-center justify-center gap-1.5 min-w-[100px] text-xs px-3 py-1.5 rounded-lg glass glass-hover text-[var(--color-fg-dim)] hover:text-[var(--color-fg)] whitespace-nowrap"
>
<Ruler className="w-3.5 h-3.5" /> Scan
</button>
)}
{state.kind === "scanning" && (
<span className="flex items-center gap-1.5 text-xs text-[var(--color-fg-dim)]">
<Loader2 className="w-3.5 h-3.5 animate-spin" /> Scanning
</span>
)}
{state.kind === "result" && (
<>
<button
onClick={scan}
disabled={pending}
className="inline-flex items-center justify-center gap-1.5 min-w-[100px] text-xs px-3 py-1.5 rounded-lg glass glass-hover text-[var(--color-fg-dim)] hover:text-[var(--color-fg)] whitespace-nowrap"
>
Re-scan
</button>
<button
onClick={() => setState({ kind: "idle" })}
className="inline-flex items-center justify-center min-w-[80px] text-xs px-3 py-1.5 rounded-lg text-[var(--color-fg-muted)] hover:text-[var(--color-fg)] hover:bg-[var(--color-glass)]"
>
Dismiss
</button>
</>
)}
</div>
</div>
{state.kind === "result" && state.rows.length > 0 && (
<div className="mt-3 max-h-72 overflow-y-auto rounded-md border border-[var(--color-glass-border)] bg-[var(--color-bg-1)]/40">
{state.rows.map((r) => (
<Link
key={r.id}
href={`/image/${r.id}`}
onClick={closeSettings}
className="flex items-center gap-3 p-2 border-b border-[var(--color-glass-border)] last:border-b-0 hover:bg-[var(--color-glass)] transition-colors"
>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src={thumbUrl({ thumbPath: r.thumbPath, code: r.code, id: r.id })}
alt=""
className="w-12 h-12 object-contain bg-black/40 rounded shrink-0"
loading="lazy"
/>
<div className="min-w-0 flex-1">
<div className="flex items-center gap-2 text-xs">
{r.code ? (
<span className="font-mono font-bold text-[var(--color-cyan)]">{r.code}</span>
) : (
<span className="font-mono text-[var(--color-fg-muted)] italic">no code</span>
)}
<span className="font-mono text-[var(--color-coral)] tabular-nums">
{r.width}×{r.height}
</span>
<span className="font-mono text-[var(--color-fg-muted)] tabular-nums">
{fmtBytes(r.bytes)}
</span>
</div>
<div className="text-[11px] text-[var(--color-fg-dim)] truncate font-mono mt-0.5">
{r.filename}
</div>
</div>
<ExternalLink className="w-3.5 h-3.5 text-[var(--color-fg-muted)] shrink-0" />
</Link>
))}
</div>
)}
</div>
);
}