mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-02-12 06:41:37 +00:00
Added suspend/resume handling for shortcut processing
This commit is contained in:
parent
55bf598912
commit
e41be099dd
@ -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!;
|
||||
|
||||
/// <summary>
|
||||
/// The current shortcut value.
|
||||
/// </summary>
|
||||
@ -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<ShortcutDialog>
|
||||
// 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<ShortcutDialog>
|
||||
{
|
||||
{ x => x.InitialShortcut, this.Shortcut() },
|
||||
{ x => x.ShortcutName, this.ShortcutName },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ShortcutDialog>(
|
||||
this.T("Configure Keyboard Shortcut"),
|
||||
dialogParameters,
|
||||
DialogOptions.FULLSCREEN);
|
||||
var dialogReference = await this.DialogService.ShowAsync<ShortcutDialog>(
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,6 +70,72 @@ public sealed partial class RustService
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <returns>True if successful, false otherwise.</returns>
|
||||
public async Task<bool> 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<ShortcutResponse>(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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resumes the shortcut processing after it was suspended.
|
||||
/// </summary>
|
||||
/// <returns>True if successful, false otherwise.</returns>
|
||||
public async Task<bool> 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<ShortcutResponse>(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);
|
||||
|
||||
@ -31,6 +31,9 @@ static EVENT_BROADCAST: Lazy<Mutex<Option<broadcast::Sender<Event>>>> = Lazy::ne
|
||||
/// Stores the currently registered global shortcuts (name -> shortcut string).
|
||||
static REGISTERED_SHORTCUTS: Lazy<Mutex<HashMap<String, String>>> = Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
|
||||
/// Flag to temporarily suspend shortcut processing (shortcuts remain registered but events are not sent).
|
||||
static SHORTCUTS_SUSPENDED: Lazy<Mutex<bool>> = 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<RegisterShortcutRequest
|
||||
// Register the new shortcut:
|
||||
let shortcut_name = name.clone();
|
||||
match shortcut_manager.register(new_shortcut.as_str(), move || {
|
||||
// Check if shortcuts are suspended:
|
||||
let is_suspended = *SHORTCUTS_SUSPENDED.lock().unwrap();
|
||||
if is_suspended {
|
||||
debug!(Source = "Tauri"; "Global shortcut '{shortcut_name}' triggered but processing is suspended.");
|
||||
return;
|
||||
}
|
||||
|
||||
info!(Source = "Tauri"; "Global shortcut triggered for '{shortcut_name}'.");
|
||||
let event = Event::new(TauriEventType::GlobalShortcutPressed, vec![shortcut_name.clone()]);
|
||||
let sender = event_sender.clone();
|
||||
@ -871,6 +881,32 @@ pub fn validate_shortcut(_token: APIToken, payload: Json<ValidateShortcutRequest
|
||||
}
|
||||
}
|
||||
|
||||
/// Suspends shortcut processing. 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.
|
||||
#[post("/shortcuts/suspend")]
|
||||
pub fn suspend_shortcuts(_token: APIToken) -> Json<ShortcutResponse> {
|
||||
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<ShortcutResponse> {
|
||||
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();
|
||||
|
||||
@ -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();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user