132 lines
5.1 KiB
HTML
132 lines
5.1 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>Extractor — Test Upload</title>
|
|
<style>
|
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
:root {
|
|
--bg: #0d0d0d; --surface: #161616; --border: #2a2a2a;
|
|
--text: #e0e0e0; --muted: #666; --accent: #2563eb; --green: #16a34a; --red: #dc2626;
|
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
font-size: 14px;
|
|
}
|
|
body { background: var(--bg); color: var(--text); min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 24px; }
|
|
.card { background: var(--surface); border: 1px solid var(--border); border-radius: 10px; padding: 28px; width: 100%; max-width: 520px; display: flex; flex-direction: column; gap: 18px; }
|
|
h1 { font-size: 15px; font-weight: 600; }
|
|
.drop-zone {
|
|
border: 2px dashed var(--border); border-radius: 7px; padding: 36px 20px;
|
|
text-align: center; cursor: pointer; transition: border-color 0.15s, background 0.15s;
|
|
color: var(--muted); font-size: 13px;
|
|
}
|
|
.drop-zone:hover, .drop-zone.over { border-color: var(--accent); background: rgba(37,99,235,0.05); color: var(--text); }
|
|
.drop-zone.has-file { border-color: var(--green); color: var(--green); }
|
|
input[type=file] { display: none; }
|
|
button {
|
|
padding: 8px 16px; border-radius: 6px; border: none; background: var(--accent);
|
|
color: #fff; font-size: 13px; cursor: pointer; transition: background 0.12s;
|
|
}
|
|
button:hover:not(:disabled) { background: #1d4ed8; }
|
|
button:disabled { opacity: 0.4; cursor: not-allowed; }
|
|
.result {
|
|
background: var(--bg); border: 1px solid var(--border); border-radius: 6px;
|
|
padding: 14px; font-family: "SF Mono", "Fira Mono", monospace; font-size: 11px;
|
|
white-space: pre-wrap; word-break: break-all; max-height: 320px; overflow-y: auto;
|
|
color: var(--text); display: none;
|
|
}
|
|
.result.error { border-color: var(--red); color: #f87171; }
|
|
.result.success { border-color: var(--green); }
|
|
.status { font-size: 12px; color: var(--muted); min-height: 16px; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="card">
|
|
<h1>Extractor — Test Upload</h1>
|
|
|
|
<div class="drop-zone" id="drop-zone">
|
|
<input type="file" id="file-input" accept=".pdf">
|
|
<span id="drop-label">Drop a PDF here, or click to select</span>
|
|
</div>
|
|
|
|
<button id="submit-btn" disabled>Extract</button>
|
|
|
|
<p class="status" id="status"></p>
|
|
<pre class="result" id="result"></pre>
|
|
</div>
|
|
|
|
<script>
|
|
const dropZone = document.getElementById("drop-zone");
|
|
const fileInput = document.getElementById("file-input");
|
|
const dropLabel = document.getElementById("drop-label");
|
|
const submitBtn = document.getElementById("submit-btn");
|
|
const statusEl = document.getElementById("status");
|
|
const resultEl = document.getElementById("result");
|
|
|
|
let selectedFile = null;
|
|
|
|
function csrfHeaders(extra = {}) {
|
|
const csrf = document.cookie
|
|
.split("; ")
|
|
.find((part) => part.startsWith("bmp_demo_csrf="))
|
|
?.slice("bmp_demo_csrf=".length);
|
|
return csrf ? { ...extra, "X-BMP-CSRF": decodeURIComponent(csrf) } : extra;
|
|
}
|
|
|
|
dropZone.addEventListener("click", () => fileInput.click());
|
|
|
|
fileInput.addEventListener("change", () => setFile(fileInput.files[0]));
|
|
|
|
dropZone.addEventListener("dragover", e => { e.preventDefault(); dropZone.classList.add("over"); });
|
|
dropZone.addEventListener("dragleave", () => dropZone.classList.remove("over"));
|
|
dropZone.addEventListener("drop", e => {
|
|
e.preventDefault();
|
|
dropZone.classList.remove("over");
|
|
setFile(e.dataTransfer.files[0]);
|
|
});
|
|
|
|
function setFile(file) {
|
|
if (!file || file.type !== "application/pdf") {
|
|
statusEl.textContent = "Please select a PDF file.";
|
|
return;
|
|
}
|
|
selectedFile = file;
|
|
dropLabel.textContent = file.name;
|
|
dropZone.classList.add("has-file");
|
|
submitBtn.disabled = false;
|
|
statusEl.textContent = "";
|
|
resultEl.style.display = "none";
|
|
}
|
|
|
|
submitBtn.addEventListener("click", async () => {
|
|
if (!selectedFile) return;
|
|
|
|
submitBtn.disabled = true;
|
|
statusEl.textContent = "Extracting…";
|
|
resultEl.style.display = "none";
|
|
|
|
const form = new FormData();
|
|
form.append("pdf", selectedFile);
|
|
|
|
try {
|
|
const res = await fetch("/extract", { method: "POST", headers: csrfHeaders(), body: form });
|
|
const json = await res.json();
|
|
|
|
resultEl.textContent = JSON.stringify(json, null, 2);
|
|
resultEl.className = "result " + (res.ok ? "success" : "error");
|
|
resultEl.style.display = "block";
|
|
statusEl.textContent = res.ok
|
|
? `Written → ${json.files?.json || json.stem}`
|
|
: `Error ${res.status}`;
|
|
} catch (err) {
|
|
resultEl.textContent = String(err);
|
|
resultEl.className = "result error";
|
|
resultEl.style.display = "block";
|
|
statusEl.textContent = "Request failed.";
|
|
} finally {
|
|
submitBtn.disabled = false;
|
|
}
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|