aboutsummaryrefslogtreecommitdiff
path: root/dotfiles/.config/noctalia/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'dotfiles/.config/noctalia/plugins')
-rw-r--r--dotfiles/.config/noctalia/plugins/file-search/LauncherProvider.qml448
-rw-r--r--dotfiles/.config/noctalia/plugins/file-search/Main.qml68
-rw-r--r--dotfiles/.config/noctalia/plugins/file-search/README.md25
-rw-r--r--dotfiles/.config/noctalia/plugins/file-search/Settings.qml142
-rw-r--r--dotfiles/.config/noctalia/plugins/file-search/i18n/en.json55
-rw-r--r--dotfiles/.config/noctalia/plugins/file-search/i18n/fr.json55
-rw-r--r--dotfiles/.config/noctalia/plugins/file-search/i18n/zh-CN.json55
-rw-r--r--dotfiles/.config/noctalia/plugins/file-search/i18n/zh-TW.json55
-rw-r--r--dotfiles/.config/noctalia/plugins/file-search/manifest.json31
-rw-r--r--dotfiles/.config/noctalia/plugins/file-search/preview.pngbin0 -> 988330 bytes
10 files changed, 934 insertions, 0 deletions
diff --git a/dotfiles/.config/noctalia/plugins/file-search/LauncherProvider.qml b/dotfiles/.config/noctalia/plugins/file-search/LauncherProvider.qml
new file mode 100644
index 0000000..3ca5e88
--- /dev/null
+++ b/dotfiles/.config/noctalia/plugins/file-search/LauncherProvider.qml
@@ -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();
+ }
+}
diff --git a/dotfiles/.config/noctalia/plugins/file-search/Main.qml b/dotfiles/.config/noctalia/plugins/file-search/Main.qml
new file mode 100644
index 0000000..087fe1e
--- /dev/null
+++ b/dotfiles/.config/noctalia/plugins/file-search/Main.qml
@@ -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);
+ });
+ }
+ }
+}
diff --git a/dotfiles/.config/noctalia/plugins/file-search/README.md b/dotfiles/.config/noctalia/plugins/file-search/README.md
new file mode 100644
index 0000000..1d5ef34
--- /dev/null
+++ b/dotfiles/.config/noctalia/plugins/file-search/README.md
@@ -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"
+```
diff --git a/dotfiles/.config/noctalia/plugins/file-search/Settings.qml b/dotfiles/.config/noctalia/plugins/file-search/Settings.qml
new file mode 100644
index 0000000..fc7ed87
--- /dev/null
+++ b/dotfiles/.config/noctalia/plugins/file-search/Settings.qml
@@ -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: 1000
+ 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");
+ }
+}
diff --git a/dotfiles/.config/noctalia/plugins/file-search/i18n/en.json b/dotfiles/.config/noctalia/plugins/file-search/i18n/en.json
new file mode 100644
index 0000000..8a56924
--- /dev/null
+++ b/dotfiles/.config/noctalia/plugins/file-search/i18n/en.json
@@ -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)"
+ }
+ }
+}
diff --git a/dotfiles/.config/noctalia/plugins/file-search/i18n/fr.json b/dotfiles/.config/noctalia/plugins/file-search/i18n/fr.json
new file mode 100644
index 0000000..361e020
--- /dev/null
+++ b/dotfiles/.config/noctalia/plugins/file-search/i18n/fr.json
@@ -0,0 +1,55 @@
+{
+ "provider": {
+ "name": "Recherche de fichiers"
+ },
+ "launcher": {
+ "command": {
+ "description": "Rechercher des fichiers et des dossiers"
+ },
+ "errors": {
+ "fdNotFound": {
+ "title": "fd non trouvé",
+ "description": "Veuillez installer fd pour utiliser la recherche de fichiers"
+ }
+ },
+ "prompts": {
+ "emptyQuery": {
+ "title": "Tapez pour rechercher des fichiers et des dossiers",
+ "description": "Commencez à taper pour rechercher dans {{root}}"
+ },
+ "searching": {
+ "title": "Recherche en cours...",
+ "description": "Recherche de fichiers et dossiers correspondant à : {{query}}"
+ },
+ "noResults": {
+ "title": "Aucun résultat trouvé",
+ "description": "Aucun fichier ou dossier ne correspond à '{{query}}'"
+ }
+ }
+ },
+ "settings": {
+ "showHidden": {
+ "label": "Inclure les fichiers cachés"
+ },
+ "fileOpener": {
+ "label": "Commande d'ouverture de fichier",
+ "description": "Commande utilisée pour ouvrir les fichiers et dossiers",
+ "placeholder": "xdg-open"
+ },
+ "searchDirectory": {
+ "label": "Répertoire de recherche",
+ "description": "Répertoire où rechercher des fichiers et des dossiers",
+ "placeholder": "~"
+ },
+ "fdCommand": {
+ "label": "Chemin de la commande fd",
+ "description": "Nom de la commande ou chemin complet",
+ "placeholder": "fd"
+ },
+ "maxResults": {
+ "label": "Nombre maximal de résultats",
+ "unlimited": "Illimité",
+ "description": "Limiter le nombre de résultats de recherche affichés (définir à 0 pour illimité)"
+ }
+ }
+}
diff --git a/dotfiles/.config/noctalia/plugins/file-search/i18n/zh-CN.json b/dotfiles/.config/noctalia/plugins/file-search/i18n/zh-CN.json
new file mode 100644
index 0000000..446924e
--- /dev/null
+++ b/dotfiles/.config/noctalia/plugins/file-search/i18n/zh-CN.json
@@ -0,0 +1,55 @@
+{
+ "provider": {
+ "name": "文件搜索"
+ },
+ "launcher": {
+ "command": {
+ "description": "搜索文件和文件夹"
+ },
+ "errors": {
+ "fdNotFound": {
+ "title": "未找到 fd",
+ "description": "请安装 fd 以使用文件搜索"
+ }
+ },
+ "prompts": {
+ "emptyQuery": {
+ "title": "输入以搜索文件和文件夹",
+ "description": "开始输入以在 {{root}} 中搜索"
+ },
+ "searching": {
+ "title": "正在搜索…",
+ "description": "正在查找匹配 {{query}} 的文件和文件夹"
+ },
+ "noResults": {
+ "title": "未找到结果",
+ "description": "没有匹配 '{{query}}' 的文件或文件夹"
+ }
+ }
+ },
+ "settings": {
+ "showHidden": {
+ "label": "包含隐藏文件"
+ },
+ "fileOpener": {
+ "label": "文件打开命令",
+ "description": "用于打开文件和文件夹的命令",
+ "placeholder": "xdg-open"
+ },
+ "searchDirectory": {
+ "label": "搜索目录",
+ "description": "用于搜索文件和文件夹的目录",
+ "placeholder": "~"
+ },
+ "fdCommand": {
+ "label": "fd 命令路径",
+ "description": "命令名称或完整路径",
+ "placeholder": "fd"
+ },
+ "maxResults": {
+ "label": "最大结果数",
+ "unlimited": "无限制",
+ "description": "限制显示的搜索结果数量(设为 0 表示无限制)"
+ }
+ }
+}
diff --git a/dotfiles/.config/noctalia/plugins/file-search/i18n/zh-TW.json b/dotfiles/.config/noctalia/plugins/file-search/i18n/zh-TW.json
new file mode 100644
index 0000000..0a31b1a
--- /dev/null
+++ b/dotfiles/.config/noctalia/plugins/file-search/i18n/zh-TW.json
@@ -0,0 +1,55 @@
+{
+ "provider": {
+ "name": "檔案搜尋"
+ },
+ "launcher": {
+ "command": {
+ "description": "搜尋檔案與資料夾"
+ },
+ "errors": {
+ "fdNotFound": {
+ "title": "找不到 fd",
+ "description": "請安裝 fd 以使用檔案搜尋"
+ }
+ },
+ "prompts": {
+ "emptyQuery": {
+ "title": "輸入以搜尋檔案與資料夾",
+ "description": "開始輸入以在 {{root}} 中搜尋"
+ },
+ "searching": {
+ "title": "正在搜尋…",
+ "description": "正在尋找符合 {{query}} 的檔案與資料夾"
+ },
+ "noResults": {
+ "title": "找不到結果",
+ "description": "沒有符合 '{{query}}' 的檔案或資料夾"
+ }
+ }
+ },
+ "settings": {
+ "showHidden": {
+ "label": "包含隱藏檔案"
+ },
+ "fileOpener": {
+ "label": "檔案開啟命令",
+ "description": "用於開啟檔案與資料夾的命令",
+ "placeholder": "xdg-open"
+ },
+ "searchDirectory": {
+ "label": "搜尋目錄",
+ "description": "用於搜尋檔案與資料夾的目錄",
+ "placeholder": "~"
+ },
+ "fdCommand": {
+ "label": "fd 命令路徑",
+ "description": "命令名稱或完整路徑",
+ "placeholder": "fd"
+ },
+ "maxResults": {
+ "label": "最大結果數",
+ "unlimited": "無限制",
+ "description": "限制顯示的搜尋結果數量(設為 0 表示無限制)"
+ }
+ }
+}
diff --git a/dotfiles/.config/noctalia/plugins/file-search/manifest.json b/dotfiles/.config/noctalia/plugins/file-search/manifest.json
new file mode 100644
index 0000000..0d0facf
--- /dev/null
+++ b/dotfiles/.config/noctalia/plugins/file-search/manifest.json
@@ -0,0 +1,31 @@
+{
+ "id": "file-search",
+ "name": "File Search",
+ "version": "1.0.4",
+ "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": 100,
+ "fileOpener": "xdg-open",
+ "searchDirectory": "~",
+ "fdCommand": "fd"
+ }
+ }
+}
diff --git a/dotfiles/.config/noctalia/plugins/file-search/preview.png b/dotfiles/.config/noctalia/plugins/file-search/preview.png
new file mode 100644
index 0000000..e6cf000
--- /dev/null
+++ b/dotfiles/.config/noctalia/plugins/file-search/preview.png
Binary files differ