Migrate to MudBlazor v7.x.x (#41)

This commit is contained in:
Thorsten Sommer 2024-07-24 15:17:45 +02:00 committed by GitHub
parent a956b55d6b
commit 5250e5d2fb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 253 additions and 151 deletions

View File

@ -20,7 +20,7 @@
{
<MudPaper Class="pr-2 mt-3" Outlined="@true">
<MudText Typo="Typo.h6">Issues</MudText>
<MudList Clickable="@true">
<MudList T="string">
@foreach (var issue in this.inputIssues)
{
<MudListItem Icon="@Icons.Material.Filled.Error" IconColor="Color.Error">

View File

@ -1,4 +1,4 @@
<MudExpansionPanel Class="border-solid border rounded-lg" IsInitiallyExpanded="@this.IsExpanded">
<MudExpansionPanel Class="border-solid border rounded-lg" Expanded="@this.IsExpanded">
<TitleContent>
<div class="d-flex align-center">
<MudIcon Icon="@this.HeaderIcon" Size="@this.IconSize" Color="@this.IconColor" class="mr-3"/>

View File

@ -1,7 +1,7 @@
<MudList Clickable="@this.Clickable" Class="@this.Classes">
<MudList T="string" ReadOnly="@(!this.Clickable)" Class="@this.Classes">
@foreach(var item in this.Items)
{
<MudListItem Icon="@this.Icon" Style="display: flex; align-items: flex-start;">
<MudListItem T="string" Icon="@this.Icon" Style="display: flex; align-items: flex-start;">
<MudText Typo="Typo.body1" Style="text-align: justify; hyphens: auto;"><b>@item.Header:</b> @item.Text</MudText>
</MudListItem>
}

View File

@ -18,5 +18,5 @@ public class TreeItemData : ITreeItem
public bool Expandable { get; init; } = true;
public HashSet<ITreeItem> Children { get; init; } = [];
public IReadOnlyCollection<TreeItemData<ITreeItem>> Children { get; init; } = [];
}

View File

@ -1,6 +1,6 @@
<MudTreeView T="ITreeItem" Items="@this.treeItems" MultiSelection="@false" Hover="@true" ExpandOnClick="@true">
<MudTreeView T="ITreeItem" Items="@this.treeItems" SelectionMode="SelectionMode.SingleSelection" Hover="@true" ExpandOnClick="@true">
<ItemTemplate Context="item">
@switch (item)
@switch (item.Value)
{
case TreeDivider:
<li style="min-height: 1em;">
@ -11,7 +11,7 @@
case TreeItemData treeItem:
@if (treeItem.Type is TreeItemType.CHAT)
{
<MudTreeViewItem T="ITreeItem" Icon="@treeItem.Icon" Value="@item" CanExpand="@treeItem.Expandable" Items="@treeItem.Children" OnClick="() => this.LoadChat(treeItem.Path, true)">
<MudTreeViewItem T="ITreeItem" Icon="@treeItem.Icon" Value="@item.Value" CanExpand="@treeItem.Expandable" Items="@treeItem.Children" OnClick="() => this.LoadChat(treeItem.Path, true)">
<BodyContent>
<div style="display: grid; grid-template-columns: 1fr auto; align-items: center; width: 100%">
<MudText Style="justify-self: start;">
@ -44,7 +44,7 @@
}
else if (treeItem.Type is TreeItemType.WORKSPACE)
{
<MudTreeViewItem T="ITreeItem" Icon="@treeItem.Icon" Value="@item" CanExpand="@treeItem.Expandable" Items="@treeItem.Children">
<MudTreeViewItem T="ITreeItem" Icon="@treeItem.Icon" Value="@item.Value" CanExpand="@treeItem.Expandable" Items="@treeItem.Children">
<BodyContent>
<div style="display: grid; grid-template-columns: 1fr auto; align-items: center; width: 100%">
<MudText Style="justify-self: start;">@treeItem.Text</MudText>
@ -63,7 +63,7 @@
}
else
{
<MudTreeViewItem T="ITreeItem" Icon="@treeItem.Icon" Value="@item" CanExpand="@treeItem.Expandable" Items="@treeItem.Children">
<MudTreeViewItem T="ITreeItem" Icon="@treeItem.Icon" Value="@item.Value" CanExpand="@treeItem.Expandable" Items="@treeItem.Children">
<BodyContent>
<div style="display: grid; grid-template-columns: 1fr auto; align-items: center; width: 100%">
<MudText Style="justify-self: start;">@treeItem.Text</MudText>

View File

@ -47,8 +47,8 @@ public partial class Workspaces : ComponentBase
new JsonStringEnumConverter(JsonNamingPolicy.SnakeCaseUpper),
}
};
private readonly HashSet<ITreeItem> treeItems = new();
private readonly List<TreeItemData<ITreeItem>> treeItems = new();
#region Overrides of ComponentBase
@ -70,33 +70,46 @@ public partial class Workspaces : ComponentBase
private async Task LoadTreeItems()
{
this.treeItems.Clear();
this.treeItems.Add(new TreeItemData
this.treeItems.Add(new TreeItemData<ITreeItem>
{
Depth = 0,
Branch = WorkspaceBranch.WORKSPACES,
Text = "Workspaces",
Icon = Icons.Material.Filled.Folder,
Expandable = true,
Path = "root",
Children = await this.LoadWorkspaces(),
Value = new TreeItemData
{
Depth = 0,
Branch = WorkspaceBranch.WORKSPACES,
Text = "Workspaces",
Icon = Icons.Material.Filled.Folder,
Expandable = true,
Path = "root",
Children = await this.LoadWorkspaces(),
},
});
this.treeItems.Add(new TreeDivider());
this.treeItems.Add(new TreeItemData
this.treeItems.Add(new TreeItemData<ITreeItem>
{
Expandable = false,
Value = new TreeDivider(),
});
this.treeItems.Add(new TreeItemData<ITreeItem>
{
Depth = 0,
Branch = WorkspaceBranch.TEMPORARY_CHATS,
Text = "Temporary chats",
Icon = Icons.Material.Filled.Timer,
Expandable = true,
Path = "temp",
Children = await this.LoadTemporaryChats(),
Value = new TreeItemData
{
Depth = 0,
Branch = WorkspaceBranch.TEMPORARY_CHATS,
Text = "Temporary chats",
Icon = Icons.Material.Filled.Timer,
Expandable = true,
Path = "temp",
Children = await this.LoadTemporaryChats(),
},
});
}
private async Task<HashSet<ITreeItem>> LoadTemporaryChats()
private async Task<IReadOnlyCollection<TreeItemData<ITreeItem>>> LoadTemporaryChats()
{
var tempChildren = new HashSet<ITreeItem>();
var tempChildren = new List<TreeItemData<ITreeItem>>();
//
// Search for workspace folders in the data directory:
@ -115,15 +128,19 @@ public partial class Workspaces : ComponentBase
var chatNamePath = Path.Join(tempChatDirPath, "name");
var chatName = await File.ReadAllTextAsync(chatNamePath, Encoding.UTF8);
tempChildren.Add(new TreeItemData
tempChildren.Add(new TreeItemData<ITreeItem>
{
Type = TreeItemType.CHAT,
Depth = 1,
Branch = WorkspaceBranch.TEMPORARY_CHATS,
Text = chatName,
Icon = Icons.Material.Filled.Timer,
Expandable = false,
Path = tempChatDirPath,
Value = new TreeItemData
{
Type = TreeItemType.CHAT,
Depth = 1,
Branch = WorkspaceBranch.TEMPORARY_CHATS,
Text = chatName,
Icon = Icons.Material.Filled.Timer,
Expandable = false,
Path = tempChatDirPath,
},
});
}
@ -140,9 +157,9 @@ public partial class Workspaces : ComponentBase
return await File.ReadAllTextAsync(workspaceNamePath, Encoding.UTF8);
}
private async Task<HashSet<ITreeItem>> LoadWorkspaces()
private async Task<IReadOnlyCollection<TreeItemData<ITreeItem>>> LoadWorkspaces()
{
var workspaces = new HashSet<ITreeItem>();
var workspaces = new List<TreeItemData<ITreeItem>>();
//
// Search for workspace folders in the data directory:
@ -161,26 +178,34 @@ public partial class Workspaces : ComponentBase
var workspaceNamePath = Path.Join(workspaceDirPath, "name");
var workspaceName = await File.ReadAllTextAsync(workspaceNamePath, Encoding.UTF8);
workspaces.Add(new TreeItemData
workspaces.Add(new TreeItemData<ITreeItem>
{
Type = TreeItemType.WORKSPACE,
Depth = 1,
Branch = WorkspaceBranch.WORKSPACES,
Text = workspaceName,
Icon = Icons.Material.Filled.Description,
Expandable = true,
Path = workspaceDirPath,
Children = await this.LoadWorkspaceChats(workspaceDirPath),
Value = new TreeItemData
{
Type = TreeItemType.WORKSPACE,
Depth = 1,
Branch = WorkspaceBranch.WORKSPACES,
Text = workspaceName,
Icon = Icons.Material.Filled.Description,
Expandable = true,
Path = workspaceDirPath,
Children = await this.LoadWorkspaceChats(workspaceDirPath),
},
});
}
workspaces.Add(new TreeButton(WorkspaceBranch.WORKSPACES, 1, "Add workspace",Icons.Material.Filled.LibraryAdd, this.AddWorkspace));
workspaces.Add(new TreeItemData<ITreeItem>
{
Expandable = false,
Value = new TreeButton(WorkspaceBranch.WORKSPACES, 1, "Add workspace",Icons.Material.Filled.LibraryAdd, this.AddWorkspace),
});
return workspaces;
}
private async Task<HashSet<ITreeItem>> LoadWorkspaceChats(string workspacePath)
private async Task<IReadOnlyCollection<TreeItemData<ITreeItem>>> LoadWorkspaceChats(string workspacePath)
{
var workspaceChats = new HashSet<ITreeItem>();
var workspaceChats = new List<TreeItemData<ITreeItem>>();
// Enumerate the workspace directory:
foreach (var chatPath in Directory.EnumerateDirectories(workspacePath))
@ -189,19 +214,28 @@ public partial class Workspaces : ComponentBase
var chatNamePath = Path.Join(chatPath, "name");
var chatName = await File.ReadAllTextAsync(chatNamePath, Encoding.UTF8);
workspaceChats.Add(new TreeItemData
workspaceChats.Add(new TreeItemData<ITreeItem>
{
Type = TreeItemType.CHAT,
Depth = 2,
Branch = WorkspaceBranch.WORKSPACES,
Text = chatName,
Icon = Icons.Material.Filled.Chat,
Expandable = false,
Path = chatPath,
Value = new TreeItemData
{
Type = TreeItemType.CHAT,
Depth = 2,
Branch = WorkspaceBranch.WORKSPACES,
Text = chatName,
Icon = Icons.Material.Filled.Chat,
Expandable = false,
Path = chatPath,
},
});
}
workspaceChats.Add(new TreeButton(WorkspaceBranch.WORKSPACES, 2, "Add chat",Icons.Material.Filled.AddComment, () => this.AddChat(workspacePath)));
workspaceChats.Add(new TreeItemData<ITreeItem>
{
Expandable = false,
Value = new TreeButton(WorkspaceBranch.WORKSPACES, 2, "Add chat",Icons.Material.Filled.AddComment, () => this.AddChat(workspacePath)),
});
return workspaceChats;
}
@ -247,7 +281,7 @@ public partial class Workspaces : ComponentBase
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>("Load Chat", dialogParameters, DialogOptions.FULLSCREEN);
var dialogResult = await dialogReference.Result;
if (dialogResult.Canceled)
if (dialogResult is null || dialogResult.Canceled)
return null;
}
@ -294,7 +328,7 @@ public partial class Workspaces : ComponentBase
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>("Delete Chat", dialogParameters, DialogOptions.FULLSCREEN);
var dialogResult = await dialogReference.Result;
if (dialogResult.Canceled)
if (dialogResult is null || dialogResult.Canceled)
return;
}
@ -331,7 +365,7 @@ public partial class Workspaces : ComponentBase
var dialogReference = await this.DialogService.ShowAsync<SingleInputDialog>("Rename Chat", dialogParameters, DialogOptions.FULLSCREEN);
var dialogResult = await dialogReference.Result;
if (dialogResult.Canceled)
if (dialogResult is null || dialogResult.Canceled)
return;
chat.Name = (dialogResult.Data as string)!;
@ -356,7 +390,7 @@ public partial class Workspaces : ComponentBase
var dialogReference = await this.DialogService.ShowAsync<SingleInputDialog>("Rename Workspace", dialogParameters, DialogOptions.FULLSCREEN);
var dialogResult = await dialogReference.Result;
if (dialogResult.Canceled)
if (dialogResult is null || dialogResult.Canceled)
return;
var alteredWorkspaceName = (dialogResult.Data as string)!;
@ -377,7 +411,7 @@ public partial class Workspaces : ComponentBase
var dialogReference = await this.DialogService.ShowAsync<SingleInputDialog>("Add Workspace", dialogParameters, DialogOptions.FULLSCREEN);
var dialogResult = await dialogReference.Result;
if (dialogResult.Canceled)
if (dialogResult is null || dialogResult.Canceled)
return;
var workspaceId = Guid.NewGuid();
@ -408,7 +442,7 @@ public partial class Workspaces : ComponentBase
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>("Delete Workspace", dialogParameters, DialogOptions.FULLSCREEN);
var dialogResult = await dialogReference.Result;
if (dialogResult.Canceled)
if (dialogResult is null || dialogResult.Canceled)
return;
Directory.Delete(workspacePath, true);
@ -430,7 +464,7 @@ public partial class Workspaces : ComponentBase
var dialogReference = await this.DialogService.ShowAsync<WorkspaceSelectionDialog>("Move Chat to Workspace", dialogParameters, DialogOptions.FULLSCREEN);
var dialogResult = await dialogReference.Result;
if (dialogResult.Canceled)
if (dialogResult is null || dialogResult.Canceled)
return;
var workspaceId = dialogResult.Data is Guid id ? id : default;
@ -475,7 +509,7 @@ public partial class Workspaces : ComponentBase
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>("Create Chat", dialogParameters, DialogOptions.FULLSCREEN);
var dialogResult = await dialogReference.Result;
if (dialogResult.Canceled)
if (dialogResult is null || dialogResult.Canceled)
return;
}

View File

@ -1,4 +1,5 @@
using AIStudio.Settings;
using AIStudio.Tools;
using Microsoft.AspNetCore.Components;
@ -23,7 +24,12 @@ public partial class ConfigurationBase : ComponentBase
public string OptionHelp { get; set; } = string.Empty;
[Inject]
public SettingsManager SettingsManager { get; init; } = null!;
protected SettingsManager SettingsManager { get; init; } = null!;
[Inject]
protected MessageBus MessageBus { get; init; } = null!;
protected const string MARGIN_CLASS = "mb-6";
protected async Task InformAboutChange() => await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
}

View File

@ -35,5 +35,6 @@ public partial class ConfigurationOption : ConfigurationBase
{
this.StateUpdate(updatedState);
await this.SettingsManager.StoreSettings();
await this.InformAboutChange();
}
}

View File

@ -30,6 +30,7 @@ public partial class ConfigurationSelect<T> : ConfigurationBase
{
this.SelectionUpdate(updatedValue);
await this.SettingsManager.StoreSettings();
await this.InformAboutChange();
}
private static string GetClass => $"{MARGIN_CLASS} rounded-lg";

View File

@ -47,4 +47,12 @@ public static class ConfigurationSelectDataFactory
yield return new("Delete temporary chats older than 180 days", WorkspaceStorageTemporaryMaintenancePolicy.DELETE_OLDER_THAN_180_DAYS);
yield return new("Delete temporary chats older than 1 year", WorkspaceStorageTemporaryMaintenancePolicy.DELETE_OLDER_THAN_365_DAYS);
}
public static IEnumerable<ConfigurationSelectData<NavBehavior>> GetNavBehaviorData()
{
yield return new("Navigation expands on mouse hover", NavBehavior.EXPAND_ON_HOVER);
yield return new("Navigation never expands, but there are tooltips", NavBehavior.NEVER_EXPAND_USE_TOOLTIPS);
yield return new("Navigation never expands, no tooltips", NavBehavior.NEVER_EXPAND_NO_TOOLTIPS);
yield return new("Always expand navigation", NavBehavior.ALWAYS_EXPAND);
}
}

View File

@ -1,36 +1,32 @@
@inherits LayoutComponentBase
@using AIStudio.Settings
@inherits LayoutComponentBase
<MudPaper Height="calc(100vh);" Elevation="0">
<MudLayout>
@if (!this.performingUpdate)
{
<MudDrawerContainer Class="mud-height-full absolute">
<MudDrawer Elevation="0" Variant="@DrawerVariant.Mini" OpenMiniOnHover="@true" Color="Color.Default">
<MudDrawer @bind-Open="@this.navBarOpen" MiniWidth="@NAVBAR_COLLAPSED_WIDTH" Width="@NAVBAR_EXPANDED_WIDTH" Elevation="1" Fixed="@true" Variant="@DrawerVariant.Mini" OpenMiniOnHover="@(this.SettingsManager.ConfigurationData.NavigationBehavior is NavBehavior.EXPAND_ON_HOVER)" 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="Chat" Placement="Placement.Right">
<MudNavLink Href="/chat" Icon="@Icons.Material.Filled.Chat">Chat</MudNavLink>
</MudTooltip>
<MudTooltip Text="Assistants" Placement="Placement.Right">
<MudNavLink Href="/assistants" Icon="@Icons.Material.Filled.Apps">Assistants</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>
@foreach (var navBarItem in NAV_ITEMS)
{
if (this.SettingsManager.ConfigurationData.NavigationBehavior is NavBehavior.NEVER_EXPAND_USE_TOOLTIPS)
{
<MudTooltip Text="@navBarItem.Name" Placement="Placement.Right">
<MudNavLink Href="@navBarItem.Path" Match="@(navBarItem.MatchAll ? NavLinkMatch.All : NavLinkMatch.Prefix)" Icon="@navBarItem.Icon" IconColor="@navBarItem.IconColor">@navBarItem.Name</MudNavLink>
</MudTooltip>
}
else
{
<MudNavLink Href="@navBarItem.Path" Match="@(navBarItem.MatchAll ? NavLinkMatch.All : NavLinkMatch.Prefix)" Icon="@navBarItem.Icon" IconColor="@navBarItem.IconColor">@navBarItem.Name</MudNavLink>
}
}
</MudNavMenu>
</MudDrawer>
</MudDrawerContainer>
}
<MudMainContent Class="mud-height-full pt-1">
<MudMainContent Class="mud-height-full pt-1" Style="@this.PaddingLeft">
<MudContainer Fixed="@true" Class="mud-height-full" Style="margin-left: 5em; width: calc(100% - 5em);">
@if (!this.performingUpdate && this.IsUpdateAlertVisible)
{

View File

@ -34,12 +34,30 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver
public string AdditionalHeight { get; private set; } = "0em";
private string PaddingLeft => this.navBarOpen ? $"padding-left: {NAVBAR_EXPANDED_WIDTH_INT - NAVBAR_COLLAPSED_WIDTH_INT}em;" : "padding-left: 0em;";
private const int NAVBAR_COLLAPSED_WIDTH_INT = 4;
private const int NAVBAR_EXPANDED_WIDTH_INT = 10;
private static readonly string NAVBAR_COLLAPSED_WIDTH = $"{NAVBAR_COLLAPSED_WIDTH_INT}em";
private static readonly string NAVBAR_EXPANDED_WIDTH = $"{NAVBAR_EXPANDED_WIDTH_INT}em";
private bool navBarOpen;
private bool isUpdateAvailable;
private bool performingUpdate;
private bool userDismissedUpdate;
private string updateToVersion = string.Empty;
private UpdateResponse? currentUpdateResponse;
private static readonly IReadOnlyCollection<NavBarItem> NAV_ITEMS = new List<NavBarItem>
{
new("Home", Icons.Material.Filled.Home, Color.Default, "/", true),
new("Chat", Icons.Material.Filled.Chat, Color.Default, "/chat", false),
new("Assistants", Icons.Material.Filled.Apps, Color.Default ,"/assistants", false),
new("Supporters", Icons.Material.Filled.Favorite, Color.Error ,"/supporters", false),
new("About", Icons.Material.Filled.Info, Color.Default ,"/about", false),
new("Settings", Icons.Material.Filled.Settings, Color.Default ,"/settings", false),
};
#region Overrides of ComponentBase
protected override async Task OnInitializedAsync()
@ -63,12 +81,16 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver
// Register this component with the message bus:
this.MessageBus.RegisterComponent(this);
this.MessageBus.ApplyFilters(this, [], [ Event.UPDATE_AVAILABLE, Event.USER_SEARCH_FOR_UPDATE ]);
this.MessageBus.ApplyFilters(this, [], [ Event.UPDATE_AVAILABLE, Event.USER_SEARCH_FOR_UPDATE, Event.CONFIGURATION_CHANGED ]);
// Set the js runtime for the update service:
UpdateService.SetBlazorDependencies(this.JsRuntime, this.Snackbar);
TemporaryChatService.Initialize();
// Should the navigation bar be open by default?
if(this.SettingsManager.ConfigurationData.NavigationBehavior is NavBehavior.ALWAYS_EXPAND)
this.navBarOpen = true;
await base.OnInitializedAsync();
}
@ -96,6 +118,15 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver
}
break;
case Event.CONFIGURATION_CHANGED:
if(this.SettingsManager.ConfigurationData.NavigationBehavior is NavBehavior.ALWAYS_EXPAND)
this.navBarOpen = true;
else
this.navBarOpen = false;
this.StateHasChanged();
break;
}
}
@ -105,7 +136,7 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver
}
#endregion
private async Task DismissUpdate()
{
this.userDismissedUpdate = true;
@ -151,7 +182,7 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver
var dialogReference = await this.DialogService.ShowAsync<UpdateDialog>("Update", dialogParameters, DialogOptions.FULLSCREEN_NO_HEADER);
var dialogResult = await dialogReference.Result;
if (dialogResult.Canceled)
if (dialogResult is null || dialogResult.Canceled)
return;
this.performingUpdate = true;
@ -170,7 +201,7 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>("Leave Chat Page", dialogParameters, DialogOptions.FULLSCREEN);
var dialogResult = await dialogReference.Result;
if (dialogResult.Canceled)
if (dialogResult is null || dialogResult.Canceled)
{
context.PreventNavigation();
return;

View File

@ -0,0 +1,3 @@
namespace AIStudio.Components.Layout;
public record NavBarItem(string Name, string Icon, Color IconColor, string Path, bool MatchAll);

View File

@ -9,14 +9,14 @@
<MudText>
The following list shows the versions of the MindWork AI Studio, the used compilers, build time, etc.:
</MudText>
<MudList Clickable="@true">
<MudListItem Icon="@Icons.Material.Outlined.Chat" Text="@VersionApp"/>
<MudListItem Icon="@Icons.Material.Outlined.Timer" Text="@BuildTime"/>
<MudListItem Icon="@Icons.Material.Outlined.Build" Text="@VersionDotnetSdk"/>
<MudListItem Icon="@Icons.Material.Outlined.Memory" Text="@VersionDotnetRuntime"/>
<MudListItem Icon="@Icons.Material.Outlined.Build" Text="@VersionRust"/>
<MudListItem Icon="@Icons.Material.Outlined.Widgets" Text="@MudBlazorVersion"/>
<MudListItem Icon="@Icons.Material.Outlined.Memory" Text="@TauriVersion"/>
<MudList T="string">
<MudListItem T="string" Icon="@Icons.Material.Outlined.Chat" Text="@VersionApp"/>
<MudListItem T="string" Icon="@Icons.Material.Outlined.Timer" Text="@BuildTime"/>
<MudListItem T="string" Icon="@Icons.Material.Outlined.Build" Text="@VersionDotnetSdk"/>
<MudListItem T="string" Icon="@Icons.Material.Outlined.Memory" Text="@VersionDotnetRuntime"/>
<MudListItem T="string" Icon="@Icons.Material.Outlined.Build" Text="@VersionRust"/>
<MudListItem T="string" Icon="@Icons.Material.Outlined.Widgets" Text="@MudBlazorVersion"/>
<MudListItem T="string" 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

View File

@ -235,7 +235,7 @@ public partial class Chat : MSGComponentBase, IAsyncDisposable
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>("Delete Chat", dialogParameters, DialogOptions.FULLSCREEN);
var dialogResult = await dialogReference.Result;
if (dialogResult.Canceled)
if (dialogResult is null || dialogResult.Canceled)
return;
}
@ -297,7 +297,7 @@ public partial class Chat : MSGComponentBase, IAsyncDisposable
var confirmationDialogReference = await this.DialogService.ShowAsync<ConfirmDialog>("Unsaved Changes", confirmationDialogParameters, DialogOptions.FULLSCREEN);
var confirmationDialogResult = await confirmationDialogReference.Result;
if (confirmationDialogResult.Canceled)
if (confirmationDialogResult is null || confirmationDialogResult.Canceled)
return;
}
@ -310,7 +310,7 @@ public partial class Chat : MSGComponentBase, IAsyncDisposable
var dialogReference = await this.DialogService.ShowAsync<WorkspaceSelectionDialog>("Move Chat to Workspace", dialogParameters, DialogOptions.FULLSCREEN);
var dialogResult = await dialogReference.Result;
if (dialogResult.Canceled)
if (dialogResult is null || dialogResult.Canceled)
return;
var workspaceId = dialogResult.Data is Guid id ? id : default;

View File

@ -1,5 +1,3 @@
using AIStudio.Provider;
namespace AIStudio.Components.Pages.IconFinder;
public partial class AssistantIconFinder : AssistantBaseCore

View File

@ -73,5 +73,6 @@
<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?"/>
<ConfigurationSelect OptionDescription="Workspace behavior" SelectedValue="@(() => this.SettingsManager.ConfigurationData.WorkspaceStorageBehavior)" Data="@ConfigurationSelectDataFactory.GetWorkspaceStorageBehaviorData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.WorkspaceStorageBehavior = selectedValue)" OptionHelp="Should we store your chats?"/>
<ConfigurationSelect OptionDescription="Workspace maintenance" SelectedValue="@(() => this.SettingsManager.ConfigurationData.WorkspaceStorageTemporaryMaintenancePolicy)" Data="@ConfigurationSelectDataFactory.GetWorkspaceStorageTemporaryMaintenancePolicyData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.WorkspaceStorageTemporaryMaintenancePolicy = selectedValue)" OptionHelp="If and when should we delete your temporary chats?"/>
<ConfigurationSelect OptionDescription="Navigation bar behavior" SelectedValue="@(() => this.SettingsManager.ConfigurationData.NavigationBehavior)" Data="@ConfigurationSelectDataFactory.GetNavBehaviorData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.NavigationBehavior = selectedValue)" OptionHelp="Select the desired behavior for the navigation bar."/>
</MudPaper>
</InnerScrolling>

View File

@ -1,6 +1,8 @@
using AIStudio.Components.CommonDialogs;
using AIStudio.Provider;
using AIStudio.Settings;
using AIStudio.Tools;
using Microsoft.AspNetCore.Components;
using DialogOptions = AIStudio.Components.CommonDialogs.DialogOptions;
@ -19,6 +21,9 @@ public partial class Settings : ComponentBase
[Inject]
public IJSRuntime JsRuntime { get; init; } = null!;
[Inject]
protected MessageBus MessageBus { get; init; } = null!;
#region Provider related
@ -31,14 +36,15 @@ public partial class Settings : ComponentBase
var dialogReference = await this.DialogService.ShowAsync<ProviderDialog>("Add Provider", dialogParameters, DialogOptions.FULLSCREEN);
var dialogResult = await dialogReference.Result;
if (dialogResult.Canceled)
if (dialogResult is null || dialogResult.Canceled)
return;
var addedProvider = (AIStudio.Settings.Provider)dialogResult.Data;
var addedProvider = (AIStudio.Settings.Provider)dialogResult.Data!;
addedProvider = addedProvider with { Num = this.SettingsManager.ConfigurationData.NextProviderNum++ };
this.SettingsManager.ConfigurationData.Providers.Add(addedProvider);
await this.SettingsManager.StoreSettings();
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
}
private async Task EditProvider(AIStudio.Settings.Provider provider)
@ -58,10 +64,10 @@ public partial class Settings : ComponentBase
var dialogReference = await this.DialogService.ShowAsync<ProviderDialog>("Edit Provider", dialogParameters, DialogOptions.FULLSCREEN);
var dialogResult = await dialogReference.Result;
if (dialogResult.Canceled)
if (dialogResult is null || dialogResult.Canceled)
return;
var editedProvider = (AIStudio.Settings.Provider)dialogResult.Data;
var editedProvider = (AIStudio.Settings.Provider)dialogResult.Data!;
// Set the provider number if it's not set. This is important for providers
// added before we started saving the provider number.
@ -70,6 +76,7 @@ public partial class Settings : ComponentBase
this.SettingsManager.ConfigurationData.Providers[this.SettingsManager.ConfigurationData.Providers.IndexOf(provider)] = editedProvider;
await this.SettingsManager.StoreSettings();
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
}
private async Task DeleteProvider(AIStudio.Settings.Provider provider)
@ -81,7 +88,7 @@ public partial class Settings : ComponentBase
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>("Delete Provider", dialogParameters, DialogOptions.FULLSCREEN);
var dialogResult = await dialogReference.Result;
if (dialogResult.Canceled)
if (dialogResult is null || dialogResult.Canceled)
return;
var providerInstance = provider.CreateProvider();
@ -91,6 +98,8 @@ public partial class Settings : ComponentBase
this.SettingsManager.ConfigurationData.Providers.Remove(provider);
await this.SettingsManager.StoreSettings();
}
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
}
private string GetProviderDashboardURL(Providers provider) => provider switch

View File

@ -1,4 +1,3 @@
using AIStudio.Provider;
using AIStudio.Tools;
namespace AIStudio.Components.Pages.TextSummarizer;

View File

@ -1,4 +1,3 @@
using AIStudio.Provider;
using AIStudio.Tools;
namespace AIStudio.Components.Pages.Translator;

View File

@ -7,4 +7,5 @@
<MudThemeProvider />
<MudDialogProvider />
<MudPopoverProvider />
<MudSnackbarProvider />

View File

@ -45,8 +45,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="8.0.6" />
<PackageReference Include="MudBlazor" Version="6.20.0" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="8.0.7" />
<PackageReference Include="MudBlazor" Version="7.4.0" />
<PackageReference Include="MudBlazor.Markdown" Version="1.0.2" />
</ItemGroup>

View File

@ -51,4 +51,9 @@ public sealed class Data
/// The chat storage maintenance behavior.
/// </summary>
public WorkspaceStorageTemporaryMaintenancePolicy WorkspaceStorageTemporaryMaintenancePolicy { get; set; } = WorkspaceStorageTemporaryMaintenancePolicy.DELETE_OLDER_THAN_90_DAYS;
/// <summary>
/// The navigation behavior.
/// </summary>
public NavBehavior NavigationBehavior { get; set; } = NavBehavior.EXPAND_ON_HOVER;
}

View File

@ -0,0 +1,9 @@
namespace AIStudio.Settings;
public enum NavBehavior
{
EXPAND_ON_HOVER,
NEVER_EXPAND_USE_TOOLTIPS,
NEVER_EXPAND_NO_TOOLTIPS,
ALWAYS_EXPAND,
}

View File

@ -79,10 +79,10 @@
{
<MudPaper Class="pa-2 mt-3">
<MudText Typo="Typo.h6">Issues</MudText>
<MudList Clickable="@true">
<MudList T="string">
@foreach (var issue in this.dataIssues)
{
<MudListItem Icon="@Icons.Material.Filled.Error" IconColor="Color.Error">
<MudListItem T="string" Icon="@Icons.Material.Filled.Error" IconColor="Color.Error">
@issue
</MudListItem>
}

View File

@ -6,6 +6,7 @@ public enum Event
// Common events:
STATE_HAS_CHANGED,
CONFIGURATION_CHANGED,
// Update events:
USER_SEARCH_FOR_UPDATE,

View File

@ -4,9 +4,9 @@
"net8.0": {
"Microsoft.Extensions.FileProviders.Embedded": {
"type": "Direct",
"requested": "[8.0.6, )",
"resolved": "8.0.6",
"contentHash": "hQlf5+YxiUbKdpaPBf/zdMGItnWF8ai9ToPjeZ6gnxT10V4RGDKvChl5MSdc838+2SRHLAC0cdJwG0+L7dqR0g==",
"requested": "[8.0.7, )",
"resolved": "8.0.7",
"contentHash": "ABsn0T09b5lzVNbOcuRc10+kNZkO+RGtZWfzqVay0Ah+/ouhEvG7JrXc+9+7zFgoPuH4E4N6+uWfTp+pJqMeGw==",
"dependencies": {
"Microsoft.Extensions.FileProviders.Abstractions": "8.0.0"
}
@ -19,13 +19,13 @@
},
"MudBlazor": {
"type": "Direct",
"requested": "[6.20.0, )",
"resolved": "6.20.0",
"contentHash": "2MqW/E1OLszSqDhW06rpRTN4OpIRJU0iVzeiZLPzymckrKJH1Hg09St+O3kaEnsbXRcjcdM9iE5cSKzCgxvySQ==",
"requested": "[7.4.0, )",
"resolved": "7.4.0",
"contentHash": "dm8ZD6OhyI6icE3Si0V3BJvwPZp8fEspPFq4/iQXU8/i7L39/ny2bPgQqPnQmIojppd9cR/H4RdsaBlSLe6rIw==",
"dependencies": {
"Microsoft.AspNetCore.Components": "8.0.3",
"Microsoft.AspNetCore.Components.Web": "8.0.3",
"Microsoft.Extensions.Localization": "8.0.3"
"Microsoft.AspNetCore.Components": "8.0.6",
"Microsoft.AspNetCore.Components.Web": "8.0.6",
"Microsoft.Extensions.Localization": "8.0.6"
}
},
"MudBlazor.Markdown": {
@ -45,53 +45,53 @@
},
"Microsoft.AspNetCore.Authorization": {
"type": "Transitive",
"resolved": "8.0.3",
"contentHash": "9Nic17acgZbysUlhGc+TEd9F8jI01kC6+V31sC7/xI5v2OSWGL8NhdYaB/Iu4KnDRoQEolg6qvepGsVfeYpIYA==",
"resolved": "8.0.6",
"contentHash": "H1CSbD7UeSPsrJSUpvbms6SqWMa5y8ch4Rw+gyHh2uztOEb20fTP2r0AUnStn1Q9WYghikiDO5wzkgV+n6KC2Q==",
"dependencies": {
"Microsoft.AspNetCore.Metadata": "8.0.3",
"Microsoft.AspNetCore.Metadata": "8.0.6",
"Microsoft.Extensions.Logging.Abstractions": "8.0.1",
"Microsoft.Extensions.Options": "8.0.2"
}
},
"Microsoft.AspNetCore.Components": {
"type": "Transitive",
"resolved": "8.0.3",
"contentHash": "q1Da8sfxG+B+BSYpc/3RKNEdzGcLbDTXkTUqekY65kXMMVCTqTAQ0Zs4csmB7FNVTFSjwaw1dGMFD0bQ+erlBw==",
"resolved": "8.0.6",
"contentHash": "Je2l+rd5i8gB+ZWsN5wBHOsnyAh81h6+a5vNq6rjfQhM2ZYxH+BVsmanCYa+F8BFy6rmd+ZL61SEiNaAyAOgtg==",
"dependencies": {
"Microsoft.AspNetCore.Authorization": "8.0.3",
"Microsoft.AspNetCore.Components.Analyzers": "8.0.3"
"Microsoft.AspNetCore.Authorization": "8.0.6",
"Microsoft.AspNetCore.Components.Analyzers": "8.0.6"
}
},
"Microsoft.AspNetCore.Components.Analyzers": {
"type": "Transitive",
"resolved": "8.0.3",
"contentHash": "iERLuYM+YFI/K1jkinr1YeAkJYHUcijPiPCKgmgs2ZhJLqiIVJRT08vUtIsfhiFtGiI5MIzK0R1BZHyS3yAQng=="
"resolved": "8.0.6",
"contentHash": "SEL0CN1jJdJkCDwRox3kSY1ffMnahlOCDJZYrYWfQ6ftjqRuuiPtwyvI3VIeVHLcpT3VP1AXww8wzKDK3oeFxg=="
},
"Microsoft.AspNetCore.Components.Forms": {
"type": "Transitive",
"resolved": "8.0.3",
"contentHash": "OxY5NDmePnn6FMb+Fum57YL7LCHk3u2Wg0qSln3uZSayo+oIxYuoGnqH2dUMp1P5vOPfq17NKCIIEbxfU2dirQ==",
"resolved": "8.0.6",
"contentHash": "49wIE1ns3ZGDU4NPMcKCSTC716IxujUeaynsUyflM5Qmb2WlF6M6m4pMcJ6UET1Ixmn027nZjMEc8J7pU4BLBg==",
"dependencies": {
"Microsoft.AspNetCore.Components": "8.0.3"
"Microsoft.AspNetCore.Components": "8.0.6"
}
},
"Microsoft.AspNetCore.Components.Web": {
"type": "Transitive",
"resolved": "8.0.3",
"contentHash": "bHWJiz/JhjptK3iYzha0Rm73chjFcbMAOD9DdDq2tn1rp4rQa/K7O/zdnZpSYAT3nI33Q0aY6ts6t0PUVu5hCA==",
"resolved": "8.0.6",
"contentHash": "xxYlTpcTEWsxaWUscDLN32mM32ysslLFtNQBS6wJOxaKJ9LCvusXDyBVEKL2DPbB8PZdereNvyrVr0deHkuOEw==",
"dependencies": {
"Microsoft.AspNetCore.Components": "8.0.3",
"Microsoft.AspNetCore.Components.Forms": "8.0.3",
"Microsoft.AspNetCore.Components": "8.0.6",
"Microsoft.AspNetCore.Components.Forms": "8.0.6",
"Microsoft.Extensions.DependencyInjection": "8.0.0",
"Microsoft.Extensions.Primitives": "8.0.0",
"Microsoft.JSInterop": "8.0.3",
"Microsoft.JSInterop": "8.0.6",
"System.IO.Pipelines": "8.0.0"
}
},
"Microsoft.AspNetCore.Metadata": {
"type": "Transitive",
"resolved": "8.0.3",
"contentHash": "MAdmICjtSckGDutLRMydRI0pvBcGw/WoC4UC+hZ999idVW96l5iJlhyrtakgYkF5Rp0ekNVNWA+onP2gJZUkpw=="
"resolved": "8.0.6",
"contentHash": "3e7S/kz1MGds8zDA8SfQiutqifyVcULs5P/8Bxpes0WIkJRMGkU/l+XlMllj9KrDfCGwHW8bgjRy/zxfoILgfg=="
},
"Microsoft.Extensions.DependencyInjection": {
"type": "Transitive",
@ -116,19 +116,19 @@
},
"Microsoft.Extensions.Localization": {
"type": "Transitive",
"resolved": "8.0.3",
"contentHash": "EjHnIiEwNq9xl8S36hf0nj64IF3DTQ1l5PVK8Vo+QocYKDKpDPONHNJC8env0DPwod7T5oA1HYLLo2mdSTCgrQ==",
"resolved": "8.0.6",
"contentHash": "EXcSpQE6E80Qutej0ZShz1oRWBFocDzir/R3Gd1jAqZ5SnPpSr1Ep/tH5ha0cRXY22cWVuOb7I9rM32Ew+ju7g==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.1",
"Microsoft.Extensions.Localization.Abstractions": "8.0.3",
"Microsoft.Extensions.Localization.Abstractions": "8.0.6",
"Microsoft.Extensions.Logging.Abstractions": "8.0.1",
"Microsoft.Extensions.Options": "8.0.2"
}
},
"Microsoft.Extensions.Localization.Abstractions": {
"type": "Transitive",
"resolved": "8.0.3",
"contentHash": "k/kUPm1FQBxcs9/vsM1eF4qIOg2Sovqh/+KUGHur5Mc0Y3OFGuoz9ktBX7LA0gPz53SZhW3W3oaSaMFFcjgM6Q=="
"resolved": "8.0.6",
"contentHash": "oQdKQ4xlb+Qa4t1gWGj58cocLdWZynP+wLExIAxozh2SrvffEzTvTGI2IbRx4mBNgg9jbbyoPE8nNOwJ1Thddg=="
},
"Microsoft.Extensions.Logging.Abstractions": {
"type": "Transitive",
@ -154,8 +154,8 @@
},
"Microsoft.JSInterop": {
"type": "Transitive",
"resolved": "8.0.3",
"contentHash": "Oi21Fa7KubCzafwXb2IOdSGg24+/ylYGwrJgAYdWmgXBj04Oj/1b8vr9hrcoFKjQ6K18ryHYh35ZO/CCIEhuzg=="
"resolved": "8.0.6",
"contentHash": "E1djWS3d41fcd++7sGlbYhOHde5Pb0oBpOcNvUbn+1ga/yCvsjzUfbd/tDRg1qacNKS0iwKWYOIqTZxJnh99dQ=="
},
"System.IO.Pipelines": {
"type": "Transitive",
@ -163,6 +163,6 @@
"contentHash": "FHNOatmUq0sqJOkTx+UF/9YK1f180cnW5FVqnQMvYUN0elp6wFzbtPSiqbo1/ru8ICp43JM1i7kKkk6GsNGHlA=="
}
},
"net8.0/osx-x64": {}
"net8.0/osx-arm64": {}
}
}