github dheerajshenoy/lektra v0.7.6

5 hours ago

New Features

  • narrow_to_section command (and Lua API): Narrow the view to a named section from the document outline. With no arguments a picker dialog lists all outline entries (indented to show hierarchy); with an argument the first exact then substring match is used. The narrow region extends from the section's start page through the page where the next non-descendant section begins (inclusive), so overflow content that spills onto the next heading's page is always visible. Works with embedded, generated, and loaded outlines. Also exposed to Lua as view:narrow_to_section(title). Descendant detection uses both depth (properly nested outlines) and title prefix (flat PDF outlines where sub-entries share the parent's depth level).

  • narrow_to_pages command (and Lua API): Narrow the view to an inclusive range of 1-indexed pages instead of a single-page rubber-banded region. Invoke as narrow_to_pages 5 10 or narrow_to_pages 5-10; the narrow rect becomes the union of the scene rects of every page in the range, scrolling and rendering focus stay inside the range, and search is already page-range aware so it only searches within those pages. widen_region exits pages-narrow the same way it exits region-narrow. Also exposed to Lua as view:narrow_to_pages(start, end).

  • search_below and search_above commands (and Lua APIs): Two new directional-search commands scope the search to a page range relative to the current page. search_below searches from m_pageno forward (inclusive); search_above searches from page 0 through m_pageno (inclusive). Both accept an optional inline term (searches immediately) or open the search bar with the direction primed — Enter then runs the scoped search. The scope is one-shot: after the search is dispatched, DocumentView reverts to full-document scope so the next plain search behaves normally. Narrow region mode still takes precedence over the directional scope. Also exposed to Lua as view:search_below(query, regex?) and view:search_above(query, regex?).

  • Narrowed search restricts hits to the narrowed page and region: With a narrow region active, search now runs only on the narrow page (via a new pageTo parameter on Model::search) instead of scanning the entire document. Results are then post-filtered in DocumentView::handleSearchResults / handlePartialSearchResults to drop any hit whose quad center falls outside the narrow rect. The search count reported to the search bar reflects the filtered hits so navigation index/total stays consistent.

  • open_config now handles both config.toml and init.lua: The command opens config.toml when present, falls back to init.lua when only the Lua config exists, and shows a chooser dialog when both are present so users can pick which file to edit.

  • Narrow-region indicator in the statusbar: When the document is in narrow region mode, an orange N badge appears in the left section of the statusbar. Hovering it shows a tooltip explaining how to exit. The badge is cleared automatically when Wide Region is invoked, and is correctly seeded when switching between tabs.

  • Viewport-clipped rendering at high zoom for major performance improvement: When zoom exceeds 2.5×, the render pipeline now clips the MuPDF pixmap to the visible viewport region plus a 50% margin in each direction, instead of rasterising the entire page. At extreme zoom levels (e.g. 8×) the full page can be 50–100× larger than the viewport; only ~2× the viewport area is now rasterised, giving proportional speedups. The clipped image is drawn at the correct offset within the full-page bounding rect so link, annotation, and search overlays remain correctly positioned. A fresh clip is requested automatically after each scroll or zoom change.

  • Region selection and context menu now available in image mode: Region select mode can now be activated when viewing images (JPEG, PNG, GIF, etc.) via the mode menu or keyboard shortcut. Drawing a rubber band shows the context menu with all compatible actions. The context menu adapts to the file type: "Copy Region as Image (Custom DPI)" is hidden for pure raster images (re-rendering at a different DPI is only meaningful for vector/text-based formats), and "Copy Text from Region" is hidden unless the document supports text extraction.

  • Generate outline from document text: When a PDF has no embedded outline, a heuristic outline can be generated via the generate_outline command or the "Generate Outline" menu entry. The algorithm scans all pages for text lines whose maximum character font size is at least outline.generate_heading_ratio times the modal (body) body text size (default 1.2×). Up to outline.generate_max_levels distinct font-size tiers are recognised as heading levels (default 3). The generated outline is shown in the standard outline picker and persists for the lifetime of the document tab — calling ShowOutline (picker_outline) will use it automatically if no real outline exists.

  • Export outline to JSON file: The export_outline command opens a save-file dialog and writes the currently active outline (real PDF outline, or generated/loaded synthetic outline) to a human-readable JSON file. Each entry records title, page (1-based), x, y, and a children array for nested outlines.

  • Load outline from JSON file: The load_outline command opens a file dialog, reads a previously exported outline JSON, and immediately displays it in the outline picker. The loaded outline is stored per-document and persists across tab switches for the session — invoking picker_outline on a document with no embedded outline will automatically fall back to the loaded one.


Removed

  • --check-config CLI flag removed: The TOML config validator maintained a hard-coded whitelist of every valid section and key, which drifted out of sync with the actual config schema as options were added. The flag and its checkConfigFile() implementation have been removed.

Bug Fixes

  • Fix search bar close button leaving search highlights on the page: Clicking the close button in the search bar only called clearFocus() and hide(), so the last search's highlights and index/count labels stayed active until a new search was started. It now also clears the input and emits an empty searchRequested signal — the same path the input takes when its text is emptied — so highlights are dropped and search mode exits cleanly.

  • Fix Visual Line mode not activating when selected via the statusbar mode label: Clicking the mode label to cycle into "Visual Line" only called GraphicsView::setMode(VisualLine), leaving DocumentView::m_visual_line_mode false and never running snapVisualLine() — so the label updated but the mode did nothing. NextSelectionMode now routes through set_visual_line_mode(true) when entering VisualLine and tears it down cleanly when cycling out.

  • Fix applyNarrow restoring text selection mode in image mode: After drawing a narrow region, the mode was restored using TextSelection as the hardcoded fallback when the document's default mode was None. Image documents have None as their default mode (no text layer), so they always ended up in TextSelection after narrowing — a mode that does nothing for images. The fallback now restores RegionSelection for image documents instead.

  • Fix narrow region breaking after document rotation or flip: Two bugs compounded. (1) The dim overlay and scrolling-bounds scene rect were never refreshed after the async re-render that follows a rotation — the overlay stayed frozen at the pre-rotation position until the next scroll. Fixed by calling refreshNarrowVisuals() from renderPageFromImage() when the re-rendered page is the narrow page. (2) The narrow region was stored as normalized fractions of the page's width/height. A 90° rotation swaps the page dimensions, so the same fractions mapped to a completely different visual area. Fixed by geometrically remapping m_narrow_local_normalized before each rotation/flip: 90° CW (l, t, w, h)(t, 1−l−w, h, w); 90° CCW → (1−t−h, l, h, w); FlipH → (1−l−w, t, w, h); FlipV → (l, 1−t−h, w, h).

  • Fix text highlight annotation not applied for short adjacent-line selections: In TextHighlight mode, the annotation was only created when the release-time drag distance exceeded 50 units in scene coordinates. Two bugs compounded: (1) the threshold was measured in scene units, so at high zoom mapToScene compressed the movement — e.g. dragging across two lines at 4× zoom yielded only ~12 scene units, well below 50; (2) even with a corrected screen-pixel threshold, 50 pixels is too large for a typical one-line-down drag (~25 px). The root fix: TextHighlight mode no longer gates annotation creation on the drag threshold at all. The annotation handler already guards via hasTextSelection(), which is false when no mouse movement occurred (no mousemove emission → no selection built), so accidental single-click highlights are still impossible. Additionally, the drag check for TextSelection mode (visual selection overlay) was switched from scene-coordinate distance to screen-pixel distance so it remains zoom-independent.

Don't miss a new lektra release

NewReleases is sending notifications on new releases.