From 4b8ad632aaa3fec345dee83dcb597b229f803bab Mon Sep 17 00:00:00 2001 From: Alessandro Pizzetti Date: Fri, 12 Jun 2026 17:36:38 -0300 Subject: [PATCH 1/3] feat(clipboard): add startup cleanup and automatic paste options Add opt-in settings to clear regular clipboard history when Noctalia starts and to paste selected entries into the previously focused window. Preserve pinned entries during startup cleanup, defer Ctrl+V until the panel closes, document the optional wtype dependency, and add English and Portuguese translations. --- clipboard/Main.qml | 43 ++++++++++++++++++++++++++++++++++++++--- clipboard/Panel.qml | 28 ++++++++++++++++++++++++--- clipboard/README.md | 3 +++ clipboard/Settings.qml | 30 ++++++++++++++++++++++++---- clipboard/i18n/en.json | 4 ++++ clipboard/i18n/pt.json | 4 ++++ clipboard/manifest.json | 4 +++- 7 files changed, 105 insertions(+), 11 deletions(-) diff --git a/clipboard/Main.qml b/clipboard/Main.qml index 34ed56c8c..b6482327c 100644 --- a/clipboard/Main.qml +++ b/clipboard/Main.qml @@ -96,6 +96,20 @@ Item { // Id of the decode currently in flight ("" when idle). property string decodingId: "" + // Sends Ctrl+V after the panel has closed and focus has returned to the + // previously active window. The explicit Wayland environment keeps wtype + // working when Noctalia is launched as a user service. + function pasteIntoPreviousWindow() { + if (pasteProc.running) + return; + const runtimeDir = Quickshell.env("XDG_RUNTIME_DIR") || ""; + const waylandDisplay = Quickshell.env("WAYLAND_DISPLAY") || ""; + pasteProc.command = ["bash", "-c", + 'sleep 0.5; XDG_RUNTIME_DIR="$1" WAYLAND_DISPLAY="$2" wtype -M ctrl -P v -p v -m ctrl', + "--", runtimeDir, waylandDisplay]; + pasteProc.running = true; + } + // Pending ids waiting for decodeProc to become free. FIFO — delegates // are rendered top-to-bottom so the first enqueued id is the most // visible one. @@ -408,6 +422,21 @@ Item { stderr: StdioCollector {} } + // Sends the paste shortcut to the window focused after the panel closes. + Process { + id: pasteProc + + stdout: StdioCollector {} + stderr: StdioCollector {} + + onExited: exitCode => { + if (exitCode !== 0) { + Logger.w("Clipboard Plugin", "automatic paste failed:", + String(pasteProc.stderr.text).trim() || "exit " + exitCode); + } + } + } + // Deletes a specific entry, then refreshes the list on success. Process { id: removeProc @@ -1139,8 +1168,8 @@ Item { // --- Lifecycle ----------------------------------------------------------- - // Seed the list once the shell has injected pluginApi so maxHistorySize - // resolves to the user's actual setting (not the fallback 100). + // Seed or clear the list once the shell has injected pluginApi so startup + // behavior resolves to the user's actual settings. // // Also ensure the pinned-file parent directory exists. FileView.setText // does not create missing parent directories, so a fresh install with @@ -1150,7 +1179,13 @@ Item { // copied-at.json have a parent to write into. onPluginApiChanged: { if (pluginApi) { - root.refresh(); + const cfg = pluginApi?.pluginSettings || ({}); + const defaults = pluginApi?.manifest?.metadata?.defaultSettings || ({}); + if (cfg.clearHistoryOnStartup ?? defaults.clearHistoryOnStartup ?? false) { + root.wipe(); + } else { + root.refresh(); + } if (root.dataDir) { pinnedDirProc.command = ["mkdir", "-p", root.dataDir]; pinnedDirProc.running = true; @@ -1165,6 +1200,8 @@ Item { listProc.running = false; if (copyProc.running) copyProc.running = false; + if (pasteProc.running) + pasteProc.running = false; if (removeProc.running) removeProc.running = false; if (wipeProc.running) diff --git a/clipboard/Panel.qml b/clipboard/Panel.qml index 286b96d4b..f2e979f6f 100644 --- a/clipboard/Panel.qml +++ b/clipboard/Panel.qml @@ -116,6 +116,7 @@ Item { // which clamps to [0, length-1] instead of resetting so the user's // focus stays on the row that slid up into the deleted position. property int selectedIndex: -1 + property bool pasteAfterClose: false // Sentinel index to restore AFTER a delete-triggered refresh. -1 // means "no pending clamp" (normal reset semantics apply). Any @@ -271,7 +272,14 @@ Item { Logger.w("Clipboard Plugin", "activateSelection: no entry at index", idx); return; } - root.pluginApi?.mainInstance?.copy(entry.id); + if (entry.pinned && entry.pinnedIndex >= 0) { + root.pluginApi?.mainInstance?.copyPinned(entry.pinnedIndex); + } else { + root.pluginApi?.mainInstance?.copy(entry.id); + } + const cfg = root.pluginApi?.pluginSettings || ({}); + const defaults = root.pluginApi?.manifest?.metadata?.defaultSettings || ({}); + root.pasteAfterClose = cfg.autoPasteOnSelect ?? defaults.autoPasteOnSelect ?? false; const typeSlug = entry.type === "file" ? "file" : entry.type === "image" ? "image" : "text"; @@ -650,6 +658,10 @@ Item { // the Quickshell log without flooding on repeat attempts. Logger.w("Clipboard Plugin", "closePanel returned false — screen:", targetScreen?.name ?? "null"); } + if (ok && root.pasteAfterClose) { + root.pluginApi?.mainInstance?.pasteIntoPreviousWindow(); + } + root.pasteAfterClose = false; } } @@ -1152,7 +1164,12 @@ Item { // (reuseItems: true) cannot carry a stale screen // reference through the deferred-close timer — see the // closePanelTimer commentary above. - onCopied: closePanelTimer.restart() + onCopied: { + const cfg = root.pluginApi?.pluginSettings || ({}); + const defaults = root.pluginApi?.manifest?.metadata?.defaultSettings || ({}); + root.pasteAfterClose = cfg.autoPasteOnSelect ?? defaults.autoPasteOnSelect ?? false; + closePanelTimer.restart(); + } } // A subtle scrollbar keeps the user oriented in a long list. @@ -1203,7 +1220,12 @@ Item { // background Rectangle) — so grid cells highlight // consistently on the Images tab. selected: index === root.selectedIndex - onCopied: closePanelTimer.restart() + onCopied: { + const cfg = root.pluginApi?.pluginSettings || ({}); + const defaults = root.pluginApi?.manifest?.metadata?.defaultSettings || ({}); + root.pasteAfterClose = cfg.autoPasteOnSelect ?? defaults.autoPasteOnSelect ?? false; + closePanelTimer.restart(); + } } } diff --git a/clipboard/README.md b/clipboard/README.md index 9e3bc0349..f7455f95f 100644 --- a/clipboard/README.md +++ b/clipboard/README.md @@ -20,6 +20,7 @@ A clipboard history panel for the [Noctalia](https://github.com/noctalia-dev/noc - Noctalia `4.1.2`+ - [cliphist](https://github.com/sentriz/cliphist) - `wl-clipboard` +- `wtype` (optional, required for automatic paste after selection) ## Installation @@ -42,6 +43,8 @@ Settings are stored in `~/.config/noctalia/plugins/clipboard/settings.json` (cre |---|---|---| | `maxHistorySize` | `100` | Maximum number of entries retained in history. | | `showImagePreviews` | `true` | Show image thumbnails inline in the history panel. | +| `clearHistoryOnStartup` | `false` | Clear regular history whenever the shell starts; pinned items remain. | +| `autoPasteOnSelect` | `false` | Paste the selected entry into the previously focused window. Requires `wtype`. | | `density` | `"comfortable"` | Visual density of the list. `"comfortable"` or `"compact"`. | ## IPC diff --git a/clipboard/Settings.qml b/clipboard/Settings.qml index 02d4eaf7b..651f06175 100644 --- a/clipboard/Settings.qml +++ b/clipboard/Settings.qml @@ -13,7 +13,7 @@ import qs.Widgets // - docs/specs/plugin-qml-idioms.md (two-phase save pattern, // null-guarding, widget library, i18n via pluginApi?.tr) // -// Three controls stacked vertically map to the keys in +// Controls stacked vertically map to the keys in // manifest.json metadata.defaultSettings: // maxHistorySize — NSpinBox, 20–500, default 100 // showImagePreviews — NToggle, default true @@ -29,6 +29,8 @@ ColumnLayout { // Injected by the shell after instantiation. Always null-guard every // access with optional chaining (?.) — per docs/specs/plugin-qml-idioms.md. property var pluginApi: null + property var cfg: pluginApi?.pluginSettings || ({}) + property var defaults: pluginApi?.manifest?.metadata?.defaultSettings || ({}) // --- Pending state ------------------------------------------------------ // @@ -38,9 +40,11 @@ ColumnLayout { // exactly — drift between the two is a real bug (a plugin instance that // predates a defaults change falls back to the QML literal here, not // the manifest value). - property int valueMaxHistorySize: pluginApi?.pluginSettings?.maxHistorySize ?? 100 - property bool valueShowImagePreviews: pluginApi?.pluginSettings?.showImagePreviews ?? true - property string valueDensity: pluginApi?.pluginSettings?.density ?? "comfortable" + property int valueMaxHistorySize: cfg.maxHistorySize ?? defaults.maxHistorySize ?? 100 + property bool valueShowImagePreviews: cfg.showImagePreviews ?? defaults.showImagePreviews ?? true + property bool valueClearHistoryOnStartup: cfg.clearHistoryOnStartup ?? defaults.clearHistoryOnStartup ?? false + property bool valueAutoPasteOnSelect: cfg.autoPasteOnSelect ?? defaults.autoPasteOnSelect ?? false + property string valueDensity: cfg.density ?? defaults.density ?? "comfortable" // --- Controls ----------------------------------------------------------- @@ -69,6 +73,22 @@ ColumnLayout { onToggled: checked => { root.valueShowImagePreviews = checked; } } + NToggle { + Layout.fillWidth: true + label: pluginApi?.tr("settings.clear-history-on-startup") + description: pluginApi?.tr("settings.clear-history-on-startup-description") + checked: root.valueClearHistoryOnStartup + onToggled: checked => { root.valueClearHistoryOnStartup = checked; } + } + + NToggle { + Layout.fillWidth: true + label: pluginApi?.tr("settings.auto-paste-on-select") + description: pluginApi?.tr("settings.auto-paste-on-select-description") + checked: root.valueAutoPasteOnSelect + onToggled: checked => { root.valueAutoPasteOnSelect = checked; } + } + // density — enum of "compact" / "comfortable" / "spacious". The // NComboBox model uses { key, name } entries; we bind selection through // currentKey so the stored value is the enum string itself (matching @@ -102,6 +122,8 @@ ColumnLayout { } pluginApi.pluginSettings.maxHistorySize = root.valueMaxHistorySize; pluginApi.pluginSettings.showImagePreviews = root.valueShowImagePreviews; + pluginApi.pluginSettings.clearHistoryOnStartup = root.valueClearHistoryOnStartup; + pluginApi.pluginSettings.autoPasteOnSelect = root.valueAutoPasteOnSelect; pluginApi.pluginSettings.density = root.valueDensity; pluginApi.saveSettings(); } diff --git a/clipboard/i18n/en.json b/clipboard/i18n/en.json index e33cc6c9a..a6fe0ac8d 100644 --- a/clipboard/i18n/en.json +++ b/clipboard/i18n/en.json @@ -30,6 +30,10 @@ "max-history-size-description": "Number of clipboard entries kept in history. Takes effect on the next refresh.", "show-image-previews": "Show image previews", "show-image-previews-description": "Decode and display thumbnails for image entries. Disable on low-memory systems.", + "clear-history-on-startup": "Clear history on startup", + "clear-history-on-startup-description": "Clear regular history whenever Noctalia Shell starts or restarts. Pinned items are preserved.", + "auto-paste-on-select": "Paste automatically on selection", + "auto-paste-on-select-description": "Close the panel and send Ctrl+V to the previous window after selecting an entry.", "density": "Density", "density-description": "Spacing of clipboard entries in the panel.", "density-compact": "Compact", diff --git a/clipboard/i18n/pt.json b/clipboard/i18n/pt.json index f9ecac912..7eebae259 100644 --- a/clipboard/i18n/pt.json +++ b/clipboard/i18n/pt.json @@ -30,6 +30,10 @@ "max-history-size-description": "Numero de entradas da area de transferencia no historico. Entra em vigor na proxima atualizacao.", "show-image-previews": "Mostrar pre-visualizacoes de imagens", "show-image-previews-description": "Decodificar e exibir miniaturas para entradas de imagem. Desativar em sistemas com pouca memoria.", + "clear-history-on-startup": "Limpar historico ao iniciar", + "clear-history-on-startup-description": "Limpar o historico comum sempre que o Noctalia Shell iniciar ou reiniciar. Itens fixados sao preservados.", + "auto-paste-on-select": "Colar automaticamente ao selecionar", + "auto-paste-on-select-description": "Fechar o painel e enviar Ctrl+V para a janela anterior depois de selecionar uma entrada.", "density": "Densidade", "density-description": "Espacamento das entradas da area de transferencia no painel.", "density-compact": "Compacto", diff --git a/clipboard/manifest.json b/clipboard/manifest.json index 776056a54..f180a54ee 100644 --- a/clipboard/manifest.json +++ b/clipboard/manifest.json @@ -1,7 +1,7 @@ { "id": "clipboard", "name": "Clipboard", - "version": "1.0.3", + "version": "1.1.0", "minNoctaliaVersion": "4.1.2", "author": "yanekyuk", "repository": "https://github.com/noctalia-dev/noctalia-plugins", @@ -25,6 +25,8 @@ "defaultSettings": { "maxHistorySize": 100, "showImagePreviews": true, + "clearHistoryOnStartup": false, + "autoPasteOnSelect": false, "density": "comfortable" } } From baed12ff5e126b3d0a7d0ee7317d2545b9fa637d Mon Sep 17 00:00:00 2001 From: Alessandro Pizzetti Date: Fri, 12 Jun 2026 18:05:05 -0300 Subject: [PATCH 2/3] fix(clipboard): address automatic paste review feedback Centralize the automatic-paste setting lookup, add a configurable 200-5000 ms focus restoration delay with a 500 ms default, and correct Portuguese diacritics. --- clipboard/Main.qml | 8 ++++++-- clipboard/Panel.qml | 25 +++++++++++++------------ clipboard/README.md | 1 + clipboard/Settings.qml | 14 ++++++++++++++ clipboard/i18n/en.json | 2 ++ clipboard/i18n/pt.json | 6 ++++-- clipboard/manifest.json | 1 + 7 files changed, 41 insertions(+), 16 deletions(-) diff --git a/clipboard/Main.qml b/clipboard/Main.qml index b6482327c..ced35049a 100644 --- a/clipboard/Main.qml +++ b/clipboard/Main.qml @@ -102,11 +102,15 @@ Item { function pasteIntoPreviousWindow() { if (pasteProc.running) return; + const cfg = pluginApi?.pluginSettings || ({}); + const defaults = pluginApi?.manifest?.metadata?.defaultSettings || ({}); + const configuredDelay = Number(cfg.autoPasteDelayMs ?? defaults.autoPasteDelayMs ?? 500); + const delayMs = Math.max(200, Math.min(5000, isNaN(configuredDelay) ? 500 : configuredDelay)); const runtimeDir = Quickshell.env("XDG_RUNTIME_DIR") || ""; const waylandDisplay = Quickshell.env("WAYLAND_DISPLAY") || ""; pasteProc.command = ["bash", "-c", - 'sleep 0.5; XDG_RUNTIME_DIR="$1" WAYLAND_DISPLAY="$2" wtype -M ctrl -P v -p v -m ctrl', - "--", runtimeDir, waylandDisplay]; + 'sleep "$1"; XDG_RUNTIME_DIR="$2" WAYLAND_DISPLAY="$3" wtype -M ctrl -P v -p v -m ctrl', + "--", String(delayMs / 1000), runtimeDir, waylandDisplay]; pasteProc.running = true; } diff --git a/clipboard/Panel.qml b/clipboard/Panel.qml index f2e979f6f..8711b0355 100644 --- a/clipboard/Panel.qml +++ b/clipboard/Panel.qml @@ -91,6 +91,11 @@ Item { // Cached settings for reactive use inside bindings. readonly property bool showImagePreviews: pluginApi?.pluginSettings?.showImagePreviews ?? true + readonly property bool autoPasteOnSelect: { + const cfg = pluginApi?.pluginSettings || ({}); + const defaults = pluginApi?.manifest?.metadata?.defaultSettings || ({}); + return cfg.autoPasteOnSelect ?? defaults.autoPasteOnSelect ?? false; + } readonly property real densitySpacing: { const d = pluginApi?.pluginSettings?.density ?? "comfortable"; if (d === "compact") return Style.marginXS; @@ -118,6 +123,11 @@ Item { property int selectedIndex: -1 property bool pasteAfterClose: false + function closeAfterCopy() { + root.pasteAfterClose = root.autoPasteOnSelect; + closePanelTimer.restart(); + } + // Sentinel index to restore AFTER a delete-triggered refresh. -1 // means "no pending clamp" (normal reset semantics apply). Any // value >= 0 means "the user just pressed Delete at this position; @@ -277,9 +287,6 @@ Item { } else { root.pluginApi?.mainInstance?.copy(entry.id); } - const cfg = root.pluginApi?.pluginSettings || ({}); - const defaults = root.pluginApi?.manifest?.metadata?.defaultSettings || ({}); - root.pasteAfterClose = cfg.autoPasteOnSelect ?? defaults.autoPasteOnSelect ?? false; const typeSlug = entry.type === "file" ? "file" : entry.type === "image" ? "image" : "text"; @@ -287,7 +294,7 @@ Item { root.pluginApi?.tr("toast.item-copied-" + typeSlug + "-title"), root.pluginApi?.tr("toast.item-copied-" + typeSlug + "-body") ); - closePanelTimer.restart(); + root.closeAfterCopy(); } // Pin / unpin the selected row. Toggles based on the entry's @@ -1165,10 +1172,7 @@ Item { // reference through the deferred-close timer — see the // closePanelTimer commentary above. onCopied: { - const cfg = root.pluginApi?.pluginSettings || ({}); - const defaults = root.pluginApi?.manifest?.metadata?.defaultSettings || ({}); - root.pasteAfterClose = cfg.autoPasteOnSelect ?? defaults.autoPasteOnSelect ?? false; - closePanelTimer.restart(); + root.closeAfterCopy(); } } @@ -1221,10 +1225,7 @@ Item { // consistently on the Images tab. selected: index === root.selectedIndex onCopied: { - const cfg = root.pluginApi?.pluginSettings || ({}); - const defaults = root.pluginApi?.manifest?.metadata?.defaultSettings || ({}); - root.pasteAfterClose = cfg.autoPasteOnSelect ?? defaults.autoPasteOnSelect ?? false; - closePanelTimer.restart(); + root.closeAfterCopy(); } } } diff --git a/clipboard/README.md b/clipboard/README.md index f7455f95f..e2119c364 100644 --- a/clipboard/README.md +++ b/clipboard/README.md @@ -45,6 +45,7 @@ Settings are stored in `~/.config/noctalia/plugins/clipboard/settings.json` (cre | `showImagePreviews` | `true` | Show image thumbnails inline in the history panel. | | `clearHistoryOnStartup` | `false` | Clear regular history whenever the shell starts; pinned items remain. | | `autoPasteOnSelect` | `false` | Paste the selected entry into the previously focused window. Requires `wtype`. | +| `autoPasteDelayMs` | `500` | Delay before automatic paste (minimum 200 ms), allowing focus to return to the previous window. | | `density` | `"comfortable"` | Visual density of the list. `"comfortable"` or `"compact"`. | ## IPC diff --git a/clipboard/Settings.qml b/clipboard/Settings.qml index 651f06175..2709b8274 100644 --- a/clipboard/Settings.qml +++ b/clipboard/Settings.qml @@ -44,6 +44,7 @@ ColumnLayout { property bool valueShowImagePreviews: cfg.showImagePreviews ?? defaults.showImagePreviews ?? true property bool valueClearHistoryOnStartup: cfg.clearHistoryOnStartup ?? defaults.clearHistoryOnStartup ?? false property bool valueAutoPasteOnSelect: cfg.autoPasteOnSelect ?? defaults.autoPasteOnSelect ?? false + property int valueAutoPasteDelayMs: cfg.autoPasteDelayMs ?? defaults.autoPasteDelayMs ?? 500 property string valueDensity: cfg.density ?? defaults.density ?? "comfortable" // --- Controls ----------------------------------------------------------- @@ -89,6 +90,18 @@ ColumnLayout { onToggled: checked => { root.valueAutoPasteOnSelect = checked; } } + NSpinBox { + Layout.fillWidth: true + visible: root.valueAutoPasteOnSelect + label: pluginApi?.tr("settings.auto-paste-delay") + description: pluginApi?.tr("settings.auto-paste-delay-description") + from: 200 + to: 5000 + stepSize: 100 + value: root.valueAutoPasteDelayMs + onValueChanged: root.valueAutoPasteDelayMs = value + } + // density — enum of "compact" / "comfortable" / "spacious". The // NComboBox model uses { key, name } entries; we bind selection through // currentKey so the stored value is the enum string itself (matching @@ -124,6 +137,7 @@ ColumnLayout { pluginApi.pluginSettings.showImagePreviews = root.valueShowImagePreviews; pluginApi.pluginSettings.clearHistoryOnStartup = root.valueClearHistoryOnStartup; pluginApi.pluginSettings.autoPasteOnSelect = root.valueAutoPasteOnSelect; + pluginApi.pluginSettings.autoPasteDelayMs = root.valueAutoPasteDelayMs; pluginApi.pluginSettings.density = root.valueDensity; pluginApi.saveSettings(); } diff --git a/clipboard/i18n/en.json b/clipboard/i18n/en.json index a6fe0ac8d..21fb831e0 100644 --- a/clipboard/i18n/en.json +++ b/clipboard/i18n/en.json @@ -34,6 +34,8 @@ "clear-history-on-startup-description": "Clear regular history whenever Noctalia Shell starts or restarts. Pinned items are preserved.", "auto-paste-on-select": "Paste automatically on selection", "auto-paste-on-select-description": "Close the panel and send Ctrl+V to the previous window after selecting an entry.", + "auto-paste-delay": "Automatic paste delay", + "auto-paste-delay-description": "Milliseconds to wait for focus to return before sending Ctrl+V (minimum 200 ms).", "density": "Density", "density-description": "Spacing of clipboard entries in the panel.", "density-compact": "Compact", diff --git a/clipboard/i18n/pt.json b/clipboard/i18n/pt.json index 7eebae259..191def9fa 100644 --- a/clipboard/i18n/pt.json +++ b/clipboard/i18n/pt.json @@ -30,10 +30,12 @@ "max-history-size-description": "Numero de entradas da area de transferencia no historico. Entra em vigor na proxima atualizacao.", "show-image-previews": "Mostrar pre-visualizacoes de imagens", "show-image-previews-description": "Decodificar e exibir miniaturas para entradas de imagem. Desativar em sistemas com pouca memoria.", - "clear-history-on-startup": "Limpar historico ao iniciar", - "clear-history-on-startup-description": "Limpar o historico comum sempre que o Noctalia Shell iniciar ou reiniciar. Itens fixados sao preservados.", + "clear-history-on-startup": "Limpar histórico ao iniciar", + "clear-history-on-startup-description": "Limpar o histórico comum sempre que o Noctalia Shell iniciar ou reiniciar. Itens fixados são preservados.", "auto-paste-on-select": "Colar automaticamente ao selecionar", "auto-paste-on-select-description": "Fechar o painel e enviar Ctrl+V para a janela anterior depois de selecionar uma entrada.", + "auto-paste-delay": "Atraso da colagem automática", + "auto-paste-delay-description": "Milissegundos de espera para o foco retornar antes de enviar Ctrl+V (mínimo de 200 ms).", "density": "Densidade", "density-description": "Espacamento das entradas da area de transferencia no painel.", "density-compact": "Compacto", diff --git a/clipboard/manifest.json b/clipboard/manifest.json index f180a54ee..bfae0d497 100644 --- a/clipboard/manifest.json +++ b/clipboard/manifest.json @@ -27,6 +27,7 @@ "showImagePreviews": true, "clearHistoryOnStartup": false, "autoPasteOnSelect": false, + "autoPasteDelayMs": 500, "density": "comfortable" } } From 408aa9f7a7ecc2919826c15bd08a04e67e7ee5b3 Mon Sep 17 00:00:00 2001 From: Alessandro Pizzetti Date: Tue, 16 Jun 2026 12:02:19 -0300 Subject: [PATCH 3/3] fix(clipboard): address review feedback --- clipboard/Main.qml | 18 +++++++++++++---- clipboard/Panel.qml | 4 ++-- clipboard/README.md | 2 +- clipboard/Settings.qml | 4 ++-- clipboard/i18n/en.json | 2 +- clipboard/i18n/pt.json | 44 +++++++++++++++++++++--------------------- 6 files changed, 42 insertions(+), 32 deletions(-) diff --git a/clipboard/Main.qml b/clipboard/Main.qml index ced35049a..af76d5e75 100644 --- a/clipboard/Main.qml +++ b/clipboard/Main.qml @@ -96,16 +96,26 @@ Item { // Id of the decode currently in flight ("" when idle). property string decodingId: "" + readonly property int minAutoPasteDelayMs: 100 + readonly property int maxAutoPasteDelayMs: 2000 + readonly property int fallbackAutoPasteDelayMs: 500 + + function configuredAutoPasteDelayMs() { + const cfg = pluginApi?.pluginSettings || ({}); + const defaults = pluginApi?.manifest?.metadata?.defaultSettings || ({}); + const configuredDelay = Number(cfg.autoPasteDelayMs ?? defaults.autoPasteDelayMs ?? root.fallbackAutoPasteDelayMs); + if (isNaN(configuredDelay)) + return root.fallbackAutoPasteDelayMs; + return Math.max(root.minAutoPasteDelayMs, Math.min(root.maxAutoPasteDelayMs, configuredDelay)); + } + // Sends Ctrl+V after the panel has closed and focus has returned to the // previously active window. The explicit Wayland environment keeps wtype // working when Noctalia is launched as a user service. function pasteIntoPreviousWindow() { if (pasteProc.running) return; - const cfg = pluginApi?.pluginSettings || ({}); - const defaults = pluginApi?.manifest?.metadata?.defaultSettings || ({}); - const configuredDelay = Number(cfg.autoPasteDelayMs ?? defaults.autoPasteDelayMs ?? 500); - const delayMs = Math.max(200, Math.min(5000, isNaN(configuredDelay) ? 500 : configuredDelay)); + const delayMs = root.configuredAutoPasteDelayMs(); const runtimeDir = Quickshell.env("XDG_RUNTIME_DIR") || ""; const waylandDisplay = Quickshell.env("WAYLAND_DISPLAY") || ""; pasteProc.command = ["bash", "-c", diff --git a/clipboard/Panel.qml b/clipboard/Panel.qml index 8711b0355..a553f4ea1 100644 --- a/clipboard/Panel.qml +++ b/clipboard/Panel.qml @@ -91,7 +91,7 @@ Item { // Cached settings for reactive use inside bindings. readonly property bool showImagePreviews: pluginApi?.pluginSettings?.showImagePreviews ?? true - readonly property bool autoPasteOnSelect: { + function shouldAutoPasteOnSelect() { const cfg = pluginApi?.pluginSettings || ({}); const defaults = pluginApi?.manifest?.metadata?.defaultSettings || ({}); return cfg.autoPasteOnSelect ?? defaults.autoPasteOnSelect ?? false; @@ -124,7 +124,7 @@ Item { property bool pasteAfterClose: false function closeAfterCopy() { - root.pasteAfterClose = root.autoPasteOnSelect; + root.pasteAfterClose = root.shouldAutoPasteOnSelect(); closePanelTimer.restart(); } diff --git a/clipboard/README.md b/clipboard/README.md index e2119c364..ee2356fa6 100644 --- a/clipboard/README.md +++ b/clipboard/README.md @@ -45,7 +45,7 @@ Settings are stored in `~/.config/noctalia/plugins/clipboard/settings.json` (cre | `showImagePreviews` | `true` | Show image thumbnails inline in the history panel. | | `clearHistoryOnStartup` | `false` | Clear regular history whenever the shell starts; pinned items remain. | | `autoPasteOnSelect` | `false` | Paste the selected entry into the previously focused window. Requires `wtype`. | -| `autoPasteDelayMs` | `500` | Delay before automatic paste (minimum 200 ms), allowing focus to return to the previous window. | +| `autoPasteDelayMs` | `500` | Delay before automatic paste (100-2000 ms), allowing focus to return to the previous window. | | `density` | `"comfortable"` | Visual density of the list. `"comfortable"` or `"compact"`. | ## IPC diff --git a/clipboard/Settings.qml b/clipboard/Settings.qml index 2709b8274..98a3270ed 100644 --- a/clipboard/Settings.qml +++ b/clipboard/Settings.qml @@ -95,8 +95,8 @@ ColumnLayout { visible: root.valueAutoPasteOnSelect label: pluginApi?.tr("settings.auto-paste-delay") description: pluginApi?.tr("settings.auto-paste-delay-description") - from: 200 - to: 5000 + from: 100 + to: 2000 stepSize: 100 value: root.valueAutoPasteDelayMs onValueChanged: root.valueAutoPasteDelayMs = value diff --git a/clipboard/i18n/en.json b/clipboard/i18n/en.json index 21fb831e0..743b68017 100644 --- a/clipboard/i18n/en.json +++ b/clipboard/i18n/en.json @@ -35,7 +35,7 @@ "auto-paste-on-select": "Paste automatically on selection", "auto-paste-on-select-description": "Close the panel and send Ctrl+V to the previous window after selecting an entry.", "auto-paste-delay": "Automatic paste delay", - "auto-paste-delay-description": "Milliseconds to wait for focus to return before sending Ctrl+V (minimum 200 ms).", + "auto-paste-delay-description": "Milliseconds to wait for focus to return before sending Ctrl+V (100-2000 ms).", "density": "Density", "density-description": "Spacing of clipboard entries in the panel.", "density-compact": "Compact", diff --git a/clipboard/i18n/pt.json b/clipboard/i18n/pt.json index 191def9fa..f8cf95c22 100644 --- a/clipboard/i18n/pt.json +++ b/clipboard/i18n/pt.json @@ -1,22 +1,22 @@ { "bar": { - "tooltip": "Historico da area de transferencia" + "tooltip": "Histórico da área de transferência" }, "context": { - "toggle": "Alternar historico da area de transferencia", - "settings": "Abrir configuracoes" + "toggle": "Alternar histórico da área de transferência", + "settings": "Abrir configurações" }, "panel": { - "title": "Historico da area de transferencia", - "empty": "Nenhum historico de area de transferencia ainda", - "no-matches": "Sem correspondencias", + "title": "Histórico da área de transferência", + "empty": "Nenhum histórico da área de transferência ainda", + "no-matches": "Sem correspondências", "search-placeholder": "Pesquisar...", "delete": "Excluir entrada", "pin": "Fixar entrada", "unpin": "Desfixar entrada", - "wipe": "Limpar historico", + "wipe": "Limpar histórico", "close": "Fechar", - "settings": "Configuracoes", + "settings": "Configurações", "filter-text": "Texto", "filter-images": "Imagens", "filter-files": "Arquivos", @@ -26,37 +26,37 @@ }, "settings": { "tab-general": "Geral", - "max-history-size": "Tamanho maximo do historico", - "max-history-size-description": "Numero de entradas da area de transferencia no historico. Entra em vigor na proxima atualizacao.", - "show-image-previews": "Mostrar pre-visualizacoes de imagens", - "show-image-previews-description": "Decodificar e exibir miniaturas para entradas de imagem. Desativar em sistemas com pouca memoria.", + "max-history-size": "Tamanho máximo do histórico", + "max-history-size-description": "Número de entradas da área de transferência no histórico. Entra em vigor na próxima atualização.", + "show-image-previews": "Mostrar pré-visualizações de imagens", + "show-image-previews-description": "Decodificar e exibir miniaturas para entradas de imagem. Desativar em sistemas com pouca memória.", "clear-history-on-startup": "Limpar histórico ao iniciar", "clear-history-on-startup-description": "Limpar o histórico comum sempre que o Noctalia Shell iniciar ou reiniciar. Itens fixados são preservados.", "auto-paste-on-select": "Colar automaticamente ao selecionar", "auto-paste-on-select-description": "Fechar o painel e enviar Ctrl+V para a janela anterior depois de selecionar uma entrada.", "auto-paste-delay": "Atraso da colagem automática", - "auto-paste-delay-description": "Milissegundos de espera para o foco retornar antes de enviar Ctrl+V (mínimo de 200 ms).", + "auto-paste-delay-description": "Milissegundos de espera para o foco retornar antes de enviar Ctrl+V (100-2000 ms).", "density": "Densidade", - "density-description": "Espacamento das entradas da area de transferencia no painel.", + "density-description": "Espaçamento das entradas da área de transferência no painel.", "density-compact": "Compacto", - "density-comfortable": "Confortavel", - "density-spacious": "Espacoso" + "density-comfortable": "Confortável", + "density-spacious": "Espaçoso" }, "toast": { "item-copied-text-title": "Texto copiado", - "item-copied-text-body": "Voce pode colar o texto da area de transferencia.", + "item-copied-text-body": "Você pode colar o texto da área de transferência.", "item-copied-image-title": "Imagem copiada", - "item-copied-image-body": "Voce pode colar a imagem da area de transferencia.", + "item-copied-image-body": "Você pode colar a imagem da área de transferência.", "item-copied-file-title": "Arquivo copiado", - "item-copied-file-body": "Voce pode colar o arquivo da area de transferencia.", + "item-copied-file-body": "Você pode colar o arquivo da área de transferência.", "item-pinned": "Entrada fixada", "item-unpinned": "Entrada desfixada", - "history-cleared": "Historico da area de transferencia limpo" + "history-cleared": "Histórico da área de transferência limpo" }, "time": { "just-now": "agora mesmo", - "minutes-ago": "ha {minutes}m", - "hours-ago": "ha {hours}h", + "minutes-ago": "há {minutes}m", + "hours-ago": "há {hours}h", "yesterday": "ontem" } }