Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions pass/Main.qml
Original file line number Diff line number Diff line change
@@ -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, {});
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not add your launcher provider this way, create an object called LauncherProvider and provide that in the manifest itself. The plugins aren't the ones responsible with registering and unregistering their launcher providers from / to the shell.

}

Component.onDestruction: {
LauncherProviderRegistry.unregisterPluginProvider(pluginApi.pluginId);
}
}
93 changes: 93 additions & 0 deletions pass/README.md
Original file line number Diff line number Diff line change
@@ -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 <query>` to fuzzy search your password store and copy a password to clipboard
- **OTP search** — type `po <query>` 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
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is the only subsection in the Installation section, it might confuse users. I would suggest either skip the installation section or add a small part saying that they can just download the plugin from the noctalia settings.


```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 <query>` | Fuzzy search all pass entries — press Enter to copy the password |
| `po <query>` | 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 <entry>` or `pass otp -c <entry>` 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 <entry>`

**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)
20 changes: 20 additions & 0 deletions pass/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"id": "pass",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pass might be a too small name I would say. Maybe password-store, or something similar like that.

"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 <name>' to copy a password or 'po <name>' to copy a TOTP code to clipboard.",
"tags": ["Launcher", "Utility"],
"entryPoints": {
"main": "Main.qml"
},
"dependencies": {
"plugins": []
},
"metadata": {
"defaultSettings": {}
}
}