From d4d1d659b4fefa81c5c05b9370f69ef7bc93d5ce Mon Sep 17 00:00:00 2001 From: Bojan Jovanovic Date: Sat, 16 May 2026 16:43:12 +0200 Subject: [PATCH 1/2] Adds a launcher intercept that integrates with linux passwordstore --- pass/Main.qml | 98 ++++++++++++++++++++++++++++++++++++++++++++++ pass/README.md | 93 +++++++++++++++++++++++++++++++++++++++++++ pass/manifest.json | 20 ++++++++++ registry.json | 16 ++++++++ 4 files changed, 227 insertions(+) create mode 100644 pass/Main.qml create mode 100644 pass/README.md create mode 100644 pass/manifest.json diff --git a/pass/Main.qml b/pass/Main.qml new file mode 100644 index 000000000..0d9c752b5 --- /dev/null +++ b/pass/Main.qml @@ -0,0 +1,98 @@ +import QtQuick +import Quickshell +import Quickshell.Io +import qs.Commons +import qs.Services.UI + +QtObject { + id: root + property var pluginApi: null + + property Component _providerComponent: Component { + Item { + id: provider + property var launcher: null + property var pluginApi: null + property string name: "Password Store" + property bool handleSearch: true + property var passwords: [] + + function init() { + listProcess.running = true; + } + + function onOpened() { + if (passwords.length === 0) + listProcess.running = true; + } + + function getResults(query) { + const isPass = query === "pass" || query.startsWith("pass "); + const isOtp = query === "po" || query.startsWith("po "); + if (!isPass && !isOtp) + return []; + + const search = isPass + ? (query.startsWith("pass ") ? query.substring(5).trim() : "") + : (query.startsWith("po ") ? query.substring(3).trim() : ""); + + const otp = isOtp; + + let matches; + if (!search) { + matches = passwords.slice(0, 15).map(p => ({ target: p, score: 1.0 })); + } else { + const results = FuzzySort.go(search, passwords, { limit: 15 }); + matches = Array.from({ length: results.length }, (_, i) => results[i]); + } + + return matches.map(function(match) { + const passName = match.target; + return { + "name": passName, + "description": otp ? "Copy OTP to clipboard" : "Copy password to clipboard", + "icon": otp ? "clock-shield" : "lock", + "isTablerIcon": true, + "isImage": false, + "_score": match.score, + "onActivate": function() { + if (provider.launcher) + provider.launcher.closeImmediately(); + Qt.callLater(function() { + Quickshell.execDetached(otp + ? ["pass", "otp", "-c", passName] + : ["pass", "-c", passName]); + }); + } + }; + }); + } + + Process { + id: listProcess + command: [ + "sh", "-c", + "store=\"${PASSWORD_STORE_DIR:-$HOME/.password-store}\"; " + + "find \"$store\" -name '*.gpg' | sed \"s|$store/||;s|\\.gpg$||\" | sort" + ] + running: false + + stdout: StdioCollector { + onStreamFinished: { + if (text && text.trim()) + provider.passwords = text.trim().split("\n").filter(p => p.length > 0); + } + } + stderr: StdioCollector {} + } + } + } + + Component.onCompleted: { + LauncherProviderRegistry.registerPluginProvider(pluginApi.pluginId, _providerComponent, {}); + } + + Component.onDestruction: { + LauncherProviderRegistry.unregisterPluginProvider(pluginApi.pluginId); + } +} diff --git a/pass/README.md b/pass/README.md new file mode 100644 index 000000000..7518029e1 --- /dev/null +++ b/pass/README.md @@ -0,0 +1,93 @@ +# Password Store + +Search and copy passwords and OTP codes from [pass](https://www.passwordstore.org/) directly in the Noctalia launcher. + +## Features + +- **Password search** — type `pass ` to fuzzy search your password store and copy a password to clipboard +- **OTP search** — type `po ` to fuzzy search your password store and copy a TOTP code to clipboard +- **Fuzzy matching** — uses Noctalia's built-in FuzzySort engine, consistent with the app launcher +- **Clipboard safety** — passwords and OTP codes are cleared from clipboard after 45 seconds (pass default) +- **XDG-aware** — respects `$PASSWORD_STORE_DIR` with fallback to `~/.password-store` + +## Requirements + +- **System packages:** `pass`, `pass-otp` +- **Wayland clipboard:** `wl-clipboard` (for `wl-copy` support — recommended on Wayland) + +### Installing dependencies + +```bash +# Arch Linux / Manjaro +sudo pacman -S pass pass-otp wl-clipboard + +# Debian / Ubuntu +sudo apt install pass pass-extension-otp wl-clipboard + +# Fedora +sudo dnf install pass pass-otp wl-clipboard +``` + +## Installation + +### Manual Installation + +```bash +git clone https://github.com/noctalia-dev/noctalia-plugins ~/.config/noctalia/plugins/pass +``` + +Then restart Noctalia: + +```bash +qs kill -c noctalia-shell && qs -c noctalia-shell -d +``` + +## Usage + +Open the launcher (`Alt+Space` by default) and type: + +| Prefix | Action | +|--------|--------| +| `pass ` | Fuzzy search all pass entries — press Enter to copy the password | +| `po ` | Fuzzy search all pass entries — press Enter to copy the TOTP code | + +### Examples + +``` +pass kortechs/aws → finds kortechs/aws/bojan@kortechs.io +pass aws bojan → finds any entry matching both "aws" and "bojan" +po github → finds OTP entry for github, copies the current TOTP code +``` + +Selecting an entry runs `pass -c ` or `pass otp -c ` respectively. If your GPG key requires a passphrase, a pinentry dialog will appear. + +## File Structure + +``` +pass/ +├── manifest.json # Plugin metadata +├── Main.qml # Launcher provider — search and activation logic +└── README.md # This file +``` + +## Troubleshooting + +**No results appear** +- Make sure `pass` is installed and `~/.password-store` exists (or `$PASSWORD_STORE_DIR` is set) +- Restart Noctalia after installing the plugin + +**OTP copy fails silently** +- Verify `pass-otp` is installed: `pass otp --help` +- Test manually in a terminal: `pass otp -c ` + +**Password not copied on Wayland** +- Install `wl-clipboard`: `sudo pacman -S wl-clipboard` +- `pass` detects Wayland via `$WAYLAND_DISPLAY` and uses `wl-copy` automatically + +## License + +MIT + +## Author + +**Bojan Jovanovic** - [github.com/virogenesis](https://github.com/virogenesis) diff --git a/pass/manifest.json b/pass/manifest.json new file mode 100644 index 000000000..af551b177 --- /dev/null +++ b/pass/manifest.json @@ -0,0 +1,20 @@ +{ + "id": "pass", + "name": "Password Store", + "version": "1.0.0", + "minNoctaliaVersion": "3.6.0", + "author": "Bojan Jovanovic", + "license": "MIT", + "repository": "https://github.com/noctalia-dev/noctalia-plugins", + "description": "Search pass (password-store) from the launcher. Type 'pass ' to copy a password or 'po ' to copy a TOTP code to clipboard.", + "tags": ["Launcher", "Utility"], + "entryPoints": { + "main": "Main.qml" + }, + "dependencies": { + "plugins": [] + }, + "metadata": { + "defaultSettings": {} + } +} diff --git a/registry.json b/registry.json index 7b3ae8f54..17ae1c13e 100644 --- a/registry.json +++ b/registry.json @@ -1299,6 +1299,22 @@ ], "lastUpdated": "2026-04-21T17:26:58+02:00" }, + { + "id": "pass", + "name": "Password Store", + "version": "1.0.0", + "official": false, + "author": "Bojan Jovanovic", + "description": "Search pass (password-store) from the launcher. Type 'pass ' to copy a password or 'po ' to copy a TOTP code to clipboard.", + "repository": "https://github.com/virogenesis/noctalia-pass-plugin", + "minNoctaliaVersion": "3.6.0", + "license": "MIT", + "tags": [ + "Launcher", + "Utility" + ], + "lastUpdated": "2026-05-16T16:25:57+02:00" + }, { "id": "plugin-manager", "name": "Plugin Manager", From 20cd7b272f374c4edf0595b94d01eca1daabc1b4 Mon Sep 17 00:00:00 2001 From: Bojan Arch Jovanovic Date: Sat, 16 May 2026 23:46:01 +0200 Subject: [PATCH 2/2] (H) Line 1804: Do not add the 'official' property. It's automatically false and is NOT needed! (H) Line 1309: The repository field needs to point to the "https://github.com/noctalia-dev/noctalia-plugins" github repository! Signed-off-by: Bojan Arch Jovanovic --- registry.json | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/registry.json b/registry.json index 17ae1c13e..7b3ae8f54 100644 --- a/registry.json +++ b/registry.json @@ -1299,22 +1299,6 @@ ], "lastUpdated": "2026-04-21T17:26:58+02:00" }, - { - "id": "pass", - "name": "Password Store", - "version": "1.0.0", - "official": false, - "author": "Bojan Jovanovic", - "description": "Search pass (password-store) from the launcher. Type 'pass ' to copy a password or 'po ' to copy a TOTP code to clipboard.", - "repository": "https://github.com/virogenesis/noctalia-pass-plugin", - "minNoctaliaVersion": "3.6.0", - "license": "MIT", - "tags": [ - "Launcher", - "Utility" - ], - "lastUpdated": "2026-05-16T16:25:57+02:00" - }, { "id": "plugin-manager", "name": "Plugin Manager",