feat: add bookmarks plugin (#132)

* dunarand/bookmarks plugin

* fix: incorrect thumbnail.webp name

* fix: corrected plugin.toml

Removed invalid tag and noctalia version field

* fix: Added missing `bar` widget entry to plugin section

* docs: Added keybinds table

* feat: added search IPC

Directly initiates the search mode on noctalia msg panel-toggle
dunarand/bookmarks:panel search

* feat: drag bookmarks into/out of folders

* fix+style: fixed regressing drag space highlight + renamed functions due to linter complaints

* refactor:style: code cleanup & helper functions

* chore: translation strings

* feat: Turkish translation strings

* chore: added nohup as dependency, bumped the version

* style: formatting

* docs: updated README to include nohup dependency
This commit is contained in:
mgu
2026-07-29 14:44:43 -04:00
committed by GitHub
parent ae86b39770
commit e02b4f3c9f
13 changed files with 1730 additions and 0 deletions
+121
View File
@@ -0,0 +1,121 @@
# Bookmarks
A bookmarks/shortcuts plugin for Noctalia. You can add folders and commands to a list of bookmarks.
Reorder bookmarks, categorize them with folders, set glyphs and labels, and simply left click to
execute. All of it is user defined, stored in a JSON file you can backup.
## Plugin
| Field | Value |
| ------- | ---------------------------------- |
| ID | `dunarand/bookmarks` |
| Entries | Bar widget: `bar`; panel: `panel`; |
Bar widget `bar` is not required. Instead, you can assign a keybind to directly open the `panel`,
which is shown in the Usage section.
## Requirements
- Noctalia v5.0.0 or higher
- `nohup` (Optional): For "Run in background" wrapper toggle
## Usage
You can add the widget to your bar or assign the following command to your compositor keybinds. For
example, in Hyprland v0.55+
```
hl.bind(
"SUPER + SHIFT + B",
hl.dsp.exec_cmd("noctalia msg panel-toggle dunarand/bookmarks:panel")
)
```
You can interact with the bookmarks list via keybinds:
| Keybind | Purpose |
| ------------------------ | ---------------------------------------------------------------- |
| `CTRL + N` or Down Arrow | Select the next (below) item |
| `CTRL + P` or Up Arrow | Select the previous (above) item |
| `CTRL + S` | Search bookmarks |
| `Enter` / `Return` | Execute the selected bookmark's command / Navigate into a folder |
- Bookmarks and folders are listed in the main panel.
![](./assets/preview-1.png)
- "?" button shows tooltips: bookmark command and its description.
![](./assets/preview-2.png)
- You can create or edit bookmarks by assigning them a glyph, a label, a command, and an
optional description.
![](./assets/preview-3.png)
- "Run in background" toggle wraps the command you defined in the following way:
`nohup <bookmark-command> >/dev/null 2>&1 &`
For example, instead of typing the whole command
`nohup xdg-open "$HOME" >/dev/null 2>&1 &`
each time, you can instead define the command as `xdg-open "$HOME"` and toggle "Run in
background" switch.
- "Run in terminal" switch executes the command with the default terminal. These switches are
mutually exclusive so you can only choose one. Toggling on a switch will result the other to
turn off.
- You can create folders and nest other bookmarks within folders.
![](./assets/preview-4.png)
Folders cannot nest other folders. This is by design and it'll not change unless I find a
genuine usecase. You can edit folders by clicking on the "pen" icon next to its name.
- You can press the "eye" icon to enter edit mode where you can edit, delete, and reorder bookmarks.
![](./assets/preview-5.png)
- Each bookmark can be edited anytime.
![](./assets/preview-6.png)
The saved bookmarks are written to `$NOCTALIA_STATE_HOME/plugins/data/dunarand/bookmarks/data.json`.
By default, `$NOCTALIA_STATE_HOME` should point to `~/.local/state/noctalia`. You can point to a
different location for saving and backing up your bookmarks. This setting is configurable via
**plugin settings** under Settings -> Plugins. Only JSON format is accepted.
## Settings
The bar widget has the following settings:
| Setting | Type | Default | Description |
| --------- | -------- | ----------- | -------------------------------- |
| `glyph` | `glyph` | `bookmark` | Glyph displayed on the bar |
| `tooltip` | `string` | `Bookmarks` | Tooltip displayed on the bar |
| `text` | `string` | `Bookmarks` | Widget text displayed on the bar |
The plugin itself has the following settings:
| Setting | Type | Default | Description |
| ------------------ | ------ | ------- | ------------------------------------------------------------------------------------- |
| `data_path` | `file` | | data.json file to store the saved bookmarks. Leave empty to use the default location. |
| `show_info_button` | `bool` | `true` | Shows the "?" tooltip button on the bookmark entries. |
## IPC
1. Open the bookmarks panel:
```sh
noctalia msg panel-toggle dunarand/bookmarks:panel
```
2. Open the bookmarks panel in search mode (immediately puts you into search mode) so that you can
use your bookmarks panel as a launcher:
```sh
noctalia msg panel-toggle dunarand/bookmarks:panel search
```
Binary file not shown.

After

Width:  |  Height:  |  Size: 260 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 277 KiB

+30
View File
@@ -0,0 +1,30 @@
local PANEL_ID = "dunarand/bookmarks:panel"
local open = false
local function render()
barWidget.setGlyph(noctalia.getConfig("glyph"))
if open then
barWidget.setGlyphColor("primary")
barWidget.setColor("primary")
else
barWidget.setGlyphColor("on_surface")
barWidget.setColor("on_surface")
end
barWidget.setTooltip(noctalia.getConfig("tooltip"))
barWidget.setText(noctalia.getConfig("text"))
end
noctalia.state.watch("bookmarks_open", function(value)
open = value == true
render()
end)
function update()
render()
end
function onClick()
noctalia.togglePanel(PANEL_ID)
end
+1387
View File
File diff suppressed because it is too large Load Diff
+60
View File
@@ -0,0 +1,60 @@
id = "dunarand/bookmarks"
name = "Bookmarks"
version = "1.2.1"
plugin_api = 13
author = "dunarand"
license = "MIT"
icon = "bookmark"
description = "A user-defined list of bookmarks. Each item has a label, a glyph, and a shell command."
tags = ["launcher", "panel", "productivity", "utility"]
dependencies = ["nohup"]
[[setting]]
key = "data_path"
type = "file"
label_key = "settings.data_path.label"
description_key = "settings.data_path.description"
default = ""
extensions = [".json"]
[[setting]]
key = "show_info_button"
type = "bool"
label_key = "settings.show_info_button.label"
description_key = "settings.show_info_button.description"
default = true
[[widget]]
id = "bar"
entry = "bar.luau"
[[widget.setting]]
key = "glyph"
type = "glyph"
label_key = "settings.glyph.label"
description_key = "settings.glyph.description"
default = "bookmark"
[[widget.setting]]
key = "tooltip"
type = "string"
label_key = "settings.tooltip.label"
description_key = "settings.tooltip.description"
default = "Bookmarks"
[[widget.setting]]
key = "text"
type = "string"
label_key = "settings.text.label"
description_key = "settings.text.description"
default = "Bookmarks"
[[panel]]
id = "panel"
entry = "panel.luau"
width = 380
height = 470
placement = "attached"
position = "auto"
open_near_click = true
capture_keys = ["ctrl+n", "ctrl+p", "up", "down", "Return", "ctrl+s"]
Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

+66
View File
@@ -0,0 +1,66 @@
{
"title": "Bookmarks",
"new_bookmark": "New Bookmark",
"edit_bookmark": "Edit Bookmark",
"no_bookmarks": "No bookmarks yet",
"search_placeholder": "Search bookmarks...",
"no_search_results": "No matching bookmarks",
"new_folder": "New Folder",
"delete_folder_button": "Delete folder",
"edit_folder": "Edit Folder",
"folder_name_field": "Folder name",
"open_tooltip": "Open",
"back_button": "Back",
"edit_tooltip": "Edit",
"delete_tooltip": "Delete",
"drag_tooltip": "Drag to reorder, or onto a folder to file it inside",
"drop_out_of_folder": "Drop here to move back to the main panel",
"show_controls_tooltip": "Show reorder/edit/delete controls",
"hide_controls_tooltip": "Hide reorder/edit/delete controls",
"cancel_button": "Cancel",
"save_button": "Save",
"glyph_field": "Glyph name (e.g. bookmark)",
"label_field": "Label",
"cmd_field": "Shell command",
"description_field": "Description (optional)",
"info_cmd_line": "Command: {cmd}",
"info_description_line": "Description: {description}",
"run_in_background_field": "Run in background",
"run_in_terminal_field": "Run in terminal",
"err_read_failed": "Failed to read bookmarks: {error}",
"err_corrupt": "Bookmarks file is corrupt: {error}",
"err_no_data_dir": "No data directory available",
"err_reorder_failed": "Failed to reorder: {error}",
"err_no_command": "This bookmark has no command",
"err_exit_code": "\"{label}\" exited with code {code}",
"err_launch_failed": "Failed to launch \"{label}\"",
"err_delete_failed": "Failed to delete: {error}",
"err_save_failed": "Failed to save: {error}",
"err_label_required": "Label is required",
"err_label_cmd_required": "Label and command are required",
"folder_deleted": "Deleted folder \"{label}\" and its contents",
"entry_updated": "Updated \"{label}\"",
"entry_saved": "Saved \"{label}\"",
"settings": {
"glyph": {
"label": "Glyph",
"description": "Bar widget's glyph"
},
"tooltip": {
"label": "Tooltip",
"description": "Bar widget's tooltip"
},
"text": {
"label": "Text",
"description": "Bar widget's label. Leave empty to display only the glyph."
},
"data_path": {
"label": "Data file",
"description": "Where data.json is stored. Leave empty to use the default plugin data directory."
},
"show_info_button": {
"label": "Show info button",
"description": "Show a \"?\" button on bookmarks (not folders) to view their description and command on hover."
}
}
}
+66
View File
@@ -0,0 +1,66 @@
{
"title": "Yer İmleri",
"new_bookmark": "Yeni Yer İmi",
"edit_bookmark": "Yer İmini Düzenle",
"no_bookmarks": "Henüz bir yer imi yok",
"new_folder": "Yeni Klasör",
"search_placeholder": "Yer imlerinde ara...",
"no_search_results": "Eşleşen yer imi yok",
"delete_folder_button": "Klasörü Sil",
"edit_folder": "Klasörü Düzenle",
"folder_name_field": "Klasör ismi",
"open_tooltip": "Aç",
"back_button": "Geri",
"edit_tooltip": "Düzenle",
"delete_tooltip": "Sil",
"drag_tooltip": "Sıralamak için sürükle veya bir klasörün içine bırak",
"drop_out_of_folder": "Ana panele göndermek için buraya bırak",
"show_controls_tooltip": "Sıralama/düzenleme/silme kontrollerini göster",
"hide_controls_tooltip": "Sıralama/düzenleme/silme kontrollerini gizle",
"cancel_button": "İptal",
"save_button": "Kaydet",
"glyph_field": "Sembol (ör. bookmark)",
"label_field": "İsim etiketi",
"cmd_field": "Shell Komutu",
"description_field": "Açıklama (İsteğe bağlı)",
"info_cmd_line": "Komut: {cmd}",
"info_description_line": "Açıklama: {description}",
"run_in_background_field": "Arka planda çalıştır",
"run_in_terminal_field": "Konsolda çalıştır",
"err_read_failed": "Yer imleri okunamadı: {error}",
"err_corrupt": "Bozuk yer imleri dosyası: {error}",
"err_no_data_dir": "Eksik veri dizgini",
"err_reorder_failed": "Sıralama Yapılamadı: {error}",
"err_no_command": "Bu yer iminin bir komutu yok",
"err_exit_code": "\"{label}\" {code} ile sonlandı",
"err_launch_failed": "\"{label}\" açılamadı",
"err_delete_failed": "Silinemedi: {error}",
"err_save_failed": "Kaydedilemedi: {error}",
"err_label_required": "Etiket gerekli",
"err_label_cmd_required": "Etiket ve komut alanları gerekli",
"folder_deleted": "\"{label}\" klasörü ve içeriği silindi",
"entry_updated": "\"{label}\" güncellendi",
"entry_saved": "\"{label}\" kaydedildi",
"settings": {
"glyph": {
"label": "Sembol",
"description": "Bar aracının sembolü (ör. bookmark)"
},
"tooltip": {
"label": "İsim etiketi",
"description": "Bar aracının isim etiketi"
},
"text": {
"label": "Yazı",
"description": "Bar aracının etiketi. Barda sadece sembolün görünmesi için boş bırakın."
},
"data_path": {
"label": "Veri dosyası",
"description": "data.json dosyasının saklandığı konum. Varsayılan eklenti veri konumunu kullanmak için boş bırakın."
},
"show_info_button": {
"label": "Bilgi butonunu göster",
"description": "Yer imlerinde (klasörlerde değil) üzerine gelindiğinde açıklamalarını ve komutlarını görüntülemek için bir \"?\" düğmesi göster."
}
}
}