From e1b4e4372fcedaad635030d1cf8c6449aa8dc52d Mon Sep 17 00:00:00 2001 From: Andreas Knuth Date: Wed, 15 Jul 2026 21:54:02 -0500 Subject: [PATCH] last --- web/pdf_viewer.js | 53 ++++++++++++++--------------------------------- web/styles.css | 46 +++++++++++++++++++++++++++++++++------- 2 files changed, 53 insertions(+), 46 deletions(-) diff --git a/web/pdf_viewer.js b/web/pdf_viewer.js index 6e597e4..86bb46a 100644 --- a/web/pdf_viewer.js +++ b/web/pdf_viewer.js @@ -1,5 +1,4 @@ // Native Chromium PDF viewer using a single persistent iframe. -// The iframe is created once in index.html and reused for all documents. class NativePdfViewer { constructor(frame, container) { @@ -17,12 +16,10 @@ class NativePdfViewer { this.loadingDiv = document.createElement("div"); this.loadingDiv.className = "native-pdf-loading"; this.loadingDiv.textContent = "Loading PDF\u2026"; - this.loadingDiv.hidden = true; this.container.appendChild(this.loadingDiv); this.errorDiv = document.createElement("div"); - this.errorDiv.className = "native-pdf-error"; - this.errorDiv.hidden = true; + //this.errorDiv.className = "native-pdf-error"; this.errorMsg = document.createElement("div"); this.errorMsg.className = "native-pdf-error-msg"; @@ -38,19 +35,11 @@ class NativePdfViewer { }); this.errorDiv.appendChild(this.retryBtn); this.container.appendChild(this.errorDiv); - - // Log iframe sizing once - console.log( - "[BizMatch QC] iframe size:", - this.frame.clientWidth + "x" + this.frame.clientHeight, - "container:", - this.container.clientWidth + "x" + this.container.clientHeight, - ); } _showState(state) { - this.loadingDiv.hidden = state !== "loading"; - this.errorDiv.hidden = state !== "error"; + this.loadingDiv.classList.toggle("is-visible", state === "loading"); + this.errorDiv.classList.toggle("is-visible", state === "error"); } load(url, _byteSize, key) { @@ -58,46 +47,34 @@ class NativePdfViewer { } _navigate(url, key, force) { - // Deduplicate only AFTER the key is accepted - if (!force && key && key === this.currentKey) { - return; - } + if (!force && key && key === this.currentKey) return; const generation = ++this.generation; this.currentKey = key; this.lastUrl = url; + // Clear old error text, show loading badge + this.errorMsg.textContent = ""; this._showState("loading"); - console.log(`[BizMatch QC] Native PDF src assigned: ${key}`); - console.log( - `[BizMatch QC] Native PDF iframe size: ${this.frame.clientWidth}x${this.frame.clientHeight}`, - ); - // Register per-navigation load handler - if (this._onLoad) { - this.frame.removeEventListener("load", this._onLoad); - } - this._onLoad = () => { + // Shared hide function, idempotent per navigation + const hideLoading = () => { if (generation !== this.generation) return; if (this.loadTimer) { clearTimeout(this.loadTimer); this.loadTimer = null; } this._showState("loaded"); - console.log(`[BizMatch QC] Native PDF load event: ${key}`); }; - this.frame.addEventListener("load", this._onLoad, { once: true }); - // Safety timeout + // Optional early signal from iframe load event + if (this._onLoad) this.frame.removeEventListener("load", this._onLoad); + this._onLoad = hideLoading; + this.frame.addEventListener("load", hideLoading, { once: true }); + + // Fallback: hide loading after 800ms regardless of load event if (this.loadTimer) clearTimeout(this.loadTimer); - this.loadTimer = setTimeout(() => { - if (generation === this.generation) { - console.warn(`[BizMatch QC] Native PDF load timeout: ${key}`); - this._showState("error"); - this.errorMsg.textContent = - "PDF load timed out. The file may be very large."; - } - }, 15000); + this.loadTimer = setTimeout(hideLoading, 800); this.frame.src = url; } diff --git a/web/styles.css b/web/styles.css index 61a600e..3cfe8ba 100644 --- a/web/styles.css +++ b/web/styles.css @@ -100,21 +100,51 @@ aside, background: rgba(0, 0, 0, 0.3); } .native-pdf-loading { + display: none; position: absolute; - inset: 0; - display: grid; - place-items: center; - color: #aaa; - background: rgba(60, 60, 60, 0.8); + top: 18px; + left: 50%; + transform: translateX(-50%); z-index: 3; - font-size: 15px; + pointer-events: none; +} +.native-pdf-loading.is-visible { + display: flex; + align-items: center; + gap: 8px; + padding: 8px 18px; + border-radius: 8px; + background: rgba(0, 0, 0, 0.72); + color: #ddd; + font-size: 13px; + white-space: nowrap; +} +.native-pdf-loading.is-visible::before { + content: ""; + width: 16px; + height: 16px; + border: 2px solid rgba(255, 255, 255, 0.25); + border-top-color: #fff; + border-radius: 50%; + animation: pdf-spin 0.7s linear infinite; + flex-shrink: 0; +} +@keyframes pdf-spin { + to { + transform: rotate(360deg); + } } .native-pdf-error { + display: none; position: absolute; inset: 0; - display: grid; - place-items: center; z-index: 3; +} +.native-pdf-error.is-visible { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; background: rgba(50, 50, 50, 0.9); text-align: center; padding: 30px 20px;