C++ / fmt (pull_request) Successful in 6s
C++ / build (pull_request) Failing after 2m2s
C++ / clang-tidy (pull_request) Failing after 2m0s
JS/TS / fmt (pull_request) Failing after 11s
JS/TS / lint (pull_request) Successful in 9s
Python / static (pull_request) Successful in 27s
Rust / build (pull_request) Successful in 50s
Python / verify (pull_request) Successful in 1m32s
Rust / fmt (pull_request) Successful in 27s
Rust / clippy (pull_request) Successful in 48s
195 lines
4.4 KiB
QML
195 lines
4.4 KiB
QML
// qs/Services/Files.qml
|
|
pragma Singleton
|
|
|
|
import QtQuick
|
|
import Quickshell
|
|
import Quickshell.Io
|
|
import ZShell.Config
|
|
|
|
Singleton {
|
|
id: root
|
|
|
|
property list<var> results: []
|
|
|
|
property string _pendingQuery: ""
|
|
property int _generation: 0
|
|
|
|
readonly property var _imageExts: ({
|
|
"png": true,
|
|
"jpg": true,
|
|
"jpeg": true,
|
|
"gif": true,
|
|
"bmp": true,
|
|
"webp": true,
|
|
"svg": true,
|
|
"tiff": true,
|
|
"tif": true,
|
|
"ico": true,
|
|
"heic": true,
|
|
"avif": true
|
|
})
|
|
|
|
// freedesktop mimetype-icon names; extend as needed
|
|
readonly property var _extIcons: ({
|
|
// images
|
|
"png": "image-x-generic",
|
|
"jpg": "image-x-generic",
|
|
"jpeg": "image-x-generic",
|
|
"gif": "image-x-generic",
|
|
"bmp": "image-x-generic",
|
|
"webp": "image-x-generic",
|
|
"svg": "image-x-generic",
|
|
"tiff": "image-x-generic",
|
|
"tif": "image-x-generic",
|
|
"ico": "image-x-generic",
|
|
"heic": "image-x-generic",
|
|
"avif": "image-x-generic",
|
|
// documents
|
|
"pdf": "application-pdf",
|
|
"doc": "x-office-document",
|
|
"docx": "x-office-document",
|
|
"odt": "x-office-document",
|
|
"txt": "text-x-generic",
|
|
"md": "text-x-generic",
|
|
"xls": "x-office-spreadsheet",
|
|
"xlsx": "x-office-spreadsheet",
|
|
"ods": "x-office-spreadsheet",
|
|
"ppt": "x-office-presentation",
|
|
"pptx": "x-office-presentation",
|
|
"odp": "x-office-presentation",
|
|
// archives
|
|
"zip": "package-x-generic",
|
|
"tar": "package-x-generic",
|
|
"gz": "package-x-generic",
|
|
"xz": "package-x-generic",
|
|
"7z": "package-x-generic",
|
|
"rar": "package-x-generic",
|
|
// audio/video
|
|
"mp3": "audio-x-generic",
|
|
"flac": "audio-x-generic",
|
|
"wav": "audio-x-generic",
|
|
"ogg": "audio-x-generic",
|
|
"mp4": "video-x-generic",
|
|
"mkv": "video-x-generic",
|
|
"webm": "video-x-generic",
|
|
"avi": "video-x-generic",
|
|
"mov": "video-x-generic",
|
|
// code
|
|
"js": "text-x-script",
|
|
"ts": "text-x-script",
|
|
"py": "text-x-script",
|
|
"sh": "text-x-script",
|
|
"qml": "text-x-script",
|
|
"cpp": "text-x-c++src",
|
|
"c": "text-x-csrc",
|
|
"h": "text-x-chdr",
|
|
"json": "application-json",
|
|
"html": "text-html",
|
|
"css": "text-css"
|
|
})
|
|
|
|
function showInFolder(path: string): void {
|
|
const explorer = Config.general.apps.explorer;
|
|
let args = [];
|
|
|
|
if (explorer.includes("dolphin"))
|
|
args = [...explorer, "--select", path];
|
|
else
|
|
args = [...explorer, path];
|
|
|
|
if (Config.launcher.uwsm)
|
|
Quickshell.execDetached(["app2unit", "--", ...args]);
|
|
else
|
|
Quickshell.execDetached([...args]);
|
|
}
|
|
|
|
function launch(command: list<string>): void {
|
|
if (Config.launcher.uwsm)
|
|
Quickshell.execDetached(["app2unit", "--"].concat(command));
|
|
else
|
|
Quickshell.execDetached(command);
|
|
}
|
|
|
|
function transformSearch(search: string): string {
|
|
return search.slice(Config.launcher.actionPrefix.length + "files ".length);
|
|
}
|
|
|
|
function query(search: string): list<var> {
|
|
search = transformSearch(search);
|
|
if (search !== _pendingQuery) {
|
|
_pendingQuery = search;
|
|
debounceTimer.restart();
|
|
}
|
|
return results;
|
|
}
|
|
|
|
function _extOf(name: string): string {
|
|
const i = name.lastIndexOf(".");
|
|
// no dot, or dotfile with no real extension (".bashrc" -> treat as no ext)
|
|
if (i <= 0)
|
|
return "";
|
|
return name.slice(i + 1).toLowerCase();
|
|
}
|
|
|
|
function _iconFor(name: string, isDir: bool): string {
|
|
if (isDir)
|
|
return "folder";
|
|
const ext = _extOf(name);
|
|
return _extIcons[ext] ?? "text-x-generic";
|
|
}
|
|
|
|
function _buildEntry(path: string): var {
|
|
const name = path.split("/").pop();
|
|
const ext = _extOf(name);
|
|
// trailing slash from plocate's directory output (if -d/dir results ever appear)
|
|
const isDir = path.endsWith("/");
|
|
|
|
return {
|
|
path: path,
|
|
name: name,
|
|
icon: _iconFor(name, isDir),
|
|
isImage: !isDir && (_imageExts[ext] ?? false)
|
|
};
|
|
}
|
|
|
|
Timer {
|
|
id: debounceTimer
|
|
interval: 120
|
|
onTriggered: root._runQuery(root._pendingQuery)
|
|
}
|
|
|
|
function _runQuery(search: string): void {
|
|
_generation++;
|
|
|
|
if (!search) {
|
|
proc.running = false;
|
|
results = [];
|
|
return;
|
|
}
|
|
|
|
if (proc.running)
|
|
proc.running = false;
|
|
|
|
proc.buffer = [];
|
|
proc.myGeneration = _generation;
|
|
proc.command = ["plocate", "-i", "-b", "--limit", "50", search];
|
|
proc.running = true;
|
|
}
|
|
|
|
Process {
|
|
id: proc
|
|
|
|
property var buffer: []
|
|
property int myGeneration: 0
|
|
|
|
stdout: SplitParser {
|
|
onRead: line => proc.buffer.push(root._buildEntry(line))
|
|
}
|
|
|
|
onRunningChanged: {
|
|
if (!running && myGeneration === root._generation)
|
|
root.results = buffer;
|
|
}
|
|
}
|
|
}
|