New Features
LLM assistant (experimental)
- The LLM panel now knows Lektra's Lua API and can drive the app: ask it to do something and it replies with a Lua script you run with the Run button, or automatically with
llm_view.auto_run = true. Scripts run in a restricted environment (noio,os.execute,require,loadorffi) with a time limit, and the result is passed back to the model with your next message. Lua code in replies is syntax highlighted, and every code block can be collapsed by clicking its header. - LLM chat: images can be attached to a message (paste, drop, an image file, the current page, or a region of the page) and are sent to vision models such as Gemini. Chats are now saved and can be reopened from the new History menu; "New chat" starts a fresh one. Turn saving off with
llm_view.save_history = false. - LLM chat: the assistant now acts through tools (run a command, run a Lua script, look up the Lua API on demand) instead of receiving the whole API with every request, which makes requests much smaller. Each action asks for confirmation unless
llm_view.auto_runis on. For models without tool support, setllm_view.tools = falseto get the old behaviour. - LLM chat: LaTeX math in replies is rendered (
$...$inline,$$...$$on its own line), using the bundled MicroTeX library. - LLM chat: hovering a message shows a Copy button that copies it as written, with LaTeX and code blocks as source.
- LLM chat: you can stop a reply while it is coming in. The Send button turns into a Stop button (or press
Esc), and what had arrived stays in the chat. - New
llm_view.font_sizeoption sets the text size of the messages in the LLM panel. - New
llm_view.show_at_startupoption opens the LLM panel when Lektra starts. It replacesllm_view.enabled, which was read but had no effect.
Lua scripting
- Lua scripting now uses a bundled LuaJIT, linked statically and enabled by default on Linux, macOS and Windows, so no Lua installation is needed to build or run Lektra. Scripts run as Lua 5.1 with LuaJIT's extensions:
gotoand thebitlibrary are available, but Lua 5.3+ features such as integer division//,utf8andstring.packare not. - New
lektra.async(function() ... end)lets a script wait for slow things and still read from top to bottom: inside it,lektra.job.await(command),lektra.sleep(seconds)and the generallektra.await(function(resume) ... end)pause it while the window stays responsive. Callbacks that a script hands to timers, jobs, dialogs,view:region_selectand the like now also work when they were set up from inside such a function. - New
lektra.job.async(command, options, callback)runs a command in the background and calls you back with its exit code and output when it finishes, so a slow command no longer freezes the window. It supportscwd,env,stdin,timeout, liveon_stdout/on_stderr, andjob:cancel(). - New Lua functions
lektra.paths.config(),data()andcache()return Lektra's folders (and create them if missing), so scripts do not have to guess~/.config/lektra. - New Lua API:
lektra.clipboard.get()/set(),view:page_text(pageno)for the text of any page, andlektra.bookmarks.add(). New eventsOnModeChanged,OnSessionLoaded,OnBookmarkAddedandOnFileSaved. lektra.ui.file_dialogcan now choose several files ("open_multiple") or a folder ("directory") as well as open and save, and takestitle,directory,filename,filters(a list or text),selected_filter,default_suffixandconfirm_overwrite; it returnsnilwhen cancelled and the chosen filter as a second value. The documentedtitle,directoryandfilteroptions used to be ignored. Existing calls keep working.- Lua scripts can now work with annotations:
view:annotations(page)lists them (type, position, colour, comment and, for highlights, the text),view:add_highlight,view:add_noteandview:add_rectcreate them (with undo), andview:set_annotationandview:remove_annotationchange or remove them. - Lua scripts can add their own pieces to the statusbar with
lektra.statusbar.register(name, function)(refreshed when the page, zoom or file changes, or every few seconds) andlektra.statusbar.set(name, text). Name the segment instatusbar.layoutto place it, otherwise it goes to the right end. - New Lua
Viewfunctions:links,follow_link,link_hints,select_range,select_region,scroll,scroll_to,scroll_position,visible_pages,page_sizeandpage_sizes. - New Lua APIs:
lektra.tabs.renameandTab:rename;View:container()now works, withvsplit,hsplit,views,focus,close_view,close_othersand more;lektra.sessions(list,exists,load,delete),lektra.recent_files.listandlektra.window.open.
Tabs and views
- Multi-select tabs with Ctrl+click (toggle) and Shift+click (range); selected tabs are highlighted.
- Right-clicking a selected tab opens a menu for the group: close, merge into a vertical or horizontal split, move to a new window, or save as a session.
- Tabs with splits get a "Move Splits to Separate Tabs" menu item, which keeps the first split in place and opens the others as tabs at their current page.
- New tab commands:
tabs_select_toggle,tabs_select_all,tabs_select_clear,tabs_close_selected,tabs_merge_vertical,tabs_merge_horizontal,tabs_split_out,tabs_move_to_window,tabs_save_session [name]. Operations act on the selected tabs, or the current tab when nothing is selected. - New
lektra.tabsLua functions mirroring these:selected,select,select_all,clear_selection,close_selected,merge,split_out,move_to_window,save_session. - New
tab_renamecommand to give a tab a custom title. - New
sync_viewcommand links views of a tab so that zooming, scrolling, rotating and fitting (width, height, window) one of them does the same in the others: numbers are drawn on the views, press them to choose,Enterto link,Escto cancel.sync_view_alllinks all views of the tab andsync_view_stopends it. Lua:lektra.view.sync(ids),lektra.view.unsync()andlektra.view.pick_views(), which lets the user pick views and returns their ids.
Reading and selection
- New caret mode (
caret_modecommand, defaultF7) — a keyboard-driven text cursor for character-by-character navigation, cf. Firefox/Okular "caret browsing". Left/Right/Up/Down (alsohjkl), Home/End, and Shift+movement (also vim-styleH/J/K/L) to extend a selection; the existing yank command now has a default binding (y) to copy it. - Hover preview for internal links: with
links.hover_preview = true, resting the cursor on a link shows a picture of what it points at (page, figure or reference) instead of the tooltip. Size and delay arehover_preview_width,hover_preview_heightandhover_preview_delay. - Click-and-drag an image out of a document (PDF) onto another app or a file manager to save or paste it as a PNG. The drag saves what the page shows in that area, including text on scanned/stencil pages, at the image's native resolution, with the current invert, tint and high-contrast settings applied. Anything else the page draws inside that rectangle is included too.
Statusbar
- New statusbar layout:
[statusbar].layout(alsolektra.opt.statusbar.layout) places the modules freely instead of in three fixed groups. Items go left to right and"|",{ stretch = 2 }and{ spacer = 12 }are gaps;{ module = "page", at = 0.25, anchor = "center" }pins a module at a position along the bar; modules takemin_width,max_width,marginandalign;{ text = "•" }adds a separator; a list of lists makes several rows.[statusbar].paddingalso takes a single number for all four sides. - New
zoomstatusbar module showing the current zoom level (e.g.150%); previously[statusbar.components.zoom]existed but nothing was shown.
Documents and export
- New
export_pagescommand and Luaview:export_pages(names, pages, opts): export a range of pages (1-5,8,10-,all,odd,even,current...) as images (PNG by default, or JPEG, WebP, BMP, TIFF), as one PDF, or as SVG, text, HTML, CBZ, DOCX or ODT, chosen by the file extension. Exporting a PDF as PDF keeps the original pages, so the text stays searchable. The command asks for the pages and format (with a checkbox for one file or one file per page, for the formats where that is a choice) and then the location, or takes them as arguments (:export_pages /tmp/p.png 3-5 200, addsplitfor one file per page); the Lua function does not overwrite existing files unlessoverwrite = true. - Open
.cbr(RAR) and.cb7(7z) comic books, in addition to.cbzand.cbt. - New
[reflow]optionsfont_family,font_sizeandline_spacingfor EPUB and other reflowable documents. Any installed font can be used.
Commands and command line
- Add
--new-windowCLI flag to open file(s) in a new window even whensingle_instanceis on. - New
run_last_commandcommand (default key.) repeats the last command that was run, with the same arguments.
Configuration and updates
- Per-view options, like Vim's window-local options: each view and split keeps its own copy of the view settings (page colours, zoom, layout, rendering, scrollbars, selection, search, annotations, links, behaviour).
view:opt()changes one view;lektra.optnow sets the global default and the current view, instead of every view. Splits inherit the options of the view they were split from. - Per-file-type options in
config.toml:[filetype.pdf.behavior],[filetype.epub.layout]and so on override the view settings for documents of that type. - The config file now reports settings with the wrong type (or an invalid colour) in a dialog with the line number, instead of ignoring them silently.
- A JSON Schema for
config.toml(schema/lektra-config.schema.json, installed toshare/lektra/): with a TOML language server such as Taplo it gives completion, option descriptions on hover, and warnings for typos and wrong values.default_config.tomlnow points at it. - Lektra now checks once a day whether a newer release is available and says so in a small banner (one request to the GitHub releases API, nothing about you is sent). Turn it off with
[updates] check = false. Thecheck_for_updatescommand (and Help ▸ Check for Updates) checks on demand. - New "What's new" list of changes for the running version (
whats_newcommand, Help ▸ What's New). With[updates] whats_new = true, a banner also offers it after an update (off by default). - A rare, dismissible banner reminds that Lektra can be supported: only after a few weeks of use, at most twice a year, and "Don't ask again" ends it.
[donate] reminders = falseturns it off.
Bug Fixes
- Fixed double and triple click selection: a double click counted as a single click (so the word was only selected on the third click), and a slight movement of the mouse afterwards replaced the selected word or line with a character range. The text of a double or triple click selection (copy,
view:selection_text()) could also come from the wrong place and contain far more than the selected word. - Fixed colour of note annotations: reading or changing it failed ("Text annotations have no IC property").
- Animated PNG (APNG) files now play. Qt only reads the first frame of an APNG, so Lektra decodes the animation itself, one frame at a time, following the file's frame delays, blend and dispose settings and loop count.
- Fix crash (SIGSEGV) on quit when the command palette had been opened and Lua commands were registered via
lektra.cmd.register:CommandPickerstores a by-value copy of the commands vector, whoseCommandlambdas holdshared_ptr<LuaRefGuard>refs. BecauseCommandPickeris a child widget ofLektra, Qt destroyed it inside~QWidget()— afterlua_close()had already freed the Lua state. TheLuaRefGuarddestructor then calledluaL_unrefon the dead state and segfaulted. Fixed by explicitly deletingm_command_pickerat the start ofLektra::~Lektra(), beforelua_close(). - Fix deep zoom being very slow: in single-page layout the page is now rescaled while zooming and re-rendered sharp once you stop, and very large pages are only rendered where they are visible.
- Fix the
url_regexstring in the default_config.toml file - Fix scrolling through image-heavy/scanned documents re-rendering every visible page on each scroll tick, even when nothing changed; pages already rendered with identical settings are now left alone.
- Fix
remember_last_visitednot restoring the last page on large documents (e.g. thousands of pages), and not restoring it at all when a file is opened from the command line. The saved page is now applied once the document's layout is ready, and--commandsteps run after it. - Fix the macOS
build_dmg.shfailing when Homebrew's Qt tools aren't onPATHor the unversionedqtpathssymlink is missing; it now resolves and fixes both automatically. - Fix diagonal colour stripes and blotches on scanned pages when
behavior.dont_invert_imagesis enabled together withinvert_modeorhigh_contrast. Images are now restored exactly as they render normally, so a scanned page that is entirely one image is left un-inverted while that option is on. - Fix crash reports being useless on a normal (Release) build — no debug info was kept and the app's own functions weren't even resolvable by name. Crash reports now include a properly symbolized stack trace (function names + file/line) alongside the raw one.
- Search on a document with no text layer (e.g. a scanned PDF) now shows an information box explaining that it can't be searched, instead of silently finding nothing.
- Fix a crash when reading the selected text while nothing is selected (for example
view:selection_text()from Lua).