Spaces:
Sleeping
Sleeping
| import { useEffect, useState } from "react"; | |
| import { listHistory, type HistoryRecord } from "@/lib/idb"; | |
| type Props = { | |
| refreshKey?: number; | |
| onRegenerate: (h: HistoryRecord) => void; | |
| onReuseSeed?: (seed: number) => void; | |
| }; | |
| function fmtTime(ts: number): string { | |
| return new Date(ts).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }); | |
| } | |
| export default function HistoryList({ refreshKey, onRegenerate, onReuseSeed }: Props) { | |
| const [items, setItems] = useState<HistoryRecord[]>([]); | |
| useEffect(() => { | |
| listHistory().then(setItems); | |
| }, [refreshKey]); | |
| if (items.length === 0) { | |
| return ( | |
| <p className="text-sm text-muted-foreground italic"> | |
| Generations will be archived here. | |
| </p> | |
| ); | |
| } | |
| return ( | |
| <ul className="space-y-3"> | |
| {items.map((h, i) => { | |
| const url = URL.createObjectURL(h.audioBlob); | |
| const kindLabel = | |
| h.kind === "dialog" | |
| ? `dialog · ${(h.speakers ?? []).length} spk · ${h.modelId.replace("chatterbox-", "")}` | |
| : `${h.modelId.replace("chatterbox-", "")} · ${h.language ?? "—"}`; | |
| return ( | |
| <li key={h.id} className="card-paper p-3 space-y-2.5"> | |
| <div className="flex items-baseline justify-between gap-3"> | |
| <span className="marker-num"> | |
| {String(items.length - i).padStart(2, "0")} | |
| </span> | |
| <span className="label-mono">{kindLabel} · {fmtTime(h.createdAt)}</span> | |
| </div> | |
| <p className="text-[13px] leading-snug line-clamp-3">{h.text}</p> | |
| <audio controls src={url} className="w-full h-9" /> | |
| <div className="flex items-center justify-between"> | |
| {h.seedUsed != null ? ( | |
| <button | |
| type="button" | |
| onClick={() => onReuseSeed?.(h.seedUsed!)} | |
| className="label-mono hover:text-[hsl(var(--ember))] transition-colors" | |
| title="Copy this seed into the active params" | |
| > | |
| seed {h.seedUsed} · ↻ | |
| </button> | |
| ) : ( | |
| <span className="label-mono text-muted-foreground/60">no seed</span> | |
| )} | |
| <div className="flex gap-3"> | |
| <a | |
| href={url} | |
| download={`${h.id}.wav`} | |
| className="label-mono hover:text-foreground transition-colors" | |
| > | |
| ↓ download | |
| </a> | |
| <button | |
| type="button" | |
| className="label-mono hover:text-[hsl(var(--ember))] transition-colors" | |
| onClick={() => onRegenerate(h)} | |
| > | |
| ↻ regenerate | |
| </button> | |
| </div> | |
| </div> | |
| </li> | |
| ); | |
| })} | |
| </ul> | |
| ); | |
| } | |