<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Surya · Full-Page OCR</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css">
  <script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js"></script>
  <style>
    :root {
      --ink: #0f1115;
      --bg: #f4f6f9;
      --panel: #ffffff;
      --border: #e4e7ec;
      --accent: #2563eb;
      --accent-hover: #1d4ed8;
      --muted: #667085;
    }
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
      background: var(--bg);
      color: var(--ink);
      height: 100vh;
      display: flex;
      flex-direction: column;
      overflow: hidden;
    }
    header {
      display: flex; align-items: center; gap: 16px;
      padding: 12px 20px;
      background: var(--panel);
      border-bottom: 1px solid var(--border);
      box-shadow: 0 1px 3px rgba(16,24,40,.04);
      z-index: 10;
    }
    .brand { display: flex; align-items: center; gap: 10px; }
    .brand img { height: 30px; width: auto; }
    .brand .title { font-size: 18px; font-weight: 700; letter-spacing: -.01em; }
    .brand .sub { font-size: 13px; color: var(--muted); font-weight: 500;
      padding-left: 10px; margin-left: 4px; border-left: 1px solid var(--border); }
    .controls { display: flex; align-items: center; gap: 10px; flex: 1; flex-wrap: wrap; }
    input[type=text] {
      flex: 1; min-width: 260px; max-width: 560px;
      padding: 9px 12px; font-size: 14px;
      border: 1px solid var(--border); border-radius: 8px; background: #fff;
    }
    input[type=text]:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px rgba(37,99,235,.12); }
    button {
      padding: 9px 14px; font-size: 14px; font-weight: 600;
      border: none; border-radius: 8px; cursor: pointer;
      background: var(--accent); color: #fff; transition: background .15s;
    }
    button:hover:not(:disabled) { background: var(--accent-hover); }
    button.secondary { background: #fff; color: var(--ink); border: 1px solid var(--border); }
    button.secondary:hover:not(:disabled) { background: #f2f4f7; }
    button:disabled { opacity: .45; cursor: not-allowed; }
    .pager { display: flex; align-items: center; gap: 6px; }
    .pager .pageind { font-size: 13px; color: var(--muted); min-width: 96px; text-align: center; }
    .pager button { padding: 7px 11px; }
    .toggle { display: flex; align-items: center; gap: 7px; font-size: 13px; color: var(--muted); cursor: pointer; user-select: none; }
    .status { font-size: 13px; font-weight: 600; min-width: 80px; }
    .status.loading { color: #b45309; }
    .status.error { color: #d92d20; }
    .status.ok { color: #079455; }

    .stage {
      flex: 1; display: flex; gap: 16px; padding: 16px; overflow: hidden;
    }
    .panel {
      flex: 1; min-width: 0; display: flex; flex-direction: column;
      background: var(--panel); border: 1px solid var(--border);
      border-radius: 12px; overflow: hidden;
      box-shadow: 0 1px 2px rgba(16,24,40,.05);
    }
    .panel-head {
      padding: 11px 16px; font-size: 13px; font-weight: 700;
      letter-spacing: .02em; text-transform: uppercase; color: var(--muted);
      border-bottom: 1px solid var(--border); background: #fcfcfd;
    }
    .panel-body { flex: 1; overflow: auto; }
    .img-wrap {
      display: flex; align-items: flex-start; justify-content: center;
      padding: 16px; background: #f0f2f5; min-height: 100%;
    }
    #pageCanvas { max-width: 100%; height: auto; border-radius: 6px;
      box-shadow: 0 2px 10px rgba(16,24,40,.12); background: #fff; }
    .ocr {
      padding: 28px 32px; line-height: 1.6; font-size: 16px; color: #1d2433;
    }
    .ocr [data-label="SectionHeader"], .ocr [data-label="Title"] { font-weight: 700; font-size: 1.15em; margin: .5em 0 .3em; }
    .ocr [data-label="PageHeader"], .ocr [data-label="PageFooter"] { color: var(--muted); font-size: .9em; }
    .ocr table { border-collapse: collapse; margin: 14px 0; width: 100%; }
    .ocr th, .ocr td { border: 1px solid #d0d5dd; padding: 6px 10px; text-align: left; }
    .ocr th { background: #f2f4f7; font-weight: 600; }
    .ocr img { max-width: 100%; height: auto; }
    .placeholder { padding: 48px 32px; color: var(--muted); font-size: 15px; text-align: center; }
    #dropOverlay { position: fixed; inset: 0; z-index: 100; display: none;
      align-items: center; justify-content: center; background: rgba(15,17,21,.55); }
    #dropOverlay.active { display: flex; }
    #dropOverlay .drop-card { padding: 40px 64px; border: 3px dashed #fff;
      border-radius: 16px; color: #fff; font-size: 22px; font-weight: 700;
      background: rgba(37,99,235,.30); }
    /* While screenshotting, expand panels to full content height so
       html2canvas captures everything, not just the visible scroll area. */
    body.capturing { height: auto !important; overflow: visible !important; }
    body.capturing .stage { height: auto !important; overflow: visible !important; }
    body.capturing .panel-body { height: auto !important; overflow: visible !important; }
  </style>
</head>
<body>
  <header>
    <div class="brand">
      {% if logo %}<img src="{{ logo }}" alt="Datalab">{% endif %}
      <span class="title">Surya</span>
      <span class="sub">Full-Page OCR</span>
    </div>
    <div class="controls">
      <input type="text" id="filePath" placeholder="Drop a file, Browse, or type a server path…">
      <input type="file" id="fileInput" accept=".pdf,.png,.jpg,.jpeg,.gif,.webp" style="display:none" onchange="onPick(this)">
      <button class="secondary" onclick="document.getElementById('fileInput').click()">Browse</button>
      <button class="secondary" id="loadBtn" onclick="loadFile()">Load</button>
      <div class="pager">
        <button class="secondary" id="prevBtn" onclick="changePage(-1)" disabled>◀</button>
        <span class="pageind" id="pageInd">—</span>
        <button class="secondary" id="nextBtn" onclick="changePage(1)" disabled>▶</button>
      </div>
      <button id="runBtn" onclick="runOCR()" disabled>Run Full-Page OCR</button>
      <label class="toggle"><input type="checkbox" id="showBoxes" checked onchange="drawLeft()"> Layout boxes</label>
      <button class="secondary" id="copyBtn" onclick="copyHtml()" disabled>Copy HTML</button>
      <button class="secondary" id="shotBtn" onclick="saveScreenshot()" disabled>Save Screenshot</button>
      <span class="status" id="status"></span>
    </div>
  </header>

  <div class="stage">
    <div class="panel">
      <div class="panel-head">PDF Page</div>
      <div class="panel-body"><div class="img-wrap"><canvas id="pageCanvas"></canvas></div></div>
    </div>
    <div class="panel">
      <div class="panel-head">Full-Page OCR</div>
      <div class="panel-body"><div class="ocr" id="ocr"><div class="placeholder">Load a file, scroll to a page, then run full-page OCR.</div></div></div>
    </div>
  </div>

  <div id="dropOverlay"><div class="drop-card">Drop a PDF or image to load</div></div>

  <script>
    const S = { path: "", name: "", page: 0, count: 0, img: null, blocks: null, html: null, ocrPage: null };

    const $ = (id) => document.getElementById(id);
    function setStatus(msg, kind) { const s = $("status"); s.textContent = msg || ""; s.className = "status " + (kind || ""); }

    async function post(url, body) {
      const r = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) });
      const data = await r.json();
      if (!r.ok) throw new Error(data.error || "Request failed");
      return data;
    }

    async function loadFile() {
      const val = $("filePath").value.trim();
      if (!val) { setStatus("Drop a file or enter a path", "error"); return; }
      // If the box still shows the name of an uploaded file, just reload it.
      if (S.path && val === S.name) { S.page = 0; await showPage(); return; }
      setStatus("Loading…", "loading");
      try {
        const info = await post("/info", { file_path: val });
        S.path = val; S.name = val; S.count = info.page_count; S.page = 0;
        await showPage();
        setStatus("");
      } catch (e) { setStatus(e.message, "error"); }
    }

    function onPick(input) { if (input.files && input.files[0]) uploadFile(input.files[0]); }

    async function uploadFile(file) {
      setStatus("Uploading…", "loading");
      const fd = new FormData(); fd.append("file", file);
      try {
        const r = await fetch("/upload", { method: "POST", body: fd });
        const data = await r.json();
        if (!r.ok) throw new Error(data.error || "Upload failed");
        S.path = data.file_path; S.name = data.name; S.count = data.page_count; S.page = 0;
        $("filePath").value = data.name;
        await showPage();
        setStatus("");
      } catch (e) { setStatus(e.message, "error"); }
    }

    async function showPage() {
      setStatus("Rendering…", "loading");
      try {
        const data = await post("/page", { file_path: S.path, page: S.page });
        // New page → clear any previous OCR output.
        S.blocks = null; S.html = null; S.ocrPage = null;
        $("ocr").innerHTML = '<div class="placeholder">Run full-page OCR to see the extracted content.</div>';
        $("shotBtn").disabled = true;
        $("copyBtn").disabled = true;
        loadImage(data.image_base64, () => drawLeft());
        updatePager();
        $("runBtn").disabled = false;
        setStatus("");
      } catch (e) { setStatus(e.message, "error"); }
    }

    function loadImage(src, cb) {
      const im = new Image();
      im.onload = () => { S.img = im; cb && cb(); };
      im.src = src;
    }

    function updatePager() {
      $("pageInd").textContent = S.count ? `Page ${S.page + 1} of ${S.count}` : "—";
      $("prevBtn").disabled = S.page <= 0;
      $("nextBtn").disabled = S.page >= S.count - 1;
    }

    function changePage(delta) {
      const next = S.page + delta;
      if (next < 0 || next >= S.count) return;
      S.page = next;
      showPage();
    }

    function drawLeft() {
      if (!S.img) return;
      const c = $("pageCanvas"), ctx = c.getContext("2d");
      c.width = S.img.naturalWidth; c.height = S.img.naturalHeight;
      ctx.drawImage(S.img, 0, 0);
      if (!S.blocks || !$("showBoxes").checked) return;
      ctx.lineWidth = 3;
      ctx.font = 'bold 15px -apple-system, "Segoe UI", sans-serif';
      ctx.textBaseline = "top";
      S.blocks.forEach((b) => {
        const [x1, y1, x2, y2] = b.bbox;
        ctx.strokeStyle = b.color; ctx.fillStyle = b.color + "26";
        ctx.fillRect(x1, y1, x2 - x1, y2 - y1);
        ctx.strokeRect(x1, y1, x2 - x1, y2 - y1);
        const tw = ctx.measureText(b.label).width, ly = Math.max(y1 - 22, 0);
        ctx.fillStyle = b.color; ctx.fillRect(x1, ly, tw + 12, 21);
        ctx.fillStyle = "#fff"; ctx.fillText(b.label, x1 + 6, ly + 3);
      });
    }

    async function runOCR() {
      if (!S.path) return;
      setStatus("Running OCR…", "loading");
      $("runBtn").disabled = true;
      try {
        const data = await post("/process", { file_path: S.path, page: S.page });
        S.blocks = data.blocks; S.html = data.html || ""; S.ocrPage = S.page;
        loadImage(data.image_base64, () => drawLeft());
        const ocr = $("ocr");
        ocr.innerHTML = data.html || '<div class="placeholder">No content detected on this page.</div>';
        renderMath(ocr);
        $("shotBtn").disabled = false;
        $("copyBtn").disabled = !S.html;
        setStatus(`${data.n_blocks} blocks`, "ok");
      } catch (e) { setStatus(e.message, "error"); }
      finally { $("runBtn").disabled = false; }
    }

    function renderMath(root) {
      root.querySelectorAll("math").forEach((el) => {
        const block = el.getAttribute("display") === "block";
        try {
          const span = document.createElement(block ? "div" : "span");
          span.innerHTML = katex.renderToString(el.textContent, { displayMode: block, throwOnError: false });
          el.replaceWith(span);
        } catch (e) { /* leave raw on failure */ }
      });
    }

    function copyHtml() {
      if (!S.html) return;
      const done = () => setStatus("HTML copied", "ok");
      if (navigator.clipboard && navigator.clipboard.writeText) {
        navigator.clipboard.writeText(S.html).then(done).catch(() => fallbackCopy(S.html, done));
      } else {
        fallbackCopy(S.html, done);
      }
    }

    function fallbackCopy(text, done) {
      const ta = document.createElement("textarea");
      ta.value = text; ta.style.position = "fixed"; ta.style.opacity = "0";
      document.body.appendChild(ta); ta.select();
      try { document.execCommand("copy"); done(); }
      catch (e) { setStatus("Copy failed", "error"); }
      document.body.removeChild(ta);
    }

    async function saveScreenshot() {
      setStatus("Capturing…", "loading");
      const stage = document.querySelector(".stage");
      document.body.classList.add("capturing");
      try {
        // Let the expanded layout settle before measuring full size.
        await new Promise((r) => requestAnimationFrame(() => requestAnimationFrame(r)));
        const w = stage.scrollWidth, h = stage.scrollHeight;
        const canvas = await html2canvas(stage, {
          backgroundColor: "#f4f6f9", scale: 2, useCORS: true, logging: false,
          width: w, height: h, windowWidth: w, windowHeight: h, scrollX: 0, scrollY: 0,
        });
        const a = document.createElement("a");
        a.href = canvas.toDataURL("image/png");
        a.download = `surya_ocr_page_${(S.ocrPage ?? S.page) + 1}.png`;
        a.click();
        setStatus("Saved", "ok");
      } catch (e) { setStatus("Screenshot failed: " + e.message, "error"); }
      finally { document.body.classList.remove("capturing"); }
    }

    $("filePath").addEventListener("keypress", (e) => { if (e.key === "Enter") loadFile(); });

    // Drag & drop anywhere in the window.
    let dragDepth = 0;
    window.addEventListener("dragenter", (e) => { e.preventDefault(); dragDepth++; $("dropOverlay").classList.add("active"); });
    window.addEventListener("dragover", (e) => { e.preventDefault(); });
    window.addEventListener("dragleave", (e) => { e.preventDefault(); if (--dragDepth <= 0) { dragDepth = 0; $("dropOverlay").classList.remove("active"); } });
    window.addEventListener("drop", (e) => {
      e.preventDefault(); dragDepth = 0; $("dropOverlay").classList.remove("active");
      const f = e.dataTransfer.files && e.dataTransfer.files[0];
      if (f) uploadFile(f);
    });
  </script>
</body>
</html>
