mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-02-12 10:21:36 +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.Dialogs;
|
||||||
|
using AIStudio.Tools.Services;
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
using DialogOptions = AIStudio.Dialogs.DialogOptions;
|
using DialogOptions = AIStudio.Dialogs.DialogOptions;
|
||||||
@ -13,6 +14,9 @@ public partial class ConfigurationShortcut : ConfigurationBaseCore
|
|||||||
[Inject]
|
[Inject]
|
||||||
private IDialogService DialogService { get; init; } = null!;
|
private IDialogService DialogService { get; init; } = null!;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private RustService RustService { get; init; } = null!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The current shortcut value.
|
/// The current shortcut value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -59,7 +63,7 @@ public partial class ConfigurationShortcut : ConfigurationBaseCore
|
|||||||
if (string.IsNullOrWhiteSpace(shortcut))
|
if (string.IsNullOrWhiteSpace(shortcut))
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
|
|
||||||
// Convert internal format to display format
|
// Convert internal format to display format:
|
||||||
return shortcut
|
return shortcut
|
||||||
.Replace("CmdOrControl", OperatingSystem.IsMacOS() ? "Cmd" : "Ctrl")
|
.Replace("CmdOrControl", OperatingSystem.IsMacOS() ? "Cmd" : "Ctrl")
|
||||||
.Replace("CommandOrControl", OperatingSystem.IsMacOS() ? "Cmd" : "Ctrl");
|
.Replace("CommandOrControl", OperatingSystem.IsMacOS() ? "Cmd" : "Ctrl");
|
||||||
@ -67,10 +71,15 @@ public partial class ConfigurationShortcut : ConfigurationBaseCore
|
|||||||
|
|
||||||
private async Task OpenDialog()
|
private async Task OpenDialog()
|
||||||
{
|
{
|
||||||
var currentShortcut = this.Shortcut();
|
// 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
|
||||||
|
{
|
||||||
var dialogParameters = new DialogParameters<ShortcutDialog>
|
var dialogParameters = new DialogParameters<ShortcutDialog>
|
||||||
{
|
{
|
||||||
{ x => x.InitialShortcut, currentShortcut },
|
{ x => x.InitialShortcut, this.Shortcut() },
|
||||||
{ x => x.ShortcutName, this.ShortcutName },
|
{ x => x.ShortcutName, this.ShortcutName },
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -90,4 +99,10 @@ public partial class ConfigurationShortcut : ConfigurationBaseCore
|
|||||||
await this.InformAboutChange();
|
await this.InformAboutChange();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// 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 RegisterShortcutRequest(string Name, string Shortcut);
|
||||||
|
|
||||||
private sealed record ShortcutResponse(bool Success, string ErrorMessage);
|
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).
|
/// Stores the currently registered global shortcuts (name -> shortcut string).
|
||||||
static REGISTERED_SHORTCUTS: Lazy<Mutex<HashMap<String, String>>> = Lazy::new(|| Mutex::new(HashMap::new()));
|
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.
|
/// Starts the Tauri app.
|
||||||
pub fn start_tauri() {
|
pub fn start_tauri() {
|
||||||
info!("Starting Tauri app...");
|
info!("Starting Tauri app...");
|
||||||
@ -770,6 +773,13 @@ pub fn register_shortcut(_token: APIToken, payload: Json<RegisterShortcutRequest
|
|||||||
// Register the new shortcut:
|
// Register the new shortcut:
|
||||||
let shortcut_name = name.clone();
|
let shortcut_name = name.clone();
|
||||||
match shortcut_manager.register(new_shortcut.as_str(), move || {
|
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}'.");
|
info!(Source = "Tauri"; "Global shortcut triggered for '{shortcut_name}'.");
|
||||||
let event = Event::new(TauriEventType::GlobalShortcutPressed, vec![shortcut_name.clone()]);
|
let event = Event::new(TauriEventType::GlobalShortcutPressed, vec![shortcut_name.clone()]);
|
||||||
let sender = event_sender.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.
|
/// Validates the syntax of a shortcut string.
|
||||||
fn validate_shortcut_syntax(shortcut: &str) -> bool {
|
fn validate_shortcut_syntax(shortcut: &str) -> bool {
|
||||||
let parts: Vec<&str> = shortcut.split('+').collect();
|
let parts: Vec<&str> = shortcut.split('+').collect();
|
||||||
|
|||||||
@ -89,6 +89,8 @@ pub fn start_runtime_api() {
|
|||||||
crate::log::log_event,
|
crate::log::log_event,
|
||||||
crate::app_window::register_shortcut,
|
crate::app_window::register_shortcut,
|
||||||
crate::app_window::validate_shortcut,
|
crate::app_window::validate_shortcut,
|
||||||
|
crate::app_window::suspend_shortcuts,
|
||||||
|
crate::app_window::resume_shortcuts,
|
||||||
])
|
])
|
||||||
.ignite().await.unwrap()
|
.ignite().await.unwrap()
|
||||||
.launch().await.unwrap();
|
.launch().await.unwrap();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user