PDF View in zid: Zoom, Scroll and Search

VerifiedSafe

Internal guide to zid's PDF viewer (Zig/mupdf): zoom, scrolling, sharp rendering, Ctrl+F search, reload behavior and E2E tests.

Sby Skills Guide Bot
DevelopmentAdvanced
10/20/2026
Claude Code
#pdf#zig#mupdf#ui-rendering#incremental-search

Recommended for

Automated summary

Documents how zid's PDF preview is implemented: zoom and scrolling, Ctrl+F search, scale-aware rendering and safe reloading of changed files through mupdf.

Strengths

  • Points to the exact files, functions and unit tests involved (pdf_nav.geometry, pdf_find.zig, fz_page_text_z, renderPage).
  • Explains the reasoning behind tricky decisions, such as the 150 ms debounce before reloading to dodge mupdf crashes on half-written PDFs.
  • Covers incremental search, the 400-highlight cap and the state exposed (zoom, hits, current) for E2E tests.

Limitations

  • Written in German and tied entirely to the private zid repository, so it is hard to reuse elsewhere.
  • The supplied content is truncated at the end (E2E script cut off), leaving it incomplete.
  • Assumes prior knowledge of the project architecture; no big-picture overview.
When to use it

Reach for it whenever you touch src/ui/pdf_view.zig, pdf_nav.zig, pdf_find.zig, pdf_handler.zig, PDF reload behaviour or scripts/e2e_pdf_*.py.

When not to use it

Not useful for PDF work outside zid (pdf.js, PyMuPDF, Electron) or for merely using a PDF reader.

Security analysis

Safe
Quality score82/100

This is a German-language internal documentation/instruction file describing a Zig/mupdf PDF preview subsystem (zoom, scroll, search, reload, E2E tests). It contains no executable install steps, no network fetches, no shell pipelines, no credential handling, and no destructive operations; the only commands referenced are repo-local Python E2E test scripts (scripts/e2e_pdf_*.py) run deliberately. Risk of listing is negligible.

No concerns found

How this analysis is made

Examples

Set zoom back to 100%
Ajoute un bouton dans la barre de l'aperçu PDF qui remet le zoom à 100 %, en réutilisant pdf_nav.zoom_levels et en vérifiant que pdf_nav.geometry recentre bien la page.
Search does not jump after reload
Dans zid, après un reload d'un PDF la recherche Ctrl+F ne saute plus au premier résultat. Regarde searchStep et pdf_find.zig pour comprendre pourquoi la recherche repart sans sauter.
mupdf crash on writing file
Le rechargement d'un PDF encore en cours d'écriture fait planter mupdf. Vérifie le debounce de 150 ms dans takeDuePdfReload et pending_pdf_reloads, puis lance scripts/e2e_pdf_zoom_find.py.

name: pdf-view description: > PDF-Vorschau in zid: Blättern, Zoom und Bildlauf in der Seite, Suche (Ctrl+F), Neuladen bei Dateiänderung, mupdf-Abstürze bei halb geschriebenen PDFs, E2E-Zustand. Use when touching src/ui/pdf_view.zig, pdf_nav.zig, pdf_find.zig, src/rendering/pdf_handler.zig, fz_page_text_z, PDF reload, or scripts/e2e_pdf_*.py.

Zoom und Bildlauf

  • Zustand je Dokument im PdfHandler (zoom, scroll_x/y), also gemeinsam für beide Panes eines Splits, wie current_page. Zoom 1.0 = Seitenbreite; Stufen pdf_nav.zoom_levels (50–400 %). Geometrie rein in pdf_nav.geometry (unit-getestet): passt die Seite, steht sie mittig, sonst verschiebt der Bildlauf sie über child_offset des Clip-Elements pdf_viewport.
  • Bedienung: Knöpfe −/Prozent/+ in der Leiste (Prozent setzt auf 100 %), Ctrl+Rad um die Mausposition (zoomAround), Ctrl+Plus/Minus/0 über die Befehle zoom_in/out/reset, die im PDF-Tab die Seite statt der Schrift zoomen. Rad ohne Ctrl scrollt in einer überstehenden Seite und blättert erst am Rand (pdf_nav.wheel); zurück landet man am Ende der vorigen Seite. Passt die Seite, blättert jede Stufe eine Seite. Shift+Rad scrollt waagrecht.
  • Scharf rendern: die Ansicht meldet je Frame den nötigen Maßstab (wanted_scale, px/pt, Maximum aller Panes), die Hauptschleife rendert neu, wenn er um mehr als 10 % von requested_scale abweicht oder needs_render (Seitenwechsel) gesetzt ist. Damit richtet sich die Auflösung nach Pane-Größe und Zoom statt fest 1.5/2.0. renderPage kappt bei 16 MP bzw. 8192 px Kante; verglichen wird mit dem angeforderten, nicht dem gekappten Maßstab, sonst würde jeden Frame neu gerendert.
  • Seiten mit MediaBox-Ursprung ungleich (0,0) werden an die Pixmap-Ecke verschoben (origin_x/y); Such-Rechtecke ziehen denselben Ursprung ab.

Suche (Ctrl+F)

  • Dieselbe Leiste wie Editor und Markdown-Vorschau (find_bar.FindState im Handler), dieselben Optionen über find_ops.Pattern. Enter/Shift+Enter springen mit Umbruch, Escape schließt, Blättern bleibt bei offener Leiste möglich.
  • Text je Seite liefert fz_page_text_z (C-Wrapper, fz_try und Strukturblöcke rekursiv): je Zeichen Codepunkt und Box, Zeilenende als Leerzeichen, Blockende als \n, beide mit leerer Box. Die Strukturen von mupdf werden in C durchlaufen, nicht in Zig: System-mupdf (1.27) und gebündeltes (1.26.5) unterscheiden sich, der Wrapper wird je gegen den passenden Header gebaut.
  • pdf_find.zig (rein, unit-getestet) baut daraus UTF-8 mit Byte-Anfang je Zeichen und macht aus einem Treffer-Bytebereich Rechtecke je Zeile. Gesucht wird schrittweise (~8 ms je Frame in der Hauptschleife, searchStep), der erste Treffer ab der Leseposition wird angesprungen, sobald er gefunden ist; spätere Funde ziehen nicht weg. Nach einem Reload läuft die Suche neu, ohne zu springen.
  • Markierung: schwebende Rechtecke am Bild (clip_to = .to_attached_parent, passthrough), höchstens 400 je Seite; aktueller Treffer orange, übrige gelb.
  • E2E: python3 scripts/e2e_pdf_zoom_find.py; pdf_state liefert dafür zusätzlich zoom, scroll_x/y, scale, find_active, searching, hits, current, hit_page.

PDF-Vorschau: Blättern und Neuladen

  • Neu laden bei Dateiänderung: handleExternalChange erkennt offene PDFs (keyForPath über open_pdfs, auch per realpath) und reiht sie in pending_pdf_reloads ein; exportMarpPdf tut das direkt. Der Main-Loop lädt erst, wenn die Datei 150 ms ruht (takeDuePdfReload, mtime): Das System-mupdf 1.27.2 (Linux linkt /lib64/libmupdf.so, nicht fancy-cats mupdf) stürzt beim Reparieren mancher halb geschriebener PDFs ab („double free“, mutool draw segfaultet auf derselben Datei), und der Watcher meldet je Datei nur ein Ereignis pro 100 ms. Neuer Handler, Seite geklemmt, Textur ersetzt; scheitert etwas, bleibt der alte Stand. wantsFrameSoon hält den Loop wach, solange ein Reload wartet. E2E python3 scripts/e2e_pdf_reload.py, Marp-Weg in e2e_marp_pdf.py (letzter Schritt).

  • Blätter-Logik als reines Modul src/ui/pdf_nav.zig (Tasten, Mausrad, Sättigung an den Rändern, Beschriftung). Die Ansicht src/ui/pdf_view.zig liefert ein Seiten-Delta, die UI setzt die Seite (PdfHandler.setPage, markiert needs_render), die Hauptschleife rendert die Textur neu.

  • Bild ab/auf und Pfeil links/rechts blättern; hoch und runter bleiben der Navigation zwischen Panes und im Explorer. Mausrad: negative Zeilen heißen nach unten, also vorwärts.

  • clay.pointerOver meldet in dieser Ansicht nichts, deshalb hat die Leiste eine eigene Schaltfläche statt components.Button: Hover und Klick rechnen gegen die Bounding-Box aus dem letzten Layout (pdf_nav.hits, Aufhellung über pdf_nav.brighten).

  • Die Beschriftung liegt in einem Puffer der UI (pdf_labels: Seite, Zoom, Trefferzahl), nicht in der Frame-Arena: beginLayout setzt die Arena zurück, Clay liest den Text erst beim Zeichnen.

  • Zustand für E2E: pdf_state liest Felder im E2EContext, die der Main-Thread pro Frame setzt, weil der Server-Thread über Tabs und open_pdfs springende Werte liest.

  • E2E: python3 scripts/e2e_pdf_pager.py, das siebenseitige PDF erzeugt write_pdf nach tmp/e2e_pdf/pager.pdf. Der Test startet mit eigenem, frischem XDG_CONFIG_HOME und übergibt das PDF als Startdatei: über eine wiederhergestellte Sitzung wechselt der aktive Tab und die Messung trifft Fremdzustand. open_file öffnet keinen PDF-Tab, das Laden hängt am Explorer-Pfad.

From the file to a first result

The download contains exactly the content displayed on this page. This copy may differ from the latest version in the source repository.

View the source and supporting files

This download contains SKILL.md only. Any scripts, references, templates and dependencies are not included. Get the required files from the source, preserving their directory structure, before continuing.

1. Download the file

Download SKILL.md

From the root of a test project, save the file as .skill-downloads/pdf-view-in-zid-zoom-scroll-and-search-mvda86xd/SKILL.md, or use the command below. This folder is for inspecting the skill before installation.

Command for macOS/Linux or WSL: requires a POSIX shell and curl. On Windows without WSL, use the download button and create the folder manually.

mkdir -p '.skill-downloads/pdf-view-in-zid-zoom-scroll-and-search-mvda86xd' && curl --fail --show-error --location 'https://skills-guides.com/api/skills/pdf-view-in-zid-zoom-scroll-and-search-mvda86xd/download' --output '.skill-downloads/pdf-view-in-zid-zoom-scroll-and-search-mvda86xd/SKILL.md.part' && mv '.skill-downloads/pdf-view-in-zid-zoom-scroll-and-search-mvda86xd/SKILL.md.part' '.skill-downloads/pdf-view-in-zid-zoom-scroll-and-search-mvda86xd/SKILL.md'

2. Check prerequisites

Read SKILL.md before using it. If it references scripts or external services, inspect them and prepare the required access in your test environment.

No recognized prerequisites section in this file. This does not mean it is self-contained: libraries, tools and access requirements may be mentioned elsewhere in the content or source repository.

The platforms on this page are catalog indications. The paths below follow tool documentation; they do not prove this skill has been run in those tools. No minimum tool version is confirmed for this listing.

3. Load the skill

After inspection, copy the complete folder into your project using one of the commands below, then open a new tool session. The command stops if a folder with the same name already exists: compare the files before replacing it.

Claude Code
test -f '.skill-downloads/pdf-view-in-zid-zoom-scroll-and-search-mvda86xd/SKILL.md' && test -s '.skill-downloads/pdf-view-in-zid-zoom-scroll-and-search-mvda86xd/SKILL.md' && mkdir -p '.claude/skills' && test ! -e '.claude/skills/pdf-view' && cp -R '.skill-downloads/pdf-view-in-zid-zoom-scroll-and-search-mvda86xd' '.claude/skills/pdf-view'

OpenSkills

OpenSkills must already be installed. The local install/read procedure is checked with OpenSkills 1.5.0, which requires Node.js ≥ 20.6.0. Catalog slugs are not resolved automatically.

Official OpenSkills installation
openskills install './.skill-downloads/pdf-view-in-zid-zoom-scroll-and-search-mvda86xd'

openskills read 'pdf-view-in-zid-zoom-scroll-and-search-mvda86xd'

Reading should display the installed file content and its base directory. It does not run the skill or install its dependencies.

For an agent that reads AGENTS.md, then sync the index. This command changes AGENTS.md: review the diff before using it.

openskills sync

Manual method or another tool: attach SKILL.md to a conversation, or open it in an agent that can read files. Include any required supporting resources. No universal folder is recognized by every tool.

4. Try a first task

Use a short, synthetic input: an API endpoint, a small document or an interface brief. In a new conversation, attach the file or give the agent its path, then adapt this request:

Read .skill-downloads/pdf-view-in-zid-zoom-scroll-and-search-mvda86xd/SKILL.md and apply its instructions to this example: [describe a test input]. Identify any missing files, dependencies or access. Produce the result requested by the skill without inventing data or running unreviewed scripts.

Expected result: output matching the skill instructions, or a precise list of missing prerequisites. A read confirmation alone does not validate a first use. If an external resource is unavailable, that path remains untested.

Related skills