Skip to content

Commit d4d1d65

Browse files
author
Bojan Jovanovic
committed
Adds a launcher intercept that integrates with linux passwordstore
1 parent 358191e commit d4d1d65

4 files changed

Lines changed: 227 additions & 0 deletions

File tree

pass/Main.qml

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import QtQuick
2+
import Quickshell
3+
import Quickshell.Io
4+
import qs.Commons
5+
import qs.Services.UI
6+
7+
QtObject {
8+
id: root
9+
property var pluginApi: null
10+
11+
property Component _providerComponent: Component {
12+
Item {
13+
id: provider
14+
property var launcher: null
15+
property var pluginApi: null
16+
property string name: "Password Store"
17+
property bool handleSearch: true
18+
property var passwords: []
19+
20+
function init() {
21+
listProcess.running = true;
22+
}
23+
24+
function onOpened() {
25+
if (passwords.length === 0)
26+
listProcess.running = true;
27+
}
28+
29+
function getResults(query) {
30+
const isPass = query === "pass" || query.startsWith("pass ");
31+
const isOtp = query === "po" || query.startsWith("po ");
32+
if (!isPass && !isOtp)
33+
return [];
34+
35+
const search = isPass
36+
? (query.startsWith("pass ") ? query.substring(5).trim() : "")
37+
: (query.startsWith("po ") ? query.substring(3).trim() : "");
38+
39+
const otp = isOtp;
40+
41+
let matches;
42+
if (!search) {
43+
matches = passwords.slice(0, 15).map(p => ({ target: p, score: 1.0 }));
44+
} else {
45+
const results = FuzzySort.go(search, passwords, { limit: 15 });
46+
matches = Array.from({ length: results.length }, (_, i) => results[i]);
47+
}
48+
49+
return matches.map(function(match) {
50+
const passName = match.target;
51+
return {
52+
"name": passName,
53+
"description": otp ? "Copy OTP to clipboard" : "Copy password to clipboard",
54+
"icon": otp ? "clock-shield" : "lock",
55+
"isTablerIcon": true,
56+
"isImage": false,
57+
"_score": match.score,
58+
"onActivate": function() {
59+
if (provider.launcher)
60+
provider.launcher.closeImmediately();
61+
Qt.callLater(function() {
62+
Quickshell.execDetached(otp
63+
? ["pass", "otp", "-c", passName]
64+
: ["pass", "-c", passName]);
65+
});
66+
}
67+
};
68+
});
69+
}
70+
71+
Process {
72+
id: listProcess
73+
command: [
74+
"sh", "-c",
75+
"store=\"${PASSWORD_STORE_DIR:-$HOME/.password-store}\"; " +
76+
"find \"$store\" -name '*.gpg' | sed \"s|$store/||;s|\\.gpg$||\" | sort"
77+
]
78+
running: false
79+
80+
stdout: StdioCollector {
81+
onStreamFinished: {
82+
if (text && text.trim())
83+
provider.passwords = text.trim().split("\n").filter(p => p.length > 0);
84+
}
85+
}
86+
stderr: StdioCollector {}
87+
}
88+
}
89+
}
90+
91+
Component.onCompleted: {
92+
LauncherProviderRegistry.registerPluginProvider(pluginApi.pluginId, _providerComponent, {});
93+
}
94+
95+
Component.onDestruction: {
96+
LauncherProviderRegistry.unregisterPluginProvider(pluginApi.pluginId);
97+
}
98+
}

pass/README.md

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# Password Store
2+
3+
Search and copy passwords and OTP codes from [pass](https://www.passwordstore.org/) directly in the Noctalia launcher.
4+
5+
## Features
6+
7+
- **Password search** — type `pass <query>` to fuzzy search your password store and copy a password to clipboard
8+
- **OTP search** — type `po <query>` to fuzzy search your password store and copy a TOTP code to clipboard
9+
- **Fuzzy matching** — uses Noctalia's built-in FuzzySort engine, consistent with the app launcher
10+
- **Clipboard safety** — passwords and OTP codes are cleared from clipboard after 45 seconds (pass default)
11+
- **XDG-aware** — respects `$PASSWORD_STORE_DIR` with fallback to `~/.password-store`
12+
13+
## Requirements
14+
15+
- **System packages:** `pass`, `pass-otp`
16+
- **Wayland clipboard:** `wl-clipboard` (for `wl-copy` support — recommended on Wayland)
17+
18+
### Installing dependencies
19+
20+
```bash
21+
# Arch Linux / Manjaro
22+
sudo pacman -S pass pass-otp wl-clipboard
23+
24+
# Debian / Ubuntu
25+
sudo apt install pass pass-extension-otp wl-clipboard
26+
27+
# Fedora
28+
sudo dnf install pass pass-otp wl-clipboard
29+
```
30+
31+
## Installation
32+
33+
### Manual Installation
34+
35+
```bash
36+
git clone https://github.com/noctalia-dev/noctalia-plugins ~/.config/noctalia/plugins/pass
37+
```
38+
39+
Then restart Noctalia:
40+
41+
```bash
42+
qs kill -c noctalia-shell && qs -c noctalia-shell -d
43+
```
44+
45+
## Usage
46+
47+
Open the launcher (`Alt+Space` by default) and type:
48+
49+
| Prefix | Action |
50+
|--------|--------|
51+
| `pass <query>` | Fuzzy search all pass entries — press Enter to copy the password |
52+
| `po <query>` | Fuzzy search all pass entries — press Enter to copy the TOTP code |
53+
54+
### Examples
55+
56+
```
57+
pass kortechs/aws → finds kortechs/aws/bojan@kortechs.io
58+
pass aws bojan → finds any entry matching both "aws" and "bojan"
59+
po github → finds OTP entry for github, copies the current TOTP code
60+
```
61+
62+
Selecting an entry runs `pass -c <entry>` or `pass otp -c <entry>` respectively. If your GPG key requires a passphrase, a pinentry dialog will appear.
63+
64+
## File Structure
65+
66+
```
67+
pass/
68+
├── manifest.json # Plugin metadata
69+
├── Main.qml # Launcher provider — search and activation logic
70+
└── README.md # This file
71+
```
72+
73+
## Troubleshooting
74+
75+
**No results appear**
76+
- Make sure `pass` is installed and `~/.password-store` exists (or `$PASSWORD_STORE_DIR` is set)
77+
- Restart Noctalia after installing the plugin
78+
79+
**OTP copy fails silently**
80+
- Verify `pass-otp` is installed: `pass otp --help`
81+
- Test manually in a terminal: `pass otp -c <entry>`
82+
83+
**Password not copied on Wayland**
84+
- Install `wl-clipboard`: `sudo pacman -S wl-clipboard`
85+
- `pass` detects Wayland via `$WAYLAND_DISPLAY` and uses `wl-copy` automatically
86+
87+
## License
88+
89+
MIT
90+
91+
## Author
92+
93+
**Bojan Jovanovic** - [github.com/virogenesis](https://github.com/virogenesis)

pass/manifest.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"id": "pass",
3+
"name": "Password Store",
4+
"version": "1.0.0",
5+
"minNoctaliaVersion": "3.6.0",
6+
"author": "Bojan Jovanovic",
7+
"license": "MIT",
8+
"repository": "https://github.com/noctalia-dev/noctalia-plugins",
9+
"description": "Search pass (password-store) from the launcher. Type 'pass <name>' to copy a password or 'po <name>' to copy a TOTP code to clipboard.",
10+
"tags": ["Launcher", "Utility"],
11+
"entryPoints": {
12+
"main": "Main.qml"
13+
},
14+
"dependencies": {
15+
"plugins": []
16+
},
17+
"metadata": {
18+
"defaultSettings": {}
19+
}
20+
}

registry.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1299,6 +1299,22 @@
12991299
],
13001300
"lastUpdated": "2026-04-21T17:26:58+02:00"
13011301
},
1302+
{
1303+
"id": "pass",
1304+
"name": "Password Store",
1305+
"version": "1.0.0",
1306+
"official": false,
1307+
"author": "Bojan Jovanovic",
1308+
"description": "Search pass (password-store) from the launcher. Type 'pass <name>' to copy a password or 'po <name>' to copy a TOTP code to clipboard.",
1309+
"repository": "https://github.com/virogenesis/noctalia-pass-plugin",
1310+
"minNoctaliaVersion": "3.6.0",
1311+
"license": "MIT",
1312+
"tags": [
1313+
"Launcher",
1314+
"Utility"
1315+
],
1316+
"lastUpdated": "2026-05-16T16:25:57+02:00"
1317+
},
13021318
{
13031319
"id": "plugin-manager",
13041320
"name": "Plugin Manager",

0 commit comments

Comments
 (0)