);
}
@@ -218,6 +234,14 @@ function DownloadRow({ job, children }: { job: DownloadJob; children?: React.Rea
{job.asset?.duration_s ? (
· {formatDuration(job.asset.duration_s)}
) : null}
+ {job.status === "done" && job.created_at ? (
+
+ · {relativeTime(job.created_at)}
+
+ ) : null}
{job.error ? · {job.error} : null}
{job.source_url && }
@@ -655,7 +679,9 @@ export default function DownloadCenter() {
const { t } = useTranslation();
const qc = useQueryClient();
const confirm = useConfirm();
- const [tab, setTab] = usePersistedTab("siftlode.downloadTab", "queue");
+ // Always land on the queue tab (not a persisted last-tab) — the queue is where new work and
+ // any failures are, so it's the useful default every visit.
+ const [tab, setTab] = useState("queue");
const [addUrl, setAddUrl] = useState("");
const [addSource, setAddSource] = useState(null);
const [manage, setManage] = useState(false);
@@ -706,14 +732,28 @@ export default function DownloadCenter() {
if (await confirm({ title, message: body, confirmLabel: title, danger: true })) fn();
};
+ // Pull a link off the clipboard into the add-URL box. Clipboard reads need a secure context +
+ // a user gesture/permission (iOS Safari/Brave are strict) — so this is best-effort: the paste
+ // button is an explicit gesture (reliable), and the input's onFocus tries silently (ignored if
+ // the browser refuses without a click). Only accept an http(s) URL, don't clobber existing text.
+ const pasteFromClipboard = async (force = false) => {
+ if (!force && addUrl.trim()) return;
+ try {
+ const text = (await navigator.clipboard?.readText())?.trim();
+ if (text && /^https?:\/\/\S+$/i.test(text)) setAddUrl(text);
+ } catch {
+ /* permission denied or unsupported — the manual paste button still works on a click */
+ }
+ };
+
const tabs = [
{ id: "queue", label: t("downloads.tabs.queue"), badge: queue.length },
{ id: "done", label: t("downloads.tabs.done"), badge: library.length },
{ id: "shared", label: t("downloads.tabs.shared") },
...(me.role === "admin" ? [{ id: "system", label: t("downloads.tabs.system") }] : []),
];
- // A persisted tab can point at one no longer available (e.g. "system" restored for a now-non-admin
- // account) — that would render a blank page with no active tab. Fall back to the default.
+ // Guard against the active tab going away under us (e.g. admin on "system" gets demoted mid-session)
+ // — that would render a blank page with no active tab. Fall back to the default.
useEffect(() => {
if (!tabs.some((tb) => tb.id === tab)) setTab("queue");
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -756,10 +796,19 @@ export default function DownloadCenter() {
setAddUrl(e.target.value)}
+ onFocus={() => pasteFromClipboard()}
onKeyDown={(e) => e.key === "Enter" && addUrl.trim() && setAddSource(addUrl.trim())}
placeholder={t("downloads.page.addUrl")}
className={inputCls}
/>
+