import { motion } from "framer-motion"; import { useCallback, useEffect, useState } from "react"; import { apiGet, apiPost, apiUrl, type Status } from "../api"; import { useNfcWs } from "../NfcWsContext"; import { useToast } from "../toast"; export default function Capture() { const toast = useToast(); const { log } = useNfcWs(); const [st, setSt] = useState(null); const refresh = useCallback(() => { apiGet("/api/status") .then(setSt) .catch(() => {}); }, []); useEffect(() => { refresh(); const id = setInterval(refresh, 2000); return () => clearInterval(id); }, [refresh]); useEffect(() => { refresh(); }, [log.length, refresh]); const lastEv = (() => { const c = [...log].reverse().find((e) => e.channel === "capture"); if (!c) { return ""; } return typeof c.payload === "object" ? JSON.stringify(c.payload) : String(c.payload); })(); const sess = st?.session; const pct = sess ? Math.min(100, (sess.usedBytes / Math.max(1, sess.maxBytes)) * 100) : 0; const setDeep = async (enable: boolean) => { try { await apiPost("/api/session/deep", { enable }); toast( enable ? "Passive read-all on (live scan on by default at boot)" : "Passive read-all off", ); refresh(); } catch (e) { toast(String(e), "err"); } }; const clearBuf = async () => { try { await apiPost("/api/session/clear", {}); toast("Buffer cleared — scanning can resume"); refresh(); } catch (e) { toast(String(e), "err"); } }; const download = async () => { try { const r = await fetch(apiUrl("/api/session/export")); if (!r.ok) { throw new Error(await r.text()); } const blob = await r.blob(); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = `pn532-deep-capture-${Date.now()}.ndjson`; a.click(); URL.revokeObjectURL(url); toast("Download started — check your Downloads folder"); } catch (e) { toast(String(e), "err"); } }; return (

Passive read-all mode

Same feature as “deep capture” — fully controlled from this screen.

Turn it on below and keep live scan running (on by default at boot). Each{" "} new tag in the field is read passively: no per-block clicks — the firmware pulls all data it can (PN532 status + Classic sector/block dump with default keys, or Ultralight/NTAG page sweep). Results queue in device RAM; when full, polling pauses until you download and clear.

Read-all session buffer

{sess?.lines ?? 0} full dumps · {sess?.usedBytes ?? 0} / {sess?.maxBytes ?? "—"} bytes RAM

{sess?.full && (

Buffer full — reader paused

Tap Download NDJSON to pull every card profile to your phone, then{" "} Clear buffer to resume field scans.

)} {sess?.deepCapture && st && !st.scanning && (

Firmware normally has Live scan on at boot. If you turned it off, enable it on the Dashboard.

)} {lastEv && (
            Last capture event: {lastEv}
          
)}
); }