Added color themes (dark & light) (#148)

This commit is contained in:
Thorsten Sommer 2024-09-15 12:30:07 +02:00 committed by GitHub
parent 0ad7b8d4dd
commit e9998348c5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 350 additions and 50 deletions

View File

@ -45,7 +45,7 @@
@if (!this.FooterButtons.Any(x => x.Type is ButtonTypes.SEND_TO))
{
<MudMenu StartIcon="@Icons.Material.Filled.Apps" EndIcon="@Icons.Material.Filled.KeyboardArrowDown" Label="Send to ..." Variant="Variant.Filled" Color="Color.Info">
<MudMenu StartIcon="@Icons.Material.Filled.Apps" EndIcon="@Icons.Material.Filled.KeyboardArrowDown" Label="Send to ..." Variant="Variant.Filled" Style="@this.GetSendToColor()" Class="rounded">
@foreach (var assistant in Enum.GetValues<Components>().OrderBy(n => n.Name().Length))
{
if (assistant is Components.NONE || this.Component == assistant)
@ -77,7 +77,7 @@
break;
case SendToButton sendToButton:
<MudMenu StartIcon="@Icons.Material.Filled.Apps" EndIcon="@Icons.Material.Filled.KeyboardArrowDown" Label="Send to ..." Variant="Variant.Filled" Color="Color.Info">
<MudMenu StartIcon="@Icons.Material.Filled.Apps" EndIcon="@Icons.Material.Filled.KeyboardArrowDown" Label="Send to ..." Variant="Variant.Filled" Style="@this.GetSendToColor()" Class="rounded">
@foreach (var assistant in Enum.GetValues<Components>().OrderBy(n => n.Name().Length))
{
if(assistant is Components.NONE || sendToButton.Self == assistant)
@ -96,7 +96,7 @@
Copy result
</MudButton>
<MudButton Variant="Variant.Filled" Color="Color.Warning" StartIcon="@Icons.Material.Filled.Refresh" OnClick="() => this.InnerResetForm()">
<MudButton Variant="Variant.Filled" Style="@this.GetResetColor()" StartIcon="@Icons.Material.Filled.Refresh" OnClick="() => this.InnerResetForm()">
Reset
</MudButton>

View File

@ -8,7 +8,7 @@ using RustService = AIStudio.Tools.RustService;
namespace AIStudio.Assistants;
public abstract partial class AssistantBase : ComponentBase
public abstract partial class AssistantBase : ComponentBase, IMessageBusReceiver, IDisposable
{
[Inject]
protected SettingsManager SettingsManager { get; init; } = null!;
@ -31,6 +31,12 @@ public abstract partial class AssistantBase : ComponentBase
[Inject]
protected ILogger<AssistantBase> Logger { get; init; } = null!;
[Inject]
private MudTheme ColorTheme { get; init; } = null!;
[Inject]
private MessageBus MessageBus { get; init; } = null!;
internal const string AFTER_RESULT_DIV_ID = "afterAssistantResult";
internal const string RESULT_DIV_ID = "assistantResult";
@ -91,6 +97,10 @@ public abstract partial class AssistantBase : ComponentBase
this.MightPreselectValues();
this.providerSettings = this.SettingsManager.GetPreselectedProvider(this.Component);
this.currentProfile = this.SettingsManager.GetPreselectedProfile(this.Component);
this.MessageBus.RegisterComponent(this);
this.MessageBus.ApplyFilters(this, [], [ Event.COLOR_THEME_CHANGED ]);
await base.OnInitializedAsync();
}
@ -113,8 +123,29 @@ public abstract partial class AssistantBase : ComponentBase
}
#endregion
#region Implementation of IMessageBusReceiver
private string SubmitButtonStyle => this.SettingsManager.ConfigurationData.LLMProviders.ShowProviderConfidence ? this.providerSettings.UsedLLMProvider.GetConfidence(this.SettingsManager).StyleBorder() : string.Empty;
public Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data)
{
switch (triggeredEvent)
{
case Event.COLOR_THEME_CHANGED:
this.StateHasChanged();
break;
}
return Task.CompletedTask;
}
public Task<TResult?> ProcessMessageWithResult<TPayload, TResult>(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data)
{
return Task.FromResult<TResult?>(default);
}
#endregion
private string SubmitButtonStyle => this.SettingsManager.ConfigurationData.LLMProviders.ShowProviderConfidence ? this.providerSettings.UsedLLMProvider.GetConfidence(this.SettingsManager).StyleBorder(this.SettingsManager) : string.Empty;
protected string? ValidatingProvider(AIStudio.Settings.Provider provider)
{
@ -251,4 +282,25 @@ public abstract partial class AssistantBase : ComponentBase
this.StateHasChanged();
this.form?.ResetValidation();
}
private string GetResetColor() => this.SettingsManager.IsDarkMode switch
{
true => $"background-color: #804000",
false => $"background-color: {this.ColorTheme.GetCurrentPalette(this.SettingsManager).Warning.Value}",
};
private string GetSendToColor() => this.SettingsManager.IsDarkMode switch
{
true => $"background-color: #004080",
false => $"background-color: {this.ColorTheme.GetCurrentPalette(this.SettingsManager).InfoLighten}",
};
#region Implementation of IDisposable
public void Dispose()
{
this.MessageBus.Unregister(this);
}
#endregion
}

View File

@ -1,4 +1,4 @@
<MudCard Outlined="@true" Style="border-width: 2px; border-color: #0d47a1; border-radius: 12px; border-style: solid; max-width: 20em;">
<MudCard Outlined="@true" Style="@this.BlockStyle">
<MudCardHeader>
<CardHeaderContent>
<MudStack AlignItems="AlignItems.Center" Row="@true">

View File

@ -1,8 +1,10 @@
using AIStudio.Settings;
using Microsoft.AspNetCore.Components;
namespace AIStudio.Components;
public partial class AssistantBlock : ComponentBase
public partial class AssistantBlock : ComponentBase, IMessageBusReceiver, IDisposable
{
[Parameter]
public string Name { get; set; } = string.Empty;
@ -18,4 +20,63 @@ public partial class AssistantBlock : ComponentBase
[Parameter]
public string Link { get; set; } = string.Empty;
[Inject]
private MudTheme ColorTheme { get; init; } = null!;
[Inject]
private SettingsManager SettingsManager { get; init; } = null!;
[Inject]
private MessageBus MessageBus { get; init; } = null!;
#region Overrides of ComponentBase
protected override async Task OnInitializedAsync()
{
this.MessageBus.RegisterComponent(this);
this.MessageBus.ApplyFilters(this, [], [ Event.COLOR_THEME_CHANGED ]);
await base.OnInitializedAsync();
}
#endregion
#region Implementation of IMessageBusReceiver
public Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data)
{
switch (triggeredEvent)
{
case Event.COLOR_THEME_CHANGED:
this.StateHasChanged();
break;
}
return Task.CompletedTask;
}
public Task<TResult?> ProcessMessageWithResult<TPayload, TResult>(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data)
{
return Task.FromResult<TResult?>(default);
}
#endregion
private string BorderColor => this.SettingsManager.IsDarkMode switch
{
true => this.ColorTheme.GetCurrentPalette(this.SettingsManager).GrayLight,
false => this.ColorTheme.GetCurrentPalette(this.SettingsManager).Primary.Value,
};
private string BlockStyle => $"border-width: 2px; border-color: {this.BorderColor}; border-radius: 12px; border-style: solid; max-width: 20em;";
#region Implementation of IDisposable
public void Dispose()
{
this.MessageBus.Unregister(this);
}
#endregion
}

View File

@ -3,11 +3,11 @@
<MudTooltip Text="Shows and hides the confidence card with information about the selected LLM provider.">
@if (this.Mode is ConfidenceInfoMode.ICON)
{
<MudIconButton Icon="@Icons.Material.Filled.Security" Class="confidence-icon" Style="@this.LLMProvider.GetConfidence(this.SettingsManager).SetColorStyle()" OnClick="@(() => this.ToggleConfidence())"/>
<MudIconButton Icon="@Icons.Material.Filled.Security" Class="confidence-icon" Style="@this.LLMProvider.GetConfidence(this.SettingsManager).SetColorStyle(this.SettingsManager)" OnClick="@(() => this.ToggleConfidence())"/>
}
else
{
<MudButton Variant="Variant.Filled" StartIcon="@Icons.Material.Filled.Security" IconClass="confidence-icon" Style="@this.LLMProvider.GetConfidence(this.SettingsManager).SetColorStyle()" OnClick="@(() => this.ToggleConfidence())">
<MudButton Variant="Variant.Filled" StartIcon="@Icons.Material.Filled.Security" IconClass="confidence-icon" Style="@this.LLMProvider.GetConfidence(this.SettingsManager).SetColorStyle(this.SettingsManager)" OnClick="@(() => this.ToggleConfidence())">
Confidence
</MudButton>
}

View File

@ -5,7 +5,7 @@ using Microsoft.AspNetCore.Components;
namespace AIStudio.Components;
public partial class ConfidenceInfo : ComponentBase
public partial class ConfidenceInfo : ComponentBase, IMessageBusReceiver, IDisposable
{
[Parameter]
public ConfidenceInfoMode Mode { get; set; } = ConfidenceInfoMode.BUTTON;
@ -15,6 +15,9 @@ public partial class ConfidenceInfo : ComponentBase
[Inject]
private SettingsManager SettingsManager { get; init; } = null!;
[Inject]
private MessageBus MessageBus { get; init; } = null!;
private Confidence currentConfidence;
private bool showConfidence;
@ -28,6 +31,9 @@ public partial class ConfidenceInfo : ComponentBase
protected override async Task OnParametersSetAsync()
{
this.MessageBus.RegisterComponent(this);
this.MessageBus.ApplyFilters(this, [], [ Event.COLOR_THEME_CHANGED ]);
this.currentConfidence = this.LLMProvider.GetConfidence(this.SettingsManager);
await base.OnParametersSetAsync();
}
@ -51,7 +57,38 @@ public partial class ConfidenceInfo : ComponentBase
yield return ($"Source {++index}", source);
}
private string GetCurrentConfidenceColor() => $"color: {this.currentConfidence.Level.GetColor()};";
private string GetCurrentConfidenceColor() => $"color: {this.currentConfidence.Level.GetColor(this.SettingsManager)};";
private string GetPopoverStyle() => $"border-color: {this.currentConfidence.Level.GetColor()}; max-width: calc(35vw);";
private string GetPopoverStyle() => $"border-color: {this.currentConfidence.Level.GetColor(this.SettingsManager)}; max-width: calc(35vw);";
#region Implementation of IMessageBusReceiver
public Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data)
{
switch (triggeredEvent)
{
case Event.COLOR_THEME_CHANGED:
this.showConfidence = false;
this.StateHasChanged();
break;
}
return Task.CompletedTask;
}
public Task<TResult?> ProcessMessageWithResult<TPayload, TResult>(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data)
{
return Task.FromResult<TResult?>(default);
}
#endregion
#region Implementation of IDisposable
public void Dispose()
{
this.MessageBus.Unregister(this);
}
#endregion
}

View File

@ -43,7 +43,7 @@ public partial class InnerScrolling : MSGComponentBase
#region Overrides of MSGComponentBase
public override Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
public override Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
{
switch (triggeredEvent)
{

View File

@ -19,7 +19,22 @@ public abstract class MSGComponentBase : ComponentBase, IDisposable, IMessageBus
#region Implementation of IMessageBusReceiver
public abstract Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data);
public Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data)
{
switch (triggeredEvent)
{
case Event.COLOR_THEME_CHANGED:
this.StateHasChanged();
break;
default:
return this.ProcessIncomingMessage(sendingComponent, triggeredEvent, data);
}
return Task.CompletedTask;
}
public abstract Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data);
public abstract Task<TResult?> ProcessMessageWithResult<TPayload, TResult>(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data);
@ -46,6 +61,12 @@ public abstract class MSGComponentBase : ComponentBase, IDisposable, IMessageBus
protected void ApplyFilters(ComponentBase[] components, Event[] events)
{
this.MessageBus.ApplyFilters(this, components, events);
// Append the color theme changed event to the list of events:
var eventsList = new List<Event>(events)
{
Event.COLOR_THEME_CHANGED
};
this.MessageBus.ApplyFilters(this, components, eventsList.ToArray());
}
}

View File

@ -11,17 +11,17 @@
<MudDrawerContainer Class="mud-height-full absolute">
<MudDrawer @bind-Open="@this.navBarOpen" MiniWidth="@NAVBAR_COLLAPSED_WIDTH" Width="@NAVBAR_EXPANDED_WIDTH" Elevation="1" Fixed="@true" Variant="@DrawerVariant.Mini" OpenMiniOnHover="@(this.SettingsManager.ConfigurationData.App.NavigationBehavior is NavBehavior.EXPAND_ON_HOVER)" Color="Color.Default">
<MudNavMenu>
@foreach (var navBarItem in NAV_ITEMS)
@foreach (var navBarItem in this.navItems)
{
if (this.SettingsManager.ConfigurationData.App.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>
<MudNavLink Href="@navBarItem.Path" Match="@(navBarItem.MatchAll ? NavLinkMatch.All : NavLinkMatch.Prefix)" Icon="@navBarItem.Icon" Style="@navBarItem.SetColorStyle(this.SettingsManager)" Class="custom-icon-color">@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>
<MudNavLink Href="@navBarItem.Path" Match="@(navBarItem.MatchAll ? NavLinkMatch.All : NavLinkMatch.Prefix)" Icon="@navBarItem.Icon" Style="@navBarItem.SetColorStyle(this.SettingsManager)" Class="custom-icon-color">@navBarItem.Name</MudNavLink>
}
}
</MudNavMenu>
@ -58,4 +58,6 @@
</MudContainer>
</MudMainContent>
</MudLayout>
</MudPaper>
</MudPaper>
<MudThemeProvider @ref="@this.themeProvider" Theme="@this.ColorTheme" IsDarkMode="@this.useDarkMode" />

View File

@ -34,6 +34,9 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, IDis
[Inject]
private ILogger<MainLayout> Logger { get; init; } = null!;
[Inject]
private MudTheme ColorTheme { get; init; } = null!;
public string AdditionalHeight { get; private set; } = "0em";
@ -50,16 +53,10 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, IDis
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, Routes.HOME, true),
new("Chat", Icons.Material.Filled.Chat, Color.Default, Routes.CHAT, false),
new("Assistants", Icons.Material.Filled.Apps, Color.Default, Routes.ASSISTANTS, false),
new("Supporters", Icons.Material.Filled.Favorite, Color.Error, Routes.SUPPORTERS, false),
new("About", Icons.Material.Filled.Info, Color.Default, Routes.ABOUT, false),
new("Settings", Icons.Material.Filled.Settings, Color.Default, Routes.SETTINGS, false),
};
private MudThemeProvider themeProvider = null!;
private bool useDarkMode;
private IReadOnlyCollection<NavBarItem> navItems = [];
#region Overrides of ComponentBase
@ -87,7 +84,7 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, IDis
// Register this component with the message bus:
this.MessageBus.RegisterComponent(this);
this.MessageBus.ApplyFilters(this, [], [ Event.UPDATE_AVAILABLE, Event.USER_SEARCH_FOR_UPDATE, Event.CONFIGURATION_CHANGED ]);
this.MessageBus.ApplyFilters(this, [], [ Event.UPDATE_AVAILABLE, Event.USER_SEARCH_FOR_UPDATE, Event.CONFIGURATION_CHANGED, Event.COLOR_THEME_CHANGED ]);
// Set the snackbar for the update service:
UpdateService.SetBlazorDependencies(this.Snackbar);
@ -96,6 +93,20 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, IDis
// Should the navigation bar be open by default?
if(this.SettingsManager.ConfigurationData.App.NavigationBehavior is NavBehavior.ALWAYS_EXPAND)
this.navBarOpen = true;
await this.themeProvider.WatchSystemPreference(this.SystemeThemeChanged);
await this.UpdateThemeConfiguration();
var palette = this.ColorTheme.GetCurrentPalette(this.SettingsManager);
this.navItems = new List<NavBarItem>
{
new("Home", Icons.Material.Filled.Home, palette.DarkLighten, palette.GrayLight, Routes.HOME, true),
new("Chat", Icons.Material.Filled.Chat, palette.DarkLighten, palette.GrayLight, Routes.CHAT, false),
new("Assistants", Icons.Material.Filled.Apps, palette.DarkLighten, palette.GrayLight, Routes.ASSISTANTS, false),
new("Supporters", Icons.Material.Filled.Favorite, palette.Error.Value, "#801a00", Routes.SUPPORTERS, false),
new("About", Icons.Material.Filled.Info, palette.DarkLighten, palette.GrayLight, Routes.ABOUT, false),
new("Settings", Icons.Material.Filled.Settings, palette.DarkLighten, palette.GrayLight, Routes.SETTINGS, false),
};
await base.OnInitializedAsync();
}
@ -131,6 +142,11 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, IDis
else
this.navBarOpen = false;
await this.UpdateThemeConfiguration();
this.StateHasChanged();
break;
case Event.COLOR_THEME_CHANGED:
this.StateHasChanged();
break;
}
@ -217,6 +233,24 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, IDis
await MessageBus.INSTANCE.SendMessage<bool>(this, Event.RESET_CHAT_STATE);
}
}
private async Task SystemeThemeChanged(bool isDark)
{
this.Logger.LogInformation($"The system theme changed to {(isDark ? "dark" : "light")}.");
await this.UpdateThemeConfiguration();
}
private async Task UpdateThemeConfiguration()
{
if (this.SettingsManager.ConfigurationData.App.PreferredTheme is Themes.SYSTEM)
this.useDarkMode = await this.themeProvider.GetSystemPreference();
else
this.useDarkMode = this.SettingsManager.ConfigurationData.App.PreferredTheme == Themes.DARK;
this.SettingsManager.IsDarkMode = this.useDarkMode;
await this.MessageBus.SendMessage<bool>(this, Event.COLOR_THEME_CHANGED);
this.StateHasChanged();
}
#region Implementation of IDisposable

View File

@ -1,3 +1,8 @@
using AIStudio.Settings;
namespace AIStudio.Layout;
public record NavBarItem(string Name, string Icon, Color IconColor, string Path, bool MatchAll);
public record NavBarItem(string Name, string Icon, string IconLightColor, string IconDarkColor, string Path, bool MatchAll)
{
public string SetColorStyle(SettingsManager settingsManager) => $"--custom-icon-color: {(settingsManager.IsDarkMode ? this.IconDarkColor : this.IconLightColor)};";
}

View File

@ -120,7 +120,7 @@ public partial class Chat : MSGComponentBase, IAsyncDisposable
private string TooltipAddChatToWorkspace => $"Start new chat in workspace \"{this.currentWorkspaceName}\"";
private string UserInputStyle => this.SettingsManager.ConfigurationData.LLMProviders.ShowProviderConfidence ? this.providerSettings.UsedLLMProvider.GetConfidence(this.SettingsManager).SetColorStyle() : string.Empty;
private string UserInputStyle => this.SettingsManager.ConfigurationData.LLMProviders.ShowProviderConfidence ? this.providerSettings.UsedLLMProvider.GetConfidence(this.SettingsManager).SetColorStyle(this.SettingsManager) : string.Empty;
private string UserInputClass => this.SettingsManager.ConfigurationData.LLMProviders.ShowProviderConfidence ? "confidence-border" : string.Empty;
@ -455,7 +455,7 @@ public partial class Chat : MSGComponentBase, IAsyncDisposable
#region Overrides of MSGComponentBase
public override Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
public override Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
{
switch (triggeredEvent)
{

View File

@ -180,6 +180,7 @@
</ExpansionPanel>
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Apps" HeaderText="App Options">
<ConfigurationSelect OptionDescription="Color theme" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreferredTheme)" Data="@ConfigurationSelectDataFactory.GetThemesData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.PreferredTheme = selectedValue)" OptionHelp="Choose the color theme that best suits for you."/>
<ConfigurationOption OptionDescription="Save energy?" LabelOn="Energy saving is enabled" LabelOff="Energy saving is disabled" State="@(() => this.SettingsManager.ConfigurationData.App.IsSavingEnergy)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.App.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.App.EnableSpellchecking)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.App.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="Check for updates" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.UpdateBehavior)" Data="@ConfigurationSelectDataFactory.GetUpdateBehaviorData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.UpdateBehavior = selectedValue)" OptionHelp="How often should we check for app updates?"/>

View File

@ -171,9 +171,9 @@ public partial class Settings : ComponentBase, IMessageBusReceiver, IDisposable
private string SetCurrentConfidenceLevelColorStyle(LLMProviders llmProvider)
{
if (this.SettingsManager.ConfigurationData.LLMProviders.CustomConfidenceScheme.TryGetValue(llmProvider, out var level))
return $"background-color: {level.GetColor()};";
return $"background-color: {level.GetColor(this.SettingsManager)};";
return $"background-color: {ConfidenceLevel.UNKNOWN.GetColor()};";
return $"background-color: {ConfidenceLevel.UNKNOWN.GetColor(this.SettingsManager)};";
}
private async Task ChangeCustomConfidenceLevel(LLMProviders llmProvider, ConfidenceLevel level)

View File

@ -106,6 +106,7 @@ internal sealed class Program
});
builder.Services.AddMudMarkdownServices();
builder.Services.AddSingleton(new MudTheme());
builder.Services.AddSingleton(MessageBus.INSTANCE);
builder.Services.AddSingleton(rust);
builder.Services.AddMudMarkdownClipboardService<MarkdownClipboardService>();

View File

@ -1,3 +1,5 @@
using AIStudio.Settings;
namespace AIStudio.Provider;
public sealed record Confidence
@ -20,9 +22,9 @@ public sealed record Confidence
public Confidence WithLevel(ConfidenceLevel level) => this with { Level = level };
public string StyleBorder() => $"border: 2px solid {this.Level.GetColor()}; border-radius: 6px;";
public string StyleBorder(SettingsManager settingsManager) => $"border: 2px solid {this.Level.GetColor(settingsManager)}; border-radius: 6px;";
public string SetColorStyle() => $"--confidence-color: {this.Level.GetColor()};";
public string SetColorStyle(SettingsManager settingsManager) => $"--confidence-color: {this.Level.GetColor(settingsManager)};";
public static readonly Confidence NONE = new()
{

View File

@ -1,3 +1,5 @@
using AIStudio.Settings;
namespace AIStudio.Provider;
public static class ConfidenceLevelExtensions
@ -16,19 +18,31 @@ public static class ConfidenceLevelExtensions
_ => "Unknown confidence level",
};
public static string GetColor(this ConfidenceLevel level) => level switch
public static string GetColor(this ConfidenceLevel level, SettingsManager settingsManager) => (level, settingsManager.IsDarkMode) switch
{
ConfidenceLevel.NONE => "#cccccc",
(ConfidenceLevel.NONE, _) => "#cccccc",
ConfidenceLevel.UNTRUSTED => "#ff0000",
ConfidenceLevel.VERY_LOW => "#ff6600",
ConfidenceLevel.LOW => "#ffcc00",
ConfidenceLevel.MODERATE => "#99cc00",
ConfidenceLevel.MEDIUM => "#86b300",
ConfidenceLevel.HIGH => "#009933",
(ConfidenceLevel.UNTRUSTED, false) => "#ff0000",
(ConfidenceLevel.UNTRUSTED, true) => "#800000",
_ => "#cc6600",
(ConfidenceLevel.VERY_LOW, false) => "#ff6600",
(ConfidenceLevel.VERY_LOW, true) => "#803300",
(ConfidenceLevel.LOW, false) => "#ffcc00",
(ConfidenceLevel.LOW, true) => "#806600",
(ConfidenceLevel.MODERATE, false) => "#99cc00",
(ConfidenceLevel.MODERATE, true) => "#4d6600",
(ConfidenceLevel.MEDIUM, false) => "#86b300",
(ConfidenceLevel.MEDIUM, true) => "#394d00",
(ConfidenceLevel.HIGH, false) => "#009933",
(ConfidenceLevel.HIGH, true) => "#004d1a",
(_, false) => "#cc6600",
(_, true) => "#663300",
};
public static string SetColorStyle(this ConfidenceLevel level) => $"--confidence-color: {level.GetColor()};";
public static string SetColorStyle(this ConfidenceLevel level, SettingsManager settingsManager) => $"--confidence-color: {level.GetColor(settingsManager)};";
}

View File

@ -8,7 +8,6 @@
</Found>
</Router>
<MudThemeProvider />
<MudDialogProvider />
<MudPopoverProvider />
<MudSnackbarProvider />

View File

@ -170,4 +170,10 @@ public static class ConfigurationSelectDataFactory
}
}
}
public static IEnumerable<ConfigurationSelectData<Themes>> GetThemesData()
{
foreach (var theme in Enum.GetValues<Themes>())
yield return new(theme.GetName(), theme);
}
}

View File

@ -2,6 +2,11 @@ namespace AIStudio.Settings.DataModel;
public sealed class DataApp
{
/// <summary>
/// The preferred theme to use.
/// </summary>
public Themes PreferredTheme { get; set; } = Themes.SYSTEM;
/// <summary>
/// Should we save energy? When true, we will update content streamed
/// from the server, i.e., AI, less frequently.

View File

@ -0,0 +1,8 @@
namespace AIStudio.Settings.DataModel;
public enum Themes
{
SYSTEM = 0,
LIGHT,
DARK,
}

View File

@ -0,0 +1,16 @@
namespace AIStudio.Settings.DataModel;
public static class ThemesExtensions
{
public static string GetName(this Themes theme)
{
return theme switch
{
Themes.SYSTEM => "Synchronized with the operating system settings",
Themes.LIGHT => "Always use light theme",
Themes.DARK => "Always use dark theme",
_ => "Unknown setting",
};
}
}

View File

@ -21,7 +21,7 @@ public sealed class SettingsManager(ILogger<SettingsManager> logger)
Converters = { new JsonStringEnumConverter() },
};
private ILogger<SettingsManager> logger = logger;
private readonly ILogger<SettingsManager> logger = logger;
/// <summary>
/// The directory where the configuration files are stored.
@ -33,6 +33,11 @@ public sealed class SettingsManager(ILogger<SettingsManager> logger)
/// </summary>
public static string? DataDirectory { get; set; }
/// <summary>
/// Whether the app is in dark mode.
/// </summary>
public bool IsDarkMode { get; set; }
/// <summary>
/// The configuration data.
/// </summary>

View File

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

View File

@ -0,0 +1,12 @@
using AIStudio.Settings;
namespace AIStudio.Tools;
public static class MudThemeExtensions
{
public static Palette GetCurrentPalette(this MudTheme theme, SettingsManager settingsManager) => settingsManager.IsDarkMode switch
{
true => theme.PaletteDark,
false => theme.PaletteLight,
};
}

View File

@ -46,4 +46,12 @@
.confidence-border > .mud-input-control-input-container > .mud-input > .mud-input-outlined-border {
border-width: 2px;
border-color: var(--confidence-color) !important;
}
:root {
--custom-icon-color: #000000;
}
.custom-icon-color > a > svg {
color: var(--custom-icon-color) !important;
}

View File

@ -1,5 +1,6 @@
# v0.9.11, build 186
- Added an option to enforce a minimum confidence level throughout the entire app.
- Added options to enforce minimum confidence levels for each assistant individually.
- Added color themes (dark & light) to the app settings. The theme can be synchronized with the system theme.
- Added a tooltip to the confidence card button.
- Renamed the `Providers` enum to `LLMProviders` for better clarity. Renamed also all dependent variables and methods.

BIN
media/Startup Icon.psd (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

BIN
runtime/ui/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

View File

@ -16,9 +16,15 @@
img {
display: block;
}
@media (prefers-color-scheme: dark) {
html, body {
background-color: #1a1a1a;
}
}
</style>
</head>
<body>
<img src="icon.jpg" width="512px" height="512px" alt="The app logo">
<img src="icon.png" width="512" height="512" alt="The app logo">
</body>
</html>