feat(viewer): thumbnails sidebar + harden store-update, fitz auth, and thumbnail epoch guard#117
Open
nelsonduarte wants to merge 3 commits into
Open
feat(viewer): thumbnails sidebar + harden store-update, fitz auth, and thumbnail epoch guard#117nelsonduarte wants to merge 3 commits into
nelsonduarte wants to merge 3 commits into
Conversation
New Pages tab in the viewer sidebar showing clickable page thumbnails
rendered from the PDF via fitz. Click a thumbnail to jump to that
page; the current page is highlighted with ACCENT and stays in sync
as the user scrolls the canvas.
Thumbnails render in a background QThread (a private fitz.Document
copy so we don't share pymupdf state with the canvas) at 40 DPI, then
smooth-scale to 120x160 on paint. Cache is capped at CACHE_MAX (200)
with LRU eviction so long PDFs don't leak memory.
The sidebar container is now a QTabWidget with two tabs:
- Contents (existing TOC) — hidden when the PDF has no outline
- Pages (new thumbnails) — always available once a doc is loaded
The bookmark button on the header now toggles the whole sidebar. It
becomes visible on any PDF load (previously only when TOC existed)
because the Pages tab is always useful.
- New: app/viewer/thumbnails.py
* ThumbnailWorker (QThread) — background render + password support
* ThumbnailModel (QAbstractListModel) — page slots + LRU cache
* ThumbnailDelegate (QStyledItemDelegate) — custom paint with
current-page ACCENT highlight and hover feedback
* ThumbnailPanel (QWidget) — sidebar container + view lifecycle
- Modified: app/viewer/panel.py
* Wrap sidebar in QTabWidget with Contents + Pages tabs
* Wire page_requested to canvas scroll_to_page
* Sync current page from _update_page_label
* Clear thumbnails on doc replace and closeEvent
* Forward theme changes to thumbnail delegate
- New i18n keys (8 languages): viewer.sidebar.contents,
viewer.sidebar.pages, viewer.thumbnails.loading
- Tests: 18 (source-level guards + ThumbnailModel behavioural +
i18n parity + panel integration)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Qt requires QPixmap to be created/mutated only in the main GUI thread. The initial ThumbnailWorker created QPixmap.fromImage() in the worker thread, which silently failed (or would crash on some platforms) - no thumbnails ever appeared in the sidebar. Now: - Worker emits QImage (thread-safe) via thumbnail_ready signal - ThumbnailPanel._on_image_ready slot converts to QPixmap on the main thread, then hands it to the model - Connection uses explicit Qt.ConnectionType.QueuedConnection to respect Py3.14 PySide6's stricter cross-thread routing (see memory/project_compress_freeze_py314.md) - @slot decorator on the receiver for the same reason Also adds debug logging in worker + panel for future diagnostics.
…nail epoch guard - i18n: validate store version format and UInt16 component magnitude; scrub corrupt dismissed-version keys so stale values no longer suppress update notifications - base: verify _open_fitz authenticate() return and raise on wrong password - viewer/thumbnails: add epoch counter to discard stale cross-document thumbnails - remove unused variables (F841); add requirements-dev.txt; ignore thumb_repro*.png Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This branch adds the page-thumbnails sidebar to the viewer and hardens several correctness/robustness issues surfaced during adversarial review. All fixes below passed adversarial review and the full test suite (443 passed, 6 skipped).
Fixes by severity
High
app/i18n.py): validate the format and per-component magnitude (each UInt16 component must be ≤ 65535) of Store versions before comparing. A new_is_valid_store_versionhelper gates parsing, and corruptdismissed-versionkeys are now auto-scrubbed so a stale/malformed persisted value can no longer permanently suppress update notifications.app/base.py):_open_fitznow verifies the return value ofauthenticate()and raises a translatedValueErroron a wrong password instead of silently proceeding with a locked document.Medium
app/viewer/thumbnails.py): added an epoch counter so in-flight thumbnail results from a previously loaded document are discarded rather than painted onto the newly opened document. The worker is now disconnected beforewait()to avoid stale-signal delivery.Low / hygiene
app/tools/_pdf_extract.py,app/viewer/panel.py, andapp/window.py.requirements-dev.txt(mypy, bandit, pip-audit, pyyaml, ruff).thumb_repro*.pngvia.gitignore.Tests
tests/test_store_updater.py,tests/test_thumbnails.py,tests/test_i18n.py.🤖 Generated with Claude Code