feat(pdf-server): lazy range-based loading + eager background preloading (#480)

* lazy PDF loading w/ PDFDataRangeTransport

* lint

* feat: lazy PDF loading via range transport + eager background preloading

Replace full-file download with PDFDataRangeTransport so PDF.js fetches
byte ranges on demand. First page renders immediately; remaining pages
load in background for search.

**Range transport (server + client)**
- server.ts: add read_pdf_bytes tool returning base64 chunks with offset
- mcp-app.ts: AppRangeTransport feeds PDF.js via fetchRange/fetchChunk
  with parallel sub-requests for large ranges, Promise-based dedup of
  concurrent requests, and a persistent range cache

**Background preloader**
- Sequential page 1→N text extraction, pauses during interactive
  navigation (preloadPaused flag set in goToPage, cleared in renderPage)
- Debounced live search refresh (500ms) as pages load
- Search UI shows '(loading…)' suffix until all pages processed

**Loading indicators**
- SVG pie in toolbar: fills as pages load, fades on completion,
  turns red on errors with tooltip listing failed pages
- Debounced page-loading overlay (150ms) for uncached page navigation

**Cleanup**
- Remove old full-file chunked download and progress bar
- Remove extractAllPageText (replaced by preloader)
- Remove verbose debug logs (Coverage, RangeReq, cache hit)
This commit is contained in:
Olivier Chafik 2026-02-21 16:33:48 -08:00 • committed by GitHub
commit d185099ead
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -11,14 +11,6 @@
<div id="loading" class="loading">
<div class="spinner"></div>
<p id="loading-text">Loading PDF...</p>
<div
id="progress-container"
class="progress-container"
style="display: none"
>
<div id="progress-bar" class="progress-bar"></div>
</div>
<p id="progress-text" class="progress-text"></p>
</div>
<!-- Error State -->
@ -33,6 +25,13 @@
<div class="toolbar">
<div class="toolbar-left">
<span id="pdf-title" class="pdf-title">Document</span>
<div id="loading-indicator" class="loading-indicator" style="display: none"
title="Loading pages...">
<svg viewBox="0 0 20 20" width="14" height="14">
<circle class="loading-indicator-bg" cx="10" cy="10" r="8"/>
<circle class="loading-indicator-arc" cx="10" cy="10" r="8"/>
</svg>
</div>
</div>
<div class="toolbar-center">
<button id="prev-btn" class="nav-btn" title="Previous page (←)">