Fixed media transcription state handling

This commit is contained in:
Thorsten Sommer 2026-07-14 18:42:48 +02:00
parent de91af6024
commit a5088d74c8
Signed by untrusted user who does not match committer: tsommer
GPG Key ID: 371BBA77A02C0108
12 changed files with 168 additions and 18 deletions

View File

@ -150,6 +150,9 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
/// </summary>
protected bool HasAssistantSession => this.assistantSessionId is not null;
/// <summary>Gets whether this assistant currently owns active media work.</summary>
protected bool IsMediaImportBusy => this.MediaTranscriptionService.IsBusy(this.CurrentMediaImportOwner);
/// <summary>
/// Gets the assistant-specific identifier used to distinguish session slots.
/// </summary>

View File

@ -21,7 +21,7 @@
}
else
{
<MudList Color="Color.Primary" T="DataDocumentAnalysisPolicy" Class="mb-1" SelectedValue="@this.selectedPolicy" SelectedValueChanged="@this.SelectedPolicyChanged">
<MudList Disabled="@this.ArePolicyControlsDisabled" Color="Color.Primary" T="DataDocumentAnalysisPolicy" Class="mb-1" SelectedValue="@this.selectedPolicy" SelectedValueChanged="@this.SelectedPolicyChanged">
@foreach (var policy in this.SettingsManager.ConfigurationData.DocumentAnalysis.Policies)
{
@if (policy.IsEnterpriseConfiguration)
@ -44,10 +44,10 @@ else
}
<MudStack Row="@true" Class="mt-1">
<MudButton OnClick="@this.AddPolicy" Variant="Variant.Filled" Color="Color.Primary">
<MudButton OnClick="@this.AddPolicy" Disabled="@this.ArePolicyControlsDisabled" Variant="Variant.Filled" Color="Color.Primary">
@T("Add policy")
</MudButton>
<MudButton OnClick="@this.RemovePolicy" Disabled="@((this.selectedPolicy?.IsProtected ?? true) || (this.selectedPolicy?.IsEnterpriseConfiguration ?? true))" Variant="Variant.Filled" Color="Color.Error">
<MudButton OnClick="@this.RemovePolicy" Disabled="@(this.ArePolicyControlsDisabled || (this.selectedPolicy?.IsProtected ?? true) || (this.selectedPolicy?.IsEnterpriseConfiguration ?? true))" Variant="Variant.Filled" Color="Color.Error">
@T("Delete this policy")
</MudButton>
</MudStack>

View File

@ -333,9 +333,14 @@ public partial class DocumentAnalysisAssistant : AssistantBaseCore<NoSettingsPan
private bool IsNoPolicySelectedOrProtected => this.selectedPolicy is null || this.selectedPolicy.IsProtected;
private bool IsNoPolicySelected => this.selectedPolicy is null;
private bool ArePolicyControlsDisabled => this.IsProcessing || this.IsMediaImportBusy;
private void SelectedPolicyChanged(DataDocumentAnalysisPolicy? policy)
{
if (this.ArePolicyControlsDisabled)
return;
this.selectedPolicy = policy;
this.ResetForm();
this.policyDefinitionExpanded = !this.selectedPolicy?.IsProtected ?? true;
@ -353,6 +358,9 @@ public partial class DocumentAnalysisAssistant : AssistantBaseCore<NoSettingsPan
private async Task AddPolicy()
{
if (this.ArePolicyControlsDisabled)
return;
this.SettingsManager.ConfigurationData.DocumentAnalysis.Policies.Add(new ()
{
Id = Guid.NewGuid().ToString(),
@ -373,6 +381,9 @@ public partial class DocumentAnalysisAssistant : AssistantBaseCore<NoSettingsPan
private async Task RemovePolicy()
{
if (this.ArePolicyControlsDisabled)
return;
if(this.selectedPolicy is null)
return;

View File

@ -120,6 +120,12 @@ public partial class AttachDocuments : MSGComponentBase
// Register this drop area:
await this.MessageBus.SendMessage(this, Event.REGISTER_FILE_DROP_AREA, this.Layer);
await base.OnInitializedAsync();
}
/// <summary>Rehydrates results after the component is assigned another chat or target.</summary>
protected override async Task OnParametersSetAsync()
{
await base.OnParametersSetAsync();
await this.SyncCompletedMediaAttachmentsAsync();
}
@ -168,11 +174,24 @@ public partial class AttachDocuments : MSGComponentBase
var completed = delivery?.Attachments ?? [];
var pending = this.OwnerChat?.PendingMediaTranscripts ?? [];
var changed = false;
var ownerPendingChanged = false;
foreach (var attachment in completed.Concat(pending))
changed |= this.DocumentPaths.Add(attachment);
if (changed)
if (this.OwnerChat is not null)
{
foreach (var attachment in completed.OfType<ManagedTranscriptAttachment>())
{
if (this.OwnerChat.PendingMediaTranscripts.All(existing => existing.FilePath != attachment.FilePath))
{
this.OwnerChat.PendingMediaTranscripts.Add(attachment);
ownerPendingChanged = true;
}
}
}
if (changed || ownerPendingChanged)
{
await this.DocumentPathsChanged.InvokeAsync(this.DocumentPaths);
await this.OnChange(this.DocumentPaths);
@ -410,12 +429,17 @@ public partial class AttachDocuments : MSGComponentBase
return;
}
var names = string.Join(Environment.NewLine, mediaPaths.Select(path => $"• {Path.GetFileName(path)}"));
var names = string.Join('\n', mediaPaths.Select(path => $"- {Markdown.EscapeInlineText(Path.GetFileName(path))}"));
var message = this.T("The selected audio and video files will be prepared locally. Their audio will then be uploaded to the configured transcription provider.");
var dialogParameters = new DialogParameters<ConfirmDialog>
{
{
x => x.Message,
$"{this.T("The selected audio and video files will be prepared locally. Their audio will then be uploaded to the configured transcription provider.")}{Environment.NewLine}{Environment.NewLine}{names}"
x => x.MarkdownBody,
$"""
{message}
{names}
"""
},
};

View File

@ -290,11 +290,16 @@ public partial class ReadFileContent : MSGComponentBase
return false;
}
var message = this.T("The selected media file will be prepared locally. Its audio will then be uploaded to the configured transcription provider.");
var dialogParameters = new DialogParameters<ConfirmDialog>
{
{
x => x.Message,
$"{this.T("The selected media file will be prepared locally. Its audio will then be uploaded to the configured transcription provider.")}{Environment.NewLine}{Environment.NewLine}{Path.GetFileName(filePath)}"
x => x.MarkdownBody,
$"""
{message}
- {Markdown.EscapeInlineText(Path.GetFileName(filePath))}
"""
},
};
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(

View File

@ -1,9 +1,16 @@
@inherits MSGComponentBase
<MudDialog>
<DialogContent>
<MudJustifiedText Typo="Typo.body1">
@this.Message
</MudJustifiedText>
@if (!string.IsNullOrWhiteSpace(this.MarkdownBody))
{
<MudJustifiedMarkdown Value="@this.MarkdownBody" />
}
else
{
<MudJustifiedText Typo="Typo.body1">
@this.Message
</MudJustifiedText>
}
</DialogContent>
<DialogActions>
<MudButton OnClick="@this.Cancel" Variant="Variant.Filled">

View File

@ -15,6 +15,12 @@ public partial class ConfirmDialog : MSGComponentBase
[Parameter]
public string Message { get; set; } = string.Empty;
/// <summary>
/// Optional Markdown content rendered instead of using the message property.
/// </summary>
[Parameter]
public string MarkdownBody { get; set; } = string.Empty;
private void Cancel() => this.MudDialog.Cancel();
private void Confirm() => this.MudDialog.Close(DialogResult.Ok(true));

View File

@ -3,6 +3,7 @@ using AIStudio.Settings;
using AIStudio.Settings.DataModel;
using AIStudio.Tools.AIJobs;
using AIStudio.Tools.AssistantSessions;
using AIStudio.Tools.Media;
using AIStudio.Tools.PluginSystem;
using AIStudio.Tools.Rust;
using AIStudio.Tools.Services;
@ -37,6 +38,9 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan
[Inject]
private AssistantSessionService AssistantSessionService { get; init; } = null!;
[Inject]
private MediaTranscriptionService MediaTranscriptionService { get; init; } = null!;
[Inject]
private ISnackbar Snackbar { get; init; } = null!;
@ -75,6 +79,7 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan
protected override async Task OnInitializedAsync()
{
this.NavigationManager.RegisterLocationChangingHandler(this.OnLocationChanging);
this.MediaTranscriptionService.StateChanged += this.OnMediaImportStateChanged;
//
// We use the Tauri API (Rust) to get the data and config directories
@ -348,6 +353,16 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan
{
this.navItems = new List<NavBarItem>(this.GetNavItems());
}
/// <summary>Refreshes navigation activity colors when a media import changes state.</summary>
private void OnMediaImportStateChanged(MediaImportOwner owner)
{
_ = this.InvokeAsync(() =>
{
this.LoadNavItems();
this.StateHasChanged();
});
}
private IEnumerable<NavBarItem> GetNavItems()
{
@ -356,10 +371,15 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan
var activityIndicatorDarkColor = this.ColorTheme.GetActivityIndicatorDarkColor();
var defaultLightColor = palette.DarkLighten;
var defaultDarkColor = palette.GrayLight;
var chatLightColor = this.AIJobService.HasActiveJobs ? activityIndicatorLightColor : defaultLightColor;
var chatDarkColor = this.AIJobService.HasActiveJobs ? activityIndicatorDarkColor : defaultDarkColor;
var assistantsLightColor = this.AssistantSessionService.HasActiveSessions ? activityIndicatorLightColor : defaultLightColor;
var assistantsDarkColor = this.AssistantSessionService.HasActiveSessions ? activityIndicatorDarkColor : defaultDarkColor;
var mediaSnapshots = this.MediaTranscriptionService.GetSnapshots();
var hasActiveChatMedia = mediaSnapshots.Any(snapshot => snapshot.IsBusy && snapshot.Owner.Kind is MediaImportOwnerKind.CHAT);
var hasActiveAssistantMedia = mediaSnapshots.Any(snapshot => snapshot.IsBusy && snapshot.Owner.Kind is MediaImportOwnerKind.ASSISTANT);
var hasActiveChatWork = this.AIJobService.HasActiveJobs || hasActiveChatMedia;
var hasActiveAssistantWork = this.AssistantSessionService.HasActiveSessions || hasActiveAssistantMedia;
var chatLightColor = hasActiveChatWork ? activityIndicatorLightColor : defaultLightColor;
var chatDarkColor = hasActiveChatWork ? activityIndicatorDarkColor : defaultDarkColor;
var assistantsLightColor = hasActiveAssistantWork ? activityIndicatorLightColor : defaultLightColor;
var assistantsDarkColor = hasActiveAssistantWork ? activityIndicatorDarkColor : defaultDarkColor;
yield return new(T("Home"), Icons.Material.Filled.Home, defaultLightColor, defaultDarkColor, Routes.HOME, true);
yield return new(T("Chat"), Icons.Material.Filled.Chat, chatLightColor, chatDarkColor, Routes.CHAT, false);
@ -535,6 +555,7 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan
public void Dispose()
{
this.MediaTranscriptionService.StateChanged -= this.OnMediaImportStateChanged;
this.MessageBus.Unregister(this);
this.mandatoryInfoDialogSemaphore.Dispose();
}

View File

@ -306,6 +306,9 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::AGENDA::NUMBERPARTICIPANTSEXTENSIONS::T81
-- Stop generation
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::ASSISTANTBASE::T1317408357"] = "Generierung stoppen"
-- The media file could not be transcribed.
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::ASSISTANTBASE::T1543974632"] = "Die Mediendatei konnte nicht transkribiert werden."
-- Reset
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::ASSISTANTBASE::T180921696"] = "Zurücksetzen"
@ -315,6 +318,9 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::ASSISTANTBASE::T1809312323"] = "Bitte wä
-- The assistant failed. The message is: '{0}'
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::ASSISTANTBASE::T1833836281"] = "Der Assistent ist fehlgeschlagen. Die Meldung lautet: „{0}“"
-- The media transcription was canceled.
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::ASSISTANTBASE::T241403726"] = "Die Transkription des Mediums wurde abgebrochen."
-- This assistant is already running. AI Studio opens the running session instead.
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::ASSISTANTBASE::T2575715765"] = "Dieser Assistent läuft bereits. AI Studio öffnet stattdessen die laufende Sitzung."
@ -2412,6 +2418,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T1358313858"] = "Klicken
-- Drop files here to attach them.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T143112277"] = "Dateien hier ablegen, um sie anzuhängen."
-- The media file could not be transcribed.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T1543974632"] = "Die Mediendatei konnte nicht transkribiert werden."
-- Click here to attach files.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T1875575968"] = "Klicken Sie hier, um Dateien anzuhängen."
@ -2421,6 +2430,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T2178031033"] = "Mediend
-- Drag and drop files into the marked area or click here to attach documents:
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T230755331"] = "Ziehen Sie Dateien in den markierten Bereich oder klicken Sie hier, um Dokumente anzuhängen:"
-- The media transcription was canceled.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T241403726"] = "Die Transkription des Mediums wurde abgebrochen."
-- Select files to attach
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T2495931372"] = "Dateien zum Anhängen auswählen"
@ -2457,6 +2469,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CHATCOMPONENT::T1317408357"] = "Generieru
-- Save chat
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CHATCOMPONENT::T1516264254"] = "Chat speichern"
-- The media file could not be transcribed.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CHATCOMPONENT::T1543974632"] = "Die Mediendatei konnte nicht transkribiert werden."
-- Type your input here...
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CHATCOMPONENT::T1849313532"] = "Geben Sie hier Ihre Eingabe ein..."
@ -2469,6 +2484,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CHATCOMPONENT::T2036185364"] = "Code"
-- Italic
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CHATCOMPONENT::T2377171085"] = "Kursiv"
-- The media transcription was canceled.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CHATCOMPONENT::T241403726"] = "Die Transkription der Mediendatei wurde abgebrochen."
-- Profile usage is disabled according to your chat template settings.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CHATCOMPONENT::T2670286472"] = "Die Profilnutzung ist gemäß den Einstellungen ihrer Chat-Vorlage deaktiviert."
@ -2829,12 +2847,18 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PROVIDERSELECTION::T4279448758"] = "Nutzt
-- Provider
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PROVIDERSELECTION::T900237532"] = "Anbieter"
-- The media file could not be transcribed.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T1543974632"] = "Die Mediendatei konnte nicht transkribiert werden."
-- Failed to load file content
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T1989554334"] = "Laden des Dateiinhalts fehlgeschlagen"
-- Drop one file here to load its content.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T2274562398"] = "Datei hier ablegen, um ihren Inhalt zu laden."
-- The media transcription was canceled.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T241403726"] = "Die Transkription des Mediums wurde abgebrochen."
-- The selected media file will be prepared locally. Its audio will then be uploaded to the configured transcription provider.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T2839709466"] = "Die ausgewählte Mediendatei wird lokal vorbereitet. Anschließend wird die Audiospur an den konfigurierten Transkriptionsanbieter hochgeladen."

View File

@ -306,6 +306,9 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::AGENDA::NUMBERPARTICIPANTSEXTENSIONS::T81
-- Stop generation
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::ASSISTANTBASE::T1317408357"] = "Stop generation"
-- The media file could not be transcribed.
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::ASSISTANTBASE::T1543974632"] = "The media file could not be transcribed."
-- Reset
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::ASSISTANTBASE::T180921696"] = "Reset"
@ -315,6 +318,9 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::ASSISTANTBASE::T1809312323"] = "Please se
-- The assistant failed. The message is: '{0}'
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::ASSISTANTBASE::T1833836281"] = "The assistant failed. The message is: '{0}'"
-- The media transcription was canceled.
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::ASSISTANTBASE::T241403726"] = "The media transcription was canceled."
-- This assistant is already running. AI Studio opens the running session instead.
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::ASSISTANTBASE::T2575715765"] = "This assistant is already running. AI Studio opens the running session instead."
@ -2412,6 +2418,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T1358313858"] = "Click t
-- Drop files here to attach them.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T143112277"] = "Drop files here to attach them."
-- The media file could not be transcribed.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T1543974632"] = "The media file could not be transcribed."
-- Click here to attach files.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T1875575968"] = "Click here to attach files."
@ -2421,6 +2430,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T2178031033"] = "Transcr
-- Drag and drop files into the marked area or click here to attach documents:
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T230755331"] = "Drag and drop files into the marked area or click here to attach documents:"
-- The media transcription was canceled.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T241403726"] = "The media transcription was canceled."
-- Select files to attach
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T2495931372"] = "Select files to attach"
@ -2457,6 +2469,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CHATCOMPONENT::T1317408357"] = "Stop gene
-- Save chat
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CHATCOMPONENT::T1516264254"] = "Save chat"
-- The media file could not be transcribed.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CHATCOMPONENT::T1543974632"] = "The media file could not be transcribed."
-- Type your input here...
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CHATCOMPONENT::T1849313532"] = "Type your input here..."
@ -2469,6 +2484,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CHATCOMPONENT::T2036185364"] = "Code"
-- Italic
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CHATCOMPONENT::T2377171085"] = "Italic"
-- The media transcription was canceled.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CHATCOMPONENT::T241403726"] = "The media transcription was canceled."
-- Profile usage is disabled according to your chat template settings.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CHATCOMPONENT::T2670286472"] = "Profile usage is disabled according to your chat template settings."
@ -2829,12 +2847,18 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PROVIDERSELECTION::T4279448758"] = "Uses
-- Provider
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PROVIDERSELECTION::T900237532"] = "Provider"
-- The media file could not be transcribed.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T1543974632"] = "The media file could not be transcribed."
-- Failed to load file content
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T1989554334"] = "Failed to load file content"
-- Drop one file here to load its content.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T2274562398"] = "Drop one file here to load its content."
-- The media transcription was canceled.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T241403726"] = "The media transcription was canceled."
-- The selected media file will be prepared locally. Its audio will then be uploaded to the configured transcription provider.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T2839709466"] = "The selected media file will be prepared locally. Its audio will then be uploaded to the configured transcription provider."

View File

@ -34,6 +34,30 @@ public static class Markdown
}
};
/// <summary>Escapes arbitrary text for literal display inside Markdown.</summary>
public static string EscapeInlineText(string value)
{
if (string.IsNullOrEmpty(value))
return string.Empty;
var escaped = new StringBuilder(value.Length);
foreach (var character in value)
{
if (character is '\r' or '\n' or '\t' || char.IsControl(character))
{
escaped.Append(' ');
continue;
}
if (character is >= '!' and <= '/' or >= ':' and <= '@' or >= '[' and <= '`' or >= '{' and <= '~')
escaped.Append('\\');
escaped.Append(character);
}
return escaped.ToString();
}
public static string RemoveSharedIndentation(string value)
{
if (string.IsNullOrWhiteSpace(value))

View File

@ -3,9 +3,10 @@
- Added audio and video transcription for chats and assistants. AI Studio now prepares supported media locally, sends only normalized audio to the configured transcription provider, and attaches the resulting transcript instead of the original media.
- Improved the "My Tasks Assistant": you can now provide one or more documents in addition to text or use documents alone when asking to identify tasks.
- Improved update guidance for Flatpak installations and added an enterprise option that lets organizations manage updates entirely through their IT department.
- Fixed an issue that could leave AI Studio unresponsive after waking the computer from sleep.
- Fixed enterprise configuration plugins from Windows-created ZIP files not loading correctly on Linux when the ZIP contained plugin files inside a folder.
- Fixed an issue that could leave AI Studio unresponsive after waking the computer from sleep. Yes, we know this was an annoying bug, and we apologize for the inconvenience.
- Fixed enterprise configuration plugins from Windows-created ZIP files may not load correctly on Linux when the ZIP contained plugin files inside a folder.
- Fixed voice recording not starting on Linux.
- Fixed being able to switch document analysis policies while an analysis or media transcription was still in progress.
- Upgraded Rust to v1.97.0.
- Upgraded Tauri to v2.11.5.
- Upgraded common dependencies.