Add Zed Provider launcher plugin (#17)
* Add Zed Provider launcher plugin. Expose recent Zed workspaces in the launcher so users can reopen projects quickly. * Update Zed Provider thumbnail with launcher screenshot. Use the official Noctalia thumbnail generator export for the plugin card. * Address PR review feedback for Zed Provider. Fix thumbnail category label, refresh project cache when the query is cleared, and use noctalia.fuzzyScore for launcher filtering. * fix(zed-provider): update thumbnail with launcher category label * chore(zed-provider): reset manifest version to 1.0.0 * docs(zed-provider): align README with plugin documentation requirements * fix(zed-provider): replace min_noctalia with plugin_api * Add 'nohup' to plugin dependencies --------- Co-authored-by: Lemmy <studio@quadbyte.net>
This commit is contained in:
@@ -0,0 +1,38 @@
|
|||||||
|
# Zed Provider
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Zed Provider integrates recent Zed workspaces with the Noctalia launcher so you
|
||||||
|
can reopen a project quickly without leaving the shell.
|
||||||
|
|
||||||
|
## Plugin
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
| --- | --- |
|
||||||
|
| ID | `cleboost/zed-provider` |
|
||||||
|
| Entry | Launcher provider: `provider` |
|
||||||
|
| Launcher Prefix | `/zed` |
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
Install [Zed](https://zed.dev) and ensure `zed` is available on `PATH` as
|
||||||
|
`zeditor`. The `sqlite3` command must also be available to read Zed's workspace
|
||||||
|
database.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Open the Noctalia launcher and type `/zed` to list recent local Zed workspaces.
|
||||||
|
Continue typing to filter projects by name, then select one to open it with
|
||||||
|
`zeditor`.
|
||||||
|
|
||||||
|
## Settings
|
||||||
|
|
||||||
|
- `db_path` — path to Zed's `db.sqlite` workspace database.
|
||||||
|
- `max_results` — maximum number of projects shown in the launcher.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
Projects are read from Zed's workspace database, typically at
|
||||||
|
`~/.local/share/zed/db/0-stable/db.sqlite`. Remote workspaces are excluded.
|
||||||
|
The list is cached for the launcher session and refreshed when you clear the
|
||||||
|
query.
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
id = "cleboost/zed-provider"
|
||||||
|
name = "Zed Provider"
|
||||||
|
version = "1.0.0"
|
||||||
|
plugin_api = 3
|
||||||
|
author = "cleboost"
|
||||||
|
license = "MIT"
|
||||||
|
icon = "folder-open"
|
||||||
|
description = "Open a recent Zed project from the launcher. Type /zed to list your projects."
|
||||||
|
tags = ["launcher", "development", "productivity"]
|
||||||
|
dependencies = ["sqlite3", "zed", "nohup"]
|
||||||
|
|
||||||
|
[[setting]]
|
||||||
|
key = "db_path"
|
||||||
|
type = "file"
|
||||||
|
label_key = "settings.db_path.label"
|
||||||
|
description_key = "settings.db_path.description"
|
||||||
|
default = "~/.local/share/zed/db/0-stable/db.sqlite"
|
||||||
|
|
||||||
|
[[setting]]
|
||||||
|
key = "max_results"
|
||||||
|
type = "int"
|
||||||
|
label_key = "settings.max_results.label"
|
||||||
|
description_key = "settings.max_results.description"
|
||||||
|
default = 20
|
||||||
|
min = 1
|
||||||
|
max = 100
|
||||||
|
|
||||||
|
[[launcher_provider]]
|
||||||
|
id = "provider"
|
||||||
|
entry = "zed_provider.luau"
|
||||||
|
prefix = "zed"
|
||||||
|
glyph = "bolt"
|
||||||
|
include_in_global_search = false
|
||||||
|
debounce_ms = 0
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"loading": "Loading…",
|
||||||
|
"loading-subtitle": "Reading Zed projects",
|
||||||
|
"no-projects-found": "No projects found",
|
||||||
|
"filter-empty": "Filter: \"{filter}\"",
|
||||||
|
"database-empty": "Zed database is empty or not found",
|
||||||
|
"settings.db_path.label": "Zed database path",
|
||||||
|
"settings.db_path.description": "Path to Zed's db.sqlite file.",
|
||||||
|
"settings.max_results.label": "Maximum results",
|
||||||
|
"settings.max_results.description": "Maximum number of projects to display."
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"loading": "Chargement…",
|
||||||
|
"loading-subtitle": "Lecture des projets Zed",
|
||||||
|
"no-projects-found": "Aucun projet trouvé",
|
||||||
|
"filter-empty": "Filtre : « {filter} »",
|
||||||
|
"database-empty": "La base Zed est vide ou introuvable",
|
||||||
|
"settings.db_path.label": "Chemin de la base Zed",
|
||||||
|
"settings.db_path.description": "Chemin vers le fichier db.sqlite de Zed.",
|
||||||
|
"settings.max_results.label": "Nombre maximum de résultats",
|
||||||
|
"settings.max_results.description": "Nombre maximum de projets à afficher."
|
||||||
|
}
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
--!nonstrict
|
||||||
|
|
||||||
|
local cachedProjects = nil
|
||||||
|
|
||||||
|
local function getDbPath()
|
||||||
|
local configured = noctalia.getConfig("db_path")
|
||||||
|
if type(configured) == "string" and configured ~= "" then
|
||||||
|
return noctalia.expandPath(configured)
|
||||||
|
end
|
||||||
|
return noctalia.expandPath("~/.local/share/zed/db/0-stable/db.sqlite")
|
||||||
|
end
|
||||||
|
|
||||||
|
local function getMaxResults()
|
||||||
|
local n = noctalia.getConfig("max_results")
|
||||||
|
return (type(n) == "number" and n > 0) and math.floor(n) or 20
|
||||||
|
end
|
||||||
|
|
||||||
|
local function trim(s)
|
||||||
|
return s:match("^%s*(.-)%s*$")
|
||||||
|
end
|
||||||
|
|
||||||
|
local function projectName(path)
|
||||||
|
return path:match("([^/]+)$") or path
|
||||||
|
end
|
||||||
|
|
||||||
|
local function shellQuote(s)
|
||||||
|
return "'" .. s:gsub("'", "'\"'\"'") .. "'"
|
||||||
|
end
|
||||||
|
|
||||||
|
local function loadProjects(onDone)
|
||||||
|
local db = getDbPath()
|
||||||
|
local limit = getMaxResults()
|
||||||
|
local sql = string.format(
|
||||||
|
"SELECT DISTINCT paths FROM workspaces WHERE paths IS NOT NULL AND paths != '' AND remote_connection_id IS NULL ORDER BY timestamp DESC LIMIT %d;",
|
||||||
|
limit
|
||||||
|
)
|
||||||
|
local cmd = string.format("sqlite3 -separator '\\n' %s %s 2>/dev/null", shellQuote(db), shellQuote(sql))
|
||||||
|
|
||||||
|
noctalia.runAsync(cmd, function(result)
|
||||||
|
local projects = {}
|
||||||
|
if result.exitCode == 0 and type(result.stdout) == "string" then
|
||||||
|
for line in result.stdout:gmatch("[^\n]+") do
|
||||||
|
local path = trim(line)
|
||||||
|
if path ~= "" then
|
||||||
|
table.insert(projects, path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
cachedProjects = projects
|
||||||
|
onDone(projects)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function makeRows(projects, filter)
|
||||||
|
local home = noctalia.getenv("HOME") or ""
|
||||||
|
local rows = {}
|
||||||
|
for _, path in projects do
|
||||||
|
local name = projectName(path)
|
||||||
|
local score = filter == "" and 0 or noctalia.fuzzyScore(filter, name)
|
||||||
|
if score == nil then
|
||||||
|
score = noctalia.fuzzyScore(filter, path)
|
||||||
|
end
|
||||||
|
if filter == "" or score ~= nil then
|
||||||
|
local display = (home ~= "" and path:sub(1, #home) == home) and "~" .. path:sub(#home + 1) or path
|
||||||
|
table.insert(rows, {
|
||||||
|
id = path,
|
||||||
|
title = name,
|
||||||
|
subtitle = display,
|
||||||
|
icon = "zed",
|
||||||
|
score = filter == "" and nil or score,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return rows
|
||||||
|
end
|
||||||
|
|
||||||
|
local function showResults(query, projects, filter)
|
||||||
|
local rows = makeRows(projects, filter)
|
||||||
|
if #rows == 0 then
|
||||||
|
launcher.setResults(query, {
|
||||||
|
{
|
||||||
|
id = "",
|
||||||
|
title = noctalia.tr("no-projects-found"),
|
||||||
|
subtitle = filter ~= "" and noctalia.tr("filter-empty", { filter = filter }) or noctalia.tr("database-empty"),
|
||||||
|
glyph = "folder-x",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
else
|
||||||
|
launcher.setResults(query, rows)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function onQuery(query)
|
||||||
|
local filter = trim(query)
|
||||||
|
|
||||||
|
if filter == "" then
|
||||||
|
cachedProjects = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
if cachedProjects then
|
||||||
|
showResults(query, cachedProjects, filter)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
launcher.setResults(query, {
|
||||||
|
{
|
||||||
|
id = "",
|
||||||
|
title = noctalia.tr("loading"),
|
||||||
|
subtitle = noctalia.tr("loading-subtitle"),
|
||||||
|
glyph = "loader",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
loadProjects(function(projects)
|
||||||
|
showResults(query, projects, filter)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function onActivate(id)
|
||||||
|
if id == "" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
noctalia.runAsync(string.format("nohup zeditor %s &>/dev/null &", shellQuote(id)))
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user