LaussyMenu services
Fabrice P. Lauss𝕪's Web
LaussyMenu
LaussyMenu

LaussyMenu services

This is the catalogue of everything LaussyMenu currently puts under the right-click in Dolphin. The companion page LaussyMenu explains the machinery—the generator, the three silent KF6 traps, the source; this one is the reference for what the entries actually do, what they touch, and which knobs they answer to.

A service here is one executable script in ~/.local/share/laussymenu/actions/. It carries its own metadata in comment headers, and laussymenu install turns the whole directory into the .desktop files KDE reads. Adding a service is dropping a file in; nothing else in the system needs to know about it. The word is KDE's own: a service menu is a desktop entry of Type=Service.

The catalogue

Entry, as it reads in the menu Applies to Version In one line
Shrink for upload (2000 px, q85) images 1.0.2 an upload-sized copy next to the original, which is never touched
Shrink for upload (choose size / quality…) images 1.0.2 the same, after asking for the two numbers
Send to iPad (Notability) PDF, EPUB 1.0.0 serves the file on the LAN so the iPad can fetch it; nothing goes through the web
Rename with timestamp… any file 1.0.0 what F2 does, plus the timestamps F2 cannot do

Entries appear in that order because of their LM-Order header, not alphabetically. Dolphin only shows an entry when every file in the selection matches its MIME types, which is why the image entries disappear the moment a PDF joins the selection.

Shrink for upload

The everyday one. It makes a copy named foo-web.jpg beside foo.jpg and never modifies the original—the whole point being that the original is the negative and this is the print.

What the recipe is careful about:

  • It never enlarges. The geometry is 2000x2000>, and the > means "only if bigger". A 900 px image comes out at 900 px.
  • It never re-encodes upwards. A JPEG that was saved at q60 stays at q60; the quality setting is a ceiling, not a target. Raising quality on an already-lossy file adds bytes and no information.
  • It orients before it strips. -auto-orient bakes the EXIF rotation into the pixels first, because -strip then throws that EXIF away. In the other order, portrait photographs come out on their side.
  • It only keeps PNG when the transparency is real. A PNG whose alpha channel exists but is opaque everywhere becomes a JPEG; one that actually uses transparency stays a PNG and goes through pngquant and optipng.
  • It strips metadata by default, GPS coordinates and camera serial numbers included. LM_KEEP_EXIF=1 if you want them.
  • It never overwrites. An existing foo-web.jpg makes the next one foo-web-2.jpg. It also refuses to build foo-web-web.jpg chains when you shrink a shrunk file.

The notification reports the before/after in pixels and bytes, and the percentage saved over the whole selection.

The choose size / quality variant is the same script: it asks for two numbers in a kdialog box, validates them, and execs the first with those values in the environment. There is one recipe, not two.

Send to iPad (Notability)

Right-click a PDF and it becomes available to the iPad, over the local network only. This constraint is the reason the entry exists in this shape: those documents have no business transiting through Dropbox, a mailbox, or laussy.org.

The file is hard-linked—not copied—into ~/.local/share/laussymenu/ipad/, so spooling a 56 MB thesis costs no disk and no time. A small Python 3 server (stdlib only, no dependencies) is then brought up on port 8642 if it is not already running. On the iPad: open the bookmark, the file is there, Share → Notability.

The hand-off ends with a tap because Notability has no watch folder and no import inbox. Nothing on Linux can push a file into its library. The dropbox:Notability folder is Notability's auto-backup, annotated PDFs flowing out of the iPad, not an entrance.

Two things break this silently, and both need root:

  • The firewall. ufw is active here, so the port must be opened: sudo ufw allow from 192.168.1.0/24 to any port 8642 proto tcp.
  • mDNS advertises the wrong interface. azag.local resolves to 192.168.122.1, the libvirt virbr0 bridge, and iOS then chases a dead address. The fix is deny-interfaces=virbr0 in /etc/avahi/avahi-daemon.conf. This is a general bug on the machine, not a LaussyMenu one. The action sidesteps it by never asking gethostbyname() for the LAN address: it uses a UDP connect() towards the gateway and reads back the source address the kernel picked.

laussymenu doctor checks both.

Rename with timestamp…

This one exists because of a limitation that cannot be worked around from the outside. Dolphin's F2 on a multiple selection opens KIO's RenameFileDialog, and that dialog understands exactly one placeholder. The string is in the library itself:

libKF6KIOWidgets.so.6:  "This a template for new filenames, # is replaced by a number later"

A run of # becomes the sequence number, zero-padded to the width of the run, and that is the entire language. There is no date token, no EXIF token, and no extension point—nothing short of patching KIO would add one. So this service is a stand-in dialog that speaks the same # and adds what was missing:

Token Expands to
#  ##  ### the sequence number, zero-padded to the width of the run—as in F2
%s one timestamp, taken once when the dialog opens, shared by every file in the selection
%m that file's own modification time, so each file gets a different stamp
%n the original name without its extension
%e the original extension, without the dot
%% a literal per-cent sign

The %s/%m distinction is the useful one. %s stamps a batch: forty photographs off a camera all carry the moment you filed them, and sort together. %m stamps each file: the same forty photographs carry the moment each was taken, and sort chronologically.

The dialog opens pre-filled with 20260805-143604_##, so the ordinary case is F2-plus-a-timestamp with nothing to type. The format is LM_RENAME_TSFMT, any strftime string, %Y%m%d-%H%M%S by default.

Extensions are kept automatically, and .tar.gz is kept whole rather than being read as an extension gz on a file called notes.tar. If the pattern contains a . or a %e it is assumed to handle the extension itself and nothing is appended.

Renaming is where a tool of this kind gets people into trouble, so:

  • It shows the whole plan first. Every old → new pair, and you confirm before anything moves.
  • It refuses collisions rather than resolving them. Two files landing on one name, or a name already taken on disk, and nothing is renamed at all—with the offending names listed.
  • It renames in two phases, everything to .lmrename.$$.N and only then to the final names. A pattern that shuffles names within the selection—b → a while a → b—therefore works, where a naive one-by-one mv would eat a file halfway through. It follows that a target which already exists is only a real collision when it is not itself one of the files being renamed away.
  • Every failure path rolls back and reports what it left alone.

For anything heavier—regular expressions, EXIF fields, a live preview—krename is installed and puts itself in the same context menu. This entry is deliberately the small thing: F2 with dates.

The menu's own icon

The submenu itself needs an Icon= in the [Desktop Entry] group, and this is a trap of exactly the family the rest of the project is about. Without one, KDE does not draw nothing—it borrows an icon from the entries inside. The menu therefore looks like whichever action happened to match the selection, and it changes as you right-click different files. Adding Rename with timestamp… made this obvious: being an all/allfiles entry it matches everything, so the whole menu turned into its pencil.

The icon is the name laussymenu, installed at eight sizes from 16 to 256 px in ~/.local/share/icons/hicolor/NxN/apps/. It is a crop of the same photograph as the one at the top of this page, framed tighter: at 16 px the leaded window behind me turns to noise, and the hat has to carry the recognition on its own. LM_MENU_ICON in the environment of laussymenu install overrides it, taking either an icon name or an absolute path.

laussymenu doctor now checks that the name actually resolves in a theme, because an Icon= pointing at nothing fails exactly as silently as no Icon= at all. KDE's own resolver settles it from the command line: kiconfinder6 laussymenu.

Configuration

All of it lives in ~/.config/laussymenu.conf, which is sourced at every run. Changing it needs no reinstall: edit, right-click, done.

Key Default Effect
LM_MAX 2000 longest side in pixels; never enlarges
LM_QUALITY 85 JPEG quality ceiling
LM_SUFFIX -web what the copy's name gets before the extension
LM_KEEP_EXIF 0 1 keeps camera metadata and GPS in the copy
LM_IPAD_PORT 8642 port the hand-off server listens on
LM_IPAD_AUTOOPEN 1 a single file waiting opens directly instead of being listed
LM_IPAD_SPOOL ~/.local/share/laussymenu/ipad where files wait to be collected
LM_RENAME_TSFMT %Y%m%d-%H%M%S strftime format for %s and %m

Adding a service

Drop an executable script in ~/.local/share/laussymenu/actions/, give it the four headers, run laussymenu install, restart Dolphin. That is the whole maintenance story—no .desktop file is ever written by hand.

Header Required What it does
LM-Name yes the label in the menu
LM-Mime yes ;-separated MIME types; all/allfiles for anything
LM-Icon no freedesktop icon name, application-x-executable by default
LM-Order no sort key inside the submenu, 50 by default

The script receives the selected paths as its arguments, exactly as laussymenu run <action> <files…> would from a terminal—which is the way to test one without going through Dolphin at all. Actions are grouped by MIME set when the .desktop files are generated, because KDE puts MimeType on the file rather than on the individual action.

Versions

  1. v°1.0 on 1 August (2026)Shrink for upload, in both variants
  2. v°1.1.0 on 3 August (2026)Send to iPad (Notability)
  3. v°1.2.0 on 5 August (2026)Rename with timestamp…, and this catalogue
  4. v°1.2.1 on 5 August (2026) — the submenu carries its own icon instead of borrowing one from whichever entry matched

To do

Services that belong in here next: convert to PDF and merge PDFs, "copy path to clipboard", checksum, extract audio from a video, and a send to the wiki entry that uploads the shrunk image straight to Special:Upload and puts the [[File:…]] markup on the clipboard.