diff --git a/app/MindWork AI Studio/Assistants/AssistantBase.razor b/app/MindWork AI Studio/Assistants/AssistantBase.razor index 0a7fb3d..18a7f49 100644 --- a/app/MindWork AI Studio/Assistants/AssistantBase.razor +++ b/app/MindWork AI Studio/Assistants/AssistantBase.razor @@ -45,7 +45,7 @@ @if (!this.FooterButtons.Any(x => x.Type is ButtonTypes.SEND_TO)) { - + @foreach (var assistant in Enum.GetValues().OrderBy(n => n.Name().Length)) { if (assistant is Components.NONE || this.Component == assistant) @@ -77,7 +77,7 @@ break; case SendToButton sendToButton: - + @foreach (var assistant in Enum.GetValues().OrderBy(n => n.Name().Length)) { if(assistant is Components.NONE || sendToButton.Self == assistant) @@ -96,7 +96,7 @@ Copy result - + Reset diff --git a/app/MindWork AI Studio/Assistants/AssistantBase.razor.cs b/app/MindWork AI Studio/Assistants/AssistantBase.razor.cs index da0ac6f..6fcee51 100644 --- a/app/MindWork AI Studio/Assistants/AssistantBase.razor.cs +++ b/app/MindWork AI Studio/Assistants/AssistantBase.razor.cs @@ -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 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(ComponentBase? sendingComponent, Event triggeredEvent, T? data) + { + switch (triggeredEvent) + { + case Event.COLOR_THEME_CHANGED: + this.StateHasChanged(); + break; + } + + return Task.CompletedTask; + } + + public Task ProcessMessageWithResult(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data) + { + return Task.FromResult(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 } \ No newline at end of file diff --git a/app/MindWork AI Studio/Components/AssistantBlock.razor b/app/MindWork AI Studio/Components/AssistantBlock.razor index f0bb54c..0fc497a 100644 --- a/app/MindWork AI Studio/Components/AssistantBlock.razor +++ b/app/MindWork AI Studio/Components/AssistantBlock.razor @@ -1,4 +1,4 @@ - + diff --git a/app/MindWork AI Studio/Components/AssistantBlock.razor.cs b/app/MindWork AI Studio/Components/AssistantBlock.razor.cs index 4a15ddb..f79b7bd 100644 --- a/app/MindWork AI Studio/Components/AssistantBlock.razor.cs +++ b/app/MindWork AI Studio/Components/AssistantBlock.razor.cs @@ -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(ComponentBase? sendingComponent, Event triggeredEvent, T? data) + { + switch (triggeredEvent) + { + case Event.COLOR_THEME_CHANGED: + this.StateHasChanged(); + break; + } + + return Task.CompletedTask; + } + + public Task ProcessMessageWithResult(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data) + { + return Task.FromResult(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 } \ No newline at end of file diff --git a/app/MindWork AI Studio/Components/ConfidenceInfo.razor b/app/MindWork AI Studio/Components/ConfidenceInfo.razor index 6623220..e43a4cb 100644 --- a/app/MindWork AI Studio/Components/ConfidenceInfo.razor +++ b/app/MindWork AI Studio/Components/ConfidenceInfo.razor @@ -3,11 +3,11 @@ @if (this.Mode is ConfidenceInfoMode.ICON) { - + } else { - + Confidence } diff --git a/app/MindWork AI Studio/Components/ConfidenceInfo.razor.cs b/app/MindWork AI Studio/Components/ConfidenceInfo.razor.cs index 7e2fb67..a541b27 100644 --- a/app/MindWork AI Studio/Components/ConfidenceInfo.razor.cs +++ b/app/MindWork AI Studio/Components/ConfidenceInfo.razor.cs @@ -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(ComponentBase? sendingComponent, Event triggeredEvent, T? data) + { + switch (triggeredEvent) + { + case Event.COLOR_THEME_CHANGED: + this.showConfidence = false; + this.StateHasChanged(); + break; + } + + return Task.CompletedTask; + } + + public Task ProcessMessageWithResult(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data) + { + return Task.FromResult(default); + } + + #endregion + + #region Implementation of IDisposable + + public void Dispose() + { + this.MessageBus.Unregister(this); + } + + #endregion } \ No newline at end of file diff --git a/app/MindWork AI Studio/Components/InnerScrolling.razor.cs b/app/MindWork AI Studio/Components/InnerScrolling.razor.cs index 6a3c057..0ac5846 100644 --- a/app/MindWork AI Studio/Components/InnerScrolling.razor.cs +++ b/app/MindWork AI Studio/Components/InnerScrolling.razor.cs @@ -43,7 +43,7 @@ public partial class InnerScrolling : MSGComponentBase #region Overrides of MSGComponentBase - public override Task ProcessMessage(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default + public override Task ProcessIncomingMessage(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default { switch (triggeredEvent) { diff --git a/app/MindWork AI Studio/Components/MSGComponentBase.cs b/app/MindWork AI Studio/Components/MSGComponentBase.cs index ae88bbd..08f5416 100644 --- a/app/MindWork AI Studio/Components/MSGComponentBase.cs +++ b/app/MindWork AI Studio/Components/MSGComponentBase.cs @@ -19,7 +19,22 @@ public abstract class MSGComponentBase : ComponentBase, IDisposable, IMessageBus #region Implementation of IMessageBusReceiver - public abstract Task ProcessMessage(ComponentBase? sendingComponent, Event triggeredEvent, T? data); + public Task ProcessMessage(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(ComponentBase? sendingComponent, Event triggeredEvent, T? data); public abstract Task ProcessMessageWithResult(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(events) + { + Event.COLOR_THEME_CHANGED + }; + + this.MessageBus.ApplyFilters(this, components, eventsList.ToArray()); } } \ No newline at end of file diff --git a/app/MindWork AI Studio/Layout/MainLayout.razor b/app/MindWork AI Studio/Layout/MainLayout.razor index 9fbb328..8d92955 100644 --- a/app/MindWork AI Studio/Layout/MainLayout.razor +++ b/app/MindWork AI Studio/Layout/MainLayout.razor @@ -11,17 +11,17 @@ - @foreach (var navBarItem in NAV_ITEMS) + @foreach (var navBarItem in this.navItems) { if (this.SettingsManager.ConfigurationData.App.NavigationBehavior is NavBehavior.NEVER_EXPAND_USE_TOOLTIPS) { - @navBarItem.Name + @navBarItem.Name } else { - @navBarItem.Name + @navBarItem.Name } } @@ -58,4 +58,6 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/app/MindWork AI Studio/Layout/MainLayout.razor.cs b/app/MindWork AI Studio/Layout/MainLayout.razor.cs index 18961fe..4581ee3 100644 --- a/app/MindWork AI Studio/Layout/MainLayout.razor.cs +++ b/app/MindWork AI Studio/Layout/MainLayout.razor.cs @@ -34,6 +34,9 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, IDis [Inject] private ILogger 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 NAV_ITEMS = new List - { - 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 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 + { + 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(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(this, Event.COLOR_THEME_CHANGED); + this.StateHasChanged(); + } #region Implementation of IDisposable diff --git a/app/MindWork AI Studio/Layout/NavBarItem.cs b/app/MindWork AI Studio/Layout/NavBarItem.cs index d76a806..efa90cd 100644 --- a/app/MindWork AI Studio/Layout/NavBarItem.cs +++ b/app/MindWork AI Studio/Layout/NavBarItem.cs @@ -1,3 +1,8 @@ +using AIStudio.Settings; + namespace AIStudio.Layout; -public record NavBarItem(string Name, string Icon, Color IconColor, string Path, bool MatchAll); \ No newline at end of file +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)};"; +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Pages/Chat.razor.cs b/app/MindWork AI Studio/Pages/Chat.razor.cs index ceed6cd..c5d3180 100644 --- a/app/MindWork AI Studio/Pages/Chat.razor.cs +++ b/app/MindWork AI Studio/Pages/Chat.razor.cs @@ -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(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default + public override Task ProcessIncomingMessage(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default { switch (triggeredEvent) { diff --git a/app/MindWork AI Studio/Pages/Settings.razor b/app/MindWork AI Studio/Pages/Settings.razor index 1f4e20c..27e565e 100644 --- a/app/MindWork AI Studio/Pages/Settings.razor +++ b/app/MindWork AI Studio/Pages/Settings.razor @@ -180,6 +180,7 @@ + diff --git a/app/MindWork AI Studio/Pages/Settings.razor.cs b/app/MindWork AI Studio/Pages/Settings.razor.cs index b0789ab..9ff7beb 100644 --- a/app/MindWork AI Studio/Pages/Settings.razor.cs +++ b/app/MindWork AI Studio/Pages/Settings.razor.cs @@ -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) diff --git a/app/MindWork AI Studio/Program.cs b/app/MindWork AI Studio/Program.cs index a68a933..00d434a 100644 --- a/app/MindWork AI Studio/Program.cs +++ b/app/MindWork AI Studio/Program.cs @@ -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(); diff --git a/app/MindWork AI Studio/Provider/Confidence.cs b/app/MindWork AI Studio/Provider/Confidence.cs index 3cf08ac..ac5557a 100644 --- a/app/MindWork AI Studio/Provider/Confidence.cs +++ b/app/MindWork AI Studio/Provider/Confidence.cs @@ -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() { diff --git a/app/MindWork AI Studio/Provider/ConfidenceLevelExtensions.cs b/app/MindWork AI Studio/Provider/ConfidenceLevelExtensions.cs index 2f8d451..116238f 100644 --- a/app/MindWork AI Studio/Provider/ConfidenceLevelExtensions.cs +++ b/app/MindWork AI Studio/Provider/ConfidenceLevelExtensions.cs @@ -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)};"; } \ No newline at end of file diff --git a/app/MindWork AI Studio/Routes.razor b/app/MindWork AI Studio/Routes.razor index 4a026ad..3988f98 100644 --- a/app/MindWork AI Studio/Routes.razor +++ b/app/MindWork AI Studio/Routes.razor @@ -8,7 +8,6 @@ - \ No newline at end of file diff --git a/app/MindWork AI Studio/Settings/ConfigurationSelectData.cs b/app/MindWork AI Studio/Settings/ConfigurationSelectData.cs index 2ba2a49..517a8a1 100644 --- a/app/MindWork AI Studio/Settings/ConfigurationSelectData.cs +++ b/app/MindWork AI Studio/Settings/ConfigurationSelectData.cs @@ -170,4 +170,10 @@ public static class ConfigurationSelectDataFactory } } } + + public static IEnumerable> GetThemesData() + { + foreach (var theme in Enum.GetValues()) + yield return new(theme.GetName(), theme); + } } \ No newline at end of file diff --git a/app/MindWork AI Studio/Settings/DataModel/DataApp.cs b/app/MindWork AI Studio/Settings/DataModel/DataApp.cs index ce1f475..f749c3c 100644 --- a/app/MindWork AI Studio/Settings/DataModel/DataApp.cs +++ b/app/MindWork AI Studio/Settings/DataModel/DataApp.cs @@ -2,6 +2,11 @@ namespace AIStudio.Settings.DataModel; public sealed class DataApp { + /// + /// The preferred theme to use. + /// + public Themes PreferredTheme { get; set; } = Themes.SYSTEM; + /// /// Should we save energy? When true, we will update content streamed /// from the server, i.e., AI, less frequently. diff --git a/app/MindWork AI Studio/Settings/DataModel/Themes.cs b/app/MindWork AI Studio/Settings/DataModel/Themes.cs new file mode 100644 index 0000000..177f67a --- /dev/null +++ b/app/MindWork AI Studio/Settings/DataModel/Themes.cs @@ -0,0 +1,8 @@ +namespace AIStudio.Settings.DataModel; + +public enum Themes +{ + SYSTEM = 0, + LIGHT, + DARK, +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Settings/DataModel/ThemesExtensions.cs b/app/MindWork AI Studio/Settings/DataModel/ThemesExtensions.cs new file mode 100644 index 0000000..7942ab7 --- /dev/null +++ b/app/MindWork AI Studio/Settings/DataModel/ThemesExtensions.cs @@ -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", + }; + } +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Settings/SettingsManager.cs b/app/MindWork AI Studio/Settings/SettingsManager.cs index 8dee766..4944521 100644 --- a/app/MindWork AI Studio/Settings/SettingsManager.cs +++ b/app/MindWork AI Studio/Settings/SettingsManager.cs @@ -21,7 +21,7 @@ public sealed class SettingsManager(ILogger logger) Converters = { new JsonStringEnumConverter() }, }; - private ILogger logger = logger; + private readonly ILogger logger = logger; /// /// The directory where the configuration files are stored. @@ -33,6 +33,11 @@ public sealed class SettingsManager(ILogger logger) /// public static string? DataDirectory { get; set; } + /// + /// Whether the app is in dark mode. + /// + public bool IsDarkMode { get; set; } + /// /// The configuration data. /// diff --git a/app/MindWork AI Studio/Tools/Event.cs b/app/MindWork AI Studio/Tools/Event.cs index cacc17f..54ae9d0 100644 --- a/app/MindWork AI Studio/Tools/Event.cs +++ b/app/MindWork AI Studio/Tools/Event.cs @@ -7,6 +7,7 @@ public enum Event // Common events: STATE_HAS_CHANGED, CONFIGURATION_CHANGED, + COLOR_THEME_CHANGED, // Update events: USER_SEARCH_FOR_UPDATE, diff --git a/app/MindWork AI Studio/Tools/MudThemeExtensions.cs b/app/MindWork AI Studio/Tools/MudThemeExtensions.cs new file mode 100644 index 0000000..7a6108c --- /dev/null +++ b/app/MindWork AI Studio/Tools/MudThemeExtensions.cs @@ -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, + }; +} \ No newline at end of file diff --git a/app/MindWork AI Studio/wwwroot/app.css b/app/MindWork AI Studio/wwwroot/app.css index bc7638d..f4277fc 100644 --- a/app/MindWork AI Studio/wwwroot/app.css +++ b/app/MindWork AI Studio/wwwroot/app.css @@ -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; } \ No newline at end of file diff --git a/app/MindWork AI Studio/wwwroot/changelog/v0.9.11.md b/app/MindWork AI Studio/wwwroot/changelog/v0.9.11.md index 538a925..5fc956a 100644 --- a/app/MindWork AI Studio/wwwroot/changelog/v0.9.11.md +++ b/app/MindWork AI Studio/wwwroot/changelog/v0.9.11.md @@ -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. \ No newline at end of file diff --git a/media/Startup Icon.psd b/media/Startup Icon.psd new file mode 100644 index 0000000..2e392b5 --- /dev/null +++ b/media/Startup Icon.psd @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6330c250dfaa88bca9668d7ff050d664b78fa424ab4595a1a651cbbbc85bb206 +size 559978 diff --git a/runtime/ui/icon.jpg b/runtime/ui/icon.jpg deleted file mode 100644 index 35c27eb..0000000 Binary files a/runtime/ui/icon.jpg and /dev/null differ diff --git a/runtime/ui/icon.png b/runtime/ui/icon.png new file mode 100644 index 0000000..d5308a2 Binary files /dev/null and b/runtime/ui/icon.png differ diff --git a/runtime/ui/index.html b/runtime/ui/index.html index 901a4d5..c7882d7 100644 --- a/runtime/ui/index.html +++ b/runtime/ui/index.html @@ -16,9 +16,15 @@ img { display: block; } + + @media (prefers-color-scheme: dark) { + html, body { + background-color: #1a1a1a; + } + } - The app logo + The app logo \ No newline at end of file