up su Gitea
This commit is contained in:
@@ -0,0 +1,448 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.Commons
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property var pluginApi: null
|
||||
|
||||
// Provider metadata
|
||||
property string name: pluginApi?.tr("provider.name")
|
||||
property var launcher: null
|
||||
property bool handleSearch: false
|
||||
property string supportedLayouts: "list"
|
||||
property bool supportsAutoPaste: false
|
||||
|
||||
// Search state
|
||||
property var currentResults: []
|
||||
property string currentQuery: ""
|
||||
property bool searching: false
|
||||
property int nextRequestId: 0
|
||||
property int activeRequestId: 0
|
||||
property int fileProcessRequestId: 0
|
||||
property int dirProcessRequestId: 0
|
||||
property int pendingProcessCount: 0
|
||||
property bool currentRequestFailed: false
|
||||
property var pendingResultsByType: ({ "files": [], "dirs": [] })
|
||||
property string fdCommandPath: ""
|
||||
property bool fdAvailable: false
|
||||
|
||||
// Settings shortcuts
|
||||
property var cfg: pluginApi?.pluginSettings || ({})
|
||||
property var defaults: pluginApi?.manifest?.metadata?.defaultSettings || ({})
|
||||
property bool showHidden: cfg.showHidden ?? defaults.showHidden ?? false
|
||||
property int maxResults: cfg.maxResults ?? defaults.maxResults ?? 0
|
||||
property string fileOpener: cfg.fileOpener ?? defaults.fileOpener ?? "xdg-open"
|
||||
property string fdCommand: cfg.fdCommand ?? defaults.fdCommand ?? "fd"
|
||||
property string searchDirectory: cfg.searchDirectory ?? defaults.searchDirectory ?? "~"
|
||||
|
||||
Process {
|
||||
id: fileSearchProcess
|
||||
running: false
|
||||
|
||||
stdout: StdioCollector {
|
||||
id: fileStdoutCollector
|
||||
}
|
||||
|
||||
stderr: StdioCollector {
|
||||
id: fileStderrCollector
|
||||
}
|
||||
|
||||
onExited: function(exitCode) {
|
||||
root.handleSearchProcessExit("files", fileProcessRequestId, exitCode, fileStdoutCollector.text, fileStderrCollector.text);
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: dirSearchProcess
|
||||
running: false
|
||||
|
||||
stdout: StdioCollector {
|
||||
id: dirStdoutCollector
|
||||
}
|
||||
|
||||
stderr: StdioCollector {
|
||||
id: dirStderrCollector
|
||||
}
|
||||
|
||||
onExited: function(exitCode) {
|
||||
root.handleSearchProcessExit("dirs", dirProcessRequestId, exitCode, dirStdoutCollector.text, dirStderrCollector.text);
|
||||
}
|
||||
}
|
||||
|
||||
// Debounce timer for search
|
||||
Timer {
|
||||
id: searchDebouncer
|
||||
interval: 300
|
||||
repeat: false
|
||||
onTriggered: root.executeSearch(root.currentQuery)
|
||||
}
|
||||
|
||||
function init() {
|
||||
Logger.i("FileSearch", "Initializing plugin");
|
||||
fdCommandPath = fdCommand;
|
||||
fdAvailable = true;
|
||||
Logger.i("FileSearch", "Using fd command:", fdCommandPath);
|
||||
}
|
||||
|
||||
function handleCommand(searchText) {
|
||||
return searchText.startsWith(">file");
|
||||
}
|
||||
|
||||
function commands() {
|
||||
return [{
|
||||
"name": ">file",
|
||||
"description": pluginApi?.tr("launcher.command.description"),
|
||||
"icon": "file-search",
|
||||
"isTablerIcon": true,
|
||||
"isImage": false,
|
||||
"onActivate": function() {
|
||||
launcher.setSearchText(">file ");
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
function getResults(searchText) {
|
||||
if (!searchText.startsWith(">file")) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!fdAvailable) {
|
||||
return [{
|
||||
"name": pluginApi?.tr("launcher.errors.fdNotFound.title"),
|
||||
"description": pluginApi?.tr("launcher.errors.fdNotFound.description"),
|
||||
"icon": "alert-circle",
|
||||
"isTablerIcon": true,
|
||||
"isImage": false,
|
||||
"onActivate": function() {}
|
||||
}];
|
||||
}
|
||||
|
||||
var query = searchText.slice(5).trim();
|
||||
|
||||
if (query === "") {
|
||||
return [{
|
||||
"name": pluginApi?.tr("launcher.prompts.emptyQuery.title"),
|
||||
"description": pluginApi?.tr("launcher.prompts.emptyQuery.description", { "root": displaySearchDirectory() }),
|
||||
"icon": "file-search",
|
||||
"isTablerIcon": true,
|
||||
"isImage": false,
|
||||
"onActivate": function() {}
|
||||
}];
|
||||
}
|
||||
|
||||
if (query !== currentQuery) {
|
||||
currentQuery = query;
|
||||
activeRequestId = 0;
|
||||
currentRequestFailed = false;
|
||||
searching = true;
|
||||
searchDebouncer.restart();
|
||||
|
||||
return [{
|
||||
"name": pluginApi?.tr("launcher.prompts.searching.title"),
|
||||
"description": pluginApi?.tr("launcher.prompts.searching.description", { "query": query }),
|
||||
"icon": "refresh",
|
||||
"isTablerIcon": true,
|
||||
"isImage": false,
|
||||
"onActivate": function() {}
|
||||
}];
|
||||
}
|
||||
|
||||
if (searching) {
|
||||
return [{
|
||||
"name": pluginApi?.tr("launcher.prompts.searching.title"),
|
||||
"description": pluginApi?.tr("launcher.prompts.searching.description", { "query": query }),
|
||||
"icon": "refresh",
|
||||
"isTablerIcon": true,
|
||||
"isImage": false,
|
||||
"onActivate": function() {}
|
||||
}];
|
||||
}
|
||||
|
||||
return currentResults;
|
||||
}
|
||||
|
||||
function executeSearch(query) {
|
||||
if (!fdAvailable || query === "") {
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.d("FileSearch", "Executing search for:", query);
|
||||
|
||||
if (fileSearchProcess.running) {
|
||||
fileSearchProcess.running = false;
|
||||
}
|
||||
if (dirSearchProcess.running) {
|
||||
dirSearchProcess.running = false;
|
||||
}
|
||||
|
||||
var expandedDir = expandHomePath(searchDirectory);
|
||||
|
||||
nextRequestId += 1;
|
||||
var requestId = nextRequestId;
|
||||
activeRequestId = requestId;
|
||||
currentRequestFailed = false;
|
||||
pendingProcessCount = 2;
|
||||
pendingResultsByType = ({ "files": [], "dirs": [] });
|
||||
|
||||
var commonArgs = [];
|
||||
|
||||
if (showHidden) {
|
||||
commonArgs.push("--hidden");
|
||||
}
|
||||
|
||||
if (maxResults > 0) {
|
||||
commonArgs.push("--max-results", maxResults.toString());
|
||||
}
|
||||
commonArgs.push("--base-directory", expandedDir);
|
||||
commonArgs.push("--absolute-path");
|
||||
commonArgs.push("--color", "never");
|
||||
commonArgs.push(query);
|
||||
|
||||
var fileArgs = ["--type", "f"].concat(commonArgs);
|
||||
var dirArgs = ["--type", "d"].concat(commonArgs);
|
||||
|
||||
fileProcessRequestId = requestId;
|
||||
fileSearchProcess.command = [fdCommandPath].concat(fileArgs);
|
||||
fileSearchProcess.running = true;
|
||||
|
||||
dirProcessRequestId = requestId;
|
||||
dirSearchProcess.command = [fdCommandPath].concat(dirArgs);
|
||||
dirSearchProcess.running = true;
|
||||
|
||||
Logger.d("FileSearch", "Running file command:", fdCommandPath, fileArgs.join(" "));
|
||||
Logger.d("FileSearch", "Running dir command:", fdCommandPath, dirArgs.join(" "));
|
||||
}
|
||||
|
||||
function handleSearchProcessExit(kind, requestId, exitCode, stdoutText, stderrText) {
|
||||
if (requestId !== activeRequestId || currentRequestFailed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (exitCode !== 0) {
|
||||
currentRequestFailed = true;
|
||||
searching = false;
|
||||
pendingProcessCount = 0;
|
||||
Logger.e("FileSearch", "fd command failed with exit code:", exitCode);
|
||||
Logger.e("FileSearch", "stderr:", stderrText);
|
||||
currentResults = [{
|
||||
"name": pluginApi?.tr("launcher.errors.fdNotFound.title"),
|
||||
"description": pluginApi?.tr("launcher.errors.fdNotFound.description"),
|
||||
"icon": "alert-circle",
|
||||
"isTablerIcon": true,
|
||||
"onActivate": function() {}
|
||||
}];
|
||||
if (launcher) {
|
||||
launcher.updateResults();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
pendingResultsByType[kind] = parseRawPaths(stdoutText);
|
||||
pendingProcessCount -= 1;
|
||||
|
||||
if (pendingProcessCount <= 0) {
|
||||
finalizeSearchResults(requestId);
|
||||
}
|
||||
}
|
||||
|
||||
function finalizeSearchResults(requestId) {
|
||||
if (requestId !== activeRequestId || currentRequestFailed) {
|
||||
return;
|
||||
}
|
||||
|
||||
var results = [];
|
||||
|
||||
for (var i = 0; i < pendingResultsByType.dirs.length; i++) {
|
||||
results.push(formatFileEntry(pendingResultsByType.dirs[i], true));
|
||||
}
|
||||
for (var j = 0; j < pendingResultsByType.files.length; j++) {
|
||||
results.push(formatFileEntry(pendingResultsByType.files[j], false));
|
||||
}
|
||||
|
||||
results = sortResults(results, currentQuery);
|
||||
|
||||
if (maxResults > 0 && results.length > maxResults) {
|
||||
results = results.slice(0, maxResults);
|
||||
}
|
||||
|
||||
if (results.length === 0) {
|
||||
results.push({
|
||||
"name": pluginApi?.tr("launcher.prompts.noResults.title"),
|
||||
"description": pluginApi?.tr("launcher.prompts.noResults.description", { "query": currentQuery }),
|
||||
"icon": "file-off",
|
||||
"isTablerIcon": true,
|
||||
"isImage": false,
|
||||
"onActivate": function() {}
|
||||
});
|
||||
searching = false;
|
||||
currentResults = results;
|
||||
if (launcher) {
|
||||
launcher.updateResults();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
searching = false;
|
||||
currentResults = results;
|
||||
Logger.d("FileSearch", "Found", results.length, "results");
|
||||
if (launcher) {
|
||||
launcher.updateResults();
|
||||
}
|
||||
}
|
||||
|
||||
function parseRawPaths(output) {
|
||||
var trimmed = output.trim();
|
||||
if (trimmed === "") {
|
||||
return [];
|
||||
}
|
||||
return trimmed.split("\n").filter(function(line) { return line.trim() !== ""; });
|
||||
}
|
||||
|
||||
function expandHomePath(pathValue) {
|
||||
var expandedPath = pathValue;
|
||||
if (expandedPath.startsWith("~")) {
|
||||
expandedPath = Quickshell.env("HOME") + expandedPath.substring(1);
|
||||
}
|
||||
return expandedPath;
|
||||
}
|
||||
|
||||
function displaySearchDirectory() {
|
||||
var expandedPath = expandHomePath(searchDirectory);
|
||||
var homeDir = Quickshell.env("HOME");
|
||||
if (expandedPath.startsWith(homeDir)) {
|
||||
return "~" + expandedPath.slice(homeDir.length);
|
||||
}
|
||||
return expandedPath;
|
||||
}
|
||||
|
||||
function sortResults(results, query) {
|
||||
var queryLower = query.toLowerCase();
|
||||
results.sort(function(a, b) {
|
||||
var rankA = resultRank(a, queryLower);
|
||||
var rankB = resultRank(b, queryLower);
|
||||
if (rankA !== rankB) {
|
||||
return rankA - rankB;
|
||||
}
|
||||
|
||||
var nameA = (a.name || "").toLowerCase();
|
||||
var nameB = (b.name || "").toLowerCase();
|
||||
if (nameA < nameB) {
|
||||
return -1;
|
||||
}
|
||||
if (nameA > nameB) {
|
||||
return 1;
|
||||
}
|
||||
return (a.description || "").length - (b.description || "").length;
|
||||
});
|
||||
return results;
|
||||
}
|
||||
|
||||
function resultRank(result, queryLower) {
|
||||
var name = (result.name || "").toLowerCase();
|
||||
var description = (result.description || "").toLowerCase();
|
||||
var fullPath = description + "/" + name;
|
||||
|
||||
if (name === queryLower) {
|
||||
return 0;
|
||||
}
|
||||
if (name.startsWith(queryLower)) {
|
||||
return 1;
|
||||
}
|
||||
if (name.indexOf(queryLower) !== -1) {
|
||||
return 2;
|
||||
}
|
||||
if (fullPath.indexOf(queryLower) !== -1) {
|
||||
return 3;
|
||||
}
|
||||
return 4;
|
||||
}
|
||||
|
||||
function formatFileEntry(filePath, forcedIsDirectory) {
|
||||
var normalizedPath = filePath;
|
||||
while (normalizedPath.length > 1 && normalizedPath.endsWith("/")) {
|
||||
normalizedPath = normalizedPath.slice(0, -1);
|
||||
}
|
||||
|
||||
var isDirectory = (forcedIsDirectory !== undefined) ? forcedIsDirectory : normalizedPath !== filePath;
|
||||
var parts = normalizedPath.split("/");
|
||||
var filename = parts[parts.length - 1];
|
||||
var parentPath = parts.slice(0, -1).join("/");
|
||||
|
||||
if (filename === "") {
|
||||
filename = normalizedPath;
|
||||
}
|
||||
|
||||
var homeDir = Quickshell.env("HOME");
|
||||
if (parentPath.startsWith(homeDir)) {
|
||||
parentPath = "~" + parentPath.slice(homeDir.length);
|
||||
}
|
||||
|
||||
return {
|
||||
"name": filename,
|
||||
"description": parentPath,
|
||||
"icon": isDirectory ? "folder" : getFileIcon(filename),
|
||||
"isTablerIcon": true,
|
||||
"isImage": false,
|
||||
"singleLine": false,
|
||||
"onActivate": function() {
|
||||
root.openFile(normalizedPath);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function getFileIcon(filename) {
|
||||
var ext = filename.split(".").pop().toLowerCase();
|
||||
|
||||
// Images
|
||||
if (["jpg", "jpeg", "png", "gif", "svg", "webp", "bmp", "ico"].indexOf(ext) !== -1) {
|
||||
return "photo";
|
||||
}
|
||||
|
||||
// Documents
|
||||
if (["txt", "md", "pdf", "doc", "docx", "odt", "rtf"].indexOf(ext) !== -1) {
|
||||
return "file-text";
|
||||
}
|
||||
|
||||
// Code files
|
||||
if (["js", "ts", "py", "java", "cpp", "c", "h", "qml", "rs", "go", "rb", "php", "html", "css", "json", "xml", "yaml", "yml"].indexOf(ext) !== -1) {
|
||||
return "code";
|
||||
}
|
||||
|
||||
// Archives
|
||||
if (["zip", "tar", "gz", "bz2", "xz", "7z", "rar"].indexOf(ext) !== -1) {
|
||||
return "file-zip";
|
||||
}
|
||||
|
||||
// Audio
|
||||
if (["mp3", "wav", "flac", "ogg", "m4a", "aac", "wma"].indexOf(ext) !== -1) {
|
||||
return "music";
|
||||
}
|
||||
|
||||
// Video
|
||||
if (["mp4", "mkv", "avi", "mov", "wmv", "flv", "webm"].indexOf(ext) !== -1) {
|
||||
return "video";
|
||||
}
|
||||
|
||||
// Spreadsheets
|
||||
if (["xls", "xlsx", "ods", "csv"].indexOf(ext) !== -1) {
|
||||
return "table";
|
||||
}
|
||||
|
||||
// Presentations
|
||||
if (["ppt", "pptx", "odp"].indexOf(ext) !== -1) {
|
||||
return "presentation";
|
||||
}
|
||||
|
||||
// Default
|
||||
return "file";
|
||||
}
|
||||
|
||||
function openFile(filePath) {
|
||||
Logger.i("FileSearch", "Opening file:", filePath);
|
||||
Quickshell.execDetached([fileOpener, filePath]);
|
||||
launcher.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import QtQuick
|
||||
import Quickshell.Io
|
||||
import qs.Commons
|
||||
import qs.Services.UI
|
||||
|
||||
Item {
|
||||
property var pluginApi: null
|
||||
|
||||
Component.onCompleted: {
|
||||
if (pluginApi) {
|
||||
Logger.i("FileSearch", "Plugin initialized");
|
||||
}
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "plugin:file-search"
|
||||
|
||||
// Toggle launcher in file search mode
|
||||
function toggle() {
|
||||
if (!pluginApi) return;
|
||||
|
||||
pluginApi.withCurrentScreen(screen => {
|
||||
var launcherPanel = PanelService.getPanel("launcherPanel", screen);
|
||||
if (!launcherPanel) {
|
||||
Logger.e("FileSearch", "Could not get launcher panel");
|
||||
return;
|
||||
}
|
||||
|
||||
var searchText = launcherPanel.searchText || "";
|
||||
var isInFileMode = searchText.startsWith(">file");
|
||||
|
||||
if (!launcherPanel.isPanelOpen) {
|
||||
// Launcher closed - open with file search
|
||||
Logger.i("FileSearch", "Opening launcher in file search mode");
|
||||
launcherPanel.open();
|
||||
launcherPanel.setSearchText(">file ");
|
||||
} else if (isInFileMode) {
|
||||
// Already in file mode - close launcher
|
||||
Logger.i("FileSearch", "Closing launcher (toggle off)");
|
||||
launcherPanel.close();
|
||||
} else {
|
||||
// Launcher open but different mode - switch to file search
|
||||
Logger.i("FileSearch", "Switching to file search mode");
|
||||
launcherPanel.setSearchText(">file ");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Open launcher with file search and specific query
|
||||
function search(query: string) {
|
||||
if (!pluginApi) return;
|
||||
|
||||
pluginApi.withCurrentScreen(screen => {
|
||||
var launcherPanel = PanelService.getPanel("launcherPanel", screen);
|
||||
if (!launcherPanel) {
|
||||
Logger.e("FileSearch", "Could not get launcher panel");
|
||||
return;
|
||||
}
|
||||
|
||||
var searchQuery = query || "";
|
||||
Logger.i("FileSearch", "Opening launcher with search query:", searchQuery);
|
||||
|
||||
launcherPanel.open();
|
||||
launcherPanel.setSearchText(">file " + searchQuery);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
# File Search Plugin
|
||||
|
||||
File search from the launcher.
|
||||
|
||||
## Requirements
|
||||
|
||||
This plugin requires [fd](https://github.com/sharkdp/fd#installation) to be installed.
|
||||
|
||||
## Usage
|
||||
|
||||
**Access from launcher:**
|
||||
|
||||
Type `>file` in the Noctalia launcher to activate file search.
|
||||
|
||||
**Toggle file search:**
|
||||
|
||||
```bash
|
||||
noctalia-shell ipc call plugin:file-search toggle
|
||||
```
|
||||
|
||||
**Search with pre-filled query:**
|
||||
|
||||
```bash
|
||||
noctalia-shell ipc call plugin:file-search search "eko"
|
||||
```
|
||||
@@ -0,0 +1,142 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Commons
|
||||
import qs.Widgets
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
property var pluginApi: null
|
||||
|
||||
property var cfg: pluginApi?.pluginSettings || ({})
|
||||
property var defaults: pluginApi?.manifest?.metadata?.defaultSettings || ({})
|
||||
|
||||
property bool valueShowHidden: cfg.showHidden ?? defaults.showHidden
|
||||
property int valueMaxResults: cfg.maxResults ?? defaults.maxResults
|
||||
property string valueFileOpener: cfg.fileOpener ?? defaults.fileOpener
|
||||
property string valueFdCommand: cfg.fdCommand ?? defaults.fdCommand
|
||||
property string valueSearchDirectory: cfg.searchDirectory ?? defaults.searchDirectory
|
||||
|
||||
spacing: Style.marginL
|
||||
|
||||
Component.onCompleted: {
|
||||
Logger.d("FileSearch", "Settings UI loaded");
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
spacing: Style.marginM
|
||||
Layout.fillWidth: true
|
||||
|
||||
// Show Hidden Files Toggle
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginM
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginS
|
||||
|
||||
NText {
|
||||
text: pluginApi?.tr("settings.showHidden.label")
|
||||
font.pointSize: Style.fontSizeL
|
||||
font.weight: Font.Medium
|
||||
color: Color.mOnSurface
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
NToggle {
|
||||
checked: root.valueShowHidden
|
||||
onToggled: root.valueShowHidden = checked
|
||||
}
|
||||
}
|
||||
|
||||
// File Opener Input
|
||||
NTextInput {
|
||||
Layout.fillWidth: true
|
||||
label: pluginApi?.tr("settings.fileOpener.label")
|
||||
description: pluginApi?.tr("settings.fileOpener.description")
|
||||
placeholderText: pluginApi?.tr("settings.fileOpener.placeholder")
|
||||
text: root.valueFileOpener
|
||||
onTextChanged: root.valueFileOpener = text
|
||||
}
|
||||
|
||||
// Search Directory Input
|
||||
NTextInput {
|
||||
Layout.fillWidth: true
|
||||
label: pluginApi?.tr("settings.searchDirectory.label")
|
||||
description: pluginApi?.tr("settings.searchDirectory.description")
|
||||
placeholderText: pluginApi?.tr("settings.searchDirectory.placeholder")
|
||||
text: root.valueSearchDirectory
|
||||
onTextChanged: root.valueSearchDirectory = text
|
||||
}
|
||||
|
||||
// fd Command Path Input
|
||||
NTextInput {
|
||||
Layout.fillWidth: true
|
||||
label: pluginApi?.tr("settings.fdCommand.label")
|
||||
description: pluginApi?.tr("settings.fdCommand.description")
|
||||
placeholderText: pluginApi?.tr("settings.fdCommand.placeholder")
|
||||
text: root.valueFdCommand
|
||||
onTextChanged: root.valueFdCommand = text
|
||||
}
|
||||
}
|
||||
|
||||
// Max Results Slider
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginS
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
NText {
|
||||
text: pluginApi?.tr("settings.maxResults.label")
|
||||
font.pointSize: Style.fontSizeL
|
||||
font.weight: Font.Medium
|
||||
color: Color.mOnSurface
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
NText {
|
||||
text: root.valueMaxResults === 0 ? pluginApi?.tr("settings.maxResults.unlimited") : root.valueMaxResults.toString()
|
||||
font.pointSize: Style.fontSizeM
|
||||
font.weight: Font.Medium
|
||||
color: Color.mPrimary
|
||||
}
|
||||
}
|
||||
|
||||
NText {
|
||||
text: pluginApi?.tr("settings.maxResults.description")
|
||||
font.pointSize: Style.fontSizeS
|
||||
color: Color.mOnSurfaceVariant
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
NSlider {
|
||||
Layout.fillWidth: true
|
||||
from: 0
|
||||
to: 200
|
||||
stepSize: 10
|
||||
value: root.valueMaxResults
|
||||
onMoved: root.valueMaxResults = Math.round(value)
|
||||
}
|
||||
}
|
||||
|
||||
function saveSettings() {
|
||||
if (!pluginApi) {
|
||||
Logger.e("FileSearch", "Cannot save settings: pluginApi is null");
|
||||
return;
|
||||
}
|
||||
|
||||
pluginApi.pluginSettings.showHidden = root.valueShowHidden;
|
||||
pluginApi.pluginSettings.maxResults = root.valueMaxResults;
|
||||
pluginApi.pluginSettings.fileOpener = root.valueFileOpener;
|
||||
pluginApi.pluginSettings.searchDirectory = root.valueSearchDirectory;
|
||||
pluginApi.pluginSettings.fdCommand = root.valueFdCommand;
|
||||
pluginApi.saveSettings();
|
||||
|
||||
Logger.d("FileSearch", "Settings saved successfully");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"provider": {
|
||||
"name": "File Search"
|
||||
},
|
||||
"launcher": {
|
||||
"command": {
|
||||
"description": "Search files and folders"
|
||||
},
|
||||
"errors": {
|
||||
"fdNotFound": {
|
||||
"title": "fd not found",
|
||||
"description": "Please install fd to use file search"
|
||||
}
|
||||
},
|
||||
"prompts": {
|
||||
"emptyQuery": {
|
||||
"title": "Type to search files and folders",
|
||||
"description": "Start typing to search in {{root}}"
|
||||
},
|
||||
"searching": {
|
||||
"title": "Searching...",
|
||||
"description": "Looking for files and folders matching: {{query}}"
|
||||
},
|
||||
"noResults": {
|
||||
"title": "No results found",
|
||||
"description": "No files or folders matching '{{query}}'"
|
||||
}
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"showHidden": {
|
||||
"label": "Include hidden files"
|
||||
},
|
||||
"fileOpener": {
|
||||
"label": "File opener command",
|
||||
"description": "Command used to open files and folders",
|
||||
"placeholder": "xdg-open"
|
||||
},
|
||||
"searchDirectory": {
|
||||
"label": "Search directory",
|
||||
"description": "Directory to search for files and folders",
|
||||
"placeholder": "~"
|
||||
},
|
||||
"fdCommand": {
|
||||
"label": "fd command path",
|
||||
"description": "Command name or full path",
|
||||
"placeholder": "fd"
|
||||
},
|
||||
"maxResults": {
|
||||
"label": "Maximum results",
|
||||
"unlimited": "Unlimited",
|
||||
"description": "Limit the number of search results displayed (set to 0 for unlimited)"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"id": "file-search",
|
||||
"name": "File Search",
|
||||
"version": "1.0.1",
|
||||
"minNoctaliaVersion": "4.1.2",
|
||||
"author": "ericbreh",
|
||||
"license": "MIT",
|
||||
"repository": "https://github.com/noctalia-dev/noctalia-plugins",
|
||||
"description": "File search from the launcher.",
|
||||
"tags": [
|
||||
"Launcher",
|
||||
"Productivity"
|
||||
],
|
||||
"entryPoints": {
|
||||
"main": "Main.qml",
|
||||
"launcherProvider": "LauncherProvider.qml",
|
||||
"settings": "Settings.qml"
|
||||
},
|
||||
"dependencies": {
|
||||
"plugins": []
|
||||
},
|
||||
"metadata": {
|
||||
"defaultSettings": {
|
||||
"showHidden": false,
|
||||
"maxResults": 0,
|
||||
"fileOpener": "xdg-open",
|
||||
"searchDirectory": "~",
|
||||
"fdCommand": "fd"
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 965 KiB |
Reference in New Issue
Block a user