Omahub
← All plugins
M

QR generator

by Mahmood Ihab Khalil

Turn a link into a styled QR code from the bar — offline, with a scan check

Security review

Review recommended · 1 finding

Deterministic scan — not a security guarantee

Low
Risk level
Low
Analyzed commit
5935958
Scanned
1 month ago
  • low obfuscation renderer.js:49

    Augments a command with octal/hex escape sequences.

    \6HV_6IXb7K[e8N^i9Pam;Rdp<Tgt".charCodeAt(n) - 35, i = n > 8 ? o : 1, s = r / i | 0, a = r % i, l = i - a, c = n > 8 ? s * ye[t] + (e > 5) & -2 : o, u = s - c; return { t: l * u + a * u + a, o: [[l, u

Automated analysis only — not a security guarantee.

AI advisory review

No obvious issues detected

Language-model assessment · ~deepseek/deepseek-v4-flash-latest — advisory only

Low
AI risk level
Low
Recommendation
install
Model
~deepseek/deepseek-v4-flash-latest
Analyzed commit
5935958
Reviewed
1 month ago

The plugin is a QR code generator bar widget with clearly documented functionality. The deterministic scan's obfuscation finding is a false positive from minified bundled library code (qrgen/lean-qr), not malicious obfuscation. All subprocesses are optional, well-scoped, and include proper input validation and size limits; no destructive or hidden behavior was found.

  • The renderer.js file is heavily minified, which triggered the obfuscation rule, but it is a standard bundled build artifact of the qrgen and lean-qr libraries, as documented in THIRD-PARTY.md.
  • Link compression sends the user's link to the third-party ha.mr service, but this is optional, clearly disclosed, and only activated when the user enables it.
  • The asset picker script reads arbitrary files, but it validates that the target is a regular readable image file under 2 MiB, so it is not a vector for reading sensitive data without user intent.
How this check works

This review combines the deterministic scan (the rule-based results above) with an independent look at the plugin's code by a language model. The model reads a trimmed sample of the repository's files, the manifest, and the README, then gives a plain-language risk level and a recommendation: install (no notable danger), review (look closer first), or avoid (clearly dangerous).

It runs on the same analyzed commit as the deterministic scan and is strictly advisory — it is not a security guarantee and never blocks a plugin by itself. A human moderator still reviews plugins before they are listed.

AI advisory only — automated analysis, not a security guarantee.

Install
$ omarchy plugin add https://github.com/MahmoodKhalil57/omarchy-qrgen --enable
Widgets #bar #quickshell

QR generator

An Omarchy bar widget that turns a link into a QR code in a dropdown under its own icon. Styled codes, a centre asset, and optional link compression — and it checks that what it drew actually scans.

the panel

Left click opens it with the link field focused. Escape, or a click outside, closes it.

Install

omarchy plugin add https://github.com/MahmoodKhalil57/omarchy-qrgen --enable

That clones it into ~/.config/omarchy/plugins/, registers it, and puts the icon in the bar. omarchy bar move mahmoodkhalil57.qrgen --section right moves it; the shell picks up changes without a restart.

To update: omarchy plugin update mahmoodkhalil57.qrgen.

Remove

omarchy plugin remove mahmoodkhalil57.qrgen

That takes the icon out of the bar and deletes the plugin directory. Nothing is left behind anywhere else: the plugin writes only its own entry in ~/.config/omarchy/shell.json, which goes with it, and saved codes are ordinary files in ~/Pictures that are yours to keep or delete.

How it runs

The generator runs inside the shell's own JavaScript engine. There is no build step, no node_modules, no daemon and no subprocess: a code takes about twenty milliseconds and is handed to the preview as a string, so nothing is written to disk to show you a QR code.

renderer.js is a bundle of qrgen's generator — the same shape registries, colour handling and centre-asset geometry as the web app it comes from, rather than a reimplementation that would drift from it. The shape dropdowns are built from that bundle, so a shape added upstream appears here without this plugin changing.

Two optional things use programs you may already have:

Feature Needs Without it
Copy / Save PNG librsvg, wl-clipboard The buttons report what is missing
Scan check zbar, librsvg Silently skipped
Link compression node or bun The option says so and stays off

Omarchy already ships the first two.

Customize

Body, eye-frame and eye-ball shapes; foreground and background colours, with separate eye colours that fall back to the foreground when left blank; a transparent background; and a centre asset chosen by path or through Omarchy's image picker, with size, padding, punch-out and backdrop.

The asset is inlined into the SVG, so the code renders the same everywhere it is opened — the preview, the exported PNG, and the file itself.

Advanced

Error correction, quiet zone and link compression, each with an Automatic switch. Turn one on and the control below is replaced by a line saying what was chosen and why; turn it off and the manual control returns holding its value.

Automatic decides by measuring. A QR code's size is a step function of its payload, so the only way to know whether compression helps is to encode it both ways and compare:

  • Error correction — the strongest level the payload carries without the code getting bigger. A centre asset raises the floor to Q, since the code has to be readable through a hole.
  • Quiet zone — four modules, which is what the specification asks for. There is no cleverer number; automatic exists so it cannot be left somewhere unhelpful.
  • Link compression — every candidate is built and measured. Smallest wins; ties go to whatever depends on least, so a plain link beats a redirect and a redirect beats emoji.

Emoji therefore never wins on its own merits, and cannot: emoji are UTF-8 and a QR code is charged by the byte, so an emoji payload reliably makes a bigger code even though the link reads shorter.

Where automatic earns its keep is the interaction. With a centre asset it prefers a payload that keeps the code under version 7 — the point where an asset starts landing on the middle alignment pattern — so a link that would otherwise produce a handsome unscannable code gets compressed into one that works:

ha.mr · 37×37 instead of 45×45 · keeps the asset off the centre alignment pattern

All three are on by default. Compression is the one with a consequence outside the code — a compressed link is a redirect through ha.mr, so it is only as durable as that service — which is why the panel always states what it chose and why, and the switch to turn it off is right beside the answer. It only ever turns compression on when doing so makes a genuinely smaller code.

Codes that do not scan

A centre asset lands on the middle alignment pattern from version 7 up. That is structure, not payload, so error correction cannot put it back — and nothing about the picture says the code is dead.

So every render is read back: the SVG is rasterised onto white and decoded, and the panel says plainly when what came out is not what went in. Transparent backgrounds do not trip it, and it stays quiet where there is no decoder.

Link compression

Optional, and the only part that needs a runtime present. It compresses the link with ha.mr and encodes a short redirect instead of the URL — a smaller code, at the cost of a link that only works while that redirector does.

Compressed codes point at ha.mr unless you set siteRoot to somewhere running qrgen's own resolver, in which case that becomes a second choice in the dropdown. There is no default site: a plugin should not quietly route other people's links through somebody's personal domain.

Settings

Every option is remembered on the widget's entry in ~/.config/omarchy/shell.json, written as it changes. Which sections were open is remembered too.

{
  "id": "mahmoodkhalil57.qrgen",
  "correctionAuto": true,
  "marginAuto": true,
  "compressAuto": true,
  "siteRoot": "",
  "assetDirs": "~/Pictures\n~/Downloads"
}

assetDirs is newline separated and sets where the asset picker looks.

Over IPC

omarchy-shell mahmoodkhalil57.qrgen toggle
omarchy-shell mahmoodkhalil57.qrgen encode "$(wl-paste)"   # open holding a link
omarchy-shell mahmoodkhalil57.qrgen copy                   # PNG to the clipboard
omarchy-shell mahmoodkhalil57.qrgen save                   # PNG to ~/Pictures
omarchy-shell mahmoodkhalil57.qrgen level H                # L | M | Q | H

encode is the one worth a keybinding — it makes "QR the link I just copied" a single keystroke.

Rebuilding renderer.js

renderer.js is a committed build artefact — bundling the generator from qrgen so the panel draws codes with the app's own shape registries and geometry rather than a reimplementation.

The script that produces it lives with the source it bundles, in that repository under tools/, not here: this plugin ships the built file and nothing that builds it, so installing it never involves a toolchain. See THIRD-PARTY.md for what goes into the bundle and what is done to it.

Licence

MIT — see LICENSE. Bundled and vendored code is MIT too; THIRD-PARTY.md lists what is included, whose it is, and what was done to it.