mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-08-24 20:52:11 +00:00
Improved voice recording shortcut labels
This commit is contained in:
parent
d91b11e32a
commit
a41c2e3350
@ -30,6 +30,24 @@ public partial class ConfigurationShortcut : ConfigurationBaseCore
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public Action<string> ShortcutUpdate { get; set; } = _ => { };
|
public Action<string> ShortcutUpdate { get; set; } = _ => { };
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The optional user-facing shortcut label.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public Func<string> ShortcutDisplayName { get; set; } = () => string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The canonical shortcut value the optional user-facing label belongs to.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public Func<string> ShortcutDisplaySource { get; set; } = () => string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An action which is called when the user-facing shortcut label was changed.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public Action<string, string> ShortcutDisplayUpdate { get; set; } = (_, _) => { };
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The name/identifier of the shortcut (used for conflict detection and registration).
|
/// The name/identifier of the shortcut (used for conflict detection and registration).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -64,6 +82,14 @@ public partial class ConfigurationShortcut : ConfigurationBaseCore
|
|||||||
if (string.IsNullOrWhiteSpace(shortcut))
|
if (string.IsNullOrWhiteSpace(shortcut))
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
|
|
||||||
|
var shortcutDisplayName = this.ShortcutDisplayName();
|
||||||
|
var shortcutDisplaySource = this.ShortcutDisplaySource();
|
||||||
|
if (!string.IsNullOrWhiteSpace(shortcutDisplayName)
|
||||||
|
&& string.Equals(shortcutDisplaySource, shortcut, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
return shortcutDisplayName;
|
||||||
|
}
|
||||||
|
|
||||||
// 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")
|
||||||
@ -93,9 +119,17 @@ public partial class ConfigurationShortcut : ConfigurationBaseCore
|
|||||||
if (dialogResult is null || dialogResult.Canceled)
|
if (dialogResult is null || dialogResult.Canceled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (dialogResult.Data is string newShortcut)
|
if (dialogResult.Data is ShortcutDialogResult shortcutResult)
|
||||||
|
{
|
||||||
|
this.ShortcutUpdate(shortcutResult.Shortcut);
|
||||||
|
this.ShortcutDisplayUpdate(shortcutResult.DisplayName, shortcutResult.DisplaySource);
|
||||||
|
await this.SettingsManager.StoreSettings();
|
||||||
|
await this.InformAboutChange();
|
||||||
|
}
|
||||||
|
else if (dialogResult.Data is string newShortcut)
|
||||||
{
|
{
|
||||||
this.ShortcutUpdate(newShortcut);
|
this.ShortcutUpdate(newShortcut);
|
||||||
|
this.ShortcutDisplayUpdate(string.Empty, string.Empty);
|
||||||
await this.SettingsManager.StoreSettings();
|
await this.SettingsManager.StoreSettings();
|
||||||
await this.InformAboutChange();
|
await this.InformAboutChange();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,7 +37,7 @@
|
|||||||
@if (PreviewFeatures.PRE_SPEECH_TO_TEXT_2026.IsEnabled(this.SettingsManager))
|
@if (PreviewFeatures.PRE_SPEECH_TO_TEXT_2026.IsEnabled(this.SettingsManager))
|
||||||
{
|
{
|
||||||
<ConfigurationSelect OptionDescription="@T("Select a transcription provider")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.UseTranscriptionProvider)" Data="@this.GetFilteredTranscriptionProviders()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.UseTranscriptionProvider = selectedValue)" OptionHelp="@T("Select a transcription provider for transcribing your voice. Without a selected provider, dictation and transcription features will be disabled.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.UseTranscriptionProvider, out var meta) && meta.IsLocked"/>
|
<ConfigurationSelect OptionDescription="@T("Select a transcription provider")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.UseTranscriptionProvider)" Data="@this.GetFilteredTranscriptionProviders()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.UseTranscriptionProvider = selectedValue)" OptionHelp="@T("Select a transcription provider for transcribing your voice. Without a selected provider, dictation and transcription features will be disabled.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.UseTranscriptionProvider, out var meta) && meta.IsLocked"/>
|
||||||
<ConfigurationShortcut ShortcutId="Shortcut.VOICE_RECORDING_TOGGLE" OptionDescription="@T("Voice recording shortcut")" Shortcut="@(() => this.SettingsManager.ConfigurationData.App.ShortcutVoiceRecording)" ShortcutUpdate="@(shortcut => this.SettingsManager.ConfigurationData.App.ShortcutVoiceRecording = shortcut)" OptionHelp="@T("The global keyboard shortcut for toggling voice recording. This shortcut works system-wide, even when the app is not focused.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.ShortcutVoiceRecording, out var meta) && meta.IsLocked"/>
|
<ConfigurationShortcut ShortcutId="Shortcut.VOICE_RECORDING_TOGGLE" OptionDescription="@T("Voice recording shortcut")" Shortcut="@(() => this.SettingsManager.ConfigurationData.App.ShortcutVoiceRecording)" ShortcutUpdate="@(shortcut => this.SettingsManager.ConfigurationData.App.ShortcutVoiceRecording = shortcut)" ShortcutDisplayName="@(() => this.SettingsManager.ConfigurationData.App.ShortcutVoiceRecordingDisplayName)" ShortcutDisplaySource="@(() => this.SettingsManager.ConfigurationData.App.ShortcutVoiceRecordingDisplaySource)" ShortcutDisplayUpdate="@this.UpdateShortcutVoiceRecordingDisplay" OptionHelp="@T("The global keyboard shortcut for toggling voice recording. This shortcut works system-wide, even when the app is not focused.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.ShortcutVoiceRecording, out var meta) && meta.IsLocked"/>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (this.SettingsManager.ConfigurationData.App.ShowAdminSettings)
|
@if (this.SettingsManager.ConfigurationData.App.ShowAdminSettings)
|
||||||
|
|||||||
@ -93,6 +93,12 @@ public partial class SettingsPanelApp : SettingsPanelBase
|
|||||||
this.SettingsManager.ConfigurationData.App.EnabledPreviewFeatures = selectedFeatures;
|
this.SettingsManager.ConfigurationData.App.EnabledPreviewFeatures = selectedFeatures;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateShortcutVoiceRecordingDisplay(string displayName, string displaySource)
|
||||||
|
{
|
||||||
|
this.SettingsManager.ConfigurationData.App.ShortcutVoiceRecordingDisplayName = displayName;
|
||||||
|
this.SettingsManager.ConfigurationData.App.ShortcutVoiceRecordingDisplaySource = displaySource;
|
||||||
|
}
|
||||||
|
|
||||||
private async Task UpdateLangBehaviour(LangBehavior behavior)
|
private async Task UpdateLangBehaviour(LangBehavior behavior)
|
||||||
{
|
{
|
||||||
this.SettingsManager.ConfigurationData.App.LanguageBehavior = behavior;
|
this.SettingsManager.ConfigurationData.App.LanguageBehavior = behavior;
|
||||||
|
|||||||
@ -34,6 +34,7 @@ public partial class ShortcutDialog : MSGComponentBase
|
|||||||
|
|
||||||
private string currentShortcut = string.Empty;
|
private string currentShortcut = string.Empty;
|
||||||
private string originalShortcut = string.Empty;
|
private string originalShortcut = string.Empty;
|
||||||
|
private string currentDisplayName = string.Empty;
|
||||||
private string validationMessage = string.Empty;
|
private string validationMessage = string.Empty;
|
||||||
private Severity validationSeverity = Severity.Info;
|
private Severity validationSeverity = Severity.Info;
|
||||||
private bool hasValidationError;
|
private bool hasValidationError;
|
||||||
@ -115,6 +116,7 @@ public partial class ShortcutDialog : MSGComponentBase
|
|||||||
{
|
{
|
||||||
this.UpdateModifiers(e);
|
this.UpdateModifiers(e);
|
||||||
this.currentKey = null;
|
this.currentKey = null;
|
||||||
|
this.currentDisplayName = string.Empty;
|
||||||
this.UpdateShortcutString();
|
this.UpdateShortcutString();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -123,10 +125,12 @@ public partial class ShortcutDialog : MSGComponentBase
|
|||||||
|
|
||||||
// Get the key:
|
// Get the key:
|
||||||
this.currentKey = TranslateKeyCode(e.Code);
|
this.currentKey = TranslateKeyCode(e.Code);
|
||||||
|
this.currentDisplayName = this.BuildDisplayShortcut(e.Key);
|
||||||
|
|
||||||
// Validate: must have at least one modifier + a key
|
// Validate: must have at least one modifier + a key
|
||||||
if (!this.hasCtrl && !this.hasShift && !this.hasAlt && !this.hasMeta)
|
if (!this.hasCtrl && !this.hasShift && !this.hasAlt && !this.hasMeta)
|
||||||
{
|
{
|
||||||
|
this.currentDisplayName = string.Empty;
|
||||||
this.validationMessage = T("Please include at least one modifier key (Ctrl, Shift, Alt, or Cmd).");
|
this.validationMessage = T("Please include at least one modifier key (Ctrl, Shift, Alt, or Cmd).");
|
||||||
this.validationSeverity = Severity.Warning;
|
this.validationSeverity = Severity.Warning;
|
||||||
this.hasValidationError = true;
|
this.hasValidationError = true;
|
||||||
@ -216,6 +220,9 @@ public partial class ShortcutDialog : MSGComponentBase
|
|||||||
|
|
||||||
private string GetDisplayShortcut()
|
private string GetDisplayShortcut()
|
||||||
{
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(this.currentDisplayName))
|
||||||
|
return this.currentDisplayName;
|
||||||
|
|
||||||
// Convert internal format to display format:
|
// Convert internal format to display format:
|
||||||
return this.currentShortcut
|
return this.currentShortcut
|
||||||
.Replace("CmdOrControl", OperatingSystem.IsMacOS() ? "Cmd" : "Ctrl")
|
.Replace("CmdOrControl", OperatingSystem.IsMacOS() ? "Cmd" : "Ctrl")
|
||||||
@ -225,6 +232,7 @@ public partial class ShortcutDialog : MSGComponentBase
|
|||||||
private void ClearShortcut()
|
private void ClearShortcut()
|
||||||
{
|
{
|
||||||
this.currentShortcut = string.Empty;
|
this.currentShortcut = string.Empty;
|
||||||
|
this.currentDisplayName = string.Empty;
|
||||||
this.currentKey = null;
|
this.currentKey = null;
|
||||||
this.hasCtrl = false;
|
this.hasCtrl = false;
|
||||||
this.hasShift = false;
|
this.hasShift = false;
|
||||||
@ -237,7 +245,17 @@ public partial class ShortcutDialog : MSGComponentBase
|
|||||||
|
|
||||||
private void Cancel() => this.MudDialog.Cancel();
|
private void Cancel() => this.MudDialog.Cancel();
|
||||||
|
|
||||||
private void Confirm() => this.MudDialog.Close(DialogResult.Ok(this.currentShortcut));
|
private void Confirm()
|
||||||
|
{
|
||||||
|
var displaySource = string.IsNullOrWhiteSpace(this.currentDisplayName)
|
||||||
|
? string.Empty
|
||||||
|
: this.currentShortcut;
|
||||||
|
|
||||||
|
this.MudDialog.Close(DialogResult.Ok(new ShortcutDialogResult(
|
||||||
|
this.currentShortcut,
|
||||||
|
this.currentDisplayName,
|
||||||
|
displaySource)));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if the key code represents a modifier key.
|
/// Checks if the key code represents a modifier key.
|
||||||
@ -377,6 +395,36 @@ public partial class ShortcutDialog : MSGComponentBase
|
|||||||
_ => code,
|
_ => code,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private string BuildDisplayShortcut(string? key)
|
||||||
|
{
|
||||||
|
var displayKey = GetDisplayKey(key);
|
||||||
|
if (string.IsNullOrWhiteSpace(displayKey))
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
var parts = new List<string>();
|
||||||
|
|
||||||
|
if (this.hasCtrl)
|
||||||
|
parts.Add(OperatingSystem.IsMacOS() ? "Cmd" : "Ctrl");
|
||||||
|
|
||||||
|
if (this.hasShift)
|
||||||
|
parts.Add("Shift");
|
||||||
|
|
||||||
|
if (this.hasAlt)
|
||||||
|
parts.Add("Alt");
|
||||||
|
|
||||||
|
parts.Add(displayKey);
|
||||||
|
return string.Join("+", parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetDisplayKey(string? key) => key switch
|
||||||
|
{
|
||||||
|
null or "" => string.Empty,
|
||||||
|
" " => "Space",
|
||||||
|
"Control" or "Shift" or "Alt" or "Meta" => string.Empty,
|
||||||
|
_ when key.Length == 1 && key[0] >= 'a' && key[0] <= 'z' => key.ToUpperInvariant(),
|
||||||
|
_ => key,
|
||||||
|
};
|
||||||
|
|
||||||
private void HandleBlur()
|
private void HandleBlur()
|
||||||
{
|
{
|
||||||
// Re-focus the input field to keep capturing keys:
|
// Re-focus the input field to keep capturing keys:
|
||||||
|
|||||||
3
app/MindWork AI Studio/Dialogs/ShortcutDialogResult.cs
Normal file
3
app/MindWork AI Studio/Dialogs/ShortcutDialogResult.cs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
namespace AIStudio.Dialogs;
|
||||||
|
|
||||||
|
public readonly record struct ShortcutDialogResult(string Shortcut, string DisplayName, string DisplaySource);
|
||||||
@ -99,6 +99,16 @@ public sealed class DataApp(Expression<Func<Data, DataApp>>? configSelection = n
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string ShortcutVoiceRecording { get; set; } = ManagedConfiguration.Register(configSelection, n => n.ShortcutVoiceRecording, string.Empty);
|
public string ShortcutVoiceRecording { get; set; } = ManagedConfiguration.Register(configSelection, n => n.ShortcutVoiceRecording, string.Empty);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The user-facing label for the voice recording shortcut, based on the user's keyboard layout.
|
||||||
|
/// </summary>
|
||||||
|
public string ShortcutVoiceRecordingDisplayName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The canonical voice recording shortcut value this display label belongs to.
|
||||||
|
/// </summary>
|
||||||
|
public string ShortcutVoiceRecordingDisplaySource { get; set; } = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The HTTP timeout in seconds for external HTTP clients.
|
/// The HTTP timeout in seconds for external HTTP clients.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
- Improved workspaces by highlighting the currently open chat in the workspace view.
|
- Improved workspaces by highlighting the currently open chat in the workspace view.
|
||||||
- Improved workspaces by adding a shortcut to start a new chat directly from each workspace row.
|
- Improved workspaces by adding a shortcut to start a new chat directly from each workspace row.
|
||||||
- Improved workspaces by allowing new workspaces to be created while moving a chat.
|
- Improved workspaces by allowing new workspaces to be created while moving a chat.
|
||||||
|
- Improved voice recording shortcut labels so they match the user's keyboard layout after being configured.
|
||||||
- Improved the enterprise configuration details on the information page by showing where each configuration comes from and which configuration slot was used.
|
- Improved the enterprise configuration details on the information page by showing where each configuration comes from and which configuration slot was used.
|
||||||
- Fixed workspace creation and renaming to prevent new workspaces from using an existing name.
|
- Fixed workspace creation and renaming to prevent new workspaces from using an existing name.
|
||||||
- Fixed an issue on Microsoft Windows where reading attached documents could briefly open a terminal window while processing files.
|
- Fixed an issue on Microsoft Windows where reading attached documents could briefly open a terminal window while processing files.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user