Skip to content

feat(renderer): add PDF first-page preview thumbnail (#480) - #1280

Closed
bergeouss wants to merge 2 commits into
nesquena:masterfrom
bergeouss:feat/pdf-preview-thumbnail
Closed

bergeouss wants to merge 2 commits into
nesquena:masterfrom
bergeouss:feat/pdf-preview-thumbnail

Conversation

@bergeouss

Copy link
Copy Markdown
Contributor

Closes #480

Summary

Adds inline first-page preview for PDF files sent as MEDIA: tokens.

How it works

  • Extension detection: New _PDF_EXTS regex (.pdf) checked in the MEDIA restore block
  • Lazy-load placeholder: PDF files get a <div class="pdf-preview-load" data-path="..."> placeholder (same pattern as diffs)
  • PDF.js CDN: loadPdfInline()\) dynamically loads PDF.js from cdnjs on first use, then renders page 1 onto a ` at 1.5x scale
  • Size cap: 4 MB limit — larger PDFs show a download link instead
  • Download link: Header bar with filename + download button on successful render
  • Error fallback: On any failure, shows download link with error message

Files changed

Screenshots

PDFs will render as a canvas thumbnail showing the first page, with a header bar containing the filename and a download button.


Co-authored-by: Hermes AI Agent (bergeouss/hermes-webui fork)

…esquena#480 nesquena#482)

- PDF files: lazy-load first page via PDF.js CDN, render on canvas with
  download link and size cap (4 MB)
- HTML files: fetch and render in sandboxed iframe (srcdoc) with
  allow-scripts only, 256 KB cap, download fallback
- Added _PDF_EXTS and _HTML_EXTS regexes for MEDIA: token detection
- Added loadPdfInline() and loadHtmlInline() in all 4 rAF blocks
- CSS styles for .pdf-preview-* and .html-preview-* components
- 41 new tests covering regexes, placeholders, lazy-load functions,
  rAF integration, CSS classes, and i18n keys (10 keys x 7 locales)
@nesquena-hermes

Copy link
Copy Markdown
Collaborator

Thanks for this PR! Closes #480.

What this does: Adds inline first-page PDF preview for MEDIA: tokens pointing to .pdf files. PDF.js is loaded lazily from CDN on first use, renders page 1 onto a <canvas> at 1.5× scale, with a 4 MB size cap (falls back to a download link for oversized files). Error states also degrade gracefully to a download link.

Scope of changes:

  • static/ui.js — _PDF_EXTS regex, MEDIA handler, loadPdfInline(), 4 rAF block calls
  • static/style.css — .pdf-preview-* styles
  • static/i18n.js — 5 new keys across all 7 locales
  • tests/test_pdf_html_preview.py — 41 tests

Things to verify before merge:

  • The CDN dependency on cdnjs for PDF.js is worth noting — offline/airgapped deployments won't get PDF previews. If that's a known use case, a fallback or a comment in the code would be helpful. Otherwise this is a reasonable trade-off for keeping bundle size down.
  • The 4 MB cap is server-side byte count; worth confirming the api/media endpoint enforces this before streaming begins (rather than downloading everything then checking size client-side).
  • The rAF block calls (4 of them) — confirm these are needed for correct paint sequencing and aren't just defensive copy-paste from another handler.

Overall this is a well-structured addition that follows the established loadDiffInline() pattern. The test coverage looks solid.

@nesquena-hermes

Copy link
Copy Markdown
Collaborator

✅ Merged into release branch — included in PR #1285 (v0.50.240). Thanks for the contribution!

@bergeouss

Copy link
Copy Markdown
Contributor Author

Review Feedback Addressed

  • Issue: Reviewer asked about CDN dependency for offline/air-gapped deployments, 4 MB cap enforcement, and rAF call count
  • Fix: Added documentation comment at the top of the PDF inline preview section noting: (1) CDN dependency with 15 s fallback to download link for offline users, (2) 4 MB cap is client-side — server-side enforcement would be a separate backend change, (3) rAF calls follow the established post-render pipeline pattern shared by all inline renderers (diff, HTML, mermaid, katex, PDF)
  • Files: static/ui.js

🤖 AI-assisted via Hermes Agent

nesquena-hermes added a commit that referenced this pull request Apr 30, 2026
## Release v0.50.240

Batch release of 13 PRs that passed full triage + code review + test suite (3199 tests, 0 failures).

---

### Added

- **Compact tool activity mode** (`simplified_tool_calling`, default on) — groups tool calls and thinking traces into a single collapsed "Activity" disclosure card per assistant turn. Also adds a new **Calm Console** theme with earth/slate palette and serif prose. @Michaelyklam — #1282
- **PDF first-page preview** — `MEDIA:` `.pdf` files render a canvas thumbnail via PDF.js CDN (4 MB cap). **HTML sandbox iframe** — `.html`/`.htm` files render inline in a sandboxed `<iframe srcdoc>` (256 KB cap). 10 i18n keys × 7 locales. @bergeouss — #1280, closes #480 #482
- **Inline Excalidraw diagram preview** — `.excalidraw` files render as pure SVG (no external deps; rectangles, ellipses, diamonds, text, lines, arrows, freehand; 512 KB cap). @bergeouss — #1279, closes #479
- **Inline CSV table rendering** — fenced `csv` blocks and `MEDIA:` CSV files render as scrollable HTML tables with auto-separator detection. @bergeouss — #1277, closes #485
- **Inline SVG, audio, and video rendering** — SVG as `<img>`, audio as `<audio controls>`, video as `<video controls>`. @bergeouss — #1276, closes #481
- **Batch session select mode** — multi-select sessions for bulk Archive/Delete/Move. 11 i18n keys × 7 locales. @bergeouss — #1275, closes #568
- **Collapsible skill category headers** — click to collapse/expand without re-render; state persists across filter cycles. @bergeouss — #1281
- **`providers.only_configured` setting** — opt-in flag to restrict the model picker to explicitly configured providers. @KingBoyAndGirl — #1268
- **OpenCode Go model catalog** — adds Kimi K2.6, DeepSeek V4 Pro/Flash, MiMo V2.5/Pro, Qwen3.6/3.5 Plus. @nesquena-hermes — #1284, closes #1269

### Fixed

- **Profile `TERMINAL_CWD` TypeError** — `_build_agent_thread_env()` helper merges env before `_set_thread_env()` call. @hi-friday — #1266
- **Service worker subpath cache bypass** — regex now matches `/api/*` under any mount prefix. @Michaelyklam — #1278
- **SSE client disconnect leaks** — `TimeoutError`/`OSError` treated as clean disconnects; server backlog 64, threads daemonized; session list renders before saved-session restore. @KayZz69 — #1267
- **i18n locale corrections** — Korean MCP strings (23), Chinese MCP strings (23), zh-Hant missing keys (41), de missing keys (229). @bergeouss — #1274, closes #1273

---

### Test results

```
3199 passed, 2 skipped, 3 xpassed in 72.79s
```

### PRs on hold (not included)

#1265 (draft), #1271 (superseded by #1266), #1272 (skipped XSS tests), #1232 (partial test run), #1222 (review questions open), #1134 (live-server tests), #1132 (superseded by #1134), #1108 (negative UX review), #1084 (empty description)
@nesquena-hermes

Copy link
Copy Markdown
Collaborator

Shipped in release v0.50.240 (PR #1285). Thanks!

SysAdminDoc pushed a commit to SysAdminDoc/hermes-webui that referenced this pull request Jun 26, 2026
## Release v0.50.240

Batch release of 13 PRs that passed full triage + code review + test suite (3199 tests, 0 failures).

---

### Added

- **Compact tool activity mode** (`simplified_tool_calling`, default on) — groups tool calls and thinking traces into a single collapsed "Activity" disclosure card per assistant turn. Also adds a new **Calm Console** theme with earth/slate palette and serif prose. @Michaelyklam — nesquena#1282
- **PDF first-page preview** — `MEDIA:` `.pdf` files render a canvas thumbnail via PDF.js CDN (4 MB cap). **HTML sandbox iframe** — `.html`/`.htm` files render inline in a sandboxed `<iframe srcdoc>` (256 KB cap). 10 i18n keys × 7 locales. @bergeouss — nesquena#1280, closes nesquena#480 nesquena#482
- **Inline Excalidraw diagram preview** — `.excalidraw` files render as pure SVG (no external deps; rectangles, ellipses, diamonds, text, lines, arrows, freehand; 512 KB cap). @bergeouss — nesquena#1279, closes nesquena#479
- **Inline CSV table rendering** — fenced `csv` blocks and `MEDIA:` CSV files render as scrollable HTML tables with auto-separator detection. @bergeouss — nesquena#1277, closes nesquena#485
- **Inline SVG, audio, and video rendering** — SVG as `<img>`, audio as `<audio controls>`, video as `<video controls>`. @bergeouss — nesquena#1276, closes nesquena#481
- **Batch session select mode** — multi-select sessions for bulk Archive/Delete/Move. 11 i18n keys × 7 locales. @bergeouss — nesquena#1275, closes nesquena#568
- **Collapsible skill category headers** — click to collapse/expand without re-render; state persists across filter cycles. @bergeouss — nesquena#1281
- **`providers.only_configured` setting** — opt-in flag to restrict the model picker to explicitly configured providers. @KingBoyAndGirl — nesquena#1268
- **OpenCode Go model catalog** — adds Kimi K2.6, DeepSeek V4 Pro/Flash, MiMo V2.5/Pro, Qwen3.6/3.5 Plus. @nesquena-hermes — nesquena#1284, closes nesquena#1269

### Fixed

- **Profile `TERMINAL_CWD` TypeError** — `_build_agent_thread_env()` helper merges env before `_set_thread_env()` call. @hi-friday — nesquena#1266
- **Service worker subpath cache bypass** — regex now matches `/api/*` under any mount prefix. @Michaelyklam — nesquena#1278
- **SSE client disconnect leaks** — `TimeoutError`/`OSError` treated as clean disconnects; server backlog 64, threads daemonized; session list renders before saved-session restore. @KayZz69 — nesquena#1267
- **i18n locale corrections** — Korean MCP strings (23), Chinese MCP strings (23), zh-Hant missing keys (41), de missing keys (229). @bergeouss — nesquena#1274, closes nesquena#1273

---

### Test results

```
3199 passed, 2 skipped, 3 xpassed in 72.79s
```

### PRs on hold (not included)

nesquena#1265 (draft), nesquena#1271 (superseded by nesquena#1266), nesquena#1272 (skipped XSS tests), nesquena#1232 (partial test run), nesquena#1222 (review questions open), nesquena#1134 (live-server tests), nesquena#1132 (superseded by nesquena#1134), nesquena#1108 (negative UX review), nesquena#1084 (empty description)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(renderer): PDF first-page preview thumbnail

2 participants