diff --git a/app/MindWork AI Studio/Components/ConfigurationShortcut.razor.cs b/app/MindWork AI Studio/Components/ConfigurationShortcut.razor.cs
index 6d312caa..1bf605d2 100644
--- a/app/MindWork AI Studio/Components/ConfigurationShortcut.razor.cs
+++ b/app/MindWork AI Studio/Components/ConfigurationShortcut.razor.cs
@@ -1,4 +1,5 @@
using AIStudio.Dialogs;
+using AIStudio.Tools.Services;
using Microsoft.AspNetCore.Components;
using DialogOptions = AIStudio.Dialogs.DialogOptions;
@@ -13,6 +14,9 @@ public partial class ConfigurationShortcut : ConfigurationBaseCore
[Inject]
private IDialogService DialogService { get; init; } = null!;
+ [Inject]
+ private RustService RustService { get; init; } = null!;
+
///
/// The current shortcut value.
///
@@ -59,7 +63,7 @@ public partial class ConfigurationShortcut : ConfigurationBaseCore
if (string.IsNullOrWhiteSpace(shortcut))
return string.Empty;
- // Convert internal format to display format
+ // Convert internal format to display format:
return shortcut
.Replace("CmdOrControl", OperatingSystem.IsMacOS() ? "Cmd" : "Ctrl")
.Replace("CommandOrControl", OperatingSystem.IsMacOS() ? "Cmd" : "Ctrl");
@@ -67,27 +71,38 @@ public partial class ConfigurationShortcut : ConfigurationBaseCore
private async Task OpenDialog()
{
- var currentShortcut = this.Shortcut();
- var dialogParameters = new DialogParameters
+ // Suspend shortcut processing while the dialog is open, so the user can
+ // press the current shortcut to re-enter it without triggering the action:
+ await this.RustService.SuspendShortcutProcessing();
+
+ try
{
- { x => x.InitialShortcut, currentShortcut },
- { x => x.ShortcutName, this.ShortcutName },
- };
+ var dialogParameters = new DialogParameters
+ {
+ { x => x.InitialShortcut, this.Shortcut() },
+ { x => x.ShortcutName, this.ShortcutName },
+ };
- var dialogReference = await this.DialogService.ShowAsync(
- this.T("Configure Keyboard Shortcut"),
- dialogParameters,
- DialogOptions.FULLSCREEN);
+ var dialogReference = await this.DialogService.ShowAsync(
+ this.T("Configure Keyboard Shortcut"),
+ dialogParameters,
+ DialogOptions.FULLSCREEN);
- var dialogResult = await dialogReference.Result;
- if (dialogResult is null || dialogResult.Canceled)
- return;
+ var dialogResult = await dialogReference.Result;
+ if (dialogResult is null || dialogResult.Canceled)
+ return;
- if (dialogResult.Data is string newShortcut)
+ if (dialogResult.Data is string newShortcut)
+ {
+ await this.ShortcutUpdate(newShortcut);
+ await this.SettingsManager.StoreSettings();
+ await this.InformAboutChange();
+ }
+ }
+ finally
{
- await this.ShortcutUpdate(newShortcut);
- await this.SettingsManager.StoreSettings();
- await this.InformAboutChange();
+ // Resume the shortcut processing when the dialog is closed:
+ await this.RustService.ResumeShortcutProcessing();
}
}
}
diff --git a/app/MindWork AI Studio/Tools/Services/RustService.Shortcuts.cs b/app/MindWork AI Studio/Tools/Services/RustService.Shortcuts.cs
index 62ea6ded..5da8d6ac 100644
--- a/app/MindWork AI Studio/Tools/Services/RustService.Shortcuts.cs
+++ b/app/MindWork AI Studio/Tools/Services/RustService.Shortcuts.cs
@@ -70,6 +70,72 @@ public sealed partial class RustService
}
}
+ ///
+ /// Suspends shortcut processing. The shortcuts remain registered, but events are not sent.
+ /// This is useful when opening a dialog to configure shortcuts, so the user can
+ /// press the current shortcut to re-enter it without triggering the action.
+ ///
+ /// True if successful, false otherwise.
+ public async Task SuspendShortcutProcessing()
+ {
+ try
+ {
+ var response = await this.http.PostAsync("/shortcuts/suspend", null);
+ if (!response.IsSuccessStatusCode)
+ {
+ this.logger?.LogError("Failed to suspend the shortcut processing due to network error: {StatusCode}.", response.StatusCode);
+ return false;
+ }
+
+ var result = await response.Content.ReadFromJsonAsync(this.jsonRustSerializerOptions);
+ if (result is null || !result.Success)
+ {
+ this.logger?.LogError("Failed to suspend shortcut processing: {Error}", result?.ErrorMessage ?? "Unknown error");
+ return false;
+ }
+
+ this.logger?.LogDebug("Shortcut processing suspended.");
+ return true;
+ }
+ catch (Exception ex)
+ {
+ this.logger?.LogError(ex, "Exception while suspending shortcut processing.");
+ return false;
+ }
+ }
+
+ ///
+ /// Resumes the shortcut processing after it was suspended.
+ ///
+ /// True if successful, false otherwise.
+ public async Task ResumeShortcutProcessing()
+ {
+ try
+ {
+ var response = await this.http.PostAsync("/shortcuts/resume", null);
+ if (!response.IsSuccessStatusCode)
+ {
+ this.logger?.LogError("Failed to resume shortcut processing due to network error: {StatusCode}.", response.StatusCode);
+ return false;
+ }
+
+ var result = await response.Content.ReadFromJsonAsync(this.jsonRustSerializerOptions);
+ if (result is null || !result.Success)
+ {
+ this.logger?.LogError("Failed to resume shortcut processing: {Error}", result?.ErrorMessage ?? "Unknown error");
+ return false;
+ }
+
+ this.logger?.LogDebug("Shortcut processing resumed.");
+ return true;
+ }
+ catch (Exception ex)
+ {
+ this.logger?.LogError(ex, "Exception while resuming shortcut processing.");
+ return false;
+ }
+ }
+
private sealed record RegisterShortcutRequest(string Name, string Shortcut);
private sealed record ShortcutResponse(bool Success, string ErrorMessage);
diff --git a/runtime/src/app_window.rs b/runtime/src/app_window.rs
index d42f76ca..a08a718d 100644
--- a/runtime/src/app_window.rs
+++ b/runtime/src/app_window.rs
@@ -31,6 +31,9 @@ static EVENT_BROADCAST: Lazy>>> = Lazy::ne
/// Stores the currently registered global shortcuts (name -> shortcut string).
static REGISTERED_SHORTCUTS: Lazy>> = Lazy::new(|| Mutex::new(HashMap::new()));
+/// Flag to temporarily suspend shortcut processing (shortcuts remain registered but events are not sent).
+static SHORTCUTS_SUSPENDED: Lazy> = Lazy::new(|| Mutex::new(false));
+
/// Starts the Tauri app.
pub fn start_tauri() {
info!("Starting Tauri app...");
@@ -770,6 +773,13 @@ pub fn register_shortcut(_token: APIToken, payload: Json Json {
+ let mut suspended = SHORTCUTS_SUSPENDED.lock().unwrap();
+ *suspended = true;
+ info!(Source = "Tauri"; "Shortcut processing has been suspended.");
+ Json(ShortcutResponse {
+ success: true,
+ error_message: String::new(),
+ })
+}
+
+/// Resumes shortcut processing after it was suspended.
+#[post("/shortcuts/resume")]
+pub fn resume_shortcuts(_token: APIToken) -> Json {
+ let mut suspended = SHORTCUTS_SUSPENDED.lock().unwrap();
+ *suspended = false;
+ info!(Source = "Tauri"; "Shortcut processing has been resumed.");
+ Json(ShortcutResponse {
+ success: true,
+ error_message: String::new(),
+ })
+}
+
/// Validates the syntax of a shortcut string.
fn validate_shortcut_syntax(shortcut: &str) -> bool {
let parts: Vec<&str> = shortcut.split('+').collect();
diff --git a/runtime/src/runtime_api.rs b/runtime/src/runtime_api.rs
index a4ba4782..de81cc18 100644
--- a/runtime/src/runtime_api.rs
+++ b/runtime/src/runtime_api.rs
@@ -89,6 +89,8 @@ pub fn start_runtime_api() {
crate::log::log_event,
crate::app_window::register_shortcut,
crate::app_window::validate_shortcut,
+ crate::app_window::suspend_shortcuts,
+ crate::app_window::resume_shortcuts,
])
.ignite().await.unwrap()
.launch().await.unwrap();