Compare commits

...
10 Commits
51 changed files with 1265 additions and 342 deletions

No files matched your search

+2
View File
@@ -0,0 +1,2 @@
github: [MindWorkAI]
open_collective: mindwork-ai
+5 -13
View File
@@ -662,18 +662,6 @@ jobs:
# Here, we create the JSON object:
platforms_json=$(jq -n '{}')
# Define the urlencode function:
urlencode() {
local length="${#1}"
for (( i = 0; i < length; i++ )); do
local c="${1:i:1}"
case $c in
[a-zA-Z0-9.~_-]) printf "$c" ;;
*) printf '%%%02X' "'$c" ;;
esac
done
}
# Display the structure of the release directory:
ls -Rlhat $GITHUB_WORKSPACE/release/assets
@@ -713,7 +701,11 @@ jobs:
# Extract the artifact name and create the URL:
artifact_name=$(basename "$sig_file" .sig)
encoded_artifact_name=$(urlencode "$artifact_name")
# Replaces spaces by '.':
encoded_artifact_name=$(echo "$artifact_name" | sed 's/ /./g')
# Create the URL:
url="https://github.com/MindWorkAI/AI-Studio/releases/download/$FORMATTED_VERSION/$encoded_artifact_name"
# Build the JSON object:
+5 -2
View File
@@ -16,13 +16,16 @@ MindWork AI Studio is a desktop application available for macOS, Windows, and Li
## Support the Project
Thank you for using MindWork AI Studio and considering supporting its development 😀. Your support helps keep the project alive and ensures continuous improvements and new features.
We offer various ways you can support the project:
- **Monthly Support**: Choose from different tiers that offer a range of benefits, including having your name or company logo featured in the app, access to exclusive content, and more.
- **Monthly Support**: By contributing a monthly amount, you can significantly help us maintain and develop the project. As a token of our appreciation, we will include your name or company logo in the app. While we cannot guarantee exclusive content at this time, we are working towards offering unique perks in the future.
- **One-Time Contributions**: Make a one-time donation and have your name or company logo included in the app as a gesture of our gratitude.
For companies, sponsoring MindWork AI Studio is not only a way to support innovation but also a valuable opportunity for public relations and marketing. Your company's name and logo will be featured prominently, showcasing your commitment to using cutting-edge AI tools and enhancing your reputation as an innovative enterprise.
To view all available tiers and their specific perks, please visit our [GitHub Sponsors page](Sponsors.md).
To view all available tiers, please visit our [GitHub Sponsors page](https://github.com/sponsors/MindWorkAI).
Your support, whether big or small, keeps the wheels turning and is deeply appreciated ❤️.
## Planned Features
-11
View File
@@ -1,11 +0,0 @@
# Thank You
Hello and welcome,
Thank you so much for considering supporting MindWork AI Studio. Your interest and support mean the world to us, as we strive to make advanced AI tools accessible and useful for everyone.
Currently, the approval of our GitHub Sponsors is still pending. We're in the process of setting it up, and it should be available soon. In the meantime, please stay tuned for updates.
Your contributions will greatly help in the continuous improvement and development of MindWork AI Studio.
Thank you for your support ❤️
+3 -1
View File
@@ -1,2 +1,4 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AI/@EntryIndexedValue">AI</s:String></wpf:ResourceDictionary>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AI/@EntryIndexedValue">AI</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=MSG/@EntryIndexedValue">MSG</s:String>
<s:Boolean x:Key="/Default/UserDictionary/Words/=tauri_0027s/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
@@ -13,6 +13,10 @@ public partial class Changelog
public static readonly Log[] LOGS =
[
new (157, "v0.6.1, build 157 (2024-06-30 19:00 UTC)", "v0.6.1.md"),
new (156, "v0.6.0, build 156 (2024-06-30 12:49 UTC)", "v0.6.0.md"),
new (155, "v0.5.2, build 155 (2024-06-25 18:07 UTC)", "v0.5.2.md"),
new (154, "v0.5.1, build 154 (2024-06-25 15:35 UTC)", "v0.5.1.md"),
new (149, "v0.5.0, build 149 (2024-06-02 18:51 UTC)", "v0.5.0.md"),
new (138, "v0.4.0, build 138 (2024-05-26 13:26 UTC)", "v0.4.0.md"),
new (120, "v0.3.0, build 120 (2024-05-18 21:57 UTC)", "v0.3.0.md"),
@@ -1,3 +1,5 @@
@inherits AIStudio.Tools.MSGComponentBase
<div class="d-flex flex-column" style="@this.Height">
<div class="flex-auto overflow-auto">
@this.ChildContent
@@ -1,12 +1,15 @@
using AIStudio.Components.Layout;
using AIStudio.Tools;
using Microsoft.AspNetCore.Components;
namespace AIStudio.Components.Blocks;
public partial class InnerScrolling : ComponentBase
public partial class InnerScrolling : MSGComponentBase
{
/// <summary>
/// Set the height of anything above the scrolling content; usually a header.
/// What we do is calc(100vh - THIS). Means, you can use multiple measures like
/// What we do is calc(100vh - HeaderHeight). Means, you can use multiple measures like
/// 230px - 3em. Default is 3em.
/// </summary>
[Parameter]
@@ -21,5 +24,34 @@ public partial class InnerScrolling : ComponentBase
[Parameter]
public RenderFragment? FooterContent { get; set; }
private string Height => $"height: calc(100vh - {this.HeaderHeight});";
[CascadingParameter]
private MainLayout MainLayout { get; set; } = null!;
#region Overrides of ComponentBase
protected override async Task OnInitializedAsync()
{
this.ApplyFilters([], [ Event.STATE_HAS_CHANGED ]);
await base.OnInitializedAsync();
}
#endregion
#region Overrides of MSGComponentBase
public override Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
{
switch (triggeredEvent)
{
case Event.STATE_HAS_CHANGED:
this.StateHasChanged();
break;
}
return Task.CompletedTask;
}
#endregion
private string Height => $"height: calc(100vh - {this.HeaderHeight} - {this.MainLayout.AdditionalHeight});";
}
@@ -0,0 +1,17 @@
namespace AIStudio.Components.CommonDialogs;
public static class DialogOptions
{
public static readonly MudBlazor.DialogOptions FULLSCREEN = new()
{
CloseOnEscapeKey = true,
FullWidth = true, MaxWidth = MaxWidth.Medium,
};
public static readonly MudBlazor.DialogOptions FULLSCREEN_NO_HEADER = new()
{
NoHeader = true,
CloseOnEscapeKey = true,
FullWidth = true, MaxWidth = MaxWidth.Medium,
};
}
@@ -0,0 +1,14 @@
@using AIStudio.Tools
<MudDialog>
<DialogContent>
<MudText Typo="Typo.h4" Class="d-inline-flex align-center">
<MudIcon Icon="@Icons.Material.Filled.Update" Size="Size.Large" Class="mr-3"/>
Update from v@(META_DATA.Version) to v@(this.UpdateResponse.NewVersion)
</MudText>
<MudMarkdown Value="@this.UpdateResponse.Changelog" OverrideHeaderTypo="@Markdown.OverrideHeaderTypo"/>
</DialogContent>
<DialogActions>
<MudButton OnClick="@this.Cancel" Variant="Variant.Filled">Install later</MudButton>
<MudButton OnClick="@this.Confirm" Variant="Variant.Filled" Color="Color.Info">Install now</MudButton>
</DialogActions>
</MudDialog>
@@ -0,0 +1,26 @@
using System.Reflection;
using AIStudio.Tools;
using Microsoft.AspNetCore.Components;
namespace AIStudio.Components.CommonDialogs;
/// <summary>
/// The update dialog that is used to inform the user about an available update.
/// </summary>
public partial class UpdateDialog : ComponentBase
{
private static readonly Assembly ASSEMBLY = Assembly.GetExecutingAssembly();
private static readonly MetaDataAttribute META_DATA = ASSEMBLY.GetCustomAttribute<MetaDataAttribute>()!;
[CascadingParameter]
private MudDialogInstance MudDialog { get; set; } = null!;
[Parameter]
public UpdateResponse UpdateResponse { get; set; }
private void Cancel() => this.MudDialog.Cancel();
private void Confirm() => this.MudDialog.Close(DialogResult.Ok(true));
}
@@ -21,4 +21,13 @@ public static class ConfigurationSelectDataFactory
yield return new("Modifier key + enter is sending the input", SendBehavior.MODIFER_ENTER_IS_SENDING);
yield return new("Enter is sending the input", SendBehavior.ENTER_IS_SENDING);
}
public static IEnumerable<ConfigurationSelectData<UpdateBehavior>> GetUpdateBehaviorData()
{
yield return new("No automatic update checks", UpdateBehavior.NO_CHECK);
yield return new("Once at startup", UpdateBehavior.ONCE_STARTUP);
yield return new("Check every hour", UpdateBehavior.HOURLY);
yield return new("Check every day", UpdateBehavior.DAILY);
yield return new ("Check every week", UpdateBehavior.WEEKLY);
}
}
@@ -2,31 +2,57 @@
<MudPaper Height="calc(100vh);" Elevation="0">
<MudLayout>
<MudDrawerContainer Class="mud-height-full absolute">
<MudDrawer Elevation="0" Variant="@DrawerVariant.Mini" OpenMiniOnHover="@true" Color="Color.Default">
<MudNavMenu>
<MudTooltip Text="Home" Placement="Placement.Right">
<MudNavLink Href="/" Match="NavLinkMatch.All" Icon="@Icons.Material.Filled.Home">Home</MudNavLink>
</MudTooltip>
<MudTooltip Text="Chats" Placement="Placement.Right">
<MudNavLink Href="/chat" Icon="@Icons.Material.Filled.Chat">Chats</MudNavLink>
</MudTooltip>
<MudTooltip Text="Supporters" Placement="Placement.Right">
<MudNavLink Href="/supporters" Icon="@Icons.Material.Filled.Favorite" IconColor="Color.Error">Supporters</MudNavLink>
</MudTooltip>
<MudTooltip Text="About" Placement="Placement.Right">
<MudNavLink Href="/about" Icon="@Icons.Material.Filled.Info">About</MudNavLink>
</MudTooltip>
<MudTooltip Text="Settings" Placement="Placement.Right">
<MudNavLink Href="/settings" Icon="@Icons.Material.Filled.Settings">Settings</MudNavLink>
</MudTooltip>
</MudNavMenu>
</MudDrawer>
</MudDrawerContainer>
@if (!this.performingUpdate)
{
<MudDrawerContainer Class="mud-height-full absolute">
<MudDrawer Elevation="0" Variant="@DrawerVariant.Mini" OpenMiniOnHover="@true" Color="Color.Default">
<MudNavMenu>
<MudTooltip Text="Home" Placement="Placement.Right">
<MudNavLink Href="/" Match="NavLinkMatch.All" Icon="@Icons.Material.Filled.Home">Home</MudNavLink>
</MudTooltip>
<MudTooltip Text="Chats" Placement="Placement.Right">
<MudNavLink Href="/chat" Icon="@Icons.Material.Filled.Chat">Chats</MudNavLink>
</MudTooltip>
<MudTooltip Text="Supporters" Placement="Placement.Right">
<MudNavLink Href="/supporters" Icon="@Icons.Material.Filled.Favorite" IconColor="Color.Error">Supporters</MudNavLink>
</MudTooltip>
<MudTooltip Text="About" Placement="Placement.Right">
<MudNavLink Href="/about" Icon="@Icons.Material.Filled.Info">About</MudNavLink>
</MudTooltip>
<MudTooltip Text="Settings" Placement="Placement.Right">
<MudNavLink Href="/settings" Icon="@Icons.Material.Filled.Settings">Settings</MudNavLink>
</MudTooltip>
</MudNavMenu>
</MudDrawer>
</MudDrawerContainer>
}
<MudMainContent Class="mud-height-full pt-1">
<MudContainer Fixed="@true" Class="mud-height-full" Style="margin-left: 5em; width: calc(100% - 5em);">
@this.Body
@if (!this.performingUpdate && this.IsUpdateAlertVisible)
{
<MudAlert NoIcon="@true" Severity="Severity.Info" Variant="Variant.Filled" ShowCloseIcon="@true" Dense="@true" CloseIconClicked="() => this.DismissUpdate()" Class="mt-2 mb-2">
<div class="d-inline-flex align-center">
<MudIcon Icon="@Icons.Material.Filled.Update" Size="Size.Medium" Class="mr-3"/>
An update to version @this.updateToVersion is available.
<MudButton Variant="Variant.Filled" Color="Color.Dark" Size="Size.Small" Class="ml-3" OnClick="() => this.ShowUpdateDialog()">
Show details
</MudButton>
</div>
</MudAlert>
}
@if (!this.performingUpdate)
{
<CascadingValue Value="@this" IsFixed="true">
@this.Body
</CascadingValue>
}
<MudOverlay Visible="@this.performingUpdate" DarkBackground="@true" LockScroll="@true">
<MudText Typo="Typo.h3">Please wait for the update to complete...</MudText>
<MudProgressLinear Color="Color.Primary" Indeterminate="@true" Size="Size.Large" Rounded="@true"/>
</MudOverlay>
</MudContainer>
</MudMainContent>
</MudLayout>
@@ -1,12 +1,40 @@
using AIStudio.Components.CommonDialogs;
using AIStudio.Settings;
using AIStudio.Tools;
using Microsoft.AspNetCore.Components;
using DialogOptions = AIStudio.Components.CommonDialogs.DialogOptions;
namespace AIStudio.Components.Layout;
public partial class MainLayout : LayoutComponentBase
public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver
{
[Inject]
private IJSRuntime JsRuntime { get; set; } = null!;
private IJSRuntime JsRuntime { get; init; } = null!;
[Inject]
private SettingsManager SettingsManager { get; init; } = null!;
[Inject]
private MessageBus MessageBus { get; init; } = null!;
[Inject]
private IDialogService DialogService { get; init; } = null!;
[Inject]
private Rust Rust { get; init; } = null!;
[Inject]
private ISnackbar Snackbar { get; init; } = null!;
public string AdditionalHeight { get; private set; } = "0em";
private bool isUpdateAvailable;
private bool performingUpdate;
private bool userDismissedUpdate;
private string updateToVersion = string.Empty;
private UpdateResponse? currentUpdateResponse;
#region Overrides of ComponentBase
@@ -23,8 +51,98 @@ public partial class MainLayout : LayoutComponentBase
SettingsManager.ConfigDirectory = configDir;
SettingsManager.DataDirectory = dataDir;
// Ensure that all settings are loaded:
await this.SettingsManager.LoadSettings();
// Register this component with the message bus:
this.MessageBus.RegisterComponent(this);
this.MessageBus.ApplyFilters(this, [], [ Event.UPDATE_AVAILABLE, Event.USER_SEARCH_FOR_UPDATE ]);
// Set the js runtime for the update service:
UpdateService.SetBlazorDependencies(this.JsRuntime, this.Snackbar);
await base.OnInitializedAsync();
}
#endregion
#region Implementation of IMessageBusReceiver
public async Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data)
{
switch (triggeredEvent)
{
case Event.USER_SEARCH_FOR_UPDATE:
this.userDismissedUpdate = false;
break;
case Event.UPDATE_AVAILABLE:
if (data is UpdateResponse updateResponse)
{
this.currentUpdateResponse = updateResponse;
this.isUpdateAvailable = updateResponse.UpdateIsAvailable;
this.updateToVersion = updateResponse.NewVersion;
await this.InvokeAsync(this.StateHasChanged);
await this.SendMessage<bool>(Event.STATE_HAS_CHANGED);
}
break;
}
}
#endregion
private async Task DismissUpdate()
{
this.userDismissedUpdate = true;
this.AdditionalHeight = "0em";
await this.SendMessage<bool>(Event.STATE_HAS_CHANGED);
}
private bool IsUpdateAlertVisible
{
get
{
var state = this.isUpdateAvailable && !this.userDismissedUpdate;
this.AdditionalHeight = state ? "3em" : "0em";
return state;
}
}
private async Task ShowUpdateDialog()
{
if(this.currentUpdateResponse is null)
return;
//
// Replace the fir line with `# Changelog`:
//
var changelog = this.currentUpdateResponse.Value.Changelog;
if (!string.IsNullOrWhiteSpace(changelog))
{
var lines = changelog.Split('\n');
if (lines.Length > 0)
lines[0] = "# Changelog";
changelog = string.Join('\n', lines);
}
var updatedResponse = this.currentUpdateResponse.Value with { Changelog = changelog };
var dialogParameters = new DialogParameters<UpdateDialog>
{
{ x => x.UpdateResponse, updatedResponse }
};
var dialogReference = await this.DialogService.ShowAsync<UpdateDialog>("Update", dialogParameters, DialogOptions.FULLSCREEN_NO_HEADER);
var dialogResult = await dialogReference.Result;
if (dialogResult.Canceled)
return;
this.performingUpdate = true;
this.StateHasChanged();
await this.Rust.InstallUpdate(this.JsRuntime);
}
}
@@ -18,6 +18,9 @@
<MudListItem Icon="@Icons.Material.Outlined.Widgets" Text="@MudBlazorVersion"/>
<MudListItem Icon="@Icons.Material.Outlined.Memory" Text="@TauriVersion"/>
</MudList>
<MudButton Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.Update" OnClick="() => this.CheckForUpdate()">
Check for updates
</MudButton>
</ExpansionPanel>
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.EventNote" HeaderText="Changelog">
@@ -1,11 +1,16 @@
using System.Reflection;
using AIStudio.Tools;
using Microsoft.AspNetCore.Components;
namespace AIStudio.Components.Pages;
public partial class About : ComponentBase
{
[Inject]
private MessageBus MessageBus { get; init; } = null!;
private static readonly Assembly ASSEMBLY = Assembly.GetExecutingAssembly();
private static readonly MetaDataAttribute META_DATA = ASSEMBLY.GetCustomAttribute<MetaDataAttribute>()!;
@@ -135,4 +140,9 @@ public partial class About : ComponentBase
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
""";
private async Task CheckForUpdate()
{
await this.MessageBus.SendMessage<bool>(this, Event.USER_SEARCH_FOR_UPDATE);
}
}
@@ -37,9 +37,6 @@ public partial class Chat : ComponentBase
protected override async Task OnInitializedAsync()
{
// Ensure that the settings are loaded:
await this.SettingsManager.LoadSettings();
// Configure the spellchecking for the user input:
this.SettingsManager.InjectSpellchecking(USER_INPUT_ATTRIBUTES);
@@ -4,7 +4,7 @@
<MudImage Src="svg/banner.svg" />
<MudText Typo="Typo.h3" Class="mt-2 mb-2">Let's get started</MudText>
<InnerScrolling HeaderHeight="288px - 3em">
<InnerScrolling HeaderHeight="288px - 8em">
<MudExpansionPanels Class="mb-3" MultiExpansion="@false">
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.MenuBook" HeaderText="Introduction" IsExpanded="@true">
@@ -52,5 +52,6 @@
<ConfigurationOption OptionDescription="Save energy?" LabelOn="Energy saving is enabled" LabelOff="Energy saving is disabled" State="@(() => this.SettingsManager.ConfigurationData.IsSavingEnergy)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.IsSavingEnergy = updatedState)" OptionHelp="When enabled, streamed content from the AI is updated once every third second. When disabled, streamed content will be updated as soon as it is available."/>
<ConfigurationOption OptionDescription="Enable spellchecking?" LabelOn="Spellchecking is enabled" LabelOff="Spellchecking is disabled" State="@(() => this.SettingsManager.ConfigurationData.EnableSpellchecking)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.EnableSpellchecking = updatedState)" OptionHelp="When enabled, spellchecking will be active in all input fields. Depending on your operating system, errors may not be visually highlighted, but right-clicking may still offer possible corrections." />
<ConfigurationSelect OptionDescription="Shortcut to send input" SelectedValue="@(() => this.SettingsManager.ConfigurationData.ShortcutSendBehavior)" Data="@ConfigurationSelectDataFactory.GetSendBehaviorData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.ShortcutSendBehavior = selectedValue)" OptionHelp="Do you want to use any shortcut to send your input?"/>
<ConfigurationSelect OptionDescription="Check for updates" SelectedValue="@(() => this.SettingsManager.ConfigurationData.UpdateBehavior)" Data="@ConfigurationSelectDataFactory.GetUpdateBehaviorData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.UpdateBehavior = selectedValue)" OptionHelp="How often should we check for app updates?"/>
</MudPaper>
</InnerScrolling>
@@ -3,6 +3,8 @@ using AIStudio.Provider;
using AIStudio.Settings;
using Microsoft.AspNetCore.Components;
using DialogOptions = AIStudio.Components.CommonDialogs.DialogOptions;
// ReSharper disable ClassNeverInstantiated.Global
namespace AIStudio.Components.Pages;
@@ -18,22 +20,6 @@ public partial class Settings : ComponentBase
[Inject]
public IJSRuntime JsRuntime { get; init; } = null!;
private static readonly DialogOptions DIALOG_OPTIONS = new()
{
CloseOnEscapeKey = true,
FullWidth = true, MaxWidth = MaxWidth.Medium,
};
#region Overrides of ComponentBase
protected override async Task OnInitializedAsync()
{
await this.SettingsManager.LoadSettings();
await base.OnInitializedAsync();
}
#endregion
#region Provider related
private async Task AddProvider()
@@ -43,7 +29,7 @@ public partial class Settings : ComponentBase
{ x => x.IsEditing, false },
};
var dialogReference = await this.DialogService.ShowAsync<ProviderDialog>("Add Provider", dialogParameters, DIALOG_OPTIONS);
var dialogReference = await this.DialogService.ShowAsync<ProviderDialog>("Add Provider", dialogParameters, DialogOptions.FULLSCREEN);
var dialogResult = await dialogReference.Result;
if (dialogResult.Canceled)
return;
@@ -67,7 +53,7 @@ public partial class Settings : ComponentBase
{ x => x.IsEditing, true },
};
var dialogReference = await this.DialogService.ShowAsync<ProviderDialog>("Edit Provider", dialogParameters, DIALOG_OPTIONS);
var dialogReference = await this.DialogService.ShowAsync<ProviderDialog>("Edit Provider", dialogParameters, DialogOptions.FULLSCREEN);
var dialogResult = await dialogReference.Result;
if (dialogResult.Canceled)
return;
@@ -90,7 +76,7 @@ public partial class Settings : ComponentBase
{ "Message", $"Are you sure you want to delete the provider '{provider.InstanceName}'?" },
};
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>("Delete Provider", dialogParameters, DIALOG_OPTIONS);
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>("Delete Provider", dialogParameters, DialogOptions.FULLSCREEN);
var dialogResult = await dialogReference.Result;
if (dialogResult.Canceled)
return;
@@ -12,7 +12,7 @@
<MudText Typo="Typo.body1" Style="text-align: justify; hyphens: auto;" Class="mb-3">
For companies, sponsoring MindWork AI Studio is not only a way to support innovation but also a valuable opportunity for public relations and marketing. Your company's name and logo will be featured prominently, showcasing your commitment to using cutting-edge AI tools and enhancing your reputation as an innovative enterprise.
</MudText>
<MudButton Href="https://github.com/MindWorkAI/AI-Studio/blob/main/Sponsors.md" StartIcon="@Icons.Material.Filled.Business" Variant="Variant.Filled" Target="_blank">
<MudButton Href="https://github.com/sponsors/MindWorkAI" StartIcon="@Icons.Material.Filled.Business" Variant="Variant.Filled" Target="_blank">
Become our first Titan
</MudButton>
</div>
@@ -25,7 +25,7 @@
Individual Contributors
</MudText>
<MudButton Href="https://github.com/MindWorkAI/AI-Studio/blob/main/Sponsors.md" StartIcon="@Icons.Material.Filled.People" Variant="Variant.Filled" Target="_blank">
<MudButton Href="https://github.com/sponsors/MindWorkAI" StartIcon="@Icons.Material.Filled.People" Variant="Variant.Filled" Target="_blank">
Become a contributor
</MudButton>
</MudPaper>
@@ -38,7 +38,7 @@
Business Contributors
</MudText>
<MudButton Href="https://github.com/MindWorkAI/AI-Studio/blob/main/Sponsors.md" StartIcon="@Icons.Material.Filled.Business" Variant="Variant.Filled" Target="_blank">
<MudButton Href="https://github.com/sponsors/MindWorkAI" StartIcon="@Icons.Material.Filled.Business" Variant="Variant.Filled" Target="_blank">
Become a contributor
</MudButton>
</MudPaper>
+2
View File
@@ -25,10 +25,12 @@ builder.Services.AddMudServices(config =>
});
builder.Services.AddMudMarkdownServices();
builder.Services.AddSingleton(MessageBus.INSTANCE);
builder.Services.AddSingleton<Rust>();
builder.Services.AddMudMarkdownClipboardService<MarkdownClipboardService>();
builder.Services.AddSingleton<SettingsManager>();
builder.Services.AddSingleton<Random>();
builder.Services.AddHostedService<UpdateService>();
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents()
.AddHubOptions(options =>
@@ -0,0 +1,19 @@
using AIStudio.Provider.OpenAI;
namespace AIStudio.Provider.Anthropic;
/// <summary>
/// The Anthropic chat request model.
/// </summary>
/// <param name="Model">Which model to use for chat completion.</param>
/// <param name="Messages">The chat messages.</param>
/// <param name="MaxTokens">The maximum number of tokens to generate.</param>
/// <param name="Stream">Whether to stream the chat completion.</param>
/// <param name="System">The system prompt for the chat completion.</param>
public readonly record struct ChatRequest(
string Model,
IList<Message> Messages,
int MaxTokens,
bool Stream,
string System
);
@@ -0,0 +1,166 @@
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.Json;
using AIStudio.Chat;
using AIStudio.Provider.OpenAI;
using AIStudio.Settings;
namespace AIStudio.Provider.Anthropic;
public sealed class ProviderAnthropic() : BaseProvider("https://api.anthropic.com/v1/"), IProvider
{
private static readonly JsonSerializerOptions JSON_SERIALIZER_OPTIONS = new()
{
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
};
#region Implementation of IProvider
public string Id => "Anthropic";
public string InstanceName { get; set; } = "Anthropic";
/// <inheritdoc />
public async IAsyncEnumerable<string> StreamChatCompletion(IJSRuntime jsRuntime, SettingsManager settings, Model chatModel, ChatThread chatThread, [EnumeratorCancellation] CancellationToken token = default)
{
// Get the API key:
var requestedSecret = await settings.GetAPIKey(jsRuntime, this);
if(!requestedSecret.Success)
yield break;
// Prepare the Anthropic HTTP chat request:
var chatRequest = JsonSerializer.Serialize(new ChatRequest
{
Model = chatModel.Id,
// Build the messages:
Messages = [..chatThread.Blocks.Where(n => n.ContentType is ContentType.TEXT && !string.IsNullOrWhiteSpace((n.Content as ContentText)?.Text)).Select(n => new Message
{
Role = n.Role switch
{
ChatRole.USER => "user",
ChatRole.AI => "assistant",
_ => "user",
},
Content = n.Content switch
{
ContentText text => text.Text,
_ => string.Empty,
}
}).ToList()],
System = chatThread.SystemPrompt,
MaxTokens = 4_096,
// Right now, we only support streaming completions:
Stream = true,
}, JSON_SERIALIZER_OPTIONS);
// Build the HTTP post request:
var request = new HttpRequestMessage(HttpMethod.Post, "messages");
// Set the authorization header:
request.Headers.Add("x-api-key", requestedSecret.Secret);
// Set the Anthropic version:
request.Headers.Add("anthropic-version", "2023-06-01");
// Set the content:
request.Content = new StringContent(chatRequest, Encoding.UTF8, "application/json");
// Send the request with the ResponseHeadersRead option.
// This allows us to read the stream as soon as the headers are received.
// This is important because we want to stream the responses.
var response = await this.httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token);
// Open the response stream:
var stream = await response.Content.ReadAsStreamAsync(token);
// Add a stream reader to read the stream, line by line:
var streamReader = new StreamReader(stream);
// Read the stream, line by line:
while(!streamReader.EndOfStream)
{
// Check if the token is canceled:
if(token.IsCancellationRequested)
yield break;
// Read the next line:
var line = await streamReader.ReadLineAsync(token);
// Skip empty lines:
if(string.IsNullOrWhiteSpace(line))
continue;
// Check for the end of the stream:
if(line.StartsWith("event: message_stop", StringComparison.InvariantCulture))
yield break;
// Skip lines that do not start with "data: ". Regard
// to the specification, we only want to read the data lines:
if(!line.StartsWith("data: ", StringComparison.InvariantCulture))
continue;
// Ignore any type except "content_block_delta":
if(!line.Contains("\"content_block_delta\"", StringComparison.InvariantCulture))
continue;
ResponseStreamLine anthropicResponse;
try
{
// We know that the line starts with "data: ". Hence, we can
// skip the first 6 characters to get the JSON data after that.
var jsonData = line[6..];
// Deserialize the JSON data:
anthropicResponse = JsonSerializer.Deserialize<ResponseStreamLine>(jsonData, JSON_SERIALIZER_OPTIONS);
}
catch
{
// Skip invalid JSON data:
continue;
}
// Skip empty responses:
if(anthropicResponse == default || string.IsNullOrWhiteSpace(anthropicResponse.Delta.Text))
continue;
// Yield the response:
yield return anthropicResponse.Delta.Text;
}
}
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
/// <inheritdoc />
public async IAsyncEnumerable<ImageURL> StreamImageCompletion(IJSRuntime jsRuntime, SettingsManager settings, Model imageModel, string promptPositive, string promptNegative = FilterOperator.String.Empty, ImageURL referenceImageURL = default, [EnumeratorCancellation] CancellationToken token = default)
{
yield break;
}
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
/// <inheritdoc />
public Task<IEnumerable<Model>> GetTextModels(IJSRuntime jsRuntime, SettingsManager settings, string? apiKeyProvisional = null, CancellationToken token = default)
{
return Task.FromResult(new[]
{
new Model("claude-3-5-sonnet-20240620"),
new Model("claude-3-opus-20240229"),
new Model("claude-3-sonnet-20240229"),
new Model("claude-3-haiku-20240307"),
}.AsEnumerable());
}
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
/// <inheritdoc />
public Task<IEnumerable<Model>> GetImageModels(IJSRuntime jsRuntime, SettingsManager settings, string? apiKeyProvisional = null, CancellationToken token = default)
{
return Task.FromResult(Enumerable.Empty<Model>());
}
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
#endregion
}
@@ -0,0 +1,17 @@
// ReSharper disable NotAccessedPositionalProperty.Global
namespace AIStudio.Provider.Anthropic;
/// <summary>
/// Represents a response stream line.
/// </summary>
/// <param name="Type">The type of the response line.</param>
/// <param name="Index">The index of the response line.</param>
/// <param name="Delta">The delta of the response line.</param>
public readonly record struct ResponseStreamLine(string Type, int Index, Delta Delta);
/// <summary>
/// The delta object of a response line.
/// </summary>
/// <param name="Type">The type of the delta.</param>
/// <param name="Text">The text of the delta.</param>
public readonly record struct Delta(string Type, string Text);
@@ -1,3 +1,4 @@
using AIStudio.Provider.Anthropic;
using AIStudio.Provider.OpenAI;
namespace AIStudio.Provider;
@@ -9,6 +10,7 @@ public enum Providers
{
NONE,
OPEN_AI,
ANTHROPIC,
}
/// <summary>
@@ -24,6 +26,7 @@ public static class ExtensionsProvider
public static string ToName(this Providers provider) => provider switch
{
Providers.OPEN_AI => "OpenAI",
Providers.ANTHROPIC => "Anthropic",
Providers.NONE => "No provider selected",
_ => "Unknown",
@@ -38,6 +41,7 @@ public static class ExtensionsProvider
public static IProvider CreateProvider(this Providers provider, string instanceName) => provider switch
{
Providers.OPEN_AI => new ProviderOpenAI { InstanceName = instanceName },
Providers.ANTHROPIC => new ProviderAnthropic { InstanceName = instanceName },
_ => new NoProvider(),
};
+6 -1
View File
@@ -6,7 +6,7 @@ namespace AIStudio.Settings;
public sealed class Data
{
/// <summary>
/// The version of the settings file. Allows us to upgrade the settings,
/// The version of the settings file. Allows us to upgrade the settings
/// when a new version is available.
/// </summary>
public Version Version { get; init; } = Version.V1;
@@ -36,4 +36,9 @@ public sealed class Data
/// Should we enable spellchecking for all input fields?
/// </summary>
public bool EnableSpellchecking { get; set; }
/// <summary>
/// If and when we should look for updates.
/// </summary>
public UpdateBehavior UpdateBehavior { get; set; } = UpdateBehavior.ONCE_STARTUP;
}
@@ -0,0 +1,10 @@
namespace AIStudio.Settings;
public enum UpdateBehavior
{
NO_CHECK,
ONCE_STARTUP,
HOURLY,
DAILY,
WEEKLY,
}
+13
View File
@@ -0,0 +1,13 @@
namespace AIStudio.Tools;
public enum Event
{
NONE,
// Common events:
STATE_HAS_CHANGED,
// Update events:
USER_SEARCH_FOR_UPDATE,
UPDATE_AVAILABLE,
}
@@ -0,0 +1,8 @@
using Microsoft.AspNetCore.Components;
namespace AIStudio.Tools;
public interface IMessageBusReceiver
{
public Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data);
}
@@ -0,0 +1,44 @@
using Microsoft.AspNetCore.Components;
namespace AIStudio.Tools;
public abstract class MSGComponentBase : ComponentBase, IDisposable, IMessageBusReceiver
{
[Inject]
protected MessageBus MessageBus { get; init; } = null!;
#region Overrides of ComponentBase
protected override void OnInitialized()
{
this.MessageBus.RegisterComponent(this);
base.OnInitialized();
}
#endregion
#region Implementation of IMessageBusReceiver
public abstract Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data);
#endregion
#region Implementation of IDisposable
public void Dispose()
{
this.MessageBus.Unregister(this);
}
#endregion
protected async Task SendMessage<T>(Event triggeredEvent, T? data = default)
{
await this.MessageBus.SendMessage(this, triggeredEvent, data);
}
protected void ApplyFilters(ComponentBase[] components, Event[] events)
{
this.MessageBus.ApplyFilters(this, components, events);
}
}
@@ -0,0 +1,67 @@
using System.Collections.Concurrent;
using Microsoft.AspNetCore.Components;
// ReSharper disable RedundantRecordClassKeyword
namespace AIStudio.Tools;
public sealed class MessageBus
{
public static readonly MessageBus INSTANCE = new();
private readonly ConcurrentDictionary<IMessageBusReceiver, ComponentBase[]> componentFilters = new();
private readonly ConcurrentDictionary<IMessageBusReceiver, Event[]> componentEvents = new();
private readonly ConcurrentQueue<Message> messageQueue = new();
private readonly SemaphoreSlim sendingSemaphore = new(1, 1);
private MessageBus()
{
}
public void ApplyFilters(IMessageBusReceiver receiver, ComponentBase[] components, Event[] events)
{
this.componentFilters[receiver] = components;
this.componentEvents[receiver] = events;
}
public void RegisterComponent(IMessageBusReceiver receiver)
{
this.componentFilters.TryAdd(receiver, []);
this.componentEvents.TryAdd(receiver, []);
}
public void Unregister(IMessageBusReceiver receiver)
{
this.componentFilters.TryRemove(receiver, out _);
this.componentEvents.TryRemove(receiver, out _);
}
private record class Message(ComponentBase? SendingComponent, Event TriggeredEvent, object? Data);
public async Task SendMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data = default)
{
this.messageQueue.Enqueue(new Message(sendingComponent, triggeredEvent, data));
try
{
await this.sendingSemaphore.WaitAsync();
while (this.messageQueue.TryDequeue(out var message))
{
foreach (var (receiver, componentFilter) in this.componentFilters)
{
if (componentFilter.Length > 0 && sendingComponent is not null && !componentFilter.Contains(sendingComponent))
continue;
var eventFilter = this.componentEvents[receiver];
if (eventFilter.Length == 0 || eventFilter.Contains(triggeredEvent))
// We don't await the task here because we don't want to block the message bus:
_ = receiver.ProcessMessage(message.SendingComponent, message.TriggeredEvent, message.Data);
}
}
}
finally
{
this.sendingSemaphore.Release();
}
}
}
@@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Components;
namespace AIStudio.Tools;
public static class MessageBusExtensions
{
public static async Task SendMessage<T>(this ComponentBase component, Event triggeredEvent, T? data = default)
{
await MessageBus.INSTANCE.SendMessage(component, triggeredEvent, data);
}
public static void ApplyFilters(this IMessageBusReceiver component, ComponentBase[] components, Event[] events)
{
MessageBus.INSTANCE.ApplyFilters(component, components, events);
}
}
+12
View File
@@ -39,4 +39,16 @@ public sealed class Rust
};
});
}
public async Task<UpdateResponse> CheckForUpdate(IJSRuntime jsRuntime)
{
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(16));
return await jsRuntime.InvokeAsync<UpdateResponse>("window.__TAURI__.invoke", cts.Token, "check_for_update");
}
public async Task InstallUpdate(IJSRuntime jsRuntime)
{
var cts = new CancellationTokenSource();
await jsRuntime.InvokeVoidAsync("window.__TAURI__.invoke", cts.Token, "install_update");
}
}
@@ -3,6 +3,6 @@ namespace AIStudio.Tools;
/// <summary>
/// The response from the set clipboard operation.
/// </summary>
/// <param name="Success">True when the operation was successful.</param>
/// <param name="Issue">The issue that occurred during the operation, empty when successful.</param>
/// <param name="Success">True, when the operation was successful.</param>
/// <param name="Issue">The issues, which occurred during the operation, empty when successful.</param>
public readonly record struct SetClipboardResponse(bool Success, string Issue);
@@ -0,0 +1,16 @@
using System.Text.Json.Serialization;
namespace AIStudio.Tools;
/// <summary>
/// The response of the update check.
/// </summary>
/// <param name="UpdateIsAvailable">True if an update is available.</param>
/// <param name="NewVersion">The new version, when available.</param>
/// <param name="Changelog">The changelog of the new version, when available.</param>
public readonly record struct UpdateResponse(
[property:JsonPropertyName("update_is_available")] bool UpdateIsAvailable,
[property:JsonPropertyName("error")] bool Error,
[property:JsonPropertyName("new_version")] string NewVersion,
string Changelog
);
@@ -0,0 +1,119 @@
using AIStudio.Settings;
using Microsoft.AspNetCore.Components;
namespace AIStudio.Tools;
public sealed class UpdateService : BackgroundService, IMessageBusReceiver
{
// We cannot inject IJSRuntime into our service. This is because
// the service is not a Blazor component. We need to pass the IJSRuntime from
// the MainLayout component to the service.
private static IJSRuntime? JS_RUNTIME;
private static bool IS_INITIALIZED;
private static ISnackbar? SNACKBAR;
private readonly SettingsManager settingsManager;
private readonly TimeSpan updateInterval;
private readonly MessageBus messageBus;
private readonly Rust rust;
public UpdateService(MessageBus messageBus, SettingsManager settingsManager, Rust rust)
{
this.settingsManager = settingsManager;
this.messageBus = messageBus;
this.rust = rust;
this.messageBus.RegisterComponent(this);
this.ApplyFilters([], [ Event.USER_SEARCH_FOR_UPDATE ]);
this.updateInterval = settingsManager.ConfigurationData.UpdateBehavior switch
{
UpdateBehavior.NO_CHECK => Timeout.InfiniteTimeSpan,
UpdateBehavior.ONCE_STARTUP => Timeout.InfiniteTimeSpan,
UpdateBehavior.HOURLY => TimeSpan.FromHours(1),
UpdateBehavior.DAILY => TimeSpan.FromDays(1),
UpdateBehavior.WEEKLY => TimeSpan.FromDays(7),
_ => TimeSpan.FromHours(1)
};
}
#region Overrides of BackgroundService
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested && !IS_INITIALIZED)
{
await Task.Delay(TimeSpan.FromSeconds(1), stoppingToken);
}
await this.settingsManager.LoadSettings();
if(this.settingsManager.ConfigurationData.UpdateBehavior != UpdateBehavior.NO_CHECK)
await this.CheckForUpdate();
while (!stoppingToken.IsCancellationRequested)
{
await Task.Delay(this.updateInterval, stoppingToken);
await this.CheckForUpdate();
}
}
#endregion
#region Implementation of IMessageBusReceiver
public async Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data)
{
switch (triggeredEvent)
{
case Event.USER_SEARCH_FOR_UPDATE:
await this.CheckForUpdate(notifyUserWhenNoUpdate: true);
break;
}
}
#endregion
#region Overrides of BackgroundService
public override async Task StopAsync(CancellationToken cancellationToken)
{
this.messageBus.Unregister(this);
await base.StopAsync(cancellationToken);
}
#endregion
private async Task CheckForUpdate(bool notifyUserWhenNoUpdate = false)
{
if(!IS_INITIALIZED)
return;
var response = await this.rust.CheckForUpdate(JS_RUNTIME!);
if (response.UpdateIsAvailable)
{
await this.messageBus.SendMessage(null, Event.UPDATE_AVAILABLE, response);
}
else
{
if (notifyUserWhenNoUpdate)
{
SNACKBAR!.Add("No update found.", Severity.Normal, config =>
{
config.Icon = Icons.Material.Filled.Update;
config.IconSize = Size.Large;
config.IconColor = Color.Primary;
});
}
}
}
public static void SetBlazorDependencies(IJSRuntime jsRuntime, ISnackbar snackbar)
{
SNACKBAR = snackbar;
JS_RUNTIME = jsRuntime;
IS_INITIALIZED = true;
}
}
+1 -1
View File
@@ -163,6 +163,6 @@
"contentHash": "FHNOatmUq0sqJOkTx+UF/9YK1f180cnW5FVqnQMvYUN0elp6wFzbtPSiqbo1/ru8ICp43JM1i7kKkk6GsNGHlA=="
}
},
"net8.0/osx-arm64": {}
"net8.0/osx-x64": {}
}
}
@@ -0,0 +1,2 @@
# v0.5.1, build 154 (2024-06-25 15:35 UTC)
- Fixed links to the GitHub sponsors page; GitHub finished our review, and we are now listed there.
@@ -0,0 +1,2 @@
# v0.5.2, build 155 (2024-06-25 18:07 UTC)
- Fixed home page layout for 1080p screens.
@@ -0,0 +1,8 @@
# v0.6.0, build 156 (2024-06-30 12:49 UTC)
- Added a setting to determine whether and how often to check for updates
- Added a bidirectional message bus for inter-component communication
- Added an update dialog for the app
- Added an update banner to show when a new version is available
- Added an option to manually check for updates to the about page
- Fixed an issue with previous automatic updates on Windows, where background processes were not terminated
- Disabled Tauri's built-in update dialog
@@ -0,0 +1,2 @@
# v0.6.1, build 157 (2024-06-30 19:00 UTC)
- Added support for the Anthropic provider
@@ -1 +1 @@
.mud-markdown-body code:not(.hljs){background:var(--mud-palette-chip-default);color:var(--mud-palette-text-primary)!important;padding:2.5px 7.5px;border-radius:5px}pre code.hljs{display:block!important;overflow-x:auto;padding:1em}.mud-markdown-body .snippet-clipboard-content{position:relative!important}.mud-markdown-body .snippet-clipboard-content:hover>.snippet-clipboard-copy-icon{display:block!important}.mud-markdown-body .snippet-clipboard-content .snippet-clipboard-copy-icon{position:absolute;display:none;top:0;right:0}.mud-markdown-body blockquote{border-left:.25em solid var(--mud-palette-text-disabled);color:var(--mud-palette-text-secondary);background-color:var(--mud-palette-drawer-background);padding:.25em 1em;margin:.5em 0 1.25em}.mud-markdown-body blockquote p{margin-bottom:0!important}.mud-markdown-body table{margin:1.25em 0}.mud-markdown-body .mud-link:hover{cursor:pointer!important}.mud-markdown-body ul{list-style-type:disc}.mud-markdown-body ol,.mud-markdown-body ul{padding-left:2em;margin-bottom:1.25em!important}.mud-markdown-body ul ul{list-style-type:circle;margin-bottom:0!important}.mud-markdown-body ul ul ul{list-style-type:square;margin-bottom:0!important}.mud-markdown-body li{display:list-item!important;text-align:-webkit-match-parent}.mud-markdown-body h1,.mud-markdown-body h2{border-bottom:1px solid var(--mud-palette-text-disabled);padding-bottom:.125em;margin-bottom:.4em}.mud-markdown-body h1,.mud-markdown-body h2,.mud-markdown-body h3,.mud-markdown-body h4,.mud-markdown-body h5,.mud-markdown-body h6{scroll-margin-top:5rem;margin-top:.25em;word-wrap:break-word;margin-bottom:.3em!important}.mud-markdown-body .mud-divider{margin:.5em 0;height:.25em}.mud-markdown-body p{margin-bottom:1.25em!important}.mud-markdown-body .mud-expand-panel p,.mud-markdown-body li p{margin-bottom:0!important}.mud-markdown-body img{max-width:100%}
.mud-markdown-body code:not(.hljs){background:var(--mud-palette-action-disabled-background);color:var(--mud-palette-text-primary)!important;padding:2.5px 7.5px;border-radius:5px}pre code.hljs{display:block!important;overflow-x:auto;padding:1em}.mud-markdown-body .snippet-clipboard-content{position:relative!important}.mud-markdown-body .snippet-clipboard-content:hover>.snippet-clipboard-copy-icon{display:block!important}.mud-markdown-body .snippet-clipboard-content .snippet-clipboard-copy-icon{position:absolute;display:none;top:0;right:0}.mud-markdown-body blockquote{border-left:.25em solid var(--mud-palette-text-disabled);color:var(--mud-palette-text-secondary);background-color:var(--mud-palette-drawer-background);padding:.25em 1em;margin:.5em 0 1.25em}.mud-markdown-body blockquote p{margin-bottom:0!important}.mud-markdown-body table{margin:1.25em 0}.mud-markdown-body .mud-link:hover{cursor:pointer!important}.mud-markdown-body ul{list-style-type:disc}.mud-markdown-body ol,.mud-markdown-body ul{padding-left:2em;margin-bottom:1.25em!important}.mud-markdown-body ul ul{list-style-type:circle;margin-bottom:0!important}.mud-markdown-body ul ul ul{list-style-type:square;margin-bottom:0!important}.mud-markdown-body li{display:list-item!important;text-align:-webkit-match-parent}.mud-markdown-body h1,.mud-markdown-body h2{border-bottom:1px solid var(--mud-palette-text-disabled);padding-bottom:.125em;margin-bottom:.4em}.mud-markdown-body h1,.mud-markdown-body h2,.mud-markdown-body h3,.mud-markdown-body h4,.mud-markdown-body h5,.mud-markdown-body h6{scroll-margin-top:5rem;margin-top:.25em;word-wrap:break-word;margin-bottom:.3em!important}.mud-markdown-body .mud-divider{margin:.5em 0;height:.25em}.mud-markdown-body p{margin-bottom:1.25em!important}.mud-markdown-body .mud-expand-panel p,.mud-markdown-body li p{margin-bottom:0!important}.mud-markdown-body img{max-width:100%}
@@ -1 +1 @@
(()=>{var e={3390:e=>{var t={exports:{}};function a(e){return e instanceof Map?e.clear=e.delete=e.set=function(){throw new Error("map is read-only")}:e instanceof Set&&(e.add=e.clear=e.delete=function(){throw new Error("set is read-only")}),Object.freeze(e),Object.getOwnPropertyNames(e).forEach((function(t){var n=e[t];"object"!=typeof n||Object.isFrozen(n)||a(n)})),e}t.exports=a,t.exports.default=a;var n=t.exports;class r{constructor(e){void 0===e.data&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1}ignoreMatch(){this.isMatchIgnored=!0}}function i(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#x27;")}function o(e,...t){const a=Object.create(null);for(const t in e)a[t]=e[t];return t.forEach((function(e){for(const t in e)a[t]=e[t]})),a}const s=e=>!!e.kind;class l{constructor(e,t){this.buffer="",this.classPrefix=t.classPrefix,e.walk(this)}addText(e){this.buffer+=i(e)}openNode(e){if(!s(e))return;let t=e.kind;t=e.sublanguage?`language-${t}`:((e,{prefix:t})=>{if(e.includes(".")){const a=e.split(".");return[`${t}${a.shift()}`,...a.map(((e,t)=>`${e}${"_".repeat(t+1)}`))].join(" ")}return`${t}${e}`})(t,{prefix:this.classPrefix}),this.span(t)}closeNode(e){s(e)&&(this.buffer+="</span>")}value(){return this.buffer}span(e){this.buffer+=`<span class="${e}">`}}class c{constructor(){this.rootNode={children:[]},this.stack=[this.rootNode]}get top(){return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){this.top.children.push(e)}openNode(e){const t={kind:e,children:[]};this.add(t),this.stack.push(t)}closeNode(){if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,t){return"string"==typeof t?e.addText(t):t.children&&(e.openNode(t),t.children.forEach((t=>this._walk(e,t))),e.closeNode(t)),e}static _collapse(e){"string"!=typeof e&&e.children&&(e.children.every((e=>"string"==typeof e))?e.children=[e.children.join("")]:e.children.forEach((e=>{c._collapse(e)})))}}class _ extends c{constructor(e){super(),this.options=e}addKeyword(e,t){""!==e&&(this.openNode(t),this.addText(e),this.closeNode())}addText(e){""!==e&&this.add(e)}addSublanguage(e,t){const a=e.root;a.kind=t,a.sublanguage=!0,this.add(a)}toHTML(){return new l(this,this.options).value()}finalize(){return!0}}function d(e){return e?"string"==typeof e?e:e.source:null}function m(...e){return e.map((e=>d(e))).join("")}function p(...e){const t=function(e){const t=e[e.length-1];return"object"==typeof t&&t.constructor===Object?(e.splice(e.length-1,1),t):{}}(e);return"("+(t.capture?"":"?:")+e.map((e=>d(e))).join("|")+")"}function u(e){return new RegExp(e.toString()+"|").exec("").length-1}const E=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./;function g(e,{joinWith:t}){let a=0;return e.map((e=>{a+=1;const t=a;let n=d(e),r="";for(;n.length>0;){const e=E.exec(n);if(!e){r+=n;break}r+=n.substring(0,e.index),n=n.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?r+="\\"+String(Number(e[1])+t):(r+=e[0],"("===e[0]&&a++)}return r})).map((e=>`(${e})`)).join(t)}const S="[a-zA-Z]\\w*",b="[a-zA-Z_]\\w*",T="\\b\\d+(\\.\\d+)?",f="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",C="\\b(0b[01]+)",N={begin:"\\\\[\\s\\S]",relevance:0},R={scope:"string",begin:"'",end:"'",illegal:"\\n",contains:[N]},O={scope:"string",begin:'"',end:'"',illegal:"\\n",contains:[N]},h=function(e,t,a={}){const n=o({scope:"comment",begin:e,end:t,contains:[]},a);n.contains.push({scope:"doctag",begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)",end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0});const r=p("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/);return n.contains.push({begin:m(/[ ]+/,"(",r,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),n},I=h("//","$"),v=h("/\\*","\\*/"),A=h("#","$"),y={scope:"number",begin:T,relevance:0},D={scope:"number",begin:f,relevance:0},M={scope:"number",begin:C,relevance:0},x={begin:/(?=\/[^/\n]*\/)/,contains:[{scope:"regexp",begin:/\//,end:/\/[gimuy]*/,illegal:/\n/,contains:[N,{begin:/\[/,end:/\]/,relevance:0,contains:[N]}]}]},L={scope:"title",begin:S,relevance:0},w={scope:"title",begin:b,relevance:0};var P=Object.freeze({__proto__:null,MATCH_NOTHING_RE:/\b\B/,IDENT_RE:S,UNDERSCORE_IDENT_RE:b,NUMBER_RE:T,C_NUMBER_RE:f,BINARY_NUMBER_RE:C,RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",SHEBANG:(e={})=>{const t=/^#![ ]*\//;return e.binary&&(e.begin=m(t,/.*\b/,e.binary,/\b.*/)),o({scope:"meta",begin:t,end:/$/,relevance:0,"on:begin":(e,t)=>{0!==e.index&&t.ignoreMatch()}},e)},BACKSLASH_ESCAPE:N,APOS_STRING_MODE:R,QUOTE_STRING_MODE:O,PHRASAL_WORDS_MODE:{begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|prettLine truncated
(()=>{var e={2689:e=>{e.exports=function(e){var t="built_in",a={},n={begin:"}",className:t,endsParent:!0},i={begin:"{",end:"}",contains:[e.QUOTE_STRING_MODE,"self"]},r=e.COMMENT("@\\*","\\*@",{relevance:10}),o={begin:"@[A-Za-z0-9\\._:-]+",returnBegin:!0,end:"(\\r|\\n|<|\\s|\"|')",subLanguage:"csharp",contains:[{begin:"@",className:t},{begin:"\\[",end:"\\]",skip:!0},{begin:"\\(",end:"\\)",skip:!0}],returnEnd:!0},s={begin:"[@]{0,1}<text>",returnBegin:!0,end:"</text>",returnEnd:!0,subLanguage:"cshtml-razor",contains:[{begin:"[@]{0,1}<text>",className:t},{begin:"</text>",className:t,endsParent:!0}]},l={begin:"@\\(",end:"\\)",returnBegin:!0,returnEnd:!0,subLanguage:"csharp",contains:[{begin:"@\\(",className:t},{begin:"\\(",end:"\\)",subLanguage:"csharp",contains:[e.QUOTE_STRING_MODE,"self",s]},s,{begin:"\\)",className:t,endsParent:!0}]},c=function(e,t){var a={endsWithParent:!0,illegal:/</,relevance:0,contains:[{className:"attr",begin:"[A-Za-z0-9\\._:-]+",relevance:0},{begin:/=\s*/,relevance:0,contains:[{className:"string",variants:[{begin:/"/,end:/"/,contains:t},{begin:/'/,end:/'/,contains:t},{begin:/[^\s"'=<>`]+/}]}]}]};return[{className:"meta",begin:"<!DOCTYPE",end:">",relevance:10,contains:[{begin:"\\[",end:"\\]"}]},e.COMMENT("\x3c!--","--\x3e",{relevance:10}),{begin:"<\\!\\[CDATA\\[",end:"\\]\\]>",relevance:10},{className:"meta",begin:/<\?xml/,end:/\?>/,relevance:10},{className:"tag",begin:"<style(?=\\s|>|$)",end:">",keywords:{name:"style"},contains:[a],starts:{end:"</style>",returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag",begin:"<script(?=\\s|>|$)",end:">",keywords:{name:"script"},contains:[a],starts:{end:"<\/script>",returnEnd:!0,subLanguage:["actionscript","javascript","handlebars","xml"]}},{className:"tag",begin:"</?",end:"/?>",contains:[{className:"name",begin:/[^\/><\s]+/,relevance:0},a]}].concat(t)}(e,[o,l]),_="^\\s*@(page|model|using|inherits|inject|layout)",d={begin:_+"[^\\r\\n{\\(]*$",end:"$",returnBegin:!0,returnEnd:!0,contains:[{begin:_,className:t},{variants:[{begin:"\\r|\\n",endsParent:!0},{begin:"\\s[^\\r\\n]+",end:"$"},{begin:"$"}],className:"type",endsParent:!0}]},m={variants:[{begin:"@\\{",end:"}"},{begin:"@code\\s*\\{",end:"}"}],returnBegin:!0,returnEnd:!0,subLanguage:"csharp",contains:[{begin:"@(code\\s*)?\\{",className:t},a,{begin:"{",end:"}",contains:["self"],skip:!0},n]},p={begin:"^\\s*@helper[\\s]*[^{]+[\\s]*{",returnBegin:!0,returnEnd:!0,end:"}",subLanguage:"cshtml-razor",contains:[{begin:"@helper",className:t},{begin:"{",className:t},n]},u=[{begin:"@for[\\s]*\\([^{]+[\\s]*{",end:"}"},{begin:"@if[\\s]*\\([^{]+[\\s]*{",end:"}"},{begin:"@switch[\\s]*\\([^{]+[\\s]*{",end:"}"},{begin:"@while[\\s]*\\([^{]+[\\s]*{",end:"}"},{begin:"@using[\\s]*\\([^{]+[\\s]*{",end:"}"},{begin:"@lock[\\s]*\\([^{]+[\\s]*{",end:"}"},{begin:"@foreach[\\s]*\\([^{]+[\\s]*{",end:"}"}],g={variants:u,returnBegin:!0,returnEnd:!0,subLanguage:"csharp",contains:[{variants:u.map((function(e){return{begin:e.begin}})),returnBegin:!0,contains:[{begin:"@",className:t},{variants:u.map((function(e){return{begin:e.begin.substr(1,e.begin.length-2)}})),subLanguage:"csharp"},{begin:"{",className:t}]},a,{variants:[{begin:"}[\\s]*else\\sif[\\s]*\\([^{]+[\\s]*{"},{begin:"}[\\s]*else[\\s]*{"}],returnBegin:!0,contains:[{begin:"}",className:t},{variants:[{begin:"[\\s]*else\\sif[\\s]*\\([^{]+[\\s]*{"},{begin:"[\\s]*else[\\s]*"}],subLanguage:"csharp"},{begin:"{",className:t}]},i,n]},E={begin:"@try[\\s]*{",end:"}",returnBegin:!0,returnEnd:!0,subLanguage:"csharp",contains:[{begin:"@",className:t},{begin:"try[\\s]*{",subLanguage:"csharp"},{variants:[{begin:"}[\\s]*catch[\\s]*\\([^\\)]+\\)[\\s]*{"},{begin:"}[\\s]*finally[\\s]*{"}],returnBegin:!0,contains:[{begin:"}",className:t},{variants:[{begin:"[\\s]*catch[\\s]*\\([^\\)]+\\)[\\s]*"},{begin:"[\\s]*finally[\\s]*"}],subLanguage:"csharp"},{begin:"{",className:t}]},a,i,n]},S="@section[\\s]+[a-zA-Z0-9]+[\\s]*{",b=[d,p,m,g,{begin:S,returnBegin:!0,returnEnd:!0,end:"}",subLanguage:"cshtml-razor",contains:[{begin:S,className:t},i,n]},{begin:"@await ",returnBegin:!0,subLanguage:"csharp",end:"(\\r|\\n|<|\\s)",contains:[{begin:"@await ",className:t},{begin:"[<\\r\\n]",endsParent:!0}]},E,{variants:[{begin:"@@"},{begin:"[a-zA-Z]+@"}],skip:!0},s,r,l,{className:"meta",begin:"<!DOCTYPE",end:">",relevance:10,contains:[{begin:"\\[",end:"\\]"}]},{begin:"<\\!\\[CDATA\\[",end:"\\]\\]>",relevance:10}].concat(c);return[m,g,E].forEach((function(e){var t=b.filter((function(t){return t!==e})),n=e.contains.indexOf(a);e.contains.splice.apply(e.contains,[n,1].concat(t))})),{aliases:["cshtml","razor","razor-cshtml","cshtml-razor"],contains:b}}},3390:e=>{function t(e){return e instanceof Map?e.clear=e.delete=e.set=function(){throw new Error("map is read-only")}:e instanceof Set&&(e.add=e.clear=e.delete=function(){throw new Error("set is read-only")}),Object.freeze(e),Object.getOwnPropertyNames(e).forEach((a=>{const n=e[a],i=typeof n;"object"!==i&&"function"!==i||Object.isFrozen(n)||t(n)})),e}class a{constructoLine truncated
+219 -219
View File
@@ -1,227 +1,11 @@
window.getTabbableElements=(element)=>{return element.querySelectorAll("a[href]:not([tabindex='-1']),"+"area[href]:not([tabindex='-1']),"+"button:not([disabled]):not([tabindex='-1']),"+"input:not([disabled]):not([tabindex='-1']):not([type='hidden']),"+"select:not([disabled]):not([tabindex='-1']),"+"textarea:not([disabled]):not([tabindex='-1']),"+"iframe:not([tabindex='-1']),"+"details:not([tabindex='-1']),"+"[tabindex]:not([tabindex='-1']),"+"[contentEditable=true]:not([tabindex='-1'])");};window.serializeParameter=(data,spec)=>{if(typeof data=="undefined"||data===null){return null;}
if(typeof data==="number"||typeof data==="string"||typeof data=="boolean"){return data;}
let res=(Array.isArray(data))?[]:{};if(!spec){spec="*";}
for(let i in data){let currentMember=data[i];if(typeof currentMember==='function'||currentMember===null){continue;}
let currentMemberSpec;if(spec!="*"){currentMemberSpec=Array.isArray(data)?spec:spec[i];if(!currentMemberSpec){continue;}}else{currentMemberSpec="*"}
if(typeof currentMember==='object'){if(Array.isArray(currentMember)||currentMember.length){res[i]=[];for(let j=0;j<currentMember.length;j++){const arrayItem=currentMember[j];if(typeof arrayItem==='object'){res[i].push(this.serializeParameter(arrayItem,currentMemberSpec));}else{res[i].push(arrayItem);}}}else{if(currentMember.length===0){res[i]=[];}else{res[i]=this.serializeParameter(currentMember,currentMemberSpec);}}}else{if(currentMember===Infinity){currentMember="Infinity";}
if(currentMember!==null){res[i]=currentMember;}}}
return res;};class MudJsEventFactory{connect(dotNetRef,elementId,options){if(!elementId)
throw"[MudBlazor | JsEvent] elementId: expected element id!";var element=document.getElementById(elementId);if(!element)
throw"[MudBlazor | JsEvent] no element found for id: "+elementId;if(!element.mudJsEvent)
element.mudJsEvent=new MudJsEvent(dotNetRef,options);element.mudJsEvent.connect(element);}
disconnect(elementId){var element=document.getElementById(elementId);if(!element||!element.mudJsEvent)
return;element.mudJsEvent.disconnect();}
subscribe(elementId,eventName){if(!elementId)
throw"[MudBlazor | JsEvent] elementId: expected element id!";var element=document.getElementById(elementId);if(!element)
throw"[MudBlazor | JsEvent] no element found for id: "+elementId;if(!element.mudJsEvent)
throw"[MudBlazor | JsEvent] please connect before subscribing"
element.mudJsEvent.subscribe(eventName);}
unsubscribe(elementId,eventName){var element=document.getElementById(elementId);if(!element||!element.mudJsEvent)
return;element.mudJsEvent.unsubscribe(element,eventName);}}
window.mudJsEvent=new MudJsEventFactory();class MudJsEvent{constructor(dotNetRef,options){this._dotNetRef=dotNetRef;this._options=options||{};this.logger=options.enableLogging?console.log:(message)=>{};this.logger('[MudBlazor | JsEvent] Initialized',{options});this._subscribedEvents={};}
connect(element){if(!this._options)
return;if(!this._options.targetClass)
throw"_options.targetClass: css class name expected";if(this._observer){return;}
var targetClass=this._options.targetClass;this.logger('[MudBlazor | JsEvent] Start observing DOM of element for changes to child with class ',{element,targetClass});this._element=element;this._observer=new MutationObserver(this.onDomChanged);this._observer.mudJsEvent=this;this._observer.observe(this._element,{attributes:false,childList:true,subtree:true});this._observedChildren=[];}
disconnect(){if(!this._observer)
return;this.logger('[MudBlazor | JsEvent] disconnect mutation observer and event handler ');this._observer.disconnect();this._observer=null;for(const child of this._observedChildren)
this.detachHandlers(child);}
subscribe(eventName){if(this._subscribedEvents[eventName]){return;}
var element=this._element;var targetClass=this._options.targetClass;this._subscribedEvents[eventName]=true;for(const child of element.getElementsByClassName(targetClass)){this.attachHandlers(child);}}
unsubscribe(eventName){if(!this._observer)
return;this.logger('[MudBlazor | JsEvent] unsubscribe event handler '+eventName);this._observer.disconnect();this._observer=null;this._subscribedEvents[eventName]=false;for(const child of this._observedChildren){this.detachHandler(child,eventName);}}
attachHandlers(child){child.mudJsEvent=this;for(var eventName of Object.getOwnPropertyNames(this._subscribedEvents)){if(!this._subscribedEvents[eventName])
continue;this.logger('[MudBlazor | JsEvent] attaching event '+eventName,child);child.addEventListener(eventName,this.eventHandler);}
if(this._observedChildren.indexOf(child)<0)
this._observedChildren.push(child);}
detachHandler(child,eventName){this.logger('[MudBlazor | JsEvent] detaching handler '+eventName,child);child.removeEventListener(eventName,this.eventHandler);}
detachHandlers(child){this.logger('[MudBlazor | JsEvent] detaching handlers ',child);for(var eventName of Object.getOwnPropertyNames(this._subscribedEvents)){if(!this._subscribedEvents[eventName])
continue;child.removeEventListener(eventName,this.eventHandler);}
this._observedChildren=this._observedChildren.filter(x=>x!==child);}
onDomChanged(mutationsList,observer){var self=this.mudJsEvent;var targetClass=self._options.targetClass;for(const mutation of mutationsList){for(const element of mutation.addedNodes){if(element.classList&&element.classList.contains(targetClass)){if(!self._options.TagName||element.tagName==self._options.TagName)
self.attachHandlers(element);}}
for(const element of mutation.removedNodes){if(element.classList&&element.classList.contains(targetClass)){if(!self._options.tagName||element.tagName==self._options.tagName)
self.detachHandlers(element);}}}}
eventHandler(e){var self=this.mudJsEvent;var eventName=e.type;self.logger('[MudBlazor | JsEvent] "'+eventName+'"',e);self["on"+eventName](self,e);}
onkeyup(self,e){const caretPosition=e.target.selectionStart;const invoke=self._subscribedEvents["keyup"];if(invoke){self._dotNetRef.invokeMethodAsync('OnCaretPositionChanged',caretPosition);}}
onclick(self,e){const caretPosition=e.target.selectionStart;const invoke=self._subscribedEvents["click"];if(invoke){self._dotNetRef.invokeMethodAsync('OnCaretPositionChanged',caretPosition);}}
onpaste(self,e){const invoke=self._subscribedEvents["paste"];if(invoke){e.preventDefault();e.stopPropagation();const text=(e.originalEvent||e).clipboardData.getData('text/plain');self._dotNetRef.invokeMethodAsync('OnPaste',text);}}
onselect(self,e){const invoke=self._subscribedEvents["select"];if(invoke){const start=e.target.selectionStart;const end=e.target.selectionEnd;if(start===end)
return;self._dotNetRef.invokeMethodAsync('OnSelect',start,end);}}}
class MudScrollListener{constructor(){this.throttleScrollHandlerId=-1;this.handlerRef=null;}
listenForScroll(dotnetReference,selector){let element=selector?document.querySelector(selector):document;this.handlerRef=this.throttleScrollHandler.bind(this,dotnetReference);element.addEventListener('scroll',this.handlerRef,false);}
throttleScrollHandler(dotnetReference,event){clearTimeout(this.throttleScrollHandlerId);this.throttleScrollHandlerId=window.setTimeout(this.scrollHandler.bind(this,dotnetReference,event),100);}
scrollHandler(dotnetReference,event){try{let element=event.target;let scrollTop=element.scrollTop;let scrollHeight=element.scrollHeight;let scrollWidth=element.scrollWidth;let scrollLeft=element.scrollLeft;let nodeName=element.nodeName;let firstChild=element.firstElementChild;let firstChildBoundingClientRect=firstChild.getBoundingClientRect();dotnetReference.invokeMethodAsync('RaiseOnScroll',{firstChildBoundingClientRect,scrollLeft,scrollTop,scrollHeight,scrollWidth,nodeName,});}catch(error){console.log('[MudBlazor] Error in scrollHandler:',{error});}}
cancelListener(selector){let element=selector?document.querySelector(selector):document;element.removeEventListener('scroll',this.handlerRef);}};window.mudScrollListener=new MudScrollListener();class MudWindow{copyToClipboard(text){navigator.clipboard.writeText(text);}
changeCssById(id,css){var element=document.getElementById(id);if(element){element.className=css;}}
updateStyleProperty(elementId,propertyName,value){const element=document.getElementById(elementId);if(element){element.style.setProperty(propertyName,value);}}
changeGlobalCssVariable(name,newValue){document.documentElement.style.setProperty(name,newValue);}
open(args){window.open(args);}};window.mudWindow=new MudWindow();class MudResizeObserverFactory{constructor(){this._maps={};}
connect(id,dotNetRef,elements,elementIds,options){var existingEntry=this._maps[id];if(!existingEntry){var observer=new MudResizeObserver(dotNetRef,options);this._maps[id]=observer;}
var result=this._maps[id].connect(elements,elementIds);return result;}
disconnect(id,element){var existingEntry=this._maps[id];if(existingEntry){existingEntry.disconnect(element);}}
cancelListener(id){var existingEntry=this._maps[id];if(existingEntry){existingEntry.cancelListener();delete this._maps[id];}}}
class MudResizeObserver{constructor(dotNetRef,options){this.logger=options.enableLogging?console.log:(message)=>{};this.options=options;this._dotNetRef=dotNetRef
var delay=(this.options||{}).reportRate||200;this.throttleResizeHandlerId=-1;var observervedElements=[];this._observervedElements=observervedElements;this.logger('[MudBlazor | ResizeObserver] Observer initialized');this._resizeObserver=new ResizeObserver(entries=>{var changes=[];this.logger('[MudBlazor | ResizeObserver] changes detected');for(let entry of entries){var target=entry.target;var affectedObservedElement=observervedElements.find((x)=>x.element==target);if(affectedObservedElement){var size=entry.target.getBoundingClientRect();if(affectedObservedElement.isInitialized==true){changes.push({id:affectedObservedElement.id,size:size});}
else{affectedObservedElement.isInitialized=true;}}}
if(changes.length>0){if(this.throttleResizeHandlerId>=0){clearTimeout(this.throttleResizeHandlerId);}
this.throttleResizeHandlerId=window.setTimeout(this.resizeHandler.bind(this,changes),delay);}});}
resizeHandler(changes){try{this.logger("[MudBlazor | ResizeObserver] OnSizeChanged handler invoked");this._dotNetRef.invokeMethodAsync("OnSizeChanged",changes);}catch(error){this.logger("[MudBlazor | ResizeObserver] Error in OnSizeChanged handler:",{error});}}
connect(elements,ids){var result=[];this.logger('[MudBlazor | ResizeObserver] Start observing elements...');for(var i=0;i<elements.length;i++){var newEntry={element:elements[i],id:ids[i],isInitialized:false,};this.logger("[MudBlazor | ResizeObserver] Start observing element:",{newEntry});result.push(elements[i].getBoundingClientRect());this._observervedElements.push(newEntry);this._resizeObserver.observe(elements[i]);}
return result;}
disconnect(elementId){this.logger('[MudBlazor | ResizeObserver] Try to unobserve element with id',{elementId});var affectedObservedElement=this._observervedElements.find((x)=>x.id==elementId);if(affectedObservedElement){var element=affectedObservedElement.element;this._resizeObserver.unobserve(element);this.logger('[MudBlazor | ResizeObserver] Element found. Ubobserving size changes of element',{element});var index=this._observervedElements.indexOf(affectedObservedElement);this._observervedElements.splice(index,1);}}
cancelListener(){this.logger('[MudBlazor | ResizeObserver] Closing ResizeObserver. Detaching all observed elements');this._resizeObserver.disconnect();this._dotNetRef=undefined;}}
window.mudResizeObserver=new MudResizeObserverFactory();window.mudpopoverHelper={calculatePopoverPosition:function(list,boundingRect,selfRect){let top=0;let left=0;if(list.indexOf('mud-popover-anchor-top-left')>=0){left=boundingRect.left;top=boundingRect.top;}else if(list.indexOf('mud-popover-anchor-top-center')>=0){left=boundingRect.left+boundingRect.width/2;top=boundingRect.top;}else if(list.indexOf('mud-popover-anchor-top-right')>=0){left=boundingRect.left+boundingRect.width;top=boundingRect.top;}else if(list.indexOf('mud-popover-anchor-center-left')>=0){left=boundingRect.left;top=boundingRect.top+boundingRect.height/2;}else if(list.indexOf('mud-popover-anchor-center-center')>=0){left=boundingRect.left+boundingRect.width/2;top=boundingRect.top+boundingRect.height/2;}else if(list.indexOf('mud-popover-anchor-center-right')>=0){left=boundingRect.left+boundingRect.width;top=boundingRect.top+boundingRect.height/2;}else if(list.indexOf('mud-popover-anchor-bottom-left')>=0){left=boundingRect.left;top=boundingRect.top+boundingRect.height;}else if(list.indexOf('mud-popover-anchor-bottom-center')>=0){left=boundingRect.left+boundingRect.width/2;top=boundingRect.top+boundingRect.height;}else if(list.indexOf('mud-popover-anchor-bottom-right')>=0){left=boundingRect.left+boundingRect.width;top=boundingRect.top+boundingRect.height;}
let offsetX=0;let offsetY=0;if(list.indexOf('mud-popover-top-left')>=0){offsetX=0;offsetY=0;}else if(list.indexOf('mud-popover-top-center')>=0){offsetX=-selfRect.width/2;offsetY=0;}else if(list.indexOf('mud-popover-top-right')>=0){offsetX=-selfRect.width;offsetY=0;}
else if(list.indexOf('mud-popover-center-left')>=0){offsetX=0;offsetY=-selfRect.height/2;}else if(list.indexOf('mud-popover-center-center')>=0){offsetX=-selfRect.width/2;offsetY=-selfRect.height/2;}else if(list.indexOf('mud-popover-center-right')>=0){offsetX=-selfRect.width;offsetY=-selfRect.height/2;}
else if(list.indexOf('mud-popover-bottom-left')>=0){offsetX=0;offsetY=-selfRect.height;}else if(list.indexOf('mud-popover-bottom-center')>=0){offsetX=-selfRect.width/2;offsetY=-selfRect.height;}else if(list.indexOf('mud-popover-bottom-right')>=0){offsetX=-selfRect.width;offsetY=-selfRect.height;}
return{top:top,left:left,offsetX:offsetX,offsetY:offsetY};},flipClassReplacements:{'top':{'mud-popover-top-left':'mud-popover-bottom-left','mud-popover-top-center':'mud-popover-bottom-center','mud-popover-anchor-bottom-center':'mud-popover-anchor-top-center','mud-popover-top-right':'mud-popover-bottom-right',},'left':{'mud-popover-top-left':'mud-popover-top-right','mud-popover-center-left':'mud-popover-center-right','mud-popover-anchor-center-right':'mud-popover-anchor-center-left','mud-popover-bottom-left':'mud-popover-bottom-right',},'right':{'mud-popover-top-right':'mud-popover-top-left','mud-popover-center-right':'mud-popover-center-left','mud-popover-anchor-center-left':'mud-popover-anchor-center-right','mud-popover-bottom-right':'mud-popover-bottom-left',},'bottom':{'mud-popover-bottom-left':'mud-popover-top-left','mud-popover-bottom-center':'mud-popover-top-center','mud-popover-anchor-top-center':'mud-popover-anchor-bottom-center','mud-popover-bottom-right':'mud-popover-top-right',},'top-and-left':{'mud-popover-top-left':'mud-popover-bottom-right',},'top-and-right':{'mud-popover-top-right':'mud-popover-bottom-left',},'bottom-and-left':{'mud-popover-bottom-left':'mud-popover-top-right',},'bottom-and-right':{'mud-popover-bottom-right':'mud-popover-top-left',},},flipMargin:0,getPositionForFlippedPopver:function(inputArray,selector,boundingRect,selfRect){const classList=[];for(var i=0;i<inputArray.length;i++){const item=inputArray[i];const replacments=window.mudpopoverHelper.flipClassReplacements[selector][item];if(replacments){classList.push(replacments);}
else{classList.push(item);}}
return window.mudpopoverHelper.calculatePopoverPosition(classList,boundingRect,selfRect);},placePopover:function(popoverNode,classSelector){if(popoverNode&&popoverNode.parentNode){const id=popoverNode.id.substr(8);const popoverContentNode=document.getElementById('popovercontent-'+id);if(!popoverContentNode){return;}
if(popoverContentNode.classList.contains('mud-popover-open')==false){return;}
if(classSelector){if(popoverContentNode.classList.contains(classSelector)==false){return;}}
const boundingRect=popoverNode.parentNode.getBoundingClientRect();if(popoverContentNode.classList.contains('mud-popover-relative-width')){popoverContentNode.style['max-width']=(boundingRect.width)+'px';}
const selfRect=popoverContentNode.getBoundingClientRect();const classList=popoverContentNode.classList;const classListArray=Array.from(popoverContentNode.classList);const postion=window.mudpopoverHelper.calculatePopoverPosition(classListArray,boundingRect,selfRect);let left=postion.left;let top=postion.top;let offsetX=postion.offsetX;let offsetY=postion.offsetY;if(classList.contains('mud-popover-overflow-flip-onopen')||classList.contains('mud-popover-overflow-flip-always')){const appBarElements=document.getElementsByClassName("mud-appbar mud-appbar-fixed-top");let appBarOffset=0;if(appBarElements.length>0){appBarOffset=appBarElements[0].getBoundingClientRect().height;}
const graceMargin=window.mudpopoverHelper.flipMargin;const deltaToLeft=left+offsetX;const deltaToRight=window.innerWidth-left-selfRect.width;const deltaTop=top-selfRect.height-appBarOffset;const spaceToTop=top-appBarOffset;const deltaBottom=window.innerHeight-top-selfRect.height;let selector=popoverContentNode.mudPopoverFliped;if(!selector){if(classList.contains('mud-popover-top-left')){if(deltaBottom<graceMargin&&deltaToRight<graceMargin&&spaceToTop>=selfRect.height&&deltaToLeft>=selfRect.width){selector='top-and-left';}else if(deltaBottom<graceMargin&&spaceToTop>=selfRect.height){selector='top';}else if(deltaToRight<graceMargin&&deltaToLeft>=selfRect.width){selector='left';}}else if(classList.contains('mud-popover-top-center')){if(deltaBottom<graceMargin&&spaceToTop>=selfRect.height){selector='top';}}else if(classList.contains('mud-popover-top-right')){if(deltaBottom<graceMargin&&deltaToLeft<graceMargin&&spaceToTop>=selfRect.height&&deltaToRight>=selfRect.width){selector='top-and-right';}else if(deltaBottom<graceMargin&&spaceToTop>=selfRect.height){selector='top';}else if(deltaToLeft<graceMargin&&deltaToRight>=selfRect.width){selector='right';}}
else if(classList.contains('mud-popover-center-left')){if(deltaToRight<graceMargin&&deltaToLeft>=selfRect.width){selector='left';}}
else if(classList.contains('mud-popover-center-right')){if(deltaToLeft<graceMargin&&deltaToRight>=selfRect.width){selector='right';}}
else if(classList.contains('mud-popover-bottom-left')){if(deltaTop<graceMargin&&deltaToRight<graceMargin&&deltaBottom>=0&&deltaToLeft>=selfRect.width){selector='bottom-and-left';}else if(deltaTop<graceMargin&&deltaBottom>=0){selector='bottom';}else if(deltaToRight<graceMargin&&deltaToLeft>=selfRect.width){selector='left';}}else if(classList.contains('mud-popover-bottom-center')){if(deltaTop<graceMargin&&deltaBottom>=0){selector='bottom';}}else if(classList.contains('mud-popover-bottom-right')){if(deltaTop<graceMargin&&deltaToLeft<graceMargin&&deltaBottom>=0&&deltaToRight>=selfRect.width){selector='bottom-and-right';}else if(deltaTop<graceMargin&&deltaBottom>=0){selector='bottom';}else if(deltaToLeft<graceMargin&&deltaToRight>=selfRect.width){selector='right';}}}
if(selector&&selector!='none'){const newPosition=window.mudpopoverHelper.getPositionForFlippedPopver(classListArray,selector,boundingRect,selfRect);left=newPosition.left;top=newPosition.top;offsetX=newPosition.offsetX;offsetY=newPosition.offsetY;popoverContentNode.setAttribute('data-mudpopover-flip','flipped');}
else{popoverContentNode.removeAttribute('data-mudpopover-flip');}
if(classList.contains('mud-popover-overflow-flip-onopen')){if(!popoverContentNode.mudPopoverFliped){popoverContentNode.mudPopoverFliped=selector||'none';}}}
if(popoverContentNode.classList.contains('mud-popover-fixed')){}
else if(window.getComputedStyle(popoverNode).position=='fixed'){popoverContentNode.style['position']='fixed';}
else{offsetX+=window.scrollX;offsetY+=window.scrollY}
popoverContentNode.style['left']=(left+offsetX)+'px';popoverContentNode.style['top']=(top+offsetY)+'px';if(window.getComputedStyle(popoverNode).getPropertyValue('z-index')!='auto'){popoverContentNode.style['z-index']=window.getComputedStyle(popoverNode).getPropertyValue('z-index');popoverContentNode.skipZIndex=true;}}},placePopoverByClassSelector:function(classSelector=null){var items=window.mudPopover.getAllObservedContainers();for(let i=0;i<items.length;i++){const popoverNode=document.getElementById('popover-'+items[i]);window.mudpopoverHelper.placePopover(popoverNode,classSelector);}},placePopoverByNode:function(target){const id=target.id.substr(15);const popoverNode=document.getElementById('popover-'+id);window.mudpopoverHelper.placePopover(popoverNode);},countProviders:function(){return document.querySelectorAll(".mud-popover-provider").length;}}
class MudPopover{constructor(){this.map={};this.contentObserver=null;this.mainContainerClass=null;}
callback(id,mutationsList,observer){for(const mutation of mutationsList){if(mutation.type==='attributes'){const target=mutation.target
if(mutation.attributeName=='class'){if(target.classList.contains('mud-popover-overflow-flip-onopen')&&target.classList.contains('mud-popover-open')==false){target.mudPopoverFliped=null;target.removeAttribute('data-mudpopover-flip');}
window.mudpopoverHelper.placePopoverByNode(target);}
else if(mutation.attributeName=='data-ticks'){const tickAttribute=target.getAttribute('data-ticks');const parent=target.parentElement;const tickValues=[];let max=-1;if(parent){for(let i=0;i<parent.children.length;i++){const childNode=parent.children[i];const tickValue=parseInt(childNode.getAttribute('data-ticks'));if(tickValue==0){continue;}
if(tickValues.indexOf(tickValue)>=0){continue;}
tickValues.push(tickValue);if(tickValue>max){max=tickValue;}}}
if(tickValues.length==0){continue;}
const sortedTickValues=tickValues.sort((x,y)=>x-y);for(let i=0;i<parent.children.length;i++){const childNode=parent.children[i];const tickValue=parseInt(childNode.getAttribute('data-ticks'));if(tickValue==0){continue;}
if(childNode.skipZIndex==true){continue;}
childNode.style['z-index']='calc(var(--mud-zindex-popover) + '+(sortedTickValues.indexOf(tickValue)+3).toString()+')';}}}}}
initialize(containerClass,flipMargin){const mainContent=document.getElementsByClassName(containerClass);if(mainContent.length==0){return;}
if(flipMargin){window.mudpopoverHelper.flipMargin=flipMargin;}
this.mainContainerClass=containerClass;if(!mainContent[0].mudPopoverMark){mainContent[0].mudPopoverMark="mudded";if(this.contentObserver!=null){this.contentObserver.disconnect();this.contentObserver=null;}
this.contentObserver=new ResizeObserver(entries=>{window.mudpopoverHelper.placePopoverByClassSelector();});this.contentObserver.observe(mainContent[0]);}}
connect(id){this.initialize(this.mainContainerClass);const popoverNode=document.getElementById('popover-'+id);const popoverContentNode=document.getElementById('popovercontent-'+id);if(popoverNode&&popoverNode.parentNode&&popoverContentNode){window.mudpopoverHelper.placePopover(popoverNode);const config={attributeFilter:['class','data-ticks']};const observer=new MutationObserver(this.callback.bind(this,id));observer.observe(popoverContentNode,config);const resizeObserver=new ResizeObserver(entries=>{for(let entry of entries){const target=entry.target;for(var i=0;i<target.childNodes.length;i++){const childNode=target.childNodes[i];if(childNode.id&&childNode.id.startsWith('popover-')){window.mudpopoverHelper.placePopover(childNode);}}}});resizeObserver.observe(popoverNode.parentNode);const contentNodeObserver=new ResizeObserver(entries=>{for(let entry of entries){var target=entry.target;window.mudpopoverHelper.placePopoverByNode(target);}});contentNodeObserver.observe(popoverContentNode);this.map[id]={mutationObserver:observer,resizeObserver:resizeObserver,contentNodeObserver:contentNodeObserver};}}
disconnect(id){if(this.map[id]){const item=this.map[id]
item.mutationObserver.disconnect();item.resizeObserver.disconnect();item.contentNodeObserver.disconnect();delete this.map[id];}}
dispose(){for(var i in this.map){disconnect(i);}
this.contentObserver.disconnect();this.contentObserver=null;}
getAllObservedContainers(){const result=[];for(var i in this.map){result.push(i);}
return result;}}
window.mudPopover=new MudPopover();window.addEventListener('scroll',()=>{window.mudpopoverHelper.placePopoverByClassSelector('mud-popover-fixed');window.mudpopoverHelper.placePopoverByClassSelector('mud-popover-overflow-flip-always');});window.addEventListener('resize',()=>{window.mudpopoverHelper.placePopoverByClassSelector();});window.mudInputAutoGrow={initAutoGrow:(elem,maxLines)=>{const compStyle=getComputedStyle(elem);const lineHeight=parseFloat(compStyle.getPropertyValue('line-height'));let maxHeight=0;elem.updateParameters=function(newMaxLines){if(newMaxLines>0){maxHeight=lineHeight*newMaxLines;}else{maxHeight=0;}}
elem.adjustAutoGrowHeight=function(didReflow=false){const scrollTops=[];let curElem=elem;while(curElem&&curElem.parentNode&&curElem.parentNode instanceof Element){if(curElem.parentNode.scrollTop){scrollTops.push([curElem.parentNode,curElem.parentNode.scrollTop]);}
curElem=curElem.parentNode;}
elem.style.height=0;if(didReflow){elem.style.textAlign=null;}
let minHeight=lineHeight*elem.rows;let newHeight=Math.max(minHeight,elem.scrollHeight);let initialOverflowY=elem.style.overflowY;if(maxHeight>0&&newHeight>maxHeight){elem.style.overflowY='auto';newHeight=maxHeight;}else{elem.style.overflowY='hidden';}
elem.style.height=newHeight+"px";scrollTops.forEach(([node,scrollTop])=>{node.style.scrollBehavior='auto';node.scrollTop=scrollTop;node.style.scrollBehavior=null;});if(!didReflow&&initialOverflowY!==elem.style.overflowY&&elem.style.overflowY==='hidden'){elem.style.textAlign='end';elem.adjustAutoGrowHeight(true);}}
elem.restoreToInitialState=function(){elem.removeEventListener('input',elem.adjustAutoGrowHeight);elem.style.overflowY=null;elem.style.height=null;}
elem.addEventListener('input',elem.adjustAutoGrowHeight);window.addEventListener('resize',elem.adjustAutoGrowHeight);elem.updateParameters(maxLines);elem.adjustAutoGrowHeight();},adjustHeight:(elem)=>{if(typeof elem.adjustAutoGrowHeight==='function'){elem.adjustAutoGrowHeight();}},updateParams:(elem,maxLines)=>{if(typeof elem.updateParameters==='function'){elem.updateParameters(maxLines);}
if(typeof elem.adjustAutoGrowHeight==='function'){elem.adjustAutoGrowHeight();}},destroy:(elem)=>{window.removeEventListener('resize',elem.adjustAutoGrowHeight);if(typeof elem.restoreToInitialState==='function'){elem.restoreToInitialState();}}}
class MudScrollSpy{constructor(){this.scrollToSectionRequested=null;this.lastKnowElement=null;this.handlerRef=null;}
spying(dotnetReference,selector){this.scrollToSectionRequested=null;this.lastKnowElement=null;this.handlerRef=this.handleScroll.bind(this,selector,dotnetReference);document.addEventListener('scroll',this.handlerRef,true);window.addEventListener('resize',this.handlerRef,true);}
handleScroll(dotnetReference,selector,event){const elements=document.getElementsByClassName(selector);if(elements.length===0){return;}
const center=window.innerHeight/2.0;let minDifference=Number.MAX_SAFE_INTEGER;let elementId='';for(let i=0;i<elements.length;i++){const element=elements[i];const rect=element.getBoundingClientRect();const diff=Math.abs(rect.top-center);if(diff<minDifference){minDifference=diff;elementId=element.id;}}
if(document.getElementById(elementId).getBoundingClientRect().top<window.innerHeight*0.8===false){return;}
if(this.scrollToSectionRequested!=null){if(this.scrollToSectionRequested==' '&&window.scrollY==0){this.scrollToSectionRequested=null;}
else{if(elementId===this.scrollToSectionRequested){this.scrollToSectionRequested=null;}}
return;}
if(elementId!=this.lastKnowElement){this.lastKnowElement=elementId;history.replaceState(null,'',window.location.pathname+"#"+elementId);dotnetReference.invokeMethodAsync('SectionChangeOccured',elementId);}}
activateSection(sectionId){const element=document.getElementById(sectionId);if(element){this.lastKnowElement=sectionId;history.replaceState(null,'',window.location.pathname+"#"+sectionId);}}
scrollToSection(sectionId){if(sectionId){let element=document.getElementById(sectionId);if(element){this.scrollToSectionRequested=sectionId;element.scrollIntoView({behavior:'smooth',block:'center',inline:'start'});}}
else{window.scrollTo({top:0,behavior:'smooth'});this.scrollToSectionRequested=' ';}}
unspy(){document.removeEventListener('scroll',this.handlerRef,true);window.removeEventListener('resize',this.handlerRef,true);}};window.mudScrollSpy=new MudScrollSpy();class MudThrottledEventManager{constructor(){this.mapper={};}
subscribe(eventName,elementId,projection,throotleInterval,key,properties,dotnetReference){const handlerRef=this.throttleEventHandler.bind(this,key);let elem=document.getElementById(elementId);if(elem){elem.addEventListener(eventName,handlerRef,false);let projector=null;if(projection){const parts=projection.split('.');let functionPointer=window;let functionReferenceFound=true;if(parts.length==0||parts.length==1){functionPointer=functionPointer[projection];}
else{for(let i=0;i<parts.length;i++){functionPointer=functionPointer[parts[i]];if(!functionPointer){functionReferenceFound=false;break;}}}
if(functionReferenceFound===true){projector=functionPointer;}}
this.mapper[key]={eventName:eventName,handler:handlerRef,delay:throotleInterval,timerId:-1,reference:dotnetReference,elementId:elementId,properties:properties,projection:projector,};}}
subscribeGlobal(eventName,throotleInterval,key,properties,dotnetReference){let handlerRef=throotleInterval>0?this.throttleEventHandler.bind(this,key):this.eventHandler.bind(this,key);document.addEventListener(eventName,handlerRef,false);this.mapper[key]={eventName:eventName,handler:handlerRef,delay:throotleInterval,timerId:-1,reference:dotnetReference,elementId:document,properties:properties,projection:null,};}
throttleEventHandler(key,event){const entry=this.mapper[key];if(!entry){return;}
clearTimeout(entry.timerId);entry.timerId=window.setTimeout(this.eventHandler.bind(this,key,event),entry.delay);}
eventHandler(key,event){const entry=this.mapper[key];if(!entry){return;}
var elem=document.getElementById(entry.elementId);if(elem!=event.srcElement&&entry.elementId!=document){return;}
const eventEntry={};for(var i=0;i<entry.properties.length;i++){eventEntry[entry.properties[i]]=event[entry.properties[i]];}
if(entry.projection){if(typeof entry.projection==="function"){entry.projection.apply(null,[eventEntry,event]);}}
entry.reference.invokeMethodAsync('OnEventOccur',key,JSON.stringify(eventEntry));}
unsubscribe(key){const entry=this.mapper[key];if(!entry){return;}
entry.reference=null;if(document==entry.elementId){document.removeEventListener(entry.eventName,entry.handler,false);}else{const elem=document.getElementById(entry.elementId);if(elem){elem.removeEventListener(entry.eventName,entry.handler,false);}}
delete this.mapper[key];}};window.mudThrottledEventManager=new MudThrottledEventManager();window.mudEventProjections={correctOffset:function(eventEntry,event){var target=event.target.getBoundingClientRect();eventEntry.offsetX=event.clientX-target.x;eventEntry.offsetY=event.clientY-target.y;}};window.mudDragAndDrop={initDropZone:(id)=>{const elem=document.getElementById('mud-drop-zone-'+id);elem.addEventListener('dragover',()=>event.preventDefault());elem.addEventListener('dragstart',()=>event.dataTransfer.setData('',event.target.id));},makeDropZonesNotRelative:()=>{var firstDropItems=Array.from(document.getElementsByClassName('mud-drop-item')).filter(x=>x.getAttribute('index')=="-1");for(let dropItem of firstDropItems){dropItem.style.position='static';}
window.mudDragAndDrop={initDropZone:(id)=>{const elem=document.getElementById('mud-drop-zone-'+id);elem.addEventListener('dragover',()=>event.preventDefault());elem.addEventListener('dragstart',()=>event.dataTransfer.setData('',event.target.id));},makeDropZonesNotRelative:()=>{var firstDropItems=Array.from(document.getElementsByClassName('mud-drop-item')).filter(x=>x.getAttribute('index')=="-1");for(let dropItem of firstDropItems){dropItem.style.position='static';}
const dropZones=document.getElementsByClassName('mud-drop-zone');for(let dropZone of dropZones){dropZone.style.position='unset';}},getDropZoneIdentifierOnPosition:(x,y)=>{const elems=document.elementsFromPoint(x,y);const dropZones=elems.filter(e=>e.classList.contains('mud-drop-zone'))
const dropZone=dropZones[0];if(dropZone){return dropZone.getAttribute('identifier')||"";}
return"";},getDropIndexOnPosition:(x,y,id)=>{const elems=document.elementsFromPoint(x,y);const dropItems=elems.filter(e=>e.classList.contains('mud-drop-item')&&e.id!=('mud-drop-item-'+id))
const dropItem=dropItems[0];if(dropItem){return dropItem.getAttribute('index')||"";}
return"";},makeDropZonesRelative:()=>{const dropZones=document.getElementsByClassName('mud-drop-zone');for(let dropZone of dropZones){dropZone.style.position='relative';}
var firstDropItems=Array.from(document.getElementsByClassName('mud-drop-item')).filter(x=>x.getAttribute('index')=="-1");for(let dropItem of firstDropItems){dropItem.style.position='relative';}},moveItemByDifference:(id,dx,dy)=>{const elem=document.getElementById('mud-drop-item-'+id);var tx=(parseFloat(elem.getAttribute('data-x'))||0)+dx;var ty=(parseFloat(elem.getAttribute('data-y'))||0)+dy;elem.style.webkitTransform=elem.style.transform='translate3d('+tx+'px, '+ty+'px, 10px)';elem.setAttribute('data-x',tx);elem.setAttribute('data-y',ty);},resetItem:(id)=>{const elem=document.getElementById('mud-drop-item-'+id);if(elem){elem.style.webkitTransform=elem.style.transform='';elem.setAttribute('data-x',0);elem.setAttribute('data-y',0);}}};function setRippleOffset(event,target){const rect=target.getBoundingClientRect();const x=event.clientX-rect.left-rect.width/2;const y=event.clientY-rect.top-rect.height/2;target.style.setProperty("--mud-ripple-offset-x",`${x}px`);target.style.setProperty("--mud-ripple-offset-y",`${y}px`);}
document.addEventListener("click",function(event){const target=event.target.closest(".mud-ripple");if(target){setRippleOffset(event,target);}});class MudElementReference{constructor(){this.listenerId=0;this.eventListeners={};}
focus(element){if(element)
{element.focus();}}
blur(element){if(element){element.blur();}}
focusFirst(element,skip=0,min=0){if(element)
{let tabbables=getTabbableElements(element);if(tabbables.length<=min)
element.focus();else
tabbables[skip].focus();}}
focusLast(element,skip=0,min=0){if(element)
{let tabbables=getTabbableElements(element);if(tabbables.length<=min)
element.focus();else
tabbables[tabbables.length-skip-1].focus();}}
saveFocus(element){if(element)
{element['mudblazor_savedFocus']=document.activeElement;}}
restoreFocus(element){if(element)
{let previous=element['mudblazor_savedFocus'];delete element['mudblazor_savedFocus']
if(previous)
previous.focus();}}
selectRange(element,pos1,pos2){if(element)
{if(element.createTextRange){let selRange=element.createTextRange();selRange.collapse(true);selRange.moveStart('character',pos1);selRange.moveEnd('character',pos2);selRange.select();}else if(element.setSelectionRange){element.setSelectionRange(pos1,pos2);}else if(element.selectionStart){element.selectionStart=pos1;element.selectionEnd=pos2;}
element.focus();}}
select(element){if(element)
{element.select();}}
getBoundingClientRect(element){if(!element)return;var rect=JSON.parse(JSON.stringify(element.getBoundingClientRect()));rect.scrollY=window.scrollY||document.documentElement.scrollTop;rect.scrollX=window.scrollX||document.documentElement.scrollLeft;rect.windowHeight=window.innerHeight;rect.windowWidth=window.innerWidth;return rect;}
changeCss(element,css){if(element)
{element.className=css;}}
removeEventListener(element,event,eventId){element.removeEventListener(event,this.eventListeners[eventId]);delete this.eventListeners[eventId];}
addDefaultPreventingHandler(element,eventName){let listener=function(e){e.preventDefault();}
element.addEventListener(eventName,listener,{passive:false});this.eventListeners[++this.listenerId]=listener;return this.listenerId;}
removeDefaultPreventingHandler(element,eventName,listenerId){this.removeEventListener(element,eventName,listenerId);}
addDefaultPreventingHandlers(element,eventNames){let listeners=[];for(const eventName of eventNames){let listenerId=this.addDefaultPreventingHandler(element,eventName);listeners.push(listenerId);}
return listeners;}
removeDefaultPreventingHandlers(element,eventNames,listenerIds){for(let index=0;index<eventNames.length;++index){const eventName=eventNames[index];const listenerId=listenerIds[index];this.removeDefaultPreventingHandler(element,eventName,listenerId);}}};window.mudElementRef=new MudElementReference();class MudResizeListener{constructor(id){this.logger=function(message){};this.options={};this.throttleResizeHandlerId=-1;this.dotnet=undefined;this.breakpoint=-1;this.id=id;this.handleResize=this.throttleResizeHandler.bind(this);}
listenForResize(dotnetRef,options){if(this.dotnet){this.options=options;return;}
this.options=options;this.dotnet=dotnetRef;this.logger=options.enableLogging?console.log:(message)=>{};this.logger(`[MudBlazor]Reporting resize events at rate of:${(this.options||{}).reportRate||100}ms`);window.addEventListener("resize",this.handleResize,false);if(!this.options.suppressInitEvent){this.resizeHandler();}
this.breakpoint=this.getBreakpoint(window.innerWidth);}
throttleResizeHandler(){clearTimeout(this.throttleResizeHandlerId);this.throttleResizeHandlerId=window.setTimeout(this.resizeHandler.bind(this),((this.options||{}).reportRate||100));}
resizeHandler(){if(this.options.notifyOnBreakpointOnly){let bp=this.getBreakpoint(window.innerWidth);if(bp==this.breakpoint){return;}
this.breakpoint=bp;}
try{if(this.id){this.dotnet.invokeMethodAsync('RaiseOnResized',{height:window.innerHeight,width:window.innerWidth},this.getBreakpoint(window.innerWidth),this.id);}
else{this.dotnet.invokeMethodAsync('RaiseOnResized',{height:window.innerHeight,width:window.innerWidth},this.getBreakpoint(window.innerWidth));}}catch(error){this.logger("[MudBlazor] Error in resizeHandler:",{error});}}
cancelListener(){this.dotnet=undefined;window.removeEventListener("resize",this.handleResize);}
matchMedia(query){let m=window.matchMedia(query).matches;return m;}
getBrowserWindowSize(){return{height:window.innerHeight,width:window.innerWidth};}
getBreakpoint(width){if(width>=this.options.breakpointDefinitions["Xxl"])
return 5;else if(width>=this.options.breakpointDefinitions["Xl"])
return 4;else if(width>=this.options.breakpointDefinitions["Lg"])
return 3;else if(width>=this.options.breakpointDefinitions["Md"])
return 2;else if(width>=this.options.breakpointDefinitions["Sm"])
return 1;else
return 0;}};window.mudResizeListener=new MudResizeListener();window.mudResizeListenerFactory={mapping:{},listenForResize:(dotnetRef,options,id)=>{var map=window.mudResizeListenerFactory.mapping;if(map[id]){return;}
var listener=new MudResizeListener(id);listener.listenForResize(dotnetRef,options);map[id]=listener;},cancelListener:(id)=>{var map=window.mudResizeListenerFactory.mapping;if(!map[id]){return;}
var listener=map[id];listener.cancelListener();delete map[id];},cancelListeners:(ids)=>{for(let i=0;i<ids.length;i++){window.mudResizeListenerFactory.cancelListener(ids[i]);}},dispose(){var map=window.mudResizeListenerFactory.mapping;for(var id in map){window.mudResizeListenerFactory.cancelListener(id);}}}
class MudScrollManager{scrollToYear(elementId,offset){let element=document.getElementById(elementId);if(element){element.parentNode.scrollTop=element.offsetTop-element.parentNode.offsetTop-element.scrollHeight*3;}}
scrollToListItem(elementId){let element=document.getElementById(elementId);if(element){let parent=element.parentElement;if(parent){parent.scrollTop=element.offsetTop;}}}
scrollTo(selector,left,top,behavior){let element=document.querySelector(selector)||document.documentElement;element.scrollTo({left,top,behavior});}
scrollIntoView(selector,behavior){let element=document.querySelector(selector)||document.documentElement;if(element)
element.scrollIntoView({behavior,block:'center',inline:'start'});}
scrollToBottom(selector,behavior){let element=document.querySelector(selector);if(element)
element.scrollTop=element.scrollHeight;else
window.scrollTo(0,document.body.scrollHeight);}
lockScroll(selector,lockclass){let element=document.querySelector(selector)||document.body;let hasScrollBar=window.innerWidth>document.body.clientWidth;if(hasScrollBar){element.classList.add(lockclass);}else{let lockClassNoPadding=lockclass+"-no-padding";element.classList.add(lockClassNoPadding);}}
unlockScroll(selector,lockclass){let element=document.querySelector(selector)||document.body;element.classList.remove(lockclass);element.classList.remove(lockclass+"-no-padding");}};window.mudScrollManager=new MudScrollManager();class MudKeyInterceptorFactory{connect(dotNetRef,elementId,options){if(!elementId)
var firstDropItems=Array.from(document.getElementsByClassName('mud-drop-item')).filter(x=>x.getAttribute('index')=="-1");for(let dropItem of firstDropItems){dropItem.style.position='relative';}},moveItemByDifference:(id,dx,dy)=>{const elem=document.getElementById('mud-drop-item-'+id);var tx=(parseFloat(elem.getAttribute('data-x'))||0)+dx;var ty=(parseFloat(elem.getAttribute('data-y'))||0)+dy;elem.style.webkitTransform=elem.style.transform='translate3d('+tx+'px, '+ty+'px, 10px)';elem.setAttribute('data-x',tx);elem.setAttribute('data-y',ty);},resetItem:(id)=>{const elem=document.getElementById('mud-drop-item-'+id);if(elem){elem.style.webkitTransform=elem.style.transform='';elem.setAttribute('data-x',0);elem.setAttribute('data-y',0);}}};class MudKeyInterceptorFactory{connect(dotNetRef,elementId,options){if(!elementId)
throw"elementId: expected element id!";var element=document.getElementById(elementId);if(!element)
throw"no element found for id: "+elementId;if(!element.mudKeyInterceptor)
element.mudKeyInterceptor=new MudKeyInterceptor(dotNetRef,options);element.mudKeyInterceptor.connect(element);}
@@ -275,4 +59,220 @@ processKeyUp(args,keyOptions){if(this.matchesKeyCombination(keyOptions.preventUp
args.preventDefault();if(this.matchesKeyCombination(keyOptions.stopUp,args))
args.stopPropagation();}
toKeyboardEventArgs(args){return{Key:args.key,Code:args.code,Location:args.location,Repeat:args.repeat,CtrlKey:args.ctrlKey,ShiftKey:args.shiftKey,AltKey:args.altKey,MetaKey:args.metaKey};}}
const darkThemeMediaQuery=window.matchMedia("(prefers-color-scheme: dark)");window.darkModeChange=(dotNetHelper)=>{return darkThemeMediaQuery.matches;};function watchDarkThemeMedia(dotNetHelper){dotNetHelperTheme=dotNetHelper;window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change',function(e){dotNetHelperTheme.invokeMethodAsync('SystemPreferenceChanged',window.matchMedia("(prefers-color-scheme: dark)").matches);});}
class MudThrottledEventManager{constructor(){this.mapper={};}
subscribe(eventName,elementId,projection,throotleInterval,key,properties,dotnetReference){const handlerRef=this.throttleEventHandler.bind(this,key);let elem=document.getElementById(elementId);if(elem){elem.addEventListener(eventName,handlerRef,false);let projector=null;if(projection){const parts=projection.split('.');let functionPointer=window;let functionReferenceFound=true;if(parts.length==0||parts.length==1){functionPointer=functionPointer[projection];}
else{for(let i=0;i<parts.length;i++){functionPointer=functionPointer[parts[i]];if(!functionPointer){functionReferenceFound=false;break;}}}
if(functionReferenceFound===true){projector=functionPointer;}}
this.mapper[key]={eventName:eventName,handler:handlerRef,delay:throotleInterval,timerId:-1,reference:dotnetReference,elementId:elementId,properties:properties,projection:projector,};}}
subscribeGlobal(eventName,throotleInterval,key,properties,dotnetReference){let handlerRef=throotleInterval>0?this.throttleEventHandler.bind(this,key):this.eventHandler.bind(this,key);document.addEventListener(eventName,handlerRef,false);this.mapper[key]={eventName:eventName,handler:handlerRef,delay:throotleInterval,timerId:-1,reference:dotnetReference,elementId:document,properties:properties,projection:null,};}
throttleEventHandler(key,event){const entry=this.mapper[key];if(!entry){return;}
clearTimeout(entry.timerId);entry.timerId=window.setTimeout(this.eventHandler.bind(this,key,event),entry.delay);}
eventHandler(key,event){const entry=this.mapper[key];if(!entry){return;}
var elem=document.getElementById(entry.elementId);if(elem!=event.srcElement&&entry.elementId!=document){return;}
const eventEntry={};for(var i=0;i<entry.properties.length;i++){eventEntry[entry.properties[i]]=event[entry.properties[i]];}
if(entry.projection){if(typeof entry.projection==="function"){entry.projection.apply(null,[eventEntry,event]);}}
entry.reference.invokeMethodAsync('OnEventOccur',key,JSON.stringify(eventEntry));}
unsubscribe(key){const entry=this.mapper[key];if(!entry){return;}
entry.reference=null;if(document==entry.elementId){document.removeEventListener(entry.eventName,entry.handler,false);}else{const elem=document.getElementById(entry.elementId);if(elem){elem.removeEventListener(entry.eventName,entry.handler,false);}}
delete this.mapper[key];}};window.mudThrottledEventManager=new MudThrottledEventManager();window.mudEventProjections={correctOffset:function(eventEntry,event){var target=event.target.getBoundingClientRect();eventEntry.offsetX=event.clientX-target.x;eventEntry.offsetY=event.clientY-target.y;}};class MudJsEventFactory{connect(dotNetRef,elementId,options){if(!elementId)
throw"[MudBlazor | JsEvent] elementId: expected element id!";var element=document.getElementById(elementId);if(!element)
throw"[MudBlazor | JsEvent] no element found for id: "+elementId;if(!element.mudJsEvent)
element.mudJsEvent=new MudJsEvent(dotNetRef,options);element.mudJsEvent.connect(element);}
disconnect(elementId){var element=document.getElementById(elementId);if(!element||!element.mudJsEvent)
return;element.mudJsEvent.disconnect();}
subscribe(elementId,eventName){if(!elementId)
throw"[MudBlazor | JsEvent] elementId: expected element id!";var element=document.getElementById(elementId);if(!element)
throw"[MudBlazor | JsEvent] no element found for id: "+elementId;if(!element.mudJsEvent)
throw"[MudBlazor | JsEvent] please connect before subscribing"
element.mudJsEvent.subscribe(eventName);}
unsubscribe(elementId,eventName){var element=document.getElementById(elementId);if(!element||!element.mudJsEvent)
return;element.mudJsEvent.unsubscribe(element,eventName);}}
window.mudJsEvent=new MudJsEventFactory();class MudJsEvent{constructor(dotNetRef,options){this._dotNetRef=dotNetRef;this._options=options||{};this.logger=options.enableLogging?console.log:(message)=>{};this.logger('[MudBlazor | JsEvent] Initialized',{options});this._subscribedEvents={};}
connect(element){if(!this._options)
return;if(!this._options.targetClass)
throw"_options.targetClass: css class name expected";if(this._observer){return;}
var targetClass=this._options.targetClass;this.logger('[MudBlazor | JsEvent] Start observing DOM of element for changes to child with class ',{element,targetClass});this._element=element;this._observer=new MutationObserver(this.onDomChanged);this._observer.mudJsEvent=this;this._observer.observe(this._element,{attributes:false,childList:true,subtree:true});this._observedChildren=[];}
disconnect(){if(!this._observer)
return;this.logger('[MudBlazor | JsEvent] disconnect mutation observer and event handler ');this._observer.disconnect();this._observer=null;for(const child of this._observedChildren)
this.detachHandlers(child);}
subscribe(eventName){if(this._subscribedEvents[eventName]){return;}
var element=this._element;var targetClass=this._options.targetClass;this._subscribedEvents[eventName]=true;for(const child of element.getElementsByClassName(targetClass)){this.attachHandlers(child);}}
unsubscribe(eventName){if(!this._observer)
return;this.logger('[MudBlazor | JsEvent] unsubscribe event handler '+eventName);this._observer.disconnect();this._observer=null;this._subscribedEvents[eventName]=false;for(const child of this._observedChildren){this.detachHandler(child,eventName);}}
attachHandlers(child){child.mudJsEvent=this;for(var eventName of Object.getOwnPropertyNames(this._subscribedEvents)){if(!this._subscribedEvents[eventName])
continue;this.logger('[MudBlazor | JsEvent] attaching event '+eventName,child);child.addEventListener(eventName,this.eventHandler);}
if(this._observedChildren.indexOf(child)<0)
this._observedChildren.push(child);}
detachHandler(child,eventName){this.logger('[MudBlazor | JsEvent] detaching handler '+eventName,child);child.removeEventListener(eventName,this.eventHandler);}
detachHandlers(child){this.logger('[MudBlazor | JsEvent] detaching handlers ',child);for(var eventName of Object.getOwnPropertyNames(this._subscribedEvents)){if(!this._subscribedEvents[eventName])
continue;child.removeEventListener(eventName,this.eventHandler);}
this._observedChildren=this._observedChildren.filter(x=>x!==child);}
onDomChanged(mutationsList,observer){var self=this.mudJsEvent;var targetClass=self._options.targetClass;for(const mutation of mutationsList){for(const element of mutation.addedNodes){if(element.classList&&element.classList.contains(targetClass)){if(!self._options.TagName||element.tagName==self._options.TagName)
self.attachHandlers(element);}}
for(const element of mutation.removedNodes){if(element.classList&&element.classList.contains(targetClass)){if(!self._options.tagName||element.tagName==self._options.tagName)
self.detachHandlers(element);}}}}
eventHandler(e){var self=this.mudJsEvent;var eventName=e.type;self.logger('[MudBlazor | JsEvent] "'+eventName+'"',e);self["on"+eventName](self,e);}
onkeyup(self,e){const caretPosition=e.target.selectionStart;const invoke=self._subscribedEvents["keyup"];if(invoke){self._dotNetRef.invokeMethodAsync('OnCaretPositionChanged',caretPosition);}}
onclick(self,e){const caretPosition=e.target.selectionStart;const invoke=self._subscribedEvents["click"];if(invoke){self._dotNetRef.invokeMethodAsync('OnCaretPositionChanged',caretPosition);}}
onpaste(self,e){const invoke=self._subscribedEvents["paste"];if(invoke){e.preventDefault();e.stopPropagation();const text=(e.originalEvent||e).clipboardData.getData('text/plain');self._dotNetRef.invokeMethodAsync('OnPaste',text);}}
onselect(self,e){const invoke=self._subscribedEvents["select"];if(invoke){const start=e.target.selectionStart;const end=e.target.selectionEnd;if(start===end)
return;self._dotNetRef.invokeMethodAsync('OnSelect',start,end);}}}
class MudResizeListener{constructor(id){this.logger=function(message){};this.options={};this.throttleResizeHandlerId=-1;this.dotnet=undefined;this.breakpoint=-1;this.id=id;this.handleResize=this.throttleResizeHandler.bind(this);}
listenForResize(dotnetRef,options){if(this.dotnet){this.options=options;return;}
this.options=options;this.dotnet=dotnetRef;this.logger=options.enableLogging?console.log:(message)=>{};this.logger(`[MudBlazor]Reporting resize events at rate of:${(this.options||{}).reportRate||100}ms`);window.addEventListener("resize",this.handleResize,false);if(!this.options.suppressInitEvent){this.resizeHandler();}
this.breakpoint=this.getBreakpoint(window.innerWidth);}
throttleResizeHandler(){clearTimeout(this.throttleResizeHandlerId);this.throttleResizeHandlerId=window.setTimeout(this.resizeHandler.bind(this),((this.options||{}).reportRate||100));}
resizeHandler(){if(this.options.notifyOnBreakpointOnly){let bp=this.getBreakpoint(window.innerWidth);if(bp==this.breakpoint){return;}
this.breakpoint=bp;}
try{if(this.id){this.dotnet.invokeMethodAsync('RaiseOnResized',{height:window.innerHeight,width:window.innerWidth},this.getBreakpoint(window.innerWidth),this.id);}
else{this.dotnet.invokeMethodAsync('RaiseOnResized',{height:window.innerHeight,width:window.innerWidth},this.getBreakpoint(window.innerWidth));}}catch(error){this.logger("[MudBlazor] Error in resizeHandler:",{error});}}
cancelListener(){this.dotnet=undefined;window.removeEventListener("resize",this.handleResize);}
matchMedia(query){let m=window.matchMedia(query).matches;return m;}
getBrowserWindowSize(){return{height:window.innerHeight,width:window.innerWidth};}
getBreakpoint(width){if(width>=this.options.breakpointDefinitions["Xxl"])
return 5;else if(width>=this.options.breakpointDefinitions["Xl"])
return 4;else if(width>=this.options.breakpointDefinitions["Lg"])
return 3;else if(width>=this.options.breakpointDefinitions["Md"])
return 2;else if(width>=this.options.breakpointDefinitions["Sm"])
return 1;else
return 0;}};window.mudResizeListener=new MudResizeListener();window.mudResizeListenerFactory={mapping:{},listenForResize:(dotnetRef,options,id)=>{var map=window.mudResizeListenerFactory.mapping;if(map[id]){return;}
var listener=new MudResizeListener(id);listener.listenForResize(dotnetRef,options);map[id]=listener;},cancelListener:(id)=>{var map=window.mudResizeListenerFactory.mapping;if(!map[id]){return;}
var listener=map[id];listener.cancelListener();delete map[id];},cancelListeners:(ids)=>{for(let i=0;i<ids.length;i++){window.mudResizeListenerFactory.cancelListener(ids[i]);}},dispose(){var map=window.mudResizeListenerFactory.mapping;for(var id in map){window.mudResizeListenerFactory.cancelListener(id);}}}
const darkThemeMediaQuery=window.matchMedia("(prefers-color-scheme: dark)");window.darkModeChange=(dotNetHelper)=>{return darkThemeMediaQuery.matches;};function watchDarkThemeMedia(dotNetHelper){dotNetHelperTheme=dotNetHelper;window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change',function(e){dotNetHelperTheme.invokeMethodAsync('SystemPreferenceChanged',window.matchMedia("(prefers-color-scheme: dark)").matches);});}function setRippleOffset(event,target){const rect=target.getBoundingClientRect();const x=event.clientX-rect.left-rect.width/2;const y=event.clientY-rect.top-rect.height/2;target.style.setProperty("--mud-ripple-offset-x",`${x}px`);target.style.setProperty("--mud-ripple-offset-y",`${y}px`);}
document.addEventListener("click",function(event){const target=event.target.closest(".mud-ripple");if(target){setRippleOffset(event,target);}});class MudResizeObserverFactory{constructor(){this._maps={};}
connect(id,dotNetRef,elements,elementIds,options){var existingEntry=this._maps[id];if(!existingEntry){var observer=new MudResizeObserver(dotNetRef,options);this._maps[id]=observer;}
var result=this._maps[id].connect(elements,elementIds);return result;}
disconnect(id,element){var existingEntry=this._maps[id];if(existingEntry){existingEntry.disconnect(element);}}
cancelListener(id){var existingEntry=this._maps[id];if(existingEntry){existingEntry.cancelListener();delete this._maps[id];}}}
class MudResizeObserver{constructor(dotNetRef,options){this.logger=options.enableLogging?console.log:(message)=>{};this.options=options;this._dotNetRef=dotNetRef
var delay=(this.options||{}).reportRate||200;this.throttleResizeHandlerId=-1;var observervedElements=[];this._observervedElements=observervedElements;this.logger('[MudBlazor | ResizeObserver] Observer initialized');this._resizeObserver=new ResizeObserver(entries=>{var changes=[];this.logger('[MudBlazor | ResizeObserver] changes detected');for(let entry of entries){var target=entry.target;var affectedObservedElement=observervedElements.find((x)=>x.element==target);if(affectedObservedElement){var size=entry.target.getBoundingClientRect();if(affectedObservedElement.isInitialized==true){changes.push({id:affectedObservedElement.id,size:size});}
else{affectedObservedElement.isInitialized=true;}}}
if(changes.length>0){if(this.throttleResizeHandlerId>=0){clearTimeout(this.throttleResizeHandlerId);}
this.throttleResizeHandlerId=window.setTimeout(this.resizeHandler.bind(this,changes),delay);}});}
resizeHandler(changes){try{this.logger("[MudBlazor | ResizeObserver] OnSizeChanged handler invoked");this._dotNetRef.invokeMethodAsync("OnSizeChanged",changes);}catch(error){this.logger("[MudBlazor | ResizeObserver] Error in OnSizeChanged handler:",{error});}}
connect(elements,ids){var result=[];this.logger('[MudBlazor | ResizeObserver] Start observing elements...');for(var i=0;i<elements.length;i++){var newEntry={element:elements[i],id:ids[i],isInitialized:false,};this.logger("[MudBlazor | ResizeObserver] Start observing element:",{newEntry});result.push(elements[i].getBoundingClientRect());this._observervedElements.push(newEntry);this._resizeObserver.observe(elements[i]);}
return result;}
disconnect(elementId){this.logger('[MudBlazor | ResizeObserver] Try to unobserve element with id',{elementId});var affectedObservedElement=this._observervedElements.find((x)=>x.id==elementId);if(affectedObservedElement){var element=affectedObservedElement.element;this._resizeObserver.unobserve(element);this.logger('[MudBlazor | ResizeObserver] Element found. Ubobserving size changes of element',{element});var index=this._observervedElements.indexOf(affectedObservedElement);this._observervedElements.splice(index,1);}}
cancelListener(){this.logger('[MudBlazor | ResizeObserver] Closing ResizeObserver. Detaching all observed elements');this._resizeObserver.disconnect();this._dotNetRef=undefined;}}
window.mudResizeObserver=new MudResizeObserverFactory();class MudScrollManager{scrollToYear(elementId,offset){let element=document.getElementById(elementId);if(element){element.parentNode.scrollTop=element.offsetTop-element.parentNode.offsetTop-element.scrollHeight*3;}}
scrollToListItem(elementId){let element=document.getElementById(elementId);if(element){let parent=element.parentElement;if(parent){parent.scrollTop=element.offsetTop;}}}
scrollTo(selector,left,top,behavior){let element=document.querySelector(selector)||document.documentElement;element.scrollTo({left,top,behavior});}
scrollIntoView(selector,behavior){let element=document.querySelector(selector)||document.documentElement;if(element)
element.scrollIntoView({behavior,block:'center',inline:'start'});}
scrollToBottom(selector,behavior){let element=document.querySelector(selector);if(element)
element.scrollTop=element.scrollHeight;else
window.scrollTo(0,document.body.scrollHeight);}
lockScroll(selector,lockclass){let element=document.querySelector(selector)||document.body;let hasScrollBar=window.innerWidth>document.body.clientWidth;if(hasScrollBar){element.classList.add(lockclass);}else{let lockClassNoPadding=lockclass+"-no-padding";element.classList.add(lockClassNoPadding);}}
unlockScroll(selector,lockclass){let element=document.querySelector(selector)||document.body;element.classList.remove(lockclass);element.classList.remove(lockclass+"-no-padding");}};window.mudScrollManager=new MudScrollManager();window.mudpopoverHelper={calculatePopoverPosition:function(list,boundingRect,selfRect){let top=0;let left=0;if(list.indexOf('mud-popover-anchor-top-left')>=0){left=boundingRect.left;top=boundingRect.top;}else if(list.indexOf('mud-popover-anchor-top-center')>=0){left=boundingRect.left+boundingRect.width/2;top=boundingRect.top;}else if(list.indexOf('mud-popover-anchor-top-right')>=0){left=boundingRect.left+boundingRect.width;top=boundingRect.top;}else if(list.indexOf('mud-popover-anchor-center-left')>=0){left=boundingRect.left;top=boundingRect.top+boundingRect.height/2;}else if(list.indexOf('mud-popover-anchor-center-center')>=0){left=boundingRect.left+boundingRect.width/2;top=boundingRect.top+boundingRect.height/2;}else if(list.indexOf('mud-popover-anchor-center-right')>=0){left=boundingRect.left+boundingRect.width;top=boundingRect.top+boundingRect.height/2;}else if(list.indexOf('mud-popover-anchor-bottom-left')>=0){left=boundingRect.left;top=boundingRect.top+boundingRect.height;}else if(list.indexOf('mud-popover-anchor-bottom-center')>=0){left=boundingRect.left+boundingRect.width/2;top=boundingRect.top+boundingRect.height;}else if(list.indexOf('mud-popover-anchor-bottom-right')>=0){left=boundingRect.left+boundingRect.width;top=boundingRect.top+boundingRect.height;}
let offsetX=0;let offsetY=0;if(list.indexOf('mud-popover-top-left')>=0){offsetX=0;offsetY=0;}else if(list.indexOf('mud-popover-top-center')>=0){offsetX=-selfRect.width/2;offsetY=0;}else if(list.indexOf('mud-popover-top-right')>=0){offsetX=-selfRect.width;offsetY=0;}
else if(list.indexOf('mud-popover-center-left')>=0){offsetX=0;offsetY=-selfRect.height/2;}else if(list.indexOf('mud-popover-center-center')>=0){offsetX=-selfRect.width/2;offsetY=-selfRect.height/2;}else if(list.indexOf('mud-popover-center-right')>=0){offsetX=-selfRect.width;offsetY=-selfRect.height/2;}
else if(list.indexOf('mud-popover-bottom-left')>=0){offsetX=0;offsetY=-selfRect.height;}else if(list.indexOf('mud-popover-bottom-center')>=0){offsetX=-selfRect.width/2;offsetY=-selfRect.height;}else if(list.indexOf('mud-popover-bottom-right')>=0){offsetX=-selfRect.width;offsetY=-selfRect.height;}
return{top:top,left:left,offsetX:offsetX,offsetY:offsetY};},flipClassReplacements:{'top':{'mud-popover-top-left':'mud-popover-bottom-left','mud-popover-top-center':'mud-popover-bottom-center','mud-popover-anchor-bottom-center':'mud-popover-anchor-top-center','mud-popover-top-right':'mud-popover-bottom-right',},'left':{'mud-popover-top-left':'mud-popover-top-right','mud-popover-center-left':'mud-popover-center-right','mud-popover-anchor-center-right':'mud-popover-anchor-center-left','mud-popover-bottom-left':'mud-popover-bottom-right',},'right':{'mud-popover-top-right':'mud-popover-top-left','mud-popover-center-right':'mud-popover-center-left','mud-popover-anchor-center-left':'mud-popover-anchor-center-right','mud-popover-bottom-right':'mud-popover-bottom-left',},'bottom':{'mud-popover-bottom-left':'mud-popover-top-left','mud-popover-bottom-center':'mud-popover-top-center','mud-popover-anchor-top-center':'mud-popover-anchor-bottom-center','mud-popover-bottom-right':'mud-popover-top-right',},'top-and-left':{'mud-popover-top-left':'mud-popover-bottom-right',},'top-and-right':{'mud-popover-top-right':'mud-popover-bottom-left',},'bottom-and-left':{'mud-popover-bottom-left':'mud-popover-top-right',},'bottom-and-right':{'mud-popover-bottom-right':'mud-popover-top-left',},},flipMargin:0,getPositionForFlippedPopver:function(inputArray,selector,boundingRect,selfRect){const classList=[];for(var i=0;i<inputArray.length;i++){const item=inputArray[i];const replacments=window.mudpopoverHelper.flipClassReplacements[selector][item];if(replacments){classList.push(replacments);}
else{classList.push(item);}}
return window.mudpopoverHelper.calculatePopoverPosition(classList,boundingRect,selfRect);},placePopover:function(popoverNode,classSelector){if(popoverNode&&popoverNode.parentNode){const id=popoverNode.id.substr(8);const popoverContentNode=document.getElementById('popovercontent-'+id);if(!popoverContentNode){return;}
if(popoverContentNode.classList.contains('mud-popover-open')==false){return;}
if(classSelector){if(popoverContentNode.classList.contains(classSelector)==false){return;}}
const boundingRect=popoverNode.parentNode.getBoundingClientRect();if(popoverContentNode.classList.contains('mud-popover-relative-width')){popoverContentNode.style['max-width']=(boundingRect.width)+'px';}
const selfRect=popoverContentNode.getBoundingClientRect();const classList=popoverContentNode.classList;const classListArray=Array.from(popoverContentNode.classList);const postion=window.mudpopoverHelper.calculatePopoverPosition(classListArray,boundingRect,selfRect);let left=postion.left;let top=postion.top;let offsetX=postion.offsetX;let offsetY=postion.offsetY;if(classList.contains('mud-popover-overflow-flip-onopen')||classList.contains('mud-popover-overflow-flip-always')){const appBarElements=document.getElementsByClassName("mud-appbar mud-appbar-fixed-top");let appBarOffset=0;if(appBarElements.length>0){appBarOffset=appBarElements[0].getBoundingClientRect().height;}
const graceMargin=window.mudpopoverHelper.flipMargin;const deltaToLeft=left+offsetX;const deltaToRight=window.innerWidth-left-selfRect.width;const deltaTop=top-selfRect.height-appBarOffset;const spaceToTop=top-appBarOffset;const deltaBottom=window.innerHeight-top-selfRect.height;let selector=popoverContentNode.mudPopoverFliped;if(!selector){if(classList.contains('mud-popover-top-left')){if(deltaBottom<graceMargin&&deltaToRight<graceMargin&&spaceToTop>=selfRect.height&&deltaToLeft>=selfRect.width){selector='top-and-left';}else if(deltaBottom<graceMargin&&spaceToTop>=selfRect.height){selector='top';}else if(deltaToRight<graceMargin&&deltaToLeft>=selfRect.width){selector='left';}}else if(classList.contains('mud-popover-top-center')){if(deltaBottom<graceMargin&&spaceToTop>=selfRect.height){selector='top';}}else if(classList.contains('mud-popover-top-right')){if(deltaBottom<graceMargin&&deltaToLeft<graceMargin&&spaceToTop>=selfRect.height&&deltaToRight>=selfRect.width){selector='top-and-right';}else if(deltaBottom<graceMargin&&spaceToTop>=selfRect.height){selector='top';}else if(deltaToLeft<graceMargin&&deltaToRight>=selfRect.width){selector='right';}}
else if(classList.contains('mud-popover-center-left')){if(deltaToRight<graceMargin&&deltaToLeft>=selfRect.width){selector='left';}}
else if(classList.contains('mud-popover-center-right')){if(deltaToLeft<graceMargin&&deltaToRight>=selfRect.width){selector='right';}}
else if(classList.contains('mud-popover-bottom-left')){if(deltaTop<graceMargin&&deltaToRight<graceMargin&&deltaBottom>=0&&deltaToLeft>=selfRect.width){selector='bottom-and-left';}else if(deltaTop<graceMargin&&deltaBottom>=0){selector='bottom';}else if(deltaToRight<graceMargin&&deltaToLeft>=selfRect.width){selector='left';}}else if(classList.contains('mud-popover-bottom-center')){if(deltaTop<graceMargin&&deltaBottom>=0){selector='bottom';}}else if(classList.contains('mud-popover-bottom-right')){if(deltaTop<graceMargin&&deltaToLeft<graceMargin&&deltaBottom>=0&&deltaToRight>=selfRect.width){selector='bottom-and-right';}else if(deltaTop<graceMargin&&deltaBottom>=0){selector='bottom';}else if(deltaToLeft<graceMargin&&deltaToRight>=selfRect.width){selector='right';}}}
if(selector&&selector!='none'){const newPosition=window.mudpopoverHelper.getPositionForFlippedPopver(classListArray,selector,boundingRect,selfRect);left=newPosition.left;top=newPosition.top;offsetX=newPosition.offsetX;offsetY=newPosition.offsetY;popoverContentNode.setAttribute('data-mudpopover-flip','flipped');}
else{popoverContentNode.removeAttribute('data-mudpopover-flip');}
if(classList.contains('mud-popover-overflow-flip-onopen')){if(!popoverContentNode.mudPopoverFliped){popoverContentNode.mudPopoverFliped=selector||'none';}}}
if(popoverContentNode.classList.contains('mud-popover-fixed')){}
else if(window.getComputedStyle(popoverNode).position=='fixed'){popoverContentNode.style['position']='fixed';}
else{offsetX+=window.scrollX;offsetY+=window.scrollY}
popoverContentNode.style['left']=(left+offsetX)+'px';popoverContentNode.style['top']=(top+offsetY)+'px';if(window.getComputedStyle(popoverNode).getPropertyValue('z-index')!='auto'){popoverContentNode.style['z-index']=window.getComputedStyle(popoverNode).getPropertyValue('z-index');popoverContentNode.skipZIndex=true;}}},placePopoverByClassSelector:function(classSelector=null){var items=window.mudPopover.getAllObservedContainers();for(let i=0;i<items.length;i++){const popoverNode=document.getElementById('popover-'+items[i]);window.mudpopoverHelper.placePopover(popoverNode,classSelector);}},placePopoverByNode:function(target){const id=target.id.substr(15);const popoverNode=document.getElementById('popover-'+id);window.mudpopoverHelper.placePopover(popoverNode);},countProviders:function(){return document.querySelectorAll(".mud-popover-provider").length;}}
class MudPopover{constructor(){this.map={};this.contentObserver=null;this.mainContainerClass=null;}
callback(id,mutationsList,observer){for(const mutation of mutationsList){if(mutation.type==='attributes'){const target=mutation.target
if(mutation.attributeName=='class'){if(target.classList.contains('mud-popover-overflow-flip-onopen')&&target.classList.contains('mud-popover-open')==false){target.mudPopoverFliped=null;target.removeAttribute('data-mudpopover-flip');}
window.mudpopoverHelper.placePopoverByNode(target);}
else if(mutation.attributeName=='data-ticks'){const tickAttribute=target.getAttribute('data-ticks');const parent=target.parentElement;const tickValues=[];let max=-1;if(parent){for(let i=0;i<parent.children.length;i++){const childNode=parent.children[i];const tickValue=parseInt(childNode.getAttribute('data-ticks'));if(tickValue==0){continue;}
if(tickValues.indexOf(tickValue)>=0){continue;}
tickValues.push(tickValue);if(tickValue>max){max=tickValue;}}}
if(tickValues.length==0){continue;}
const sortedTickValues=tickValues.sort((x,y)=>x-y);for(let i=0;i<parent.children.length;i++){const childNode=parent.children[i];const tickValue=parseInt(childNode.getAttribute('data-ticks'));if(tickValue==0){continue;}
if(childNode.skipZIndex==true){continue;}
childNode.style['z-index']='calc(var(--mud-zindex-popover) + '+(sortedTickValues.indexOf(tickValue)+3).toString()+')';}}}}}
initialize(containerClass,flipMargin){const mainContent=document.getElementsByClassName(containerClass);if(mainContent.length==0){return;}
if(flipMargin){window.mudpopoverHelper.flipMargin=flipMargin;}
this.mainContainerClass=containerClass;if(!mainContent[0].mudPopoverMark){mainContent[0].mudPopoverMark="mudded";if(this.contentObserver!=null){this.contentObserver.disconnect();this.contentObserver=null;}
this.contentObserver=new ResizeObserver(entries=>{window.mudpopoverHelper.placePopoverByClassSelector();});this.contentObserver.observe(mainContent[0]);}}
connect(id){this.initialize(this.mainContainerClass);const popoverNode=document.getElementById('popover-'+id);const popoverContentNode=document.getElementById('popovercontent-'+id);if(popoverNode&&popoverNode.parentNode&&popoverContentNode){window.mudpopoverHelper.placePopover(popoverNode);const config={attributeFilter:['class','data-ticks']};const observer=new MutationObserver(this.callback.bind(this,id));observer.observe(popoverContentNode,config);const resizeObserver=new ResizeObserver(entries=>{for(let entry of entries){const target=entry.target;for(var i=0;i<target.childNodes.length;i++){const childNode=target.childNodes[i];if(childNode.id&&childNode.id.startsWith('popover-')){window.mudpopoverHelper.placePopover(childNode);}}}});resizeObserver.observe(popoverNode.parentNode);const contentNodeObserver=new ResizeObserver(entries=>{for(let entry of entries){var target=entry.target;window.mudpopoverHelper.placePopoverByNode(target);}});contentNodeObserver.observe(popoverContentNode);this.map[id]={mutationObserver:observer,resizeObserver:resizeObserver,contentNodeObserver:contentNodeObserver};}}
disconnect(id){if(this.map[id]){const item=this.map[id]
item.mutationObserver.disconnect();item.resizeObserver.disconnect();item.contentNodeObserver.disconnect();delete this.map[id];}}
dispose(){for(var i in this.map){disconnect(i);}
this.contentObserver.disconnect();this.contentObserver=null;}
getAllObservedContainers(){const result=[];for(var i in this.map){result.push(i);}
return result;}}
window.mudPopover=new MudPopover();window.addEventListener('scroll',()=>{window.mudpopoverHelper.placePopoverByClassSelector('mud-popover-fixed');window.mudpopoverHelper.placePopoverByClassSelector('mud-popover-overflow-flip-always');});window.addEventListener('resize',()=>{window.mudpopoverHelper.placePopoverByClassSelector();});window.mudInputAutoGrow={initAutoGrow:(elem,maxLines)=>{const compStyle=getComputedStyle(elem);const lineHeight=parseFloat(compStyle.getPropertyValue('line-height'));let maxHeight=0;elem.updateParameters=function(newMaxLines){if(newMaxLines>0){maxHeight=lineHeight*newMaxLines;}else{maxHeight=0;}}
elem.adjustAutoGrowHeight=function(didReflow=false){const scrollTops=[];let curElem=elem;while(curElem&&curElem.parentNode&&curElem.parentNode instanceof Element){if(curElem.parentNode.scrollTop){scrollTops.push([curElem.parentNode,curElem.parentNode.scrollTop]);}
curElem=curElem.parentNode;}
elem.style.height=0;if(didReflow){elem.style.textAlign=null;}
let minHeight=lineHeight*elem.rows;let newHeight=Math.max(minHeight,elem.scrollHeight);let initialOverflowY=elem.style.overflowY;if(maxHeight>0&&newHeight>maxHeight){elem.style.overflowY='auto';newHeight=maxHeight;}else{elem.style.overflowY='hidden';}
elem.style.height=newHeight+"px";scrollTops.forEach(([node,scrollTop])=>{node.style.scrollBehavior='auto';node.scrollTop=scrollTop;node.style.scrollBehavior=null;});if(!didReflow&&initialOverflowY!==elem.style.overflowY&&elem.style.overflowY==='hidden'){elem.style.textAlign='end';elem.adjustAutoGrowHeight(true);}}
elem.restoreToInitialState=function(){elem.removeEventListener('input',elem.adjustAutoGrowHeight);elem.style.overflowY=null;elem.style.height=null;}
elem.addEventListener('input',elem.adjustAutoGrowHeight);window.addEventListener('resize',elem.adjustAutoGrowHeight);elem.updateParameters(maxLines);elem.adjustAutoGrowHeight();},adjustHeight:(elem)=>{if(typeof elem.adjustAutoGrowHeight==='function'){elem.adjustAutoGrowHeight();}},updateParams:(elem,maxLines)=>{if(typeof elem.updateParameters==='function'){elem.updateParameters(maxLines);}
if(typeof elem.adjustAutoGrowHeight==='function'){elem.adjustAutoGrowHeight();}},destroy:(elem)=>{window.removeEventListener('resize',elem.adjustAutoGrowHeight);if(typeof elem.restoreToInitialState==='function'){elem.restoreToInitialState();}}}
class MudWindow{copyToClipboard(text){navigator.clipboard.writeText(text);}
changeCssById(id,css){var element=document.getElementById(id);if(element){element.className=css;}}
updateStyleProperty(elementId,propertyName,value){const element=document.getElementById(elementId);if(element){element.style.setProperty(propertyName,value);}}
changeGlobalCssVariable(name,newValue){document.documentElement.style.setProperty(name,newValue);}
open(args){window.open(args);}};window.mudWindow=new MudWindow();class MudElementReference{constructor(){this.listenerId=0;this.eventListeners={};}
focus(element){if(element)
{element.focus();}}
blur(element){if(element){element.blur();}}
focusFirst(element,skip=0,min=0){if(element)
{let tabbables=getTabbableElements(element);if(tabbables.length<=min)
element.focus();else
tabbables[skip].focus();}}
focusLast(element,skip=0,min=0){if(element)
{let tabbables=getTabbableElements(element);if(tabbables.length<=min)
element.focus();else
tabbables[tabbables.length-skip-1].focus();}}
saveFocus(element){if(element)
{element['mudblazor_savedFocus']=document.activeElement;}}
restoreFocus(element){if(element)
{let previous=element['mudblazor_savedFocus'];delete element['mudblazor_savedFocus']
if(previous)
previous.focus();}}
selectRange(element,pos1,pos2){if(element)
{if(element.createTextRange){let selRange=element.createTextRange();selRange.collapse(true);selRange.moveStart('character',pos1);selRange.moveEnd('character',pos2);selRange.select();}else if(element.setSelectionRange){element.setSelectionRange(pos1,pos2);}else if(element.selectionStart){element.selectionStart=pos1;element.selectionEnd=pos2;}
element.focus();}}
select(element){if(element)
{element.select();}}
getBoundingClientRect(element){if(!element)return;var rect=JSON.parse(JSON.stringify(element.getBoundingClientRect()));rect.scrollY=window.scrollY||document.documentElement.scrollTop;rect.scrollX=window.scrollX||document.documentElement.scrollLeft;rect.windowHeight=window.innerHeight;rect.windowWidth=window.innerWidth;return rect;}
changeCss(element,css){if(element)
{element.className=css;}}
removeEventListener(element,event,eventId){element.removeEventListener(event,this.eventListeners[eventId]);delete this.eventListeners[eventId];}
addDefaultPreventingHandler(element,eventName){let listener=function(e){e.preventDefault();}
element.addEventListener(eventName,listener,{passive:false});this.eventListeners[++this.listenerId]=listener;return this.listenerId;}
removeDefaultPreventingHandler(element,eventName,listenerId){this.removeEventListener(element,eventName,listenerId);}
addDefaultPreventingHandlers(element,eventNames){let listeners=[];for(const eventName of eventNames){let listenerId=this.addDefaultPreventingHandler(element,eventName);listeners.push(listenerId);}
return listeners;}
removeDefaultPreventingHandlers(element,eventNames,listenerIds){for(let index=0;index<eventNames.length;++index){const eventName=eventNames[index];const listenerId=listenerIds[index];this.removeDefaultPreventingHandler(element,eventName,listenerId);}}};window.mudElementRef=new MudElementReference();class MudScrollSpy{constructor(){this.scrollToSectionRequested=null;this.lastKnowElement=null;this.handlerRef=null;}
spying(dotnetReference,selector){this.scrollToSectionRequested=null;this.lastKnowElement=null;this.handlerRef=this.handleScroll.bind(this,selector,dotnetReference);document.addEventListener('scroll',this.handlerRef,true);window.addEventListener('resize',this.handlerRef,true);}
handleScroll(dotnetReference,selector,event){const elements=document.getElementsByClassName(selector);if(elements.length===0){return;}
const center=window.innerHeight/2.0;let minDifference=Number.MAX_SAFE_INTEGER;let elementId='';for(let i=0;i<elements.length;i++){const element=elements[i];const rect=element.getBoundingClientRect();const diff=Math.abs(rect.top-center);if(diff<minDifference){minDifference=diff;elementId=element.id;}}
if(document.getElementById(elementId).getBoundingClientRect().top<window.innerHeight*0.8===false){return;}
if(this.scrollToSectionRequested!=null){if(this.scrollToSectionRequested==' '&&window.scrollY==0){this.scrollToSectionRequested=null;}
else{if(elementId===this.scrollToSectionRequested){this.scrollToSectionRequested=null;}}
return;}
if(elementId!=this.lastKnowElement){this.lastKnowElement=elementId;history.replaceState(null,'',window.location.pathname+"#"+elementId);dotnetReference.invokeMethodAsync('SectionChangeOccured',elementId);}}
activateSection(sectionId){const element=document.getElementById(sectionId);if(element){this.lastKnowElement=sectionId;history.replaceState(null,'',window.location.pathname+"#"+sectionId);}}
scrollToSection(sectionId){if(sectionId){let element=document.getElementById(sectionId);if(element){this.scrollToSectionRequested=sectionId;element.scrollIntoView({behavior:'smooth',block:'center',inline:'start'});}}
else{window.scrollTo({top:0,behavior:'smooth'});this.scrollToSectionRequested=' ';}}
unspy(){document.removeEventListener('scroll',this.handlerRef,true);window.removeEventListener('resize',this.handlerRef,true);}};window.mudScrollSpy=new MudScrollSpy();window.getTabbableElements=(element)=>{return element.querySelectorAll("a[href]:not([tabindex='-1']),"+"area[href]:not([tabindex='-1']),"+"button:not([disabled]):not([tabindex='-1']),"+"input:not([disabled]):not([tabindex='-1']):not([type='hidden']),"+"select:not([disabled]):not([tabindex='-1']),"+"textarea:not([disabled]):not([tabindex='-1']),"+"iframe:not([tabindex='-1']),"+"details:not([tabindex='-1']),"+"[tabindex]:not([tabindex='-1']),"+"[contentEditable=true]:not([tabindex='-1'])");};window.serializeParameter=(data,spec)=>{if(typeof data=="undefined"||data===null){return null;}
if(typeof data==="number"||typeof data==="string"||typeof data=="boolean"){return data;}
let res=(Array.isArray(data))?[]:{};if(!spec){spec="*";}
for(let i in data){let currentMember=data[i];if(typeof currentMember==='function'||currentMember===null){continue;}
let currentMemberSpec;if(spec!="*"){currentMemberSpec=Array.isArray(data)?spec:spec[i];if(!currentMemberSpec){continue;}}else{currentMemberSpec="*"}
if(typeof currentMember==='object'){if(Array.isArray(currentMember)||currentMember.length){res[i]=[];for(let j=0;j<currentMember.length;j++){const arrayItem=currentMember[j];if(typeof arrayItem==='object'){res[i].push(this.serializeParameter(arrayItem,currentMemberSpec));}else{res[i].push(arrayItem);}}}else{if(currentMember.length===0){res[i]=[];}else{res[i]=this.serializeParameter(currentMember,currentMemberSpec);}}}else{if(currentMember===Infinity){currentMember="Infinity";}
if(currentMember!==null){res[i]=currentMember;}}}
return res;};class MudScrollListener{constructor(){this.throttleScrollHandlerId=-1;this.handlerRef=null;}
listenForScroll(dotnetReference,selector){let element=selector?document.querySelector(selector):document;this.handlerRef=this.throttleScrollHandler.bind(this,dotnetReference);element.addEventListener('scroll',this.handlerRef,false);}
throttleScrollHandler(dotnetReference,event){clearTimeout(this.throttleScrollHandlerId);this.throttleScrollHandlerId=window.setTimeout(this.scrollHandler.bind(this,dotnetReference,event),100);}
scrollHandler(dotnetReference,event){try{let element=event.target;let scrollTop=element.scrollTop;let scrollHeight=element.scrollHeight;let scrollWidth=element.scrollWidth;let scrollLeft=element.scrollLeft;let nodeName=element.nodeName;let firstChild=element.firstElementChild;let firstChildBoundingClientRect=firstChild.getBoundingClientRect();dotnetReference.invokeMethodAsync('RaiseOnScroll',{firstChildBoundingClientRect,scrollLeft,scrollTop,scrollHeight,scrollWidth,nodeName,});}catch(error){console.log('[MudBlazor] Error in scrollHandler:',{error});}}
cancelListener(selector){let element=selector?document.querySelector(selector):document;element.removeEventListener('scroll',this.handlerRef);}};window.mudScrollListener=new MudScrollListener();
+10 -10
View File
@@ -8,13 +8,13 @@ To get started, choose your operating system where you want to install the app:
## Windows
AI Studio is only available for modern 64-bit Windows systems. When you have an older 32-bit system, you won't be able to run the app. We require an updated Windows 10 or 11 version; the app won't properly work on Windows 7 or 8. Next, we have to figure out if you have an Intel/AMD or a modern ARM system on your Windows machine.
- **Copilot Plus PC:** Do you own one of the new Copilot Plus PCs? If so, you most likely have an ARM system. When your machine has a Qualcomm sticker on it, you have an ARM system for sure. [Download the ARM version](https://github.com/MindWorkAI/AI-Studio/releases/latest/download/MindWork%20AI%20Studio_arm64-setup.exe) of AI Studio.
- **Copilot Plus PC:** Do you own one of the new Copilot Plus PCs? If so, you most likely have an ARM system. When your machine has a Qualcomm sticker on it, you have an ARM system for sure. [Download the ARM version](https://github.com/MindWorkAI/AI-Studio/releases/latest/download/MindWork.AI.Studio_arm64-setup.exe) of AI Studio.
- **Windows on macOS:** Do you run Windows using Parallels on an Apple Silicon system (M1, M2, M3, M4)? Then you have an ARM system as well. [Download the ARM version](https://github.com/MindWorkAI/AI-Studio/releases/latest/download/MindWork%20AI%20Studio_arm64-setup.exe) of AI Studio.
- **Windows on macOS:** Do you run Windows using Parallels on an Apple Silicon system (M1, M2, M3, M4)? Then you have an ARM system as well. [Download the ARM version](https://github.com/MindWorkAI/AI-Studio/releases/latest/download/MindWork.AI.Studio_arm64-setup.exe) of AI Studio.
- **Intel/AMD:** In almost all other cases, you have an Intel/AMD system. [Download the x64 version](https://github.com/MindWorkAI/AI-Studio/releases/latest/download/MindWork%20AI%20Studio_x64-setup.exe) of AI Studio.
- **Intel/AMD:** In almost all other cases, you have an Intel/AMD system. [Download the x64 version](https://github.com/MindWorkAI/AI-Studio/releases/latest/download/MindWork.AI.Studio_x64-setup.exe) of AI Studio.
When you try to install the app, you get a message regarding protection of your PC (see screenshots below). For Windows to trust our app, we need to purchase a certificate that costs around $1000 per year. Would you like to help us with this? [Please consider supporting us](../Sponsors.md). You might want to [visit our release page](https://github.com/MindWorkAI/AI-Studio/releases/latest). There, we provide VirusTotal scan results for each release. If you are unsure about the safety of the app, you can check the results there. Ensure that the majority of scanners have a green checkmark.
When you try to install the app, you get a message regarding protection of your PC (see screenshots below). For Windows to trust our app, we need to purchase a certificate that costs around $1000 per year. Would you like to help us with this? [Please consider supporting us](https://github.com/sponsors/MindWorkAI). You might want to [visit our release page](https://github.com/MindWorkAI/AI-Studio/releases/latest). There, we provide VirusTotal scan results for each release. If you are unsure about the safety of the app, you can check the results there. Ensure that the majority of scanners have a green checkmark.
When you are confident in the app's safety, click on "More info" and then "Run anyway" to proceed with the installation:
@@ -27,13 +27,13 @@ Once the app is installed, it will check for updates automatically. If a new ver
## macOS
AI Studio is available for modern 64-bit macOS systems. The minimum requirement is macOS 10.13 (High Sierra). Next, we have to figure out if you have an Intel or a modern Apple Silicon (ARM) system.
- **Apple Silicon:** Do you have a modern Apple Silicon system (M1, M2, M3, M4)? [Download the ARM version](https://github.com/MindWorkAI/AI-Studio/releases/latest/download/MindWork%20AI%20Studio_aarch64.dmg) of AI Studio.
- **Apple Silicon:** Do you have a modern Apple Silicon system (M1, M2, M3, M4)? [Download the ARM version](https://github.com/MindWorkAI/AI-Studio/releases/latest/download/MindWork.AI.Studio_aarch64.dmg) of AI Studio.
- **Time of purchase:** Did you buy your Mac in 2021 or later? Then you probably have an ARM system. [Download the ARM version](https://github.com/MindWorkAI/AI-Studio/releases/latest/download/MindWork%20AI%20Studio_aarch64.dmg) of AI Studio.
- **Time of purchase:** Did you buy your Mac in 2021 or later? Then you probably have an ARM system. [Download the ARM version](https://github.com/MindWorkAI/AI-Studio/releases/latest/download/MindWork.AI.Studio_aarch64.dmg) of AI Studio.
- **Check System Information:** On your macOS, click on the Apple logo in the top left corner, then "About This Mac." In the window that opens, you can see the processor type. When it says "Apple M1" or similar, you have an ARM system. [Download the ARM version](https://github.com/MindWorkAI/AI-Studio/releases/latest/download/MindWork%20AI%20Studio_aarch64.dmg) of AI Studio.
- **Check System Information:** On your macOS, click on the Apple logo in the top left corner, then "About This Mac." In the window that opens, you can see the processor type. When it says "Apple M1" or similar, you have an ARM system. [Download the ARM version](https://github.com/MindWorkAI/AI-Studio/releases/latest/download/MindWork.AI.Studio_aarch64.dmg) of AI Studio.
- **Intel:** Older Macs have an Intel processor. [Download the Intel version](https://github.com/MindWorkAI/AI-Studio/releases/latest/download/MindWork%20AI%20Studio_x64.dmg) of AI Studio.
- **Intel:** Older Macs have an Intel processor. [Download the Intel version](https://github.com/MindWorkAI/AI-Studio/releases/latest/download/MindWork.AI.Studio_x64.dmg) of AI Studio.
After downloading the file, open the DMG file and drag the app to your Applications folder:
@@ -43,7 +43,7 @@ When you try to open the app, you get a message that the app is damaged:
![macOS Installation 2](macOS%20Damage.png)
This is because we don't have an Apple Developer account, which costs around $100 per year. Would you like to help us with this? [Please consider supporting us](../Sponsors.md). You might want to [visit our release page](https://github.com/MindWorkAI/AI-Studio/releases/latest). There, we provide VirusTotal scan results for each release. If you are unsure about the safety of the app, you can check the results there. Ensure that the majority of scanners have a green checkmark.
This is because we don't have an Apple Developer account, which costs around $100 per year. Would you like to help us with this? [Please consider supporting us](https://github.com/sponsors/MindWorkAI). You might want to [visit our release page](https://github.com/MindWorkAI/AI-Studio/releases/latest). There, we provide VirusTotal scan results for each release. If you are unsure about the safety of the app, you can check the results there. Ensure that the majority of scanners have a green checkmark.
When you are confident in the app's safety, follow these steps:
@@ -52,7 +52,7 @@ When you are confident in the app's safety, follow these steps:
3. Find the MindWork AI Studio app.
4. Type this command: `xattr -r -d com.apple.quarantine ` (with a space at the end).
5. Drag the MindWork AI Studio app from the Finder into the Terminal window. The path to the app will be added to the command automatically.
6. The final command should be: `xattr -r -d com.apple.quarantine /Applications/MindWork\ AI\ Studio.app`. Press Enter.
6. The final command should be: `xattr -r -d com.apple.quarantine "/Applications/MindWork AI Studio.app"`. Press Enter.
7. Now, you might close the Terminal app and the Finder.
The AI Studio app should now open without any issues. Once the app is installed, it will check for updates automatically. If a new version is available, you will be prompted to install it.
+4 -4
View File
@@ -1,9 +1,9 @@
0.5.0
2024-06-17 10:47:38 UTC
153
0.6.1
2024-06-30 19:00:03 UTC
157
8.0.206 (commit bb12410699)
8.0.6 (commit 3b8b000a0e)
1.79.0 (commit 129f3b996)
6.20.0
1.6.1
4d553138be9, dev debug
9b1649b48a7, release
+2 -1
View File
@@ -2313,12 +2313,13 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "mindwork-ai-studio"
version = "0.5.0"
version = "0.6.0"
dependencies = [
"arboard",
"flexi_logger",
"keyring",
"log",
"once_cell",
"reqwest 0.12.4",
"serde",
"serde_json",
+2 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "mindwork-ai-studio"
version = "0.5.0"
version = "0.6.1"
edition = "2021"
description = "MindWork AI Studio"
authors = ["Thorsten Sommer"]
@@ -18,6 +18,7 @@ arboard = "3.4.0"
tokio = "1.37.0"
flexi_logger = "0.28"
log = "0.4"
once_cell = "1.19.0"
[target.'cfg(target_os = "linux")'.dependencies]
# See issue https://github.com/tauri-apps/tauri/issues/4470
+175 -19
View File
@@ -5,6 +5,7 @@ extern crate core;
use std::net::TcpListener;
use std::sync::{Arc, Mutex};
use once_cell::sync::Lazy;
use arboard::Clipboard;
use keyring::Entry;
@@ -15,6 +16,11 @@ use tauri::utils::config::AppUrl;
use tokio::time;
use flexi_logger::{AdaptiveFormat, Logger};
use log::{debug, error, info, warn};
use tauri::updater::UpdateResponse;
static SERVER: Lazy<Arc<Mutex<Option<CommandChild>>>> = Lazy::new(|| Arc::new(Mutex::new(None)));
static MAIN_WINDOW: Lazy<Mutex<Option<Window>>> = Lazy::new(|| Mutex::new(None));
static CHECK_UPDATE_RESPONSE: Lazy<Mutex<Option<UpdateResponse<tauri::Wry>>>> = Lazy::new(|| Mutex::new(None));
fn main() {
@@ -30,7 +36,14 @@ fn main() {
let tauri_version = metadata_lines.next().unwrap();
let app_commit_hash = metadata_lines.next().unwrap();
Logger::try_with_str("debug").expect("Cannot create logging")
// Set the log level according to the environment:
// In debug mode, the log level is set to debug, in release mode to info.
let log_level = match is_dev() {
true => "debug",
false => "info",
};
Logger::try_with_str(log_level).expect("Cannot create logging")
.log_to_stdout()
.adaptive_format_for_stdout(AdaptiveFormat::Detailed)
.start().expect("Cannot start logging");
@@ -69,8 +82,7 @@ fn main() {
info!("Try to start the .NET server on {app_url_log}...");
// Arc for the server process to stop it later:
let server: Arc<Mutex<Option<CommandChild>>> = Arc::new(Mutex::new(None));
let server_spawn_clone = server.clone();
let server_spawn_clone = SERVER.clone();
// Channel to communicate with the server process:
let (sender, mut receiver) = tauri::async_runtime::channel(100);
@@ -114,9 +126,8 @@ fn main() {
warn!("Running in development mode, no .NET server will be started.");
}
let main_window: Arc<Mutex<Option<Window>>> = Arc::new(Mutex::new(None));
let main_window_spawn_clone = main_window.clone();
let server_receive_clone = server.clone();
let main_window_spawn_clone = &MAIN_WINDOW;
let server_receive_clone = SERVER.clone();
// Create a thread to handle server events:
tauri::async_runtime::spawn(async move {
@@ -181,29 +192,93 @@ fn main() {
});
info!("Starting Tauri app...");
tauri::Builder::default()
let app = tauri::Builder::default()
.setup(move |app| {
let window = app.get_window("main").expect("Failed to get main window.");
*main_window.lock().unwrap() = Some(window);
*MAIN_WINDOW.lock().unwrap() = Some(window);
Ok(())
})
.plugin(tauri_plugin_window_state::Builder::default().build())
.invoke_handler(tauri::generate_handler![store_secret, get_secret, delete_secret, set_clipboard])
.run(tauri::generate_context!())
.invoke_handler(tauri::generate_handler![
store_secret, get_secret, delete_secret, set_clipboard,
check_for_update, install_update
])
.build(tauri::generate_context!())
.expect("Error while running Tauri application");
app.run(|app_handle, event| match event {
tauri::RunEvent::WindowEvent { event, label, .. } => {
match event {
tauri::WindowEvent::CloseRequested { .. } => {
warn!("Window '{label}': close was requested.");
}
tauri::WindowEvent::Destroyed => {
warn!("Window '{label}': was destroyed.");
}
tauri::WindowEvent::FileDrop(files) => {
info!("Window '{label}': files were dropped: {files:?}");
}
_ => (),
}
}
tauri::RunEvent::Updater(updater_event) => {
match updater_event {
tauri::UpdaterEvent::UpdateAvailable { body, date, version } => {
let body_len = body.len();
info!("Updater: update available: body size={body_len} time={date:?} version={version}");
}
tauri::UpdaterEvent::Pending => {
info!("Updater: update is pending!");
}
tauri::UpdaterEvent::DownloadProgress { chunk_length, content_length } => {
info!("Updater: downloaded {} of {:?}", chunk_length, content_length);
}
tauri::UpdaterEvent::Downloaded => {
info!("Updater: update has been downloaded!");
warn!("Try to stop the .NET server now...");
stop_server();
}
tauri::UpdaterEvent::Updated => {
info!("Updater: app has been updated");
warn!("Try to restart the app now...");
app_handle.restart();
}
tauri::UpdaterEvent::AlreadyUpToDate => {
info!("Updater: app is already up to date");
}
tauri::UpdaterEvent::Error(error) => {
warn!("Updater: failed to update: {error}");
}
}
}
tauri::RunEvent::ExitRequested { .. } => {
warn!("Run event: exit was requested.");
}
tauri::RunEvent::Ready => {
info!("Run event: Tauri app is ready.");
}
_ => {}
});
info!("Tauri app was stopped.");
if is_prod() {
info!("Try to stop the .NET server as well...");
if let Some(server_process) = server.lock().unwrap().take() {
let server_kill_result = server_process.kill();
match server_kill_result {
Ok(_) => info!("The .NET server process was stopped."),
Err(e) => error!("Failed to stop the .NET server process: {e}."),
}
} else {
warn!("The .NET server process was not started or already stopped.");
}
stop_server();
}
}
@@ -230,6 +305,87 @@ fn get_available_port() -> Option<u16> {
.ok()
}
fn stop_server() {
if let Some(server_process) = SERVER.lock().unwrap().take() {
let server_kill_result = server_process.kill();
match server_kill_result {
Ok(_) => info!("The .NET server process was stopped."),
Err(e) => error!("Failed to stop the .NET server process: {e}."),
}
} else {
warn!("The .NET server process was not started or already stopped.");
}
}
#[tauri::command]
async fn check_for_update() -> CheckUpdateResponse {
let app_handle = MAIN_WINDOW.lock().unwrap().as_ref().unwrap().app_handle();
tauri::async_runtime::spawn(async move {
let response = app_handle.updater().check().await;
match response {
Ok(update_response) => match update_response.is_update_available() {
true => {
*CHECK_UPDATE_RESPONSE.lock().unwrap() = Some(update_response.clone());
let new_version = update_response.latest_version();
info!("Updater: update to version '{new_version}' is available.");
let changelog = update_response.body();
CheckUpdateResponse {
update_is_available: true,
error: false,
new_version: new_version.to_string(),
changelog: match changelog {
Some(c) => c.to_string(),
None => String::from(""),
},
}
},
false => {
info!("Updater: no updates available.");
CheckUpdateResponse {
update_is_available: false,
error: false,
new_version: String::from(""),
changelog: String::from(""),
}
},
},
Err(e) => {
warn!("Failed to check updater: {e}.");
CheckUpdateResponse {
update_is_available: false,
error: true,
new_version: String::from(""),
changelog: String::from(""),
}
},
}
}).await.unwrap()
}
#[derive(Serialize)]
struct CheckUpdateResponse {
update_is_available: bool,
error: bool,
new_version: String,
changelog: String,
}
#[tauri::command]
async fn install_update() {
let cloned_response_option = CHECK_UPDATE_RESPONSE.lock().unwrap().clone();
match cloned_response_option {
Some(update_response) => {
update_response.download_and_install().await.unwrap();
},
None => {
error!("Update installer: no update available to install. Did you check for updates first?");
},
}
}
#[tauri::command]
fn store_secret(destination: String, user_name: String, secret: String) -> StoreSecretResponse {
let service = format!("mindwork-ai-studio::{}", destination);
+2 -2
View File
@@ -6,7 +6,7 @@
},
"package": {
"productName": "MindWork AI Studio",
"version": "0.5.0"
"version": "0.6.1"
},
"tauri": {
"allowlist": {
@@ -76,7 +76,7 @@
"endpoints": [
"https://github.com/MindWorkAI/AI-Studio/releases/latest/download/latest.json"
],
"dialog": true,
"dialog": false,
"windows": {
"installMode": "passive"
},