bookmarks-v1.3 feat(add /bk launcher provider) (#165)

* feat: changed keybinds + new keybinds

Changes:

- ctrl+n -> ctrl+j
- ctrl+p -> ctrl+k

Additions:

- ctrl+h or left: return to the root level when in a folder
- ctrl+l or right: get into a folder

* feat: /bk Provider

- feat: /bk Provider
- chore: better preview imgs
- docs: updated README
- chore: updated Turkish translation strings
- chore: updated thumbnail

* feat: keybind changes + index tracking fix

- feat: Some more keybind changes
    - Added: ctrl + n for new bookmark
    - Changed: ctrl + f for search (changed from ctrl + s)
- fix: Added index tracking for bookmark entries
    - For keyboard-centric use, navigating to a folder by accident then
returning to root was annoying since no tracking meant that we'd always
land on the first entry within the folder or the root itself
- docs: updated README to reflected the changes and added a changelog
section

* fix translation merge

---------

Co-authored-by: Lemmy <studio@quadbyte.net>
This commit is contained in:
mgu
2026-07-30 21:22:53 -04:00
committed by GitHub
co-authored by Lemmy
parent 8cd39525e2
commit dabe695a93
13 changed files with 572 additions and 74 deletions
+128 -48
View File
@@ -1,28 +1,55 @@
# 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.
![](./thumbnail.webp)
A user-managed bookmarks plugin for Noctalia. You can add bookmarks by selecting a glyph, a label,
and a shell command to execute. You can backup your bookmarks as well!
## 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.
| Field | Value |
| --------------- | ----------------------------------------------------------------- |
| ID | `dunarand/bookmarks` |
| Entries | Bar widget: `bar`; panel: `panel`; Launcher provider: `provider`; |
| Launcher Prefix | `/bk` |
## Requirements
- Noctalia v5.0.0 or higher
- `nohup` (Optional): For "Run in background" wrapper toggle
## IPC & Keybinds
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
```
You can interact with the bookmarks list via keybinds:
| Keybind | Purpose |
| ------------------------- | ---------------------------------------------------------------- |
| `CTRL + J` or Down Arrow | Select the next (below) item |
| `CTRL + K` or Up Arrow | Select the previous (above) item |
| `CTRL + H` or Left Arrow | Return to the root level when in a folder |
| `CTRL + L` or Right Arrow | Get into a folder |
| `CTRL + F` | Search bookmarks |
| `CTRL + N` | New bookmark |
| `Enter` / `Return` | Execute the selected bookmark's command / Navigate into a folder |
## Usage
You can add the widget to your bar or assign the following command to your compositor keybinds. For
example, in Hyprland v0.55+
The plugin ships a bar widget, a panel, and a launcher provider. The bar widget just launches the
panel. The panel is able to be toggled via IPC. For example, in Hyprland v0.55 or higher, you can
assign it to a keybind as follows:
```
hl.bind(
@@ -31,27 +58,14 @@ hl.bind(
)
```
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)
![](./assets/preview-2.png)
- "Run in background" toggle wraps the command you defined in the following way:
@@ -70,24 +84,86 @@ You can interact with the bookmarks list via keybinds:
- 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.
genuine use case. 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.
This is a setting that you can disable.
![](./assets/preview-5.png)
![](./assets/preview-3.png)
- Each bookmark can be edited anytime.
- You can also use your launcher to query your bookmarks with the `/bk` prefix.
![](./assets/preview-6.png)
![](./assets/preview-4.png)
- Queries are folder-aware, meaning if you nested a bookmark inside a folder, the folder name will
be displayed as well.
## Bookmarks Data
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.
You can manage the bookmark data via external scripts.
**Root**
```
[ <entry>, <entry>]
```
The root is a JSON array of entries. Order in the array is display order (used directly by drag-and
drop reordering).
**Entry**
Two types of entries exist: `bookmark` and `folder`.
### Bookmarks
```JSON
{
"type": "bookmark",
"glyph": "bookmark",
"label": "My Bookmark",
"cmd": "firefox",
"description": "Opens Firefox",
"runInBackground": false,
"runInTerminal": false
}
```
| Key | Type | Required / Default | Notes |
| ------------------------------------------------- | ------- | ------------------------------------------- | ----------------------------------------------- |
| `type` | string | `"bookmark"` | |
| `glyph` | string | falls back to `"bookmark"` if empty/missing | |
| `label` | string | required, enforced at save time | `""` fails validation |
| `cmd` | string | required for bookmarks | shell command to execute, enforced at save time |
| `description` | string | optional, defaults to `""` | shown in the info tooltip if enabled |
| `runInBackground` | boolean | optional, defaults to `false` | mutually exclusive with `runInTerminal` in |
| the UI but the schema itself doesn't enforce that |
| `runInTerminal` | boolean | optional, defaults to `false` |
### Folders
```JSON
{
"type": "folder",
"glyph": "folder",
"label": "My Folder",
"items": [ <bookmark>, <bookmark>, ... ]
}
```
| Key | Type | Required/Default | Notes |
| ------------------------------------ | ------ | ----------------------------------------------------------------------- | ---------- |
| `type` | string | `"folder"` |
| `glyph` | string | falls back to `"folder"` if empty/missing |
| `label` | string | required |
| `items` | array | optional, treated as `{}` if missing, `folder.items=folder.items or {}` | contents — |
| bookmarks only, one level of nesting |
## Settings
The bar widget has the following settings:
@@ -100,22 +176,26 @@ The bar widget has the following settings:
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. |
| 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. |
| `enable_bk_provider` | `bool` | `true` | Enable bookmarks launcher provider |
| `bk_sort_by` | `select` | `"history"` | Provider's sorting strategy. Options: `"history"`, `"usage_count"` |
## IPC
## Changelog
1. Open the bookmarks panel:
### v1.3.0
```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
```
- Added `/bk` launcher provider
- Changed the following keybinds (check IPC & Keybinds section for what they do)
- CTRL + N changed to CTRL + J
- CTRL + P changed to CTRL + K
- CTRL + S changed to CTRL + F
- Added the following keybinds (check IPC & Keybinds section for what they do)
- CTRL + H or Left Arrow
- CTRL + L or Right Arrow
- CTRL + N
- Index tracking on root level for keyboard-centric usage
- Previously, when using a keyboard to navigate, going into a folder would reset the selected
entry's index to 1 causing fat fingers to be annoying
Binary file not shown.

Before

Width:  |  Height:  |  Size: 260 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 297 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 272 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 291 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 298 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 277 KiB

+292
View File
@@ -0,0 +1,292 @@
-- Launcher provider: "/bk" fuzzy-searches bookmark labels (root + one level
-- of folders, bookmarks only - same scope as the panel's own search) and
-- runs the selected one on activation. Reads the same data.json the panel
-- writes, resolved the same way (configured data_path, else the plugin's
-- data dir), so no state is shared beyond the file on disk.
local function resolveDataPath()
local configured = noctalia.getConfig("data_path")
if type(configured) == "string" and noctalia.string.trim(configured) ~= "" then
return noctalia.expandPath(noctalia.string.trim(configured)), nil
end
local dataDir, dataDirErr = noctalia.pluginDataDir()
if dataDir == nil then
return nil, dataDirErr
end
return dataDir .. "/data.json", nil
end
local function entryType(entry)
return entry.type == "folder" and "folder" or "bookmark"
end
-- Reads and decodes data.json fresh on every query. The panel is the only
-- writer and bookmark lists are small, so there's no need to cache across
-- queries here.
local function loadBookmarks()
local path, pathErr = resolveDataPath()
if path == nil then
noctalia.log("bookmarks-provider: could not resolve data path: " .. tostring(pathErr))
return {}
end
if not noctalia.fileExists(path) then
return {}
end
local raw, readErr = noctalia.readFile(path)
if raw == nil then
noctalia.log("bookmarks-provider: failed to read data file: " .. tostring(readErr))
return {}
end
local decoded, decodeErr = noctalia.json.decode(raw)
if type(decoded) ~= "table" then
noctalia.log("bookmarks-provider: data file is corrupt: " .. tostring(decodeErr))
return {}
end
return decoded
end
-- Flat list of { entry, path } across root and one level of folders,
-- bookmarks only (folders themselves aren't launchable results).
local function flatten(bookmarks)
local flat = {}
for _, entry in ipairs(bookmarks) do
if entryType(entry) == "folder" then
for _, subEntry in ipairs(entry.items or {}) do
if entryType(subEntry) ~= "folder" then
table.insert(flat, { entry = subEntry, path = entry.label or "" })
end
end
else
table.insert(flat, { entry = entry, path = nil })
end
end
return flat
end
-- Bookmarks are indexed by a stable synthetic id (path-qualified label),
-- rather than by array position, since onActivate only receives the id
-- string and the list is re-read from disk on every query.
local function resultId(item)
return (item.path or "") .. "\30" .. (item.entry.label or "")
end
local function buildResults(query)
local bookmarks = loadBookmarks()
local flat = flatten(bookmarks)
local results = {}
for _, item in ipairs(flat) do
local score
if query == "" then
score = 0
else
score = noctalia.fuzzyScore(query, item.entry.label or "")
end
if score ~= nil then
table.insert(results, {
id = resultId(item),
title = item.entry.label or "",
subtitle = item.path,
glyph = item.entry.glyph or "bookmark",
score = score,
})
end
end
table.sort(results, function(a, b)
return (a.score or 0) > (b.score or 0)
end)
return results
end
local function providerEnabled()
return noctalia.getConfig("enable_bk_provider") == true
end
-- "history" (most-recently-used first) or "usage_count" (most-frequently-
-- used first). Only affects ordering when the query is empty; a non-empty
-- query always ranks by fuzzy match score, same as before.
local function sortBy()
local value = noctalia.getConfig("bk_sort_by")
if value == "usage_count" then
return "usage_count"
end
return "history"
end
local function statsPath()
local dataDir = noctalia.pluginDataDir()
if dataDir == nil then
return nil
end
return dataDir .. (sortBy() == "history" and "/bk_history.json" or "/bk_usage.json")
end
-- Reads and decodes the current stats file (history array or usage-count
-- map, per sortBy()), falling back to an empty table when the file is
-- missing, unreadable, or corrupt. Shared by getSortScores/recordActivation
-- so the read/decode/default boilerplate only lives in one place.
local function readStatsData()
local path = statsPath()
if path == nil then
return {}, nil
end
local file = noctalia.readFile(path)
if file == nil then
return {}, path
end
local decoded = noctalia.json.decode(file)
if type(decoded) ~= "table" then
return {}, path
end
return decoded, path
end
-- Scores used to order results when the query is empty, keyed by result id.
-- History ranks by recency (higher = more recently used); usage_count ranks
-- by frequency (higher = used more often). Reads/decodes the stats file
-- once for the whole batch rather than once per result. Ids missing from
-- the stats file fall back to 0 (unranked, sorted last).
local function getSortScores(ids)
local data = readStatsData()
local scores = {}
if sortBy() == "history" then
for _, id in ipairs(ids) do
local i = table.find(data, id)
scores[id] = i ~= nil and (#data - i) or 0
end
else
for _, id in ipairs(ids) do
scores[id] = data[id] or 0
end
end
return scores
end
-- Records an activation for ranking purposes: pushes `id` to the front of
-- the history list, or increments its usage count, depending on `sort_by`.
local function recordActivation(id)
local data, path = readStatsData()
if path == nil then
return
end
if sortBy() == "history" then
local i = table.find(data, id)
if i ~= nil then
table.remove(data, i)
end
table.insert(data, 1, id)
else
data[id] = (data[id] or 0) + 1
end
local encoded = noctalia.json.encode(data)
if encoded ~= nil then
noctalia.writeFile(path, encoded)
end
end
function onQuery(query)
query = noctalia.string.trim(query)
if not providerEnabled() then
launcher.setResults(query, {})
return
end
local results = buildResults(query)
if query == "" then
local ids = {}
for _, result in ipairs(results) do
table.insert(ids, result.id)
end
local scores = getSortScores(ids)
for _, result in ipairs(results) do
result.score = scores[result.id] or 0
end
table.sort(results, function(a, b)
return (a.score or 0) > (b.score or 0)
end)
end
if #results == 0 then
launcher.setResults(query, {
{
id = "",
title = noctalia.tr("no_bookmarks"),
glyph = "bookmark-off",
},
})
return
end
launcher.setResults(query, results)
end
function onActivate(id)
if id == "" or not providerEnabled() then
return
end
local wantPath, wantLabel = id:match("^([^\30]*)\30(.*)$")
if wantLabel == nil then
return
end
if wantPath == "" then
wantPath = nil
end
local bookmarks = loadBookmarks()
for _, item in ipairs(flatten(bookmarks)) do
if item.entry.label == wantLabel and (item.path or nil) == wantPath then
local entry = item.entry
local cmd = entry.cmd
if type(cmd) ~= "string" or cmd == "" then
noctalia.notifyError(noctalia.tr("title"), noctalia.tr("err_no_command"))
return
end
local label = entry.label or cmd
recordActivation(id)
if entry.runInTerminal == true then
noctalia.runInTerminal(cmd)
return
end
local finalCmd = cmd
if entry.runInBackground == true then
finalCmd = "nohup " .. cmd .. " >/dev/null 2>&1 &"
end
local ok = noctalia.runAsync(finalCmd, function(result)
if entry.runInBackground == true then
return
end
if result.exitCode ~= 0 then
local detail = noctalia.string.trim(result.stderr or "")
local msg = noctalia.tr(
"err_exit_code",
{ label = label, code = tostring(result.exitCode) }
)
if detail ~= "" then
msg = msg .. ": " .. detail
end
noctalia.notifyError(noctalia.tr("title"), msg)
end
end)
if not ok then
noctalia.notifyError(
noctalia.tr("title"),
noctalia.tr("err_launch_failed", { label = label })
)
end
return
end
end
end
+94 -19
View File
@@ -33,6 +33,37 @@ local focusSearchOnRender = false -- one-shot: grabs the search input on the nex
local selectedIndex = nil -- 1-based position within the currently visible list, or nil
-- Last selected index per scope, keyed by currentFolder (0 for root, since
-- folder indices start at 1). Restores position when re-entering a folder
-- or coming back to root instead of resetting to 1.
local rememberedSelection = {}
local function rememberSelection(folder)
rememberedSelection[folder or 0] = selectedIndex
end
local function recallSelection(folder, rowCount)
if rowCount == nil or rowCount <= 0 then
return nil
end
return math.min(math.max(rememberedSelection[folder or 0] or 1, 1), rowCount)
end
-- ui.scroll has no scroll-into-view API, so instead of scrolling to the
-- selection we only render a window of rows around it - the selection is
-- then always inside what's drawn. ROWS_PER_SCREEN is a rough fit for the
-- panel's height; doesn't need to be exact.
local ROWS_PER_SCREEN = 9
local function visibleWindow(count, selected)
if count <= ROWS_PER_SCREEN or selected == nil then
return 1, math.min(count, ROWS_PER_SCREEN)
end
local startIndex =
math.max(1, math.min(selected - ROWS_PER_SCREEN // 2, count - ROWS_PER_SCREEN + 1))
return startIndex, startIndex + ROWS_PER_SCREEN - 1
end
local draftKind = "bookmark" -- "bookmark" | "folder", set when the form opens
local draftGlyph = "bookmark"
local draftLabel = ""
@@ -689,9 +720,10 @@ local function listView()
})
)
else
local startIndex, endIndex = visibleWindow(#results, selectedIndex)
local rows = {}
for rowIndex, result in ipairs(results) do
table.insert(rows, searchResultRow(result, rowIndex))
for rowIndex = startIndex, endIndex do
table.insert(rows, searchResultRow(results[rowIndex], rowIndex))
end
table.insert(children, ui.scroll({ flexGrow = 1, gap = 2 }, rows))
end
@@ -762,15 +794,20 @@ local function listView()
})
)
else
local rows = {}
local count = #list
for index, entry in ipairs(list) do
local startIndex, endIndex = visibleWindow(count, selectedIndex)
local rows = {}
for index = startIndex, endIndex do
if editMode then
table.insert(rows, insertionGap(index, "before"))
end
table.insert(rows, entryRow(entry, index))
table.insert(rows, entryRow(list[index], index))
end
if editMode and count > 0 then
-- The trailing "after last row" gap only belongs at the very end of
-- the real list, not at the end of a windowed slice that's cut off
-- partway through - an "after" gap mid-list would duplicate the
-- "before" gap of the row right after the window.
if editMode and count > 0 and endIndex == count then
table.insert(rows, insertionGap(count, "after"))
end
table.insert(children, ui.scroll({ flexGrow = 1, gap = 2 }, rows))
@@ -976,16 +1013,20 @@ function _onOpenFolder(index)
if entry == nil or entryType(entry) ~= "folder" then
return
end
-- Remember where we were in root before diving into the folder.
rememberSelection(currentFolder)
-- currentFolder is only ever set from root (folders are one level deep),
-- so `index` here is always an index into the root `bookmarks` array.
currentFolder = index
selectedIndex = 1
selectedIndex = recallSelection(currentFolder, #(entry.items or {}))
render()
end
function _onBackToRoot()
-- Remember where we were inside the folder before leaving it.
rememberSelection(currentFolder)
currentFolder = nil
selectedIndex = 1
selectedIndex = recallSelection(currentFolder, #bookmarks)
render()
end
@@ -1043,7 +1084,11 @@ function _onDeleteFolder()
noctalia.tr("title"),
noctalia.tr("folder_deleted", { label = removed.label or "" })
)
-- Folder selections are now stale (indices after the deleted one
-- shifted), so drop everything but root's.
rememberedSelection = { [0] = rememberedSelection[0] }
currentFolder = nil
selectedIndex = recallSelection(currentFolder, #bookmarks)
render()
end
@@ -1309,10 +1354,14 @@ end
-- `noctalia msg panel-open dunarand/bookmarks:panel search`, or any other
-- IPC caller) open the panel straight into a focused search box, turning
-- the bookmark list into a fast fuzzy launcher without a second keypress.
-- Root-only, same as ctrl+s: searching already isn't offered inside a
-- Root-only, same as ctrl+f: searching already isn't offered inside a
-- folder, so context is ignored there.
--
-- To open in search mode and close from a single keybind:
-- noctalia msg panel-toggle dunarand/bookmarks:panel search
function onOpen(context)
view = "list"
editingIndex = nil
editingRoot = false
editMode = false
@@ -1320,6 +1369,7 @@ function onOpen(context)
searchQuery = ""
searchRev += 1
selectedIndex = 1
rememberedSelection = {}
loadBookmarks()
noctalia.state.set("bookmarks_open", true)
if context == "search" then
@@ -1332,12 +1382,18 @@ function onClose()
noctalia.state.set("bookmarks_open", false)
end
-- Keyboard navigation: ctrl+n/down and ctrl+p/up move the selection through
-- the currently visible rows (search results or the active folder's list),
-- looping past either end; Return activates whatever is selected; ctrl+s
-- jumps focus to the search box (root view only, since that's the only
-- place it's rendered). Only acts on key-down (pressed == true) so each
-- chord fires once per press.
-- Keyboard navigation:
-- - ctrl+j/down and ctrl+k/up move the selection through the currently
-- visible rows (search results or the active folder's contents),
-- wrapping past either end.
-- - ctrl+l/right enters the selected folder, if any.
-- - ctrl+h/left returns from a folder to the root list.
-- - Return activates the selected row (opens a folder or runs a bookmark).
-- - ctrl+f focuses the search box (root view only, since folders don't
-- have one).
-- - ctrl+n opens the new-bookmark form.
-- root-only - same restriction as the "New Folder" button).
-- Only acts on key-down (pressed == true) so each chord fires once per press.
function onKey(chord, pressed)
if not pressed then
return
@@ -1346,7 +1402,7 @@ function onKey(chord, pressed)
return
end
if chord == "ctrl+s" then
if chord == "ctrl+f" then
if currentFolder == nil then
searchRev += 1
focusSearchOnRender = true
@@ -1355,6 +1411,18 @@ function onKey(chord, pressed)
return
end
if chord == "ctrl+n" then
_onNewBookmark()
return
end
if chord == "ctrl+h" or chord == "left" then
if currentFolder ~= nil then
_onBackToRoot()
end
return
end
local rows = visibleRows()
local count = #rows
if count == 0 then
@@ -1362,21 +1430,28 @@ function onKey(chord, pressed)
return
end
if chord == "ctrl+n" or chord == "down" then
if chord == "ctrl+j" or chord == "down" then
if selectedIndex == nil then
selectedIndex = 1
else
selectedIndex = selectedIndex % count + 1
end
render()
elseif chord == "ctrl+p" or chord == "up" then
elseif chord == "ctrl+k" or chord == "up" then
if selectedIndex == nil then
selectedIndex = count
else
selectedIndex = (selectedIndex - 2) % count + 1
end
render()
elseif chord == "Return" then
elseif chord == "ctrl+l" or chord == "right" then
local row = rows[selectedIndex]
if row ~= nil and row.kind == "entry" then
if entryType(row.entry) == "folder" then
_onOpenFolder(row.index)
end
end
elseif chord == "return" then
local row = rows[selectedIndex]
activateRow(row)
end
+33 -2
View File
@@ -1,6 +1,6 @@
id = "dunarand/bookmarks"
name = "Bookmarks"
version = "1.2.1"
version = "1.3.0"
plugin_api = 13
author = "dunarand"
license = "MIT"
@@ -49,6 +49,37 @@ entry = "bar.luau"
description_key = "settings.text.description"
default = "Bookmarks"
[[setting]]
key = "enable_bk_provider"
type = "bool"
label_key = "settings.enable_bk_provider.label"
description_key = "settings.enable_bk_provider.description"
default = true
[[setting]]
key = "bk_sort_by"
type = "select"
label_key = "settings.bk_sort_by.label"
description_key = "settings.bk_sort_by.description"
default = "history"
visible_when = { key = "enable_bk_provider", values = ["true"] }
[[setting.options]]
value = "history"
label_key = "settings.bk_sort_by.options.history"
[[setting.options]]
value = "usage_count"
label_key = "settings.bk_sort_by.options.usage_count"
[[launcher_provider]]
id = "provider"
entry = "bk_provider.luau"
prefix = "bk"
glyph = "bookmark"
include_in_global_search = false
debounce_ms = 100
[[panel]]
id = "panel"
entry = "panel.luau"
@@ -57,4 +88,4 @@ height = 470
placement = "attached"
position = "auto"
open_near_click = true
capture_keys = ["ctrl+n", "ctrl+p", "up", "down", "Return", "ctrl+s"]
capture_keys = ["ctrl+h", "ctrl+j", "ctrl+k", "ctrl+l", "left", "down", "up", "right", "ctrl+n", "ctrl+f", "return"]
Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 51 KiB

+12
View File
@@ -40,10 +40,22 @@
"save_button": "Save",
"search_placeholder": "Search bookmarks...",
"settings": {
"bk_sort_by": {
"description": "How bookmarks are ordered in \"/bk\" results when there's no search text.",
"label": "/bk sort order",
"options": {
"history": "Recently used",
"usage_count": "Most used"
}
},
"data_path": {
"description": "Where data.json is stored. Leave empty to use the default plugin data directory.",
"label": "Data file"
},
"enable_bk_provider": {
"description": "Adds a \"/bk\" launcher command to fuzzy-search and run your bookmarks.",
"label": "Enable /bk launcher provider"
},
"glyph": {
"description": "Bar widget's glyph",
"label": "Glyph"
+13 -5
View File
@@ -25,7 +25,7 @@
"err_save_failed": "Kaydedilemedi: {error}",
"folder_deleted": "\"{label}\" klasörü ve içeriği silindi",
"folder_name_field": "Klasör ismi",
"glyph_field": "Sembol (ör. bookmark)",
"glyph_field": "Simge (ör. bookmark)",
"hide_controls_tooltip": "Sıralama/düzenleme/silme kontrollerini gizle",
"info_cmd_line": "Komut: {cmd}",
"info_description_line": "Açıklama: {description}",
@@ -40,24 +40,32 @@
"save_button": "Kaydet",
"search_placeholder": "Yer imlerinde ara...",
"settings": {
"bk_sort_by": {
"description": "\"/bk\" sonuçlarının arama yapılmadığı durumda yer imlerini nasıl sıralacağını belirler.",
"label": "/bk sıralama önceliği",
"options": {
"history": "Son kullanılanlar",
"usage_count": "En sık kullanılanlar"
}
},
"data_path": {
"description": "data.json dosyasının saklandığı konum. Varsayılan eklenti veri konumunu kullanmak için boş bırakın.",
"label": "Veri dosyası"
},
"glyph": {
"description": "Bar aracının sembolü (ör. bookmark)",
"label": "Sembol"
"description": "Çubuk aracının simgesi (ör. bookmark)",
"label": "Simge"
},
"show_info_button": {
"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.",
"label": "Bilgi butonunu göster"
},
"text": {
"description": "Bar aracının etiketi. Barda sadece sembolün görünmesi için boş bırakın.",
"description": "Çubuk aracının etiketi. Çubukta sadece simgenin görünmesi için boş bırakın.",
"label": "Yazı"
},
"tooltip": {
"description": "Bar aracının isim etiketi",
"description": "Çubuk aracının isim etiketi",
"label": "İsim etiketi"
}
},