launcher: scan desktop files directly, keep state in XDG_STATE_HOME

The desktop-entry cache lived in $XDG_CACHE_HOME/wldash/desktop.json.
On an NFS-shared home that is a remote read and write on every start
(unless the session happens to export a machine-local XDG_CACHE_HOME),
and it went stale whenever a package was installed until someone ran
--desktop-refresh. Scanning the applications directories takes a few
milliseconds over local /usr/share and already happened on a background
thread, so the cache bought nothing: drop it, along with simd-json and
the serde derives on Desktop. --desktop-refresh stays as a no-op so old
key bindings keep working.

Calculator history and the prompt saved on Escape are state, not cache:
they move to $XDG_STATE_HOME/wldash (~/.local/state), written only on
Enter/Escape, and the old cache location is still read as a fallback.

With the serde derives gone, two Desktop fields were unused; use them:
Type=Link entries now open their URL with xdg-open, and Terminal=true
entries run under $TERMINAL -e when it is set.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Bendik Aagaard Lynghaug
2026-10-03 09:33:12 +00:00
co-authored by Claude Fable 5.1
parent 03a3c681b0
commit 9953865128
8 changed files with 82 additions and 272 deletions
+9 -19
View File
@@ -2,20 +2,17 @@ use std::{
cmp::Ordering,
collections::HashSet,
error::Error,
fs::{File, read_to_string},
fs::read_to_string,
hash::{Hash, Hasher},
io::{Error as io_error, ErrorKind},
path::Path,
};
use serde::{self, Deserialize, Serialize};
use simd_json;
use walkdir::WalkDir;
use crate::utils::{inish, xdg};
#[derive(Serialize, Deserialize, Clone, Debug, Eq)]
#[serde(rename_all = "snake_case")]
#[derive(Clone, Debug, Eq)]
pub struct Desktop {
pub entry_type: String,
pub name: String,
@@ -83,20 +80,13 @@ impl Hash for Desktop {
}
}
pub fn load_desktop_cache() -> Result<Vec<Desktop>, Box<dyn Error>> {
match File::open(format!("{}/wldash/desktop.json", xdg::cache_folder())) {
Ok(f) => simd_json::from_reader(f).map_err(|e| e.into()),
Err(e) => Err(e.into()),
}
}
pub fn write_desktop_cache(v: &Vec<Desktop>) -> Result<(), Box<dyn Error>> {
match File::create(format!("{}/wldash/desktop.json", xdg::cache_folder())) {
Ok(f) => simd_json::to_writer(f, v).map_err(|e| e.into()),
Err(e) => Err(e.into()),
}
}
/// Scan every XDG applications directory for launchable entries.
///
/// This used to be cached as JSON under XDG_CACHE_HOME and refreshed by
/// hand with `--desktop-refresh`. The scan is a few milliseconds over
/// local /usr/share and runs on a background thread, while the cache sat
/// on the (possibly NFS-mounted) home directory and went stale whenever a
/// package was installed. So: no cache.
pub fn load_desktop_files() -> Vec<Desktop> {
let dirs = xdg::data_folders();