Repository: Flow-Launcher/Flow.Launcher
Pull request #2448: Add Flow Launcher Theme Selector to Sys plugin
Author: login: Odotocodot
Labels: name: enhancement
description: New feature or request
Created: 2023-11-30T14:42:47Z | Merged: 2025-03-13T01:35:52Z

A plugin to quickly change the current Flow Launcher theme.

![image](https://github.com/Flow-Launcher/Flow.Launcher/assets/48138990/3acf1a71-e351-477e-94db-694e8fdbe04b)

If someone has a better suggestion for the default action keyword rather than `flowtheme`, would love to hear it. Was a bit apprehensive in using theme 🤷‍♂️ .




Diff:
diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs
index 4deea1f6648..cda125a39cb 100644
--- a/Flow.Launcher.Core/Resource/Theme.cs
+++ b/Flow.Launcher.Core/Resource/Theme.cs
@@ -12,7 +12,6 @@
 using Flow.Launcher.Infrastructure;
 using Flow.Launcher.Infrastructure.Logger;
 using Flow.Launcher.Infrastructure.UserSettings;
-using CommunityToolkit.Mvvm.DependencyInjection;
 using Flow.Launcher.Plugin;
 
 namespace Flow.Launcher.Core.Resource
@@ -35,6 +34,8 @@ public class Theme
         private string DirectoryPath => Path.Combine(Constant.ProgramDirectory, Folder);
         private string UserDirectoryPath => Path.Combine(DataLocation.DataDirectory(), Folder);
 
+        public string CurrentTheme => _settings.Theme;
+
         public bool BlurEnabled { get; set; }
 
         private double mainWindowWidth;
diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj b/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj
index dbc36ad424b..266c2417008 100644
--- a/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj
+++ b/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj
@@ -39,6 +39,7 @@
   <ItemGroup>
     <ProjectReference Include="..\..\Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj" />
     <ProjectReference Include="..\..\Flow.Launcher.Plugin\Flow.Launcher.Plugin.csproj" />
+    <ProjectReference Include="..\..\Flow.Launcher.Core\Flow.Launcher.Core.csproj" />
   </ItemGroup>
 
   <ItemGroup>
diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Images/theme_selector.png b/Plugins/Flow.Launcher.Plugin.Sys/Images/theme_selector.png
new file mode 100644
index 00000000000..704e9474eb2
Binary files /dev/null and b/Plugins/Flow.Launcher.Plugin.Sys/Images/theme_selector.png differ
diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/en.xaml
index 91f32a844f8..2a266f8f651 100644
--- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/en.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/en.xaml
@@ -27,6 +27,7 @@
     <system:String x:Key="flowlauncher_plugin_sys_open_docs_tips_cmd">Flow Launcher Tips</system:String>
     <system:String x:Key="flowlauncher_plugin_sys_open_userdata_location_cmd">Flow Launcher UserData Folder</system:String>
     <system:String x:Key="flowlauncher_plugin_sys_toggle_game_mode_cmd">Toggle Game Mode</system:String>
+    <system:String x:Key="flowlauncher_plugin_sys_theme_selector_cmd">Set the Flow Launcher Theme</system:String>
 
     <!--  Command Descriptions  -->
     <system:String x:Key="flowlauncher_plugin_sys_shutdown_computer">Shutdown Computer</system:String>
@@ -49,8 +50,9 @@
     <system:String x:Key="flowlauncher_plugin_sys_open_docs_tips">Visit Flow Launcher's documentation for more help and how to use tips</system:String>
     <system:String x:Key="flowlauncher_plugin_sys_open_userdata_location">Open the location where Flow Launcher's settings are stored</system:String>
     <system:String x:Key="flowlauncher_plugin_sys_toggle_game_mode">Toggle Game Mode</system:String>
+    <system:String x:Key="flowlauncher_plugin_sys_theme_selector">Quickly change the Flow Launcher theme</system:String>
 
-        <!--  Dialogs  -->
+    <!--  Dialogs  -->
     <system:String x:Key="flowlauncher_plugin_sys_dlgtitle_success">Success</system:String>
     <system:String x:Key="flowlauncher_plugin_sys_dlgtext_all_settings_saved">All Flow Launcher settings saved</system:String>
     <system:String x:Key="flowlauncher_plugin_sys_dlgtext_all_applicableplugins_reloaded">Reloaded all applicable plugin data</system:String>
diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs
index 2331ee68c05..28747cc7cf1 100644
--- a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs
+++ b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs
@@ -7,7 +7,6 @@
 using Flow.Launcher.Infrastructure;
 using Flow.Launcher.Infrastructure.Logger;
 using Flow.Launcher.Infrastructure.UserSettings;
-using Flow.Launcher.Plugin.SharedCommands;
 using Windows.Win32;
 using Windows.Win32.Foundation;
 using Windows.Win32.Security;
@@ -20,6 +19,7 @@ namespace Flow.Launcher.Plugin.Sys
     public class Main : IPlugin, ISettingProvider, IPluginI18n
     {
         private PluginInitContext context;
+        private ThemeSelector themeSelector;
         private Dictionary<string, string> KeywordTitleMappings = new Dictionary<string, string>();
 
         // SHTDN_REASON_MAJOR_OTHER indicates a generic shutdown reason that isn't categorized under hardware failure, software updates, or other predefined reasons.
@@ -34,6 +34,11 @@ public Control CreateSettingPanel()
 
         public List<Result> Query(Query query)
         {
+            if(query.Search.StartsWith(ThemeSelector.Keyword))
+            {
+                return themeSelector.Query(query);
+            }
+
             var commands = Commands();
             var results = new List<Result>();
             foreach (var c in commands)
@@ -82,6 +87,7 @@ private string GetDynamicTitle(Query query, Result result)
         public void Init(PluginInitContext context)
         {
             this.context = context;
+            themeSelector = new ThemeSelector(context);
             KeywordTitleMappings = new Dictionary<string, string>{
                 {"Shutdown", "flowlauncher_plugin_sys_shutdown_computer_cmd"},
                 {"Restart", "flowlauncher_plugin_sys_restart_computer_cmd"},
@@ -102,7 +108,8 @@ public void Init(PluginInitContext context)
                 {"Open Log Location", "flowlauncher_plugin_sys_open_log_location_cmd"},
                 {"Flow Launcher Tips", "flowlauncher_plugin_sys_open_docs_tips_cmd"},
                 {"Flow Launcher UserData Folder", "flowlauncher_plugin_sys_open_userdata_location_cmd"},
-                {"Toggle Game Mode", "flowlauncher_plugin_sys_toggle_game_mode_cmd"}
+                {"Toggle Game Mode", "flowlauncher_plugin_sys_toggle_game_mode_cmd"},
+                {"Set Flow Launcher Theme", "flowlauncher_plugin_sys_theme_selector_cmd"}
             };
         }
 
@@ -451,6 +458,18 @@ private List<Result> Commands()
                         context.API.ToggleGameMode();
                         return true;
                     }
+                },
+                new Result
+                {
+                    Title = "Set Flow Launcher Theme",
+                    SubTitle = context.API.GetTranslation("flowlauncher_plugin_sys_theme_selector"),
+                    IcoPath = "Images\\app.png",
+                    Glyph = new GlyphInfo (FontFamily:"/Resources/#Segoe Fluent Icons", Glyph:"\ue7fc"),
+                    Action = c =>
+                    {
+                        context.API.ChangeQuery($"{ThemeSelector.Keyword} ");
+                        return false;
+                    }
                 }
             });
 
diff --git a/Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs b/Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs
new file mode 100644
index 00000000000..38619cbbc2c
--- /dev/null
+++ b/Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs
@@ -0,0 +1,115 @@
+﻿using System.Collections.Generic;
+using System.Linq;
+using CommunityToolkit.Mvvm.DependencyInjection;
+using Flow.Launcher.Core.Resource;
+using Flow.Launcher.Infrastructure.UserSettings;
+
+namespace Flow.Launcher.Plugin.Sys
+{
+    public class ThemeSelector
+    {
+        public const string Keyword = "fltheme";
+
+        private readonly Settings _settings;
+        private readonly Theme _theme;
+        private readonly PluginInitContext _context;
+
+        #region Theme Selection
+
+        // Theme select codes simplified from SettingsPaneThemeViewModel.cs
+
+        private Theme.ThemeData _selectedTheme;
+        private Theme.ThemeData SelectedTheme
+        {
+            get => _selectedTheme ??= Themes.Find(v => v.FileNameWithoutExtension == _theme.CurrentTheme);
+            set
+            {
+                _selectedTheme = value;
+                _theme.ChangeTheme(value.FileNameWithoutExtension);
+
+                if (_theme.BlurEnabled && _settings.UseDropShadowEffect)
+                {
+                    _theme.RemoveDropShadowEffectFromCurrentTheme();
+                    _settings.UseDropShadowEffect = false;
+                }
+            }
+        }
+
+        private List<Theme.ThemeData> Themes => _theme.LoadAvailableThemes();
+
+        #endregion
+
+        public ThemeSelector(PluginInitContext context)
+        {
+            _context = context;
+            _theme = Ioc.Default.GetRequiredService<Theme>();
+            _settings = Ioc.Default.GetRequiredService<Settings>();
+        }
+
+        public List<Result> Query(Query query)
+        {
+            var search = query.SecondToEndSearch;
+            if (string.IsNullOrWhiteSpace(search))
+            {
+                return Themes.Select(CreateThemeResult)
+                    .OrderBy(x => x.Title)
+                    .ToList();
+            }
+
+            return Themes.Select(theme => (theme, matchResult: _context.API.FuzzySearch(search, theme.Name)))
+                .Where(x => x.matchResult.IsSearchPrecisionScoreMet())
+                .Select(x => CreateThemeResult(x.theme, x.matchResult.Score, x.matchResult.MatchData))
+                .OrderBy(x => x.Title)
+                .ToList();
+        }
+
+        private Result CreateThemeResult(Theme.ThemeData theme) => CreateThemeResult(theme, 0, null);
+
+        private Result CreateThemeResult(Theme.ThemeData theme, int score, IList<int> highlightData)
+        {
+            string themeName = theme.Name;
+            string title;
+            if (theme == SelectedTheme)
+            {
+                title = $"{theme.Name} ★";
+                // Set current theme to the top
+                score = 2000;
+            }
+            else
+            {
+                title = theme.Name;
+                // Set them to 1000 so that they are higher than other non-theme records
+                score = 1000;
+            }
+
+            string description = string.Empty;
+            if (theme.IsDark == true)
+            {
+                description += _context.API.GetTranslation("TypeIsDarkToolTip");
+            }
+
+            if (theme.HasBlur == true)
+            {
+                if (!string.IsNullOrEmpty(description))
+                    description += " ";
+                description += _context.API.GetTranslation("TypeHasBlurToolTip");
+            }
+
+            return new Result
+            {
+                Title = title,
+                TitleHighlightData = highlightData,
+                SubTitle = description,
+                IcoPath = "Images\\theme_selector.png",
+                Glyph = new GlyphInfo("/Resources/#Segoe Fluent Icons", "\ue790"),
+                Score = score,
+                Action = c =>
+                {
+                    SelectedTheme = theme;
+                    _context.API.ReQuery();
+                    return false;
+                }
+            };
+        }
+    }
+}


Reviews:
user: login: jjw24
state: COMMENTED
author_association: MEMBER
submitted_at: 2024-06-16T03:59:07Z
user: login: Odotocodot
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-06-16T12:11:00Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 2**

<details>
<summary>Outside diff range and nitpick comments (3)</summary><blockquote>

<details>
<summary>Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs (2)</summary><blockquote>

`11-11`: Consider renaming the constant `Keyword` to something more specific to avoid potential naming conflicts and increase clarity.

---

`56-83`: The methods `CreateThemeResult` are well implemented and provide a clear way to create result objects. Consider adding XML documentation to these methods to improve code maintainability.

</blockquote></details>
<details>
<summary>Plugins/Flow.Launcher.Plugin.Sys/Languages/en.xaml (1)</summary><blockquote>

`30-30`: Ensure the command description aligns with the functionality of setting themes. It might be helpful to specify that this command allows for dynamic theme switching.

</blockquote></details>

</blockquote></details>



<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-06-17T15:55:52Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 2**

<details>
<summary>♻️ Duplicate comments (2)</summary><blockquote>

<details>
<summary>Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs (2)</summary><blockquote>

`22-44`: _:hammer_and_wrench: Refactor suggestion_

**Add parameter validation and optimize string operations.**

The Query method needs parameter validation and can be optimized:

```diff
     public List<Result> Query(Query query)
     {
+        if (query == null) throw new ArgumentNullException(nameof(query));
+        if (string.IsNullOrEmpty(query.Search)) return new List<Result>();
+
         if (query.IsReQuery)
         {
             LoadThemes();
         }

         int keywordIndex = query.Search.IndexOf(Keyword, StringComparison.Ordinal);
+        if (keywordIndex == -1) return new List<Result>();
         string search = query.Search[(keywordIndex + Keyword.Length + 1)..];
```

---

`54-55`: _:hammer_and_wrench: Refactor suggestion_

**Add exception handling for theme loading.**

The LoadThemes method should handle potential IO exceptions that might occur during theme loading.

```diff
     private void LoadThemes() 
-        => themes = ThemeManager.Instance.LoadAvailableThemes().Select(Path.GetFileNameWithoutExtension);
+    {
+        try
+        {
+            themes = ThemeManager.Instance.LoadAvailableThemes().Select(Path.GetFileNameWithoutExtension);
+        }
+        catch (Exception ex) when (ex is IOException or UnauthorizedAccessException)
+        {
+            context.API.LogException("Failed to load themes", ex);
+            themes = Enumerable.Empty<string>();
+        }
+    }
```

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (3)</summary><blockquote>

<details>
<summary>Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs (3)</summary><blockquote>

`9-14`: **Add XML documentation for public members.**

Please add XML documentation for the public class and constant to improve API documentation.

```diff
+    /// <summary>
+    /// Manages theme selection functionality for the Flow Launcher Sys plugin.
+    /// </summary>
     public class ThemeSelector : IDisposable
     {
+        /// <summary>
+        /// The keyword used to trigger theme selection functionality.
+        /// </summary>
         public const string Keyword = "fltheme";
```

---

`11-11`: **Consider using a more intuitive keyword.**

The keyword "fltheme" is abbreviated and might not be immediately clear to users. Consider using "flowtheme" as mentioned in the PR description or "theme" for simplicity.

---

`59-84`: **Extract magic numbers and hardcoded values into constants.**

Consider extracting magic numbers and hardcoded values into named constants for better maintainability.

```diff
     public class ThemeSelector : IDisposable
     {
         public const string Keyword = "fltheme";
+        private const int CURRENT_THEME_SCORE_BOOST = 2000;
+        private const string THEME_ICON_FONT = "/Resources/#Segoe Fluent Icons";
+        private const string THEME_ICON_CODE = "\ue790";
+        private const string CURRENT_THEME_INDICATOR = "★";

         // ... existing code ...

         private static Result CreateThemeResult(string theme, int score, IList<int> highlightData)
         {
             string title;
             if (theme == ThemeManager.Instance.Settings.Theme)
             {
-                title = $"{theme} ★";
-                score = 2000;
+                title = $"{theme} {CURRENT_THEME_INDICATOR}";
+                score = CURRENT_THEME_SCORE_BOOST;
             }
             // ... existing code ...
-                Glyph = new GlyphInfo("/Resources/#Segoe Fluent Icons", "\ue790"),
+                Glyph = new GlyphInfo(THEME_ICON_FONT, THEME_ICON_CODE),
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 7a0be2c6103939312eeead86c3a1e01a3d618a3f and 874a785be40cf57755fd0ebadfb554f6517ac8e8.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs` (1 hunks)

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (2)</summary>

* GitHub Check: gitStream.cm
* GitHub Check: gitStream.cm

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-01-18T10:08:45Z
user: login: jjw24
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-01-18T10:08:55Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-01-18T10:09:11Z
user: login: jjw24
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-01-18T10:10:02Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-01-18T10:10:19Z
user: login: jjw24
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-01-18T10:16:16Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-01-18T10:16:32Z
user: login: jjw24
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-01-18T11:10:52Z
user: login: Odotocodot
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-01-18T13:00:23Z
user: login: Odotocodot
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-01-18T13:18:07Z
user: login: Odotocodot
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-01-18T13:23:44Z
user: login: Odotocodot
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-01-18T13:23:53Z
user: login: Jack251970
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-12T10:54:59Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>🧹 Nitpick comments (2)</summary><blockquote>

<details>
<summary>Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs (2)</summary><blockquote>

`25-29`: **Remove the finalizer if no unmanaged resources are in use.**

Since the `ThemeSelector` class primarily manages an event subscription, having a finalizer is generally unnecessary. Consider removing it along with the associated `Dispose(false)` call to simplify the code.


Apply this diff to remove the finalizer and keep the remaining disposable pattern:

```diff
-        ~ThemeSelector()
-        {
-            Dispose(false);
-        }

         // ...
         private bool disposed;
         protected virtual void Dispose(bool disposing)
         {
             if (!disposed)
             {
                 if (disposing)
                 {
                     // ...
                 }
                 // ...
                 disposed = true;
             }
         }
```


Also applies to: 96-119

---

`53-63`: **Consider adding error handling in `LoadThemes()`.**

If `LoadAvailableThemes()` encounters issues (e.g., IO exceptions), the plugin could fail silently. Adding minimal error handling can improve resiliency.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 59fbef1d7866b1cfa56871f9718ba53eb81eb44a and 468c0b23c053c6d0320c8d066a808f268dd83232.

</details>

<details>
<summary>📒 Files selected for processing (2)</summary>

* `Plugins/Flow.Launcher.Plugin.Sys/Main.cs` (6 hunks)
* `Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧠 Learnings (1)</summary>

<details>
<summary>Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs (1)</summary>

```
Learnt from: jjw24
PR: Flow-Launcher/Flow.Launcher#2448
File: Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs:16-20
Timestamp: 2025-01-18T10:10:18.414Z
Learning: In Flow Launcher's plugin system, the PluginInitContext parameter passed to plugin constructors is guaranteed to be non-null by the plugin initialization system, making null checks unnecessary.
```

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (1)</summary>

* GitHub Check: gitStream.cm

</details>

<details>
<summary>🔇 Additional comments (8)</summary><blockquote>

<details>
<summary>Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs (3)</summary>

`18-23`: **Constructor usage is consistent with the plugin system guarantee.**

No null-check for `context` is needed here since Flow Launcher guarantees a non-null `PluginInitContext`. This implementation is correct.

---

`30-51`: **Query logic is straightforward and maintainable.**

Calling `LoadThemes()` on a re-query is a clean approach to refresh theme data only when needed. This helps avoid unnecessary repeated operations and keeps performance overhead low.

---

`64-94`: **Dynamic scoring for the current theme is a nice touch.**

Using a star symbol and boosting the score for the active theme improves user experience. The code for creating theme results is clear and well-structured.

</details>
<details>
<summary>Plugins/Flow.Launcher.Plugin.Sys/Main.cs (5)</summary>

`20-23`: **Implementing `IDisposable` in `Main` is a prudent choice.**

Adding the `Disposable` interface and storing the `themeSelector` reference enables proper resource cleanup, preventing potential event leaks.

---

`38-41`: **Delegating queries to the theme selector is neat.**

Checking if the query starts with the `ThemeSelector.Keyword` and delegating to `themeSelector.Query(query)` keeps the codebase organized and extensible.

---

`90-92`: **Initialization and command mapping for theme selection is well-structured.**

Instantiating `themeSelector` in `Init`, plus adding an entry for "Set Flow Launcher Theme," cleanly integrates theme management into the Sys plugin and supports localization.




Also applies to: 112-113

---

`463-473`: **Invoking theme selection via result action is clear.**

Changing the query to `"fltheme "` upon selecting "Set Flow Launcher Theme" quickly guides users into theme-related queries. This is an intuitive approach.

---

`489-493`: **Disposing `themeSelector` ensures event subscription cleanup.**

Calling `themeSelector.Dispose()` from `Main.Dispose()` properly unsubscribes from events, preventing memory leaks and adhering to good disposable practices.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T10:57:49Z
user: login: Jack251970
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-12T10:58:37Z
user: login: Jack251970
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-12T11:02:38Z
user: login: Odotocodot
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T13:18:05Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>🧹 Nitpick comments (1)</summary><blockquote>

<details>
<summary>Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs (1)</summary><blockquote>

`62-62`: **Add null check for TitleHighlightData.**

Consider adding a null check for TitleHighlightData to prevent potential issues if highlightData is null.


```diff
-                TitleHighlightData = highlightData,
+                TitleHighlightData = highlightData != null ? highlightData : new List<int>(),
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 468c0b23c053c6d0320c8d066a808f268dd83232 and bad304b38f25aeddbb20a2c57a8d13704e99230f.

</details>

<details>
<summary>📒 Files selected for processing (2)</summary>

* `Plugins/Flow.Launcher.Plugin.Sys/Main.cs` (5 hunks)
* `Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs` (1 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (1)</summary>

* Plugins/Flow.Launcher.Plugin.Sys/Main.cs

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧠 Learnings (1)</summary>

<details>
<summary>Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs (1)</summary>

```
Learnt from: jjw24
PR: Flow-Launcher/Flow.Launcher#2448
File: Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs:16-20
Timestamp: 2025-03-12T13:10:00.156Z
Learning: In Flow Launcher's plugin system, the PluginInitContext parameter passed to plugin constructors is guaranteed to be non-null by the plugin initialization system, making null checks unnecessary.
```

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (6)</summary><blockquote>

<details>
<summary>Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs (6)</summary>

`8-11`: **LGTM: Class declaration and keyword.**

The class is well-structured with a clear constant for the keyword. The "fltheme" keyword matches the discussion in the PR objectives.

---

`15-19`: **Constructor implementation looks good.**

The constructor follows the project conventions by accepting the PluginInitContext and using dependency injection to get the theme service.

---

`21-39`: **Query method handles theme search effectively.**

The method loads available themes, properly handles empty searches, and implements fuzzy search for non-empty queries. Using `query.SecondToEndSearch` addresses the previous issue mentioned in comments when nothing is entered after the keyword.

---

`41-72`: **Theme result creation logic is well-implemented.**

The implementation correctly prioritizes the current theme with a star indicator and higher score, and provides all necessary properties for the results.

---

`8-74`: **Consider implementing IDisposable pattern.**

The class currently doesn't implement the IDisposable interface, but past review comments indicate this was suggested. If the class needs to handle unmanaged resources or event unsubscription, consider implementing the complete IDisposable pattern.



```diff
-    public class ThemeSelector
+    public class ThemeSelector : IDisposable
     {
         public const string Keyword = "fltheme";

         private readonly Theme _theme;
         private readonly PluginInitContext _context;
+        private bool _disposed;

         public ThemeSelector(PluginInitContext context)
         {
             _context = context;
             _theme = Ioc.Default.GetRequiredService<Theme>();
         }

         // ... existing methods ...

+        protected virtual void Dispose(bool disposing)
+        {
+            if (!_disposed)
+            {
+                if (disposing)
+                {
+                    // Dispose managed resources if needed
+                }
+                // Free unmanaged resources
+
+                _disposed = true;
+            }
+        }
+
+        ~ThemeSelector()
+        {
+            Dispose(false);
+        }
+
+        public void Dispose()
+        {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
     }
```

Don't forget to add `using System;` to the imports if implementing IDisposable.

---

`23-23`: <details>
<summary>✅ Verification successful</summary>

**Consider adding exception handling when loading themes.**

The method directly calls `_theme.LoadAvailableThemes()` without any exception handling. This could cause unhandled exceptions if there are issues accessing theme files.



```diff
-            var themes = _theme.LoadAvailableThemes().Select(x => x.FileNameWithoutExtension);
+            var themes = Enumerable.Empty<string>();
+            try
+            {
+                themes = _theme.LoadAvailableThemes().Select(x => x.FileNameWithoutExtension);
+            }
+            catch (System.Exception ex)
+            {
+                _context.API.LogException("Error loading themes", ex);
+                return new List<Result>
+                {
+                    new Result
+                    {
+                        Title = "Error loading themes",
+                        SubTitle = ex.Message,
+                        IcoPath = "Images\\theme_selector.png",
+                        Glyph = new GlyphInfo("/Resources/#Segoe Fluent Icons", "\ue790"),
+                    }
+                };
+            }
```

Let's verify if the `LoadAvailableThemes` method already has exception handling:

---

🏁 Script executed:

```shell
#!/bin/bash
# Check if LoadAvailableThemes has exception handling
rg -A 10 "LoadAvailableThemes" Flow.Launcher.Core/Resource/Theme.cs
```

Length of output: 567

---



</details>

**Exception Handling for Theme Loading**

After verifying that the `LoadAvailableThemes` method in `Flow.Launcher.Core/Resource/Theme.cs` does not internally handle exceptions, it's clear that wrapping its call in a try-catch block in `ThemeSelector.cs` is necessary. This change will ensure that any errors during theme file access are caught, logged (using `_context.API.LogException`), and result in an appropriate fallback response without crashing the application.

- **File:** `Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs` (Lines: 23)
- **Changes:**
  - Wrap the call to `_theme.LoadAvailableThemes().Select(...)` in a try-catch block.
  - Log any caught exceptions.
  - Return a fallback error `Result` list when an exception occurs.

The provided diff snippet correctly implements these improvements.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T13:53:13Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>🧹 Nitpick comments (5)</summary><blockquote>

<details>
<summary>Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs (5)</summary><blockquote>

`21-33`: **Consider handling a potential null assignment to SelectedTheme.**  
If a null value were somehow passed to `SelectedTheme`, referencing `value.FileNameWithoutExtension` at line 28 would cause a `NullReferenceException`. Although this is likely prevented by design, you could add a guard condition or argument check to ensure robustness.

---

`35-35`: **Evaluate caching themes to enhance performance.**  
Accessing `_theme.LoadAvailableThemes()` each time the `Themes` property is read can repeatedly scan the themes from disk or memory. Caching the themes when the plugin is invoked (e.g. on visibility change or first query) might improve performance, especially if there are many themes.

---

`69-84`: **Ensure theme loading strategy aligns with user expectations.**  
Loading all themes on every query helps ensure the list stays updated but can be more resource-intensive. If performance becomes a concern, consider loading themes only once on plugin activation or OnVisibilityChanged, then refreshing selectively if needed.

---

`86-135`: **Replace magic scores with named constants for better readability.**  
Using 2000 for the selected theme and 1000 for others is clear enough, but constants (e.g., `const int SelectedThemeScore = 2000;`) can improve maintainability by making the intent explicit.

---

`63-67`: **Consider adding a dedicated test for ThemeSelector logic.**  
Although manual testing might suffice, automated tests can validate queries, theme switching, and highlight data. Testing can help ensure future changes do not introduce regressions.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between bad304b38f25aeddbb20a2c57a8d13704e99230f and d099a392c5cbf91157587791fac2e37b2139f26e.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧠 Learnings (1)</summary>

<details>
<summary>Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs (1)</summary>

```
Learnt from: jjw24
PR: Flow-Launcher/Flow.Launcher#2448
File: Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs:16-20
Timestamp: 2025-03-12T13:10:00.156Z
Learning: In Flow Launcher's plugin system, the PluginInitContext parameter passed to plugin constructors is guaranteed to be non-null by the plugin initialization system, making null checks unnecessary.
```

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (1)</summary>

* GitHub Check: gitStream.cm

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T14:28:05Z
user: login: Jack251970
state: DISMISSED
author_association: MEMBER
submitted_at: 2025-03-12T14:32:29Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>🧹 Nitpick comments (5)</summary><blockquote>

<details>
<summary>Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs (5)</summary><blockquote>

`38-38`: **Consider caching theme data to improve performance.**

The `Themes` property directly calls `_theme.LoadAvailableThemes()` on each access, which could lead to performance issues if accessed frequently (especially during typing or multiple queries).


```diff
-        private List<Theme.ThemeData> Themes => _theme.LoadAvailableThemes();
+        private List<Theme.ThemeData> _themes;
+        private List<Theme.ThemeData> Themes => _themes ??= _theme.LoadAvailableThemes();
```

---

`86-88`: **Remove redundant boolean comparison.**

The explicit comparison with `true` is unnecessary and can be simplified.


```diff
-            if (theme.IsDark == true)
+            if (theme.IsDark)
```

---

`91-93`: **Remove redundant boolean comparison.**

Similar to the previous issue, the explicit comparison with `true` is unnecessary and can be simplified.


```diff
-            if (theme.HasBlur == true)
+            if (theme.HasBlur)
```

---

`42-47`: **Class is missing a using directive for System namespace.**

Add the System namespace for proper access to types like String and ArgumentNullException.


```diff
+using System;
 using System.Collections.Generic;
 using System.Linq;
```

---

`30-35`: **Add user notification for drop shadow effect changes.**

When automatically disabling the drop shadow effect due to blur being enabled, consider notifying the user about this change to improve user experience.


```diff
                 if (_theme.BlurEnabled && _settings.UseDropShadowEffect)
                 {
                     _theme.RemoveDropShadowEffectFromCurrentTheme();
                     _settings.UseDropShadowEffect = false;
+                    _context.API.ShowMsg(_context.API.GetTranslation("fltheme_drop_shadow_disabled"), 
+                                       _context.API.GetTranslation("fltheme_drop_shadow_disabled_due_to_blur"));
                 }
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between d099a392c5cbf91157587791fac2e37b2139f26e and 84cd1a84ac00dd301eb638f9c2293906efe30076.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧠 Learnings (1)</summary>

<details>
<summary>Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs (1)</summary>

```
Learnt from: jjw24
PR: Flow-Launcher/Flow.Launcher#2448
File: Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs:16-20
Timestamp: 2025-03-12T13:10:00.156Z
Learning: In Flow Launcher's plugin system, the PluginInitContext parameter passed to plugin constructors is guaranteed to be non-null by the plugin initialization system, making null checks unnecessary.
```

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (2)</summary>

* GitHub Check: gitStream.cm
* GitHub Check: gitStream.cm

</details>

<details>
<summary>🔇 Additional comments (4)</summary><blockquote>

<details>
<summary>Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs (4)</summary>

`42-47`: **Consider implementing IDisposable for proper resource cleanup.**

Since the class subscribes to events in other files (as seen from past review comments about `VisibilityChanged`), it should implement IDisposable to properly unsubscribe from these events.


```diff
-    public class ThemeSelector
+    public class ThemeSelector : IDisposable
     {
         // existing code...
+        
+        private bool _disposed;
+        
+        protected virtual void Dispose(bool disposing)
+        {
+            if (!_disposed)
+            {
+                if (disposing)
+                {
+                    // Unsubscribe from events
+                    // Clean up managed resources
+                }
+                
+                // Clean up unmanaged resources
+                
+                _disposed = true;
+            }
+        }
+        
+        ~ThemeSelector()
+        {
+            Dispose(false);
+        }
+        
+        public void Dispose()
+        {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
     }
```

---

`49-64`: **Good implementation of the Query method with proper handling of empty searches.**

The Query method efficiently handles both empty searches and specific search terms. It uses fuzzy search for filtering themes and properly orders the results.

---

`98-113`: **Well-structured Result object with appropriate score prioritization.**

The Result creation logic is well-designed, with thoughtful handling of the current theme (marking it with a star and giving it higher priority) and inclusion of descriptive information. The action to change the theme and requery is also appropriately implemented.

---

`103-104`: <details>
<summary>✅ Verification successful</summary>

**Verify that the theme_selector.png image exists.**

Make sure the referenced image file exists in the specified path, as missing resources could cause runtime issues.

---

🏁 Script executed:

```shell
#!/bin/bash
# Check if the theme_selector.png exists in the Images directory
ls -la Plugins/Flow.Launcher.Plugin.Sys/Images/theme_selector.png 2>/dev/null || echo "Image file not found"
```

Length of output: 175

---



</details>

**Asset Verification Passed: theme_selector.png Confirmed**

The referenced image file exists in the expected path (`Plugins/Flow.Launcher.Plugin.Sys/Images/theme_selector.png`). No further action is necessary.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T16:12:49Z
user: login: Jack251970
body: Look good to me! Thanks for your work!👍
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-12T16:22:20Z

Review comments:
diff_hunk: @@ -0,0 +1,95 @@
+﻿using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Flow.Launcher.Core.Resource;
+
+namespace Flow.Launcher.Plugin.Sys
+{
+    public class ThemeSelector : IReloadable, IDisposable
+    {
+        public const string Keyword = "fltheme";
+
+        private readonly PluginInitContext context;
+        private IEnumerable<string> themes;
+
+        public ThemeSelector(PluginInitContext context)
+        {
+            this.context = context;
+            context.API.VisibilityChanged += OnVisibilityChanged;
+        }
+
+        public List<Result> Query(Query query)
+        {
+            if (query.IsReQuery)
+            {
+                LoadThemes();
+            }
+
+            string search = query.Search[(query.Search.IndexOf(Keyword, StringComparison.Ordinal) + Keyword.Length + 1)..];
+
+            if (string.IsNullOrWhiteSpace(search))
+            {
+                return themes.Select(CreateThemeResult)
+                             .OrderBy(x => x.Title)
+                             .ToList();
+            }
+
+            return themes.Select(theme => (theme, matchResult: context.API.FuzzySearch(search, theme)))
+                         .Where(x => x.matchResult.IsSearchPrecisionScoreMet())
+                         .Select(x => CreateThemeResult(x.theme, x.matchResult.Score, x.matchResult.MatchData))
+                         .OrderBy(x => x.Title)
+                         .ToList();
+        }
+
+        private void OnVisibilityChanged(object sender, VisibilityChangedEventArgs args)
+        {
+            if (args.IsVisible && !context.CurrentPluginMetadata.Disabled)
+            {
+                LoadThemes();
+            }
+        }
+
+        private void LoadThemes() 
+            => themes = ThemeManager.Instance.LoadAvailableThemes().Select(Path.GetFileNameWithoutExtension);
+
+        private static Result CreateThemeResult(string theme) => CreateThemeResult(theme, 0, null);
+
+        private static Result CreateThemeResult(string theme, int score, IList<int> highlightData)
+        {
+            string title;
+            if (theme == ThemeManager.Instance.Settings.Theme)
+            {
+                title = $"{theme} ★";
+                score = 2000;
+            }
+            else
+            {
+                title = theme;
+            }
+
+            return new Result
+            {
+                Title = title,
+                TitleHighlightData = highlightData,
+                Glyph = new GlyphInfo("/Resources/#Segoe Fluent Icons", "\ue790"),
+                Score = score,
+                Action = c =>
+                {
+                    ThemeManager.Instance.ChangeTheme(theme);
+                    return true;
+                }
+            };
+        }
+
+        public void ReloadData() => LoadThemes();
path: Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs
user: login: jjw24
body: Why do we need to reload themes?
created_at: 2024-06-16T03:59:06Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,95 @@
+﻿using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Flow.Launcher.Core.Resource;
+
+namespace Flow.Launcher.Plugin.Sys
+{
+    public class ThemeSelector : IReloadable, IDisposable
+    {
+        public const string Keyword = "fltheme";
+
+        private readonly PluginInitContext context;
+        private IEnumerable<string> themes;
+
+        public ThemeSelector(PluginInitContext context)
+        {
+            this.context = context;
+            context.API.VisibilityChanged += OnVisibilityChanged;
+        }
+
+        public List<Result> Query(Query query)
+        {
+            if (query.IsReQuery)
+            {
+                LoadThemes();
+            }
+
+            string search = query.Search[(query.Search.IndexOf(Keyword, StringComparison.Ordinal) + Keyword.Length + 1)..];
+
+            if (string.IsNullOrWhiteSpace(search))
+            {
+                return themes.Select(CreateThemeResult)
+                             .OrderBy(x => x.Title)
+                             .ToList();
+            }
+
+            return themes.Select(theme => (theme, matchResult: context.API.FuzzySearch(search, theme)))
+                         .Where(x => x.matchResult.IsSearchPrecisionScoreMet())
+                         .Select(x => CreateThemeResult(x.theme, x.matchResult.Score, x.matchResult.MatchData))
+                         .OrderBy(x => x.Title)
+                         .ToList();
+        }
+
+        private void OnVisibilityChanged(object sender, VisibilityChangedEventArgs args)
+        {
+            if (args.IsVisible && !context.CurrentPluginMetadata.Disabled)
+            {
+                LoadThemes();
+            }
+        }
+
+        private void LoadThemes() 
+            => themes = ThemeManager.Instance.LoadAvailableThemes().Select(Path.GetFileNameWithoutExtension);
+
+        private static Result CreateThemeResult(string theme) => CreateThemeResult(theme, 0, null);
+
+        private static Result CreateThemeResult(string theme, int score, IList<int> highlightData)
+        {
+            string title;
+            if (theme == ThemeManager.Instance.Settings.Theme)
+            {
+                title = $"{theme} ★";
+                score = 2000;
+            }
+            else
+            {
+                title = theme;
+            }
+
+            return new Result
+            {
+                Title = title,
+                TitleHighlightData = highlightData,
+                Glyph = new GlyphInfo("/Resources/#Segoe Fluent Icons", "\ue790"),
+                Score = score,
+                Action = c =>
+                {
+                    ThemeManager.Instance.ChangeTheme(theme);
+                    return true;
+                }
+            };
+        }
+
+        public void ReloadData() => LoadThemes();
path: Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs
user: login: Odotocodot
body: That is a good point. I think the visibility changed should cover it!
created_at: 2024-06-16T12:11:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,93 @@
+﻿using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Flow.Launcher.Core.Resource;
+
+namespace Flow.Launcher.Plugin.Sys
+{
+    public class ThemeSelector : IDisposable
+    {
+        public const string Keyword = "fltheme";
+
+        private readonly PluginInitContext context;
+        private IEnumerable<string> themes;
+
+        public ThemeSelector(PluginInitContext context)
+        {
+            this.context = context;
+            context.API.VisibilityChanged += OnVisibilityChanged;
+        }
+
+        public List<Result> Query(Query query)
+        {
+            if (query.IsReQuery)
+            {
+                LoadThemes();
+            }
+
+            string search = query.Search[(query.Search.IndexOf(Keyword, StringComparison.Ordinal) + Keyword.Length + 1)..];
+
+            if (string.IsNullOrWhiteSpace(search))
+            {
+                return themes.Select(CreateThemeResult)
+                             .OrderBy(x => x.Title)
+                             .ToList();
+            }
+
+            return themes.Select(theme => (theme, matchResult: context.API.FuzzySearch(search, theme)))
+                         .Where(x => x.matchResult.IsSearchPrecisionScoreMet())
+                         .Select(x => CreateThemeResult(x.theme, x.matchResult.Score, x.matchResult.MatchData))
+                         .OrderBy(x => x.Title)
+                         .ToList();
+        }
path: Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs
user: login: coderabbitai[bot]
body: The `Query` method handles theme search effectively. However, consider caching the result of `query.Search.IndexOf(Keyword, StringComparison.Ordinal)` to avoid recalculating it multiple times.

```diff
- string search = query.Search[(query.Search.IndexOf(Keyword, StringComparison.Ordinal) + Keyword.Length + 1)..];
+ int keywordIndex = query.Search.IndexOf(Keyword, StringComparison.Ordinal);
+ string search = query.Search[(keywordIndex + Keyword.Length + 1)..];
```


<!-- suggestion_start -->
<details>
<summary>Committable suggestion</summary>

> :bangbang: **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
        public List<Result> Query(Query query)
        {
            if (query.IsReQuery)
            {
                LoadThemes();
            }

            int keywordIndex = query.Search.IndexOf(Keyword, StringComparison.Ordinal);
            string search = query.Search[(keywordIndex + Keyword.Length + 1)..];

            if (string.IsNullOrWhiteSpace(search))
            {
                return themes.Select(CreateThemeResult)
                             .OrderBy(x => x.Title)
                             .ToList();
            }

            return themes.Select(theme => (theme, matchResult: context.API.FuzzySearch(search, theme)))
                         .Where(x => x.matchResult.IsSearchPrecisionScoreMet())
                         .Select(x => CreateThemeResult(x.theme, x.matchResult.Score, x.matchResult.MatchData))
                         .OrderBy(x => x.Title)
                         .ToList();
        }
`````

</details>
<!-- suggestion_end -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2024-06-17T15:55:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,93 @@
+﻿using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Flow.Launcher.Core.Resource;
+
+namespace Flow.Launcher.Plugin.Sys
+{
+    public class ThemeSelector : IDisposable
+    {
+        public const string Keyword = "fltheme";
+
+        private readonly PluginInitContext context;
+        private IEnumerable<string> themes;
+
+        public ThemeSelector(PluginInitContext context)
+        {
+            this.context = context;
+            context.API.VisibilityChanged += OnVisibilityChanged;
+        }
+
+        public List<Result> Query(Query query)
+        {
+            if (query.IsReQuery)
+            {
+                LoadThemes();
+            }
+
+            string search = query.Search[(query.Search.IndexOf(Keyword, StringComparison.Ordinal) + Keyword.Length + 1)..];
+
+            if (string.IsNullOrWhiteSpace(search))
+            {
+                return themes.Select(CreateThemeResult)
+                             .OrderBy(x => x.Title)
+                             .ToList();
+            }
+
+            return themes.Select(theme => (theme, matchResult: context.API.FuzzySearch(search, theme)))
+                         .Where(x => x.matchResult.IsSearchPrecisionScoreMet())
+                         .Select(x => CreateThemeResult(x.theme, x.matchResult.Score, x.matchResult.MatchData))
+                         .OrderBy(x => x.Title)
+                         .ToList();
+        }
+
+        private void OnVisibilityChanged(object sender, VisibilityChangedEventArgs args)
+        {
+            if (args.IsVisible && !context.CurrentPluginMetadata.Disabled)
+            {
+                LoadThemes();
+            }
+        }
+
+        private void LoadThemes() 
+            => themes = ThemeManager.Instance.LoadAvailableThemes().Select(Path.GetFileNameWithoutExtension);
path: Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs
user: login: coderabbitai[bot]
body: > [!TIP]
> Codebase Verification

The `LoadAvailableThemes` method in `Flow.Launcher.Core/Resource/Theme.cs` currently does not include any exception handling for cases where the theme directories might not be accessible or other potential issues (e.g., IO exceptions). It would be prudent to add exception handling to ensure robustness.

- **Location**: `Flow.Launcher.Core/Resource/Theme.cs`
- **Method**: `LoadAvailableThemes`



<details>
<summary>Analysis chain</summary>

The method `LoadThemes` efficiently loads themes using LINQ. Ensure `ThemeManager.Instance.LoadAvailableThemes()` handles exceptions if the themes directory is not accessible.

</details>

<details>
<summary>Scripts executed</summary>

The following scripts were executed for the analysis:



---

Script:
```shell
#!/bin/bash
# Description: Check if ThemeManager handles exceptions during theme loading.

cat Plugins/Flow.Launcher.Plugin.Sys/ThemeManager.cs | grep -A 10 "LoadAvailableThemes"

```

Length of output: 168

---



---

Script:
```shell
#!/bin/bash
# Locate the file containing the ThemeManager class
fd ThemeManager.cs

# Once located, inspect the LoadAvailableThemes method
ast-grep --lang cs --pattern 'class ThemeManager { $$$ LoadAvailableThemes() { $$$ } $$$ }'

```

Length of output: 503

---



---

Script:
```shell
#!/bin/bash
# Search for the LoadAvailableThemes method in the ThemeManager.cs file
rg 'LoadAvailableThemes' Flow.Launcher.Core/Resource/ThemeManager.cs -A 20

```

Length of output: 74

---



---

Script:
```shell
#!/bin/bash
# Search for all method definitions in the ThemeManager.cs file
rg '^\s*(public|private|protected|internal)\s+\w+\s+\w+\s*\(' Flow.Launcher.Core/Resource/ThemeManager.cs

```

Length of output: 107

---



---

Script:
```shell
#!/bin/bash
# Search for all method definitions across the entire repository to locate the correct method for loading themes
rg '^\s*(public|private|protected|internal)\s+\w+\s+\w+\s*\('

```

Length of output: 81184

---



---

Script:
```shell
#!/bin/bash
# Search for the LoadAvailableThemes method across the entire repository
rg 'LoadAvailableThemes'

```

Length of output: 431

---



---

Script:
```shell
#!/bin/bash
# Inspect the LoadAvailableThemes method in Flow.Launcher.Core/Resource/Theme.cs
rg 'LoadAvailableThemes' Flow.Launcher.Core/Resource/Theme.cs -A 20

```

Length of output: 882

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2024-06-17T15:55:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,94 @@
+﻿using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Flow.Launcher.Core.Resource;
+
+namespace Flow.Launcher.Plugin.Sys
+{
+    public class ThemeSelector : IDisposable
+    {
+        public const string Keyword = "fltheme";
+
+        private readonly PluginInitContext context;
+        private IEnumerable<string> themes;
+
+        public ThemeSelector(PluginInitContext context)
+        {
+            this.context = context;
+            context.API.VisibilityChanged += OnVisibilityChanged;
+        }
path: Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs
user: login: coderabbitai[bot]
body: _:hammer_and_wrench: Refactor suggestion_

**Add parameter validation in constructor.**

Add null check for the context parameter to fail fast and provide a clear error message.

```diff
     public ThemeSelector(PluginInitContext context)
     {
+        this.context = context ?? throw new ArgumentNullException(nameof(context));
-        this.context = context;
         context.API.VisibilityChanged += OnVisibilityChanged;
     }
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
        public ThemeSelector(PluginInitContext context)
        {
            this.context = context ?? throw new ArgumentNullException(nameof(context));
            context.API.VisibilityChanged += OnVisibilityChanged;
        }
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-01-18T10:08:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,94 @@
+﻿using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Flow.Launcher.Core.Resource;
+
+namespace Flow.Launcher.Plugin.Sys
+{
+    public class ThemeSelector : IDisposable
+    {
+        public const string Keyword = "fltheme";
+
+        private readonly PluginInitContext context;
+        private IEnumerable<string> themes;
+
+        public ThemeSelector(PluginInitContext context)
+        {
+            this.context = context;
+            context.API.VisibilityChanged += OnVisibilityChanged;
+        }
+
+        public List<Result> Query(Query query)
+        {
+            if (query.IsReQuery)
+            {
+                LoadThemes();
+            }
+
+            int keywordIndex = query.Search.IndexOf(Keyword, StringComparison.Ordinal);
+            string search = query.Search[(keywordIndex + Keyword.Length + 1)..];
+
+            if (string.IsNullOrWhiteSpace(search))
+            {
+                return themes.Select(CreateThemeResult)
+                                .OrderBy(x => x.Title)
+                                .ToList();
+            }
+
+            return themes.Select(theme => (theme, matchResult: context.API.FuzzySearch(search, theme)))
+                            .Where(x => x.matchResult.IsSearchPrecisionScoreMet())
+                            .Select(x => CreateThemeResult(x.theme, x.matchResult.Score, x.matchResult.MatchData))
+                            .OrderBy(x => x.Title)
+                            .ToList();
+        }
+
+        private void OnVisibilityChanged(object sender, VisibilityChangedEventArgs args)
+        {
+            if (args.IsVisible && !context.CurrentPluginMetadata.Disabled)
+            {
+                LoadThemes();
+            }
+        }
+
+        private void LoadThemes() 
+            => themes = ThemeManager.Instance.LoadAvailableThemes().Select(Path.GetFileNameWithoutExtension);
+
+        private static Result CreateThemeResult(string theme) => CreateThemeResult(theme, 0, null);
+
+        private static Result CreateThemeResult(string theme, int score, IList<int> highlightData)
+        {
+            string title;
+            if (theme == ThemeManager.Instance.Settings.Theme)
+            {
+                title = $"{theme} ★";
+                score = 2000;
+            }
+            else
+            {
+                title = theme;
+            }
+
+            return new Result
+            {
+                Title = title,
+                TitleHighlightData = highlightData,
+                Glyph = new GlyphInfo("/Resources/#Segoe Fluent Icons", "\ue790"),
+                Score = score,
+                Action = c =>
+                {
+                    ThemeManager.Instance.ChangeTheme(theme);
+                    return true;
+                }
+            };
+        }
+
+        public void Dispose()
+        {
+            if (context != null && context.API != null)
+            {
+                context.API.VisibilityChanged -= OnVisibilityChanged;
+            }
+        }
path: Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs
user: login: coderabbitai[bot]
body: _:hammer_and_wrench: Refactor suggestion_

**Implement the complete IDisposable pattern.**

The current Dispose implementation should follow the complete IDisposable pattern with finalizer and dispose pattern.

```diff
+    private bool disposed;
+
-    public void Dispose()
+    protected virtual void Dispose(bool disposing)
     {
-        if (context != null && context.API != null)
+        if (!disposed)
         {
-            context.API.VisibilityChanged -= OnVisibilityChanged;
+            if (disposing)
+            {
+                // Dispose managed resources
+                if (context?.API != null)
+                {
+                    context.API.VisibilityChanged -= OnVisibilityChanged;
+                }
+            }
+            // Free unmanaged resources
+
+            disposed = true;
         }
     }
+
+    ~ThemeSelector()
+    {
+        Dispose(false);
+    }
+
+    public void Dispose()
+    {
+        Dispose(true);
+        GC.SuppressFinalize(this);
+    }
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
        private bool disposed;

        protected virtual void Dispose(bool disposing)
        {
            if (!disposed)
            {
                if (disposing)
                {
                    // Dispose managed resources
                    if (context?.API != null)
                    {
                        context.API.VisibilityChanged -= OnVisibilityChanged;
                    }
                }
                // Free unmanaged resources

                disposed = true;
            }
        }

        ~ThemeSelector()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-01-18T10:08:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,93 @@
+﻿using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Flow.Launcher.Core.Resource;
+
+namespace Flow.Launcher.Plugin.Sys
+{
+    public class ThemeSelector : IDisposable
+    {
+        public const string Keyword = "fltheme";
+
+        private readonly PluginInitContext context;
+        private IEnumerable<string> themes;
+
+        public ThemeSelector(PluginInitContext context)
+        {
+            this.context = context;
+            context.API.VisibilityChanged += OnVisibilityChanged;
+        }
+
+        public List<Result> Query(Query query)
+        {
+            if (query.IsReQuery)
+            {
+                LoadThemes();
+            }
+
+            string search = query.Search[(query.Search.IndexOf(Keyword, StringComparison.Ordinal) + Keyword.Length + 1)..];
+
+            if (string.IsNullOrWhiteSpace(search))
+            {
+                return themes.Select(CreateThemeResult)
+                             .OrderBy(x => x.Title)
+                             .ToList();
+            }
+
+            return themes.Select(theme => (theme, matchResult: context.API.FuzzySearch(search, theme)))
+                         .Where(x => x.matchResult.IsSearchPrecisionScoreMet())
+                         .Select(x => CreateThemeResult(x.theme, x.matchResult.Score, x.matchResult.MatchData))
+                         .OrderBy(x => x.Title)
+                         .ToList();
+        }
path: Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs
user: login: jjw24
body: 👍 
created_at: 2025-01-18T10:08:55Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,93 @@
+﻿using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Flow.Launcher.Core.Resource;
+
+namespace Flow.Launcher.Plugin.Sys
+{
+    public class ThemeSelector : IDisposable
+    {
+        public const string Keyword = "fltheme";
+
+        private readonly PluginInitContext context;
+        private IEnumerable<string> themes;
+
+        public ThemeSelector(PluginInitContext context)
+        {
+            this.context = context;
+            context.API.VisibilityChanged += OnVisibilityChanged;
+        }
+
+        public List<Result> Query(Query query)
+        {
+            if (query.IsReQuery)
+            {
+                LoadThemes();
+            }
+
+            string search = query.Search[(query.Search.IndexOf(Keyword, StringComparison.Ordinal) + Keyword.Length + 1)..];
+
+            if (string.IsNullOrWhiteSpace(search))
+            {
+                return themes.Select(CreateThemeResult)
+                             .OrderBy(x => x.Title)
+                             .ToList();
+            }
+
+            return themes.Select(theme => (theme, matchResult: context.API.FuzzySearch(search, theme)))
+                         .Where(x => x.matchResult.IsSearchPrecisionScoreMet())
+                         .Select(x => CreateThemeResult(x.theme, x.matchResult.Score, x.matchResult.MatchData))
+                         .OrderBy(x => x.Title)
+                         .ToList();
+        }
path: Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs
user: login: coderabbitai[bot]
body: @jjw24 Thank you for confirming the optimization suggestion!

---

<!-- This is an auto-generated comment: tweet message by coderabbit.ai -->

If you found this review helpful, would you consider giving us a shout-out on [X](https://twitter.com/intent/tweet?related=CodeRabbitAI&text=Great%20advice%20from%20%40coderabbitai!%20Caching%20the%20index%20in%20the%20%60Query%60%20method%20boosts%20performance%20by%20avoiding%20redundant%20calculations.%20%F0%9F%9A%80%20Optimize%20your%20code%20and%20enhance%20efficiency!%20%F0%9F%92%A1&url=https%3A%2F%2Fgithub.com%2FFlow-Launcher%2FFlow.Launcher%2Fpull%2F2448%23discussion_r1643040189)?
							Thank you for using CodeRabbit!

<!-- end of auto-generated comment: tweet message by coderabbit.ai -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-01-18T10:09:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,94 @@
+﻿using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Flow.Launcher.Core.Resource;
+
+namespace Flow.Launcher.Plugin.Sys
+{
+    public class ThemeSelector : IDisposable
+    {
+        public const string Keyword = "fltheme";
+
+        private readonly PluginInitContext context;
+        private IEnumerable<string> themes;
+
+        public ThemeSelector(PluginInitContext context)
+        {
+            this.context = context;
+            context.API.VisibilityChanged += OnVisibilityChanged;
+        }
path: Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs
user: login: jjw24
body: This is not required. context should never be null.
created_at: 2025-01-18T10:10:02Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,94 @@
+﻿using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Flow.Launcher.Core.Resource;
+
+namespace Flow.Launcher.Plugin.Sys
+{
+    public class ThemeSelector : IDisposable
+    {
+        public const string Keyword = "fltheme";
+
+        private readonly PluginInitContext context;
+        private IEnumerable<string> themes;
+
+        public ThemeSelector(PluginInitContext context)
+        {
+            this.context = context;
+            context.API.VisibilityChanged += OnVisibilityChanged;
+        }
path: Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs
user: login: coderabbitai[bot]
body: @jjw24 Understood, I'll note this for future reviews.

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: jjw24
PR: Flow-Launcher/Flow.Launcher#2448
File: Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs:16-20
Timestamp: 2025-01-18T10:10:18.273Z
Learning: In Flow Launcher's plugin system, the PluginInitContext parameter passed to plugin constructors is guaranteed to be non-null by the plugin initialization system, making null checks unnecessary.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-01-18T10:10:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,94 @@
+﻿using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Flow.Launcher.Core.Resource;
+
+namespace Flow.Launcher.Plugin.Sys
+{
+    public class ThemeSelector : IDisposable
+    {
+        public const string Keyword = "fltheme";
+
+        private readonly PluginInitContext context;
+        private IEnumerable<string> themes;
+
+        public ThemeSelector(PluginInitContext context)
+        {
+            this.context = context;
+            context.API.VisibilityChanged += OnVisibilityChanged;
+        }
+
+        public List<Result> Query(Query query)
+        {
+            if (query.IsReQuery)
+            {
+                LoadThemes();
+            }
+
+            int keywordIndex = query.Search.IndexOf(Keyword, StringComparison.Ordinal);
+            string search = query.Search[(keywordIndex + Keyword.Length + 1)..];
+
+            if (string.IsNullOrWhiteSpace(search))
+            {
+                return themes.Select(CreateThemeResult)
+                                .OrderBy(x => x.Title)
+                                .ToList();
+            }
+
+            return themes.Select(theme => (theme, matchResult: context.API.FuzzySearch(search, theme)))
+                            .Where(x => x.matchResult.IsSearchPrecisionScoreMet())
+                            .Select(x => CreateThemeResult(x.theme, x.matchResult.Score, x.matchResult.MatchData))
+                            .OrderBy(x => x.Title)
+                            .ToList();
+        }
+
+        private void OnVisibilityChanged(object sender, VisibilityChangedEventArgs args)
+        {
+            if (args.IsVisible && !context.CurrentPluginMetadata.Disabled)
+            {
+                LoadThemes();
+            }
+        }
+
+        private void LoadThemes() 
+            => themes = ThemeManager.Instance.LoadAvailableThemes().Select(Path.GetFileNameWithoutExtension);
+
+        private static Result CreateThemeResult(string theme) => CreateThemeResult(theme, 0, null);
+
+        private static Result CreateThemeResult(string theme, int score, IList<int> highlightData)
+        {
+            string title;
+            if (theme == ThemeManager.Instance.Settings.Theme)
+            {
+                title = $"{theme} ★";
+                score = 2000;
+            }
+            else
+            {
+                title = theme;
+            }
+
+            return new Result
+            {
+                Title = title,
+                TitleHighlightData = highlightData,
+                Glyph = new GlyphInfo("/Resources/#Segoe Fluent Icons", "\ue790"),
+                Score = score,
+                Action = c =>
+                {
+                    ThemeManager.Instance.ChangeTheme(theme);
+                    return true;
+                }
+            };
+        }
+
+        public void Dispose()
+        {
+            if (context != null && context.API != null)
+            {
+                context.API.VisibilityChanged -= OnVisibilityChanged;
+            }
+        }
path: Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs
user: login: jjw24
body: 👍 
created_at: 2025-01-18T10:16:16Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,94 @@
+﻿using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Flow.Launcher.Core.Resource;
+
+namespace Flow.Launcher.Plugin.Sys
+{
+    public class ThemeSelector : IDisposable
+    {
+        public const string Keyword = "fltheme";
+
+        private readonly PluginInitContext context;
+        private IEnumerable<string> themes;
+
+        public ThemeSelector(PluginInitContext context)
+        {
+            this.context = context;
+            context.API.VisibilityChanged += OnVisibilityChanged;
+        }
+
+        public List<Result> Query(Query query)
+        {
+            if (query.IsReQuery)
+            {
+                LoadThemes();
+            }
+
+            int keywordIndex = query.Search.IndexOf(Keyword, StringComparison.Ordinal);
+            string search = query.Search[(keywordIndex + Keyword.Length + 1)..];
+
+            if (string.IsNullOrWhiteSpace(search))
+            {
+                return themes.Select(CreateThemeResult)
+                                .OrderBy(x => x.Title)
+                                .ToList();
+            }
+
+            return themes.Select(theme => (theme, matchResult: context.API.FuzzySearch(search, theme)))
+                            .Where(x => x.matchResult.IsSearchPrecisionScoreMet())
+                            .Select(x => CreateThemeResult(x.theme, x.matchResult.Score, x.matchResult.MatchData))
+                            .OrderBy(x => x.Title)
+                            .ToList();
+        }
+
+        private void OnVisibilityChanged(object sender, VisibilityChangedEventArgs args)
+        {
+            if (args.IsVisible && !context.CurrentPluginMetadata.Disabled)
+            {
+                LoadThemes();
+            }
+        }
+
+        private void LoadThemes() 
+            => themes = ThemeManager.Instance.LoadAvailableThemes().Select(Path.GetFileNameWithoutExtension);
+
+        private static Result CreateThemeResult(string theme) => CreateThemeResult(theme, 0, null);
+
+        private static Result CreateThemeResult(string theme, int score, IList<int> highlightData)
+        {
+            string title;
+            if (theme == ThemeManager.Instance.Settings.Theme)
+            {
+                title = $"{theme} ★";
+                score = 2000;
+            }
+            else
+            {
+                title = theme;
+            }
+
+            return new Result
+            {
+                Title = title,
+                TitleHighlightData = highlightData,
+                Glyph = new GlyphInfo("/Resources/#Segoe Fluent Icons", "\ue790"),
+                Score = score,
+                Action = c =>
+                {
+                    ThemeManager.Instance.ChangeTheme(theme);
+                    return true;
+                }
+            };
+        }
+
+        public void Dispose()
+        {
+            if (context != null && context.API != null)
+            {
+                context.API.VisibilityChanged -= OnVisibilityChanged;
+            }
+        }
path: Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs
user: login: coderabbitai[bot]
body: @jjw24 Thank you for confirming the IDisposable pattern implementation suggestion. I'll mark this review comment as resolved.

---

<!-- This is an auto-generated comment: tweet message by coderabbit.ai -->

If you found this review helpful, would you consider giving us a shout-out on [X](https://twitter.com/intent/tweet?related=CodeRabbitAI&text=Just%20received%20a%20great%20refactor%20suggestion%20from%20%40coderabbitai!%20Implementing%20the%20complete%20IDisposable%20pattern%20enhances%20resource%20management%20and%20prevents%20leaks.%20%F0%9F%92%A1%F0%9F%94%A7%20Check%20it%20out!&url=https%3A%2F%2Fgithub.com%2FFlow-Launcher%2FFlow.Launcher%2Fpull%2F2448%23discussion_r1921035297)?
							Thank you for using CodeRabbit!

<!-- end of auto-generated comment: tweet message by coderabbit.ai -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-01-18T10:16:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -407,6 +415,18 @@ private List<Result> Commands()
                         context.API.ToggleGameMode();
                         return true;
                     }
+                },
+                new Result
+                {
+                    Title = "Set Flow Launcher Theme",
+                    SubTitle = context.API.GetTranslation("flowlauncher_plugin_sys_theme_selector"),
+                    IcoPath = "Images\\theme_selector.png",
+                    Glyph = new GlyphInfo("/Resources/#Segoe Fluent Icons", "\ue790"),
+                    Action = c =>
+                    {
+                        context.API.ChangeQuery($"{ThemeSelector.Keyword} ");
path: Plugins/Flow.Launcher.Plugin.Sys/Main.cs
user: login: jjw24
body: Actionkeyword is not supported here as it only changes the query to a keyword string. I don't think we even need to use the keyword `fltheme` perhaps.
created_at: 2025-01-18T11:06:03Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,113 @@
+﻿using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Flow.Launcher.Core.Resource;
+
+namespace Flow.Launcher.Plugin.Sys
+{
+    public class ThemeSelector : IDisposable
+    {
+        public const string Keyword = "fltheme";
+
+        private readonly PluginInitContext context;
+        private IEnumerable<string> themes;
+
+        public ThemeSelector(PluginInitContext context)
+        {
+            this.context = context;
+            context.API.VisibilityChanged += OnVisibilityChanged;
+        }
+
+        public List<Result> Query(Query query)
+        {
+            if (query.IsReQuery)
+            {
+                LoadThemes();
+            }
+
+            int keywordIndex = query.Search.IndexOf(Keyword, StringComparison.Ordinal);
+            string search = query.Search[(keywordIndex + Keyword.Length + 1)..];
path: Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs
user: login: jjw24
body: An error occurs when nothing is entered after the fltheme keyword, probably can use query.SecondToEndSearch instead of trying to work out the user input manually.
created_at: 2025-01-18T11:06:46Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,113 @@
+﻿using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Flow.Launcher.Core.Resource;
+
+namespace Flow.Launcher.Plugin.Sys
+{
+    public class ThemeSelector : IDisposable
+    {
+        public const string Keyword = "fltheme";
+
+        private readonly PluginInitContext context;
+        private IEnumerable<string> themes;
+
+        public ThemeSelector(PluginInitContext context)
+        {
+            this.context = context;
+            context.API.VisibilityChanged += OnVisibilityChanged;
+        }
+
+        public List<Result> Query(Query query)
+        {
+            if (query.IsReQuery)
+            {
+                LoadThemes();
+            }
+
+            int keywordIndex = query.Search.IndexOf(Keyword, StringComparison.Ordinal);
+            string search = query.Search[(keywordIndex + Keyword.Length + 1)..];
+
+            if (string.IsNullOrWhiteSpace(search))
+            {
+                return themes.Select(CreateThemeResult)
+                                .OrderBy(x => x.Title)
+                                .ToList();
+            }
+
+            return themes.Select(theme => (theme, matchResult: context.API.FuzzySearch(search, theme)))
+                            .Where(x => x.matchResult.IsSearchPrecisionScoreMet())
+                            .Select(x => CreateThemeResult(x.theme, x.matchResult.Score, x.matchResult.MatchData))
+                            .OrderBy(x => x.Title)
+                            .ToList();
+        }
+
+        private void OnVisibilityChanged(object sender, VisibilityChangedEventArgs args)
+        {
+            if (args.IsVisible && !context.CurrentPluginMetadata.Disabled)
+            {
+                LoadThemes();
+            }
+        }
+
+        private void LoadThemes() 
+            => themes = ThemeManager.Instance.LoadAvailableThemes().Select(Path.GetFileNameWithoutExtension);
path: Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs
user: login: jjw24
body: This is actually causing the build to fail. Needs to be `themes = ThemeManager.Instance.LoadAvailableThemes().Select(x => x.FileNameWithoutExtension);` maybe?
created_at: 2025-01-18T11:08:26Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,113 @@
+﻿using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Flow.Launcher.Core.Resource;
+
+namespace Flow.Launcher.Plugin.Sys
+{
+    public class ThemeSelector : IDisposable
+    {
+        public const string Keyword = "fltheme";
+
+        private readonly PluginInitContext context;
+        private IEnumerable<string> themes;
+
+        public ThemeSelector(PluginInitContext context)
+        {
+            this.context = context;
+            context.API.VisibilityChanged += OnVisibilityChanged;
path: Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs
user: login: jjw24
body: This is going to cause the theme to be loaded every time flow is triggered, therefore plugin is constantly called. Why do we need to do this?
created_at: 2025-01-18T11:10:15Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,113 @@
+﻿using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Flow.Launcher.Core.Resource;
+
+namespace Flow.Launcher.Plugin.Sys
+{
+    public class ThemeSelector : IDisposable
+    {
+        public const string Keyword = "fltheme";
+
+        private readonly PluginInitContext context;
+        private IEnumerable<string> themes;
+
+        public ThemeSelector(PluginInitContext context)
+        {
+            this.context = context;
+            context.API.VisibilityChanged += OnVisibilityChanged;
path: Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs
user: login: Odotocodot
body: The intention was to load themes only once when the user begins typing, this requires the `VisibilityChanged` event, which in turn requires a `PluginInitContext`. I was weary of iterating files every time the user types something.
created_at: 2025-01-18T13:00:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -407,6 +415,18 @@ private List<Result> Commands()
                         context.API.ToggleGameMode();
                         return true;
                     }
+                },
+                new Result
+                {
+                    Title = "Set Flow Launcher Theme",
+                    SubTitle = context.API.GetTranslation("flowlauncher_plugin_sys_theme_selector"),
+                    IcoPath = "Images\\theme_selector.png",
+                    Glyph = new GlyphInfo("/Resources/#Segoe Fluent Icons", "\ue790"),
+                    Action = c =>
+                    {
+                        context.API.ChangeQuery($"{ThemeSelector.Keyword} ");
path: Plugins/Flow.Launcher.Plugin.Sys/Main.cs
user: login: Odotocodot
body: I see. Its sort of a "fake" ActionKeyword its purpose was when the user query is `fltheme` they can see all their themes (and only their themes) and then search through them.
I could move this list and search functionality to a context menu on the sys plugin result perhaps?
created_at: 2025-01-18T13:18:07Z
author_association: CONTRIBUTOR
path: Plugins/Flow.Launcher.Plugin.Sys/Images/theme_selector.png
user: login: Odotocodot
body: Should this be removed to match other sys plugin results i.e. change to FL logo
created_at: 2025-01-18T13:22:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,113 @@
+﻿using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Flow.Launcher.Core.Resource;
+
+namespace Flow.Launcher.Plugin.Sys
+{
+    public class ThemeSelector : IDisposable
+    {
+        public const string Keyword = "fltheme";
+
+        private readonly PluginInitContext context;
+        private IEnumerable<string> themes;
+
+        public ThemeSelector(PluginInitContext context)
+        {
+            this.context = context;
+            context.API.VisibilityChanged += OnVisibilityChanged;
+        }
+
+        public List<Result> Query(Query query)
+        {
+            if (query.IsReQuery)
+            {
+                LoadThemes();
+            }
+
+            int keywordIndex = query.Search.IndexOf(Keyword, StringComparison.Ordinal);
+            string search = query.Search[(keywordIndex + Keyword.Length + 1)..];
path: Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs
user: login: Odotocodot
body: Fixed!
created_at: 2025-01-18T13:23:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,113 @@
+﻿using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Flow.Launcher.Core.Resource;
+
+namespace Flow.Launcher.Plugin.Sys
+{
+    public class ThemeSelector : IDisposable
+    {
+        public const string Keyword = "fltheme";
+
+        private readonly PluginInitContext context;
+        private IEnumerable<string> themes;
+
+        public ThemeSelector(PluginInitContext context)
+        {
+            this.context = context;
+            context.API.VisibilityChanged += OnVisibilityChanged;
+        }
+
+        public List<Result> Query(Query query)
+        {
+            if (query.IsReQuery)
+            {
+                LoadThemes();
+            }
+
+            int keywordIndex = query.Search.IndexOf(Keyword, StringComparison.Ordinal);
+            string search = query.Search[(keywordIndex + Keyword.Length + 1)..];
+
+            if (string.IsNullOrWhiteSpace(search))
+            {
+                return themes.Select(CreateThemeResult)
+                                .OrderBy(x => x.Title)
+                                .ToList();
+            }
+
+            return themes.Select(theme => (theme, matchResult: context.API.FuzzySearch(search, theme)))
+                            .Where(x => x.matchResult.IsSearchPrecisionScoreMet())
+                            .Select(x => CreateThemeResult(x.theme, x.matchResult.Score, x.matchResult.MatchData))
+                            .OrderBy(x => x.Title)
+                            .ToList();
+        }
+
+        private void OnVisibilityChanged(object sender, VisibilityChangedEventArgs args)
+        {
+            if (args.IsVisible && !context.CurrentPluginMetadata.Disabled)
+            {
+                LoadThemes();
+            }
+        }
+
+        private void LoadThemes() 
+            => themes = ThemeManager.Instance.LoadAvailableThemes().Select(Path.GetFileNameWithoutExtension);
path: Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs
user: login: Odotocodot
body: Fixed!
created_at: 2025-01-18T13:23:53Z
author_association: CONTRIBUTOR
path: Plugins/Flow.Launcher.Plugin.Sys/Images/theme_selector.png
user: login: Jack251970
body: Yes. Now I use this icon for those theme results.
created_at: 2025-03-12T10:54:59Z
author_association: MEMBER
diff_hunk: @@ -407,6 +415,18 @@ private List<Result> Commands()
                         context.API.ToggleGameMode();
                         return true;
                     }
+                },
+                new Result
+                {
+                    Title = "Set Flow Launcher Theme",
+                    SubTitle = context.API.GetTranslation("flowlauncher_plugin_sys_theme_selector"),
+                    IcoPath = "Images\\theme_selector.png",
+                    Glyph = new GlyphInfo("/Resources/#Segoe Fluent Icons", "\ue790"),
+                    Action = c =>
+                    {
+                        context.API.ChangeQuery($"{ThemeSelector.Keyword} ");
path: Plugins/Flow.Launcher.Plugin.Sys/Main.cs
user: login: Jack251970
body: This is used for expanding a second menu where plugin can show more and related items. And I think it works fine here.

Moving to context menu will make user confused since most users should prefer left click for further actions.
created_at: 2025-03-12T10:58:37Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,113 @@
+﻿using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Flow.Launcher.Core.Resource;
+
+namespace Flow.Launcher.Plugin.Sys
+{
+    public class ThemeSelector : IDisposable
+    {
+        public const string Keyword = "fltheme";
+
+        private readonly PluginInitContext context;
+        private IEnumerable<string> themes;
+
+        public ThemeSelector(PluginInitContext context)
+        {
+            this.context = context;
+            context.API.VisibilityChanged += OnVisibilityChanged;
path: Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs
user: login: Jack251970
body: I do not think this is a good idea. We should load themes every time users query `ThemeSelector.Keyword` keywords so that results are always correct.

By the way, this is second menu and only if users open it, we will load those themes so it will not affect so much on performance.
created_at: 2025-03-12T11:02:38Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,113 @@
+﻿using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Flow.Launcher.Core.Resource;
+
+namespace Flow.Launcher.Plugin.Sys
+{
+    public class ThemeSelector : IDisposable
+    {
+        public const string Keyword = "fltheme";
+
+        private readonly PluginInitContext context;
+        private IEnumerable<string> themes;
+
+        public ThemeSelector(PluginInitContext context)
+        {
+            this.context = context;
+            context.API.VisibilityChanged += OnVisibilityChanged;
path: Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs
user: login: Odotocodot
body: Alright so remove `OnVisibilityChanged` and the `query.IsReQuery` check, so themes are always loaded when the user types. Therefore the `Query` method would start like this:
```csharp
public List<Result> Query(Query query)
{
    LoadThemes();
    ...
```

created_at: 2025-03-12T13:18:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,115 @@
+﻿using System.Collections.Generic;
+using System.Linq;
+using CommunityToolkit.Mvvm.DependencyInjection;
+using Flow.Launcher.Core.Resource;
+using Flow.Launcher.Infrastructure.UserSettings;
+
+namespace Flow.Launcher.Plugin.Sys
+{
+    public class ThemeSelector
+    {
+        public const string Keyword = "fltheme";
+
+        private readonly Settings _settings;
+        private readonly Theme _theme;
+        private readonly PluginInitContext _context;
+
+        #region Theme Selection
+
+        // Theme select codes simplified from SettingsPaneThemeViewModel.cs
+
+        private Theme.ThemeData _selectedTheme;
+        private Theme.ThemeData SelectedTheme
+        {
+            get => _selectedTheme ??= Themes.Find(v => v.FileNameWithoutExtension == _theme.CurrentTheme);
path: Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs
user: login: coderabbitai[bot]
body: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**Add null check for potential theme not found scenario.**

The current implementation might return null if the theme is not found in the collection, which could lead to a NullReferenceException in other parts of the code.

---

🏁 Script executed:

```shell
#!/bin/bash
# Check if there's any validation or fallback for non-existent themes
rg -A 3 "ChangeTheme" --glob="*.cs" 
```

Length of output: 4932

---



</details>

**Action required: Add a null check to safeguard against an unfound theme.**

The getter in `Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs` currently uses:

```csharp
get => _selectedTheme ??= Themes.Find(v => v.FileNameWithoutExtension == _theme.CurrentTheme);
```

If no match is found in `Themes`, `_selectedTheme` remains null. This can lead to a `NullReferenceException` later in the code. To mitigate this, please add a null check and decide on a fallback mechanism—either assign a default theme or throw a descriptive exception. For example:

```csharp
get {
    _selectedTheme ??= Themes.Find(v => v.FileNameWithoutExtension == _theme.CurrentTheme);
    if (_selectedTheme == null) {
        // Option 1: Return a default theme, e.g.:
        // _selectedTheme = Themes.Find(t => t.FileNameWithoutExtension == "default");
        // Option 2: Throw an informative exception:
        throw new InvalidOperationException("Theme not found for the current theme setting.");
    }
    return _selectedTheme;
}
```

This change aligns the property’s behavior with the fallback handling observed in other parts of the codebase (e.g., in `Theme.ChangeTheme`).

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-12T16:12:48Z
author_association: CONTRIBUTOR

PR comments:
user: login: jjw24
created_at: 2023-11-30T22:00:18Z
body: Great idea. I think this plugin would be best served by publishing to the Store. We can have a look at exposing the needed methods via the API.
author_association: MEMBER
user: login: taooceros
created_at: 2023-11-30T22:19:32Z
body: > Great idea. I think this plugin would be best served by publishing to the Store. We can have a look at exposing the needed methods via the API.

I feel like we can allow this plugin to be built in. Thememanager is pretty internal.
author_association: MEMBER
user: login: jjw24
created_at: 2023-11-30T22:23:53Z
body: Well yes and no, it's directly referencing the core project, ideal design is to have even the default plugins as self contained.
author_association: MEMBER
user: login: taooceros
created_at: 2024-03-12T04:32:31Z
body: > Well yes and no, it's directly referencing the core project, ideal design is to have even the default plugins as self contained.

Some of our internal stuff uses the plugin interface for receiving query, which I think is a pretty good idea. I am not sure whether exposing everything to the interface is a very good idea. PublicAPI sometimes can be the curse as we may want to maintain backward compatibility.
author_association: MEMBER
user: login: jjw24
created_at: 2024-03-28T03:29:49Z
body: Well I think at least this functionality is a good one to enable plugins to use. Remember the more plugins we pack the more we need to maintain and the bigger the size eventually get. More ideal to encourage plugin development.
author_association: MEMBER
user: login: taooceros
created_at: 2024-04-01T16:07:52Z
body: > Well I think at least this functionality is a good one to enable plugins to use. Remember the more plugins we pack the more we need to maintain and the bigger the size eventually get. More ideal to encourage plugin development.

well a plugin only takes 400k size...which should be pretty neglectable. and this feature seems to be good to built in.
author_association: MEMBER
user: login: taooceros
created_at: 2024-04-01T16:08:35Z
body: @Odotocodot isn't you also need to add the plugin folder to the solution?
author_association: MEMBER
user: login: github-actions[bot]
created_at: 2024-04-01T16:09:25Z
body: 
# @check-spelling-bot Report
## :red_circle: Please review
### See the [:open_file_folder: files](https://github.com/Flow-Launcher/Flow.Launcher/pull/2448/files/) view, the [:scroll:action log](https://github.com/Flow-Launcher/Flow.Launcher/actions/runs/8510417023/job/23307959633#step:4:1),  or [:memo: job summary](https://github.com/Flow-Launcher/Flow.Launcher/actions/runs/8510417023/attempts/1#summary-23307959633) for details.

### Unrecognized words (4)

flowtheme
Odotocodot
Reloadable
Segoe

<details><summary>To accept  these unrecognized words as correct,
you could run the following commands</summary>

... in a clone of the [git@github.com:Odotocodot/Flow.Launcher.git](https://github.com/Odotocodot/Flow.Launcher.git) repository
on the `flow-theme-selector-plugin` branch ([:information_source: how do I use this?](
https://github.com/check-spelling/check-spelling/wiki/Accepting-Suggestions)):

``` sh
curl -s -S -L 'https://raw.githubusercontent.com/check-spelling/check-spelling/prerelease/apply.pl' |
perl - 'https://github.com/Flow-Launcher/Flow.Launcher/actions/runs/8510417023/attempts/1'

```
</details>

<details><summary>Warnings (1)</summary>

#### See the [:open_file_folder: files](https://github.com/Flow-Launcher/Flow.Launcher/pull/2448/files/) view, the [:scroll:action log](https://github.com/Flow-Launcher/Flow.Launcher/actions/runs/8510417023/job/23307959633#step:4:1),  or [:memo: job summary](https://github.com/Flow-Launcher/Flow.Launcher/actions/runs/8510417023/attempts/1#summary-23307959633) for details.

[:information_source: Warnings](https://github.com/check-spelling/check-spelling/wiki/Event-descriptions) | Count
-|-
[:information_source: non-alpha-in-dictionary](https://github.com/check-spelling/check-spelling/wiki/Event-descriptions#non-alpha-in-dictionary) | 10

See [:information_source: Event descriptions](https://github.com/check-spelling/check-spelling/wiki/Event-descriptions) for more information.

</details>

<!-- See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-advice --> <!-- markdownlint-disable MD033 MD041 -->
<details><summary>If the flagged items are :exploding_head: false positives</summary>

If items relate to a ...
* binary file (or some other file you wouldn't want to check at all).

  Please add a file path to the `excludes.txt` file matching the containing file.

  File paths are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your files.

  `^` refers to the file's path from the root of the repository, so `^README\.md$` would exclude [README.md](
../tree/HEAD/README.md) (on whichever branch you're using).

* well-formed pattern.

  If you can write a [pattern](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns) that would match it,
  try adding it to the `patterns.txt` file.

  Patterns are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your lines.

  Note that patterns can't match multiline strings.

</details>

author_association: NONE
user: login: jjw24
created_at: 2024-04-06T03:50:05Z
body: > > Well I think at least this functionality is a good one to enable plugins to use. Remember the more plugins we pack the more we need to maintain and the bigger the size eventually get. More ideal to encourage plugin development.
> 
> well a plugin only takes 400k size...which should be pretty neglectable. and this feature seems to be good to built in.

What about putting this functionality in the Sys plugin since it has flow related functionalities like open log file and reload data etc.
author_association: MEMBER
user: login: Odotocodot
created_at: 2024-04-26T17:23:20Z
body: Sorry ive been busy with uni! 
> > > Well I think at least this functionality is a good one to enable plugins to use. Remember the more plugins we pack the more we need to maintain and the bigger the size eventually get. More ideal to encourage plugin development.
> > 
> > 
> > well a plugin only takes 400k size...which should be pretty neglectable. and this feature seems to be good to built in.
> 
> What about putting this functionality in the Sys plugin since it has flow related functionalities like open log file and reload data etc.

So move to be inside the sys plugin?

> @Odotocodot isn't you also need to add the plugin folder to the solution?

Im not sure its needed? or im confused... its be a while




author_association: CONTRIBUTOR
user: login: jjw24
created_at: 2024-05-17T05:24:39Z
body: >So move to be inside the sys plugin?

I would say ideally exposing the methods via the plugin API would be the best so people can build plugin around it, but this change can be complicated, so putting it in the Sys plugin is the next best option.
author_association: MEMBER
user: login: Odotocodot
created_at: 2024-06-15T11:20:17Z
body: I've moved the theme selector plugin into the sys plugin now, though I have only done the english translation.
author_association: CONTRIBUTOR
user: login: coderabbitai[bot]
created_at: 2024-06-17T15:51:28Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

<details>
<summary>📝 Walkthrough</summary>

<details>
<summary>Walkthrough</summary>

## Walkthrough

The updates enhance the `Flow.Launcher.Plugin.Sys` by integrating theme management capabilities. Key changes include introducing a `ThemeSelector` class to manage and query themes, adding a `CurrentTheme` property to the `Theme` class for current theme access, and updating the `Main` class to utilize the `ThemeSelector` for processing theme-related queries. The `Main` class also includes a new command for setting themes.

## Changes

| File Path                                              | Change Summary                                                                              |
|--------------------------------------------------------|---------------------------------------------------------------------------------------------|
| `.../ThemeSelector.cs`                                 | Introduced `ThemeSelector` class to manage and query themes.                               |
| `.../Resource/Theme.cs`                                | Added `CurrentTheme` property to retrieve the current theme from `_settings` and removed unnecessary import. |
| `.../Main.cs`                                         | Updated `Main` class to include `themeSelector`, enhanced `Query` method for theme handling, and added new command for setting themes. |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant User
    participant Main
    participant ThemeSelector

    User->>Main: Query for themes
    Main->>ThemeSelector: Delegate theme query
    ThemeSelector-->>Main: Returns available themes
    Main-->>User: Displays themes
    User->>Main: Select a theme
    Main->>ThemeSelector: Apply selected theme
    ThemeSelector-->>Main: Theme applied
    Main-->>User: Confirmation of applied theme
```

## Suggested reviewers

- jjw24

## Poem

> In the realm of Flow Launcher bright, 🌟  
> Themes now dance with sheer delight. 🎨  
> With `ThemeSelector`, choice is near,  
> Colors and styles, crystal clear. 🖌️  
> Dispose of old, welcome new,  
> Flow shines with each theme's hue. ✨

</details>

</details>

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details>
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=Flow-Launcher/Flow.Launcher&utm_content=2448):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2024-06-17T15:52:44Z
body: 
# @check-spelling-bot Report
## :red_circle: Please review
 ### See the [:open_file_folder: files](https://github.com/Flow-Launcher/Flow.Launcher/pull/2448/files/) view, the [:scroll:action log](https://github.com/Flow-Launcher/Flow.Launcher/actions/runs/9550780120/job/26323565502#step:4:1),  or [:memo: job summary](https://github.com/Flow-Launcher/Flow.Launcher/actions/runs/9550780120/attempts/1#summary-26323565502) for details.

[:x: Errors](https://docs.check-spelling.dev/Event-descriptions) | Count
-|-
[:x: ignored-expect-variant](https://docs.check-spelling.dev/Event-descriptions#ignored-expect-variant) | 1
[:warning: non-alpha-in-dictionary](https://docs.check-spelling.dev/Event-descriptions#non-alpha-in-dictionary) | 13

See [:x: Event descriptions](https://docs.check-spelling.dev/Event-descriptions) for more information.

<!-- Because only_check_changed_files is active, checking for obsolete items cannot be performed-->

<!-- See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-advice --> <!-- markdownlint-disable MD033 MD041 -->
<details><summary>If the flagged items are :exploding_head: false positives</summary>

If items relate to a ...
* binary file (or some other file you wouldn't want to check at all).

  Please add a file path to the `excludes.txt` file matching the containing file.

  File paths are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your files.

  `^` refers to the file's path from the root of the repository, so `^README\.md$` would exclude [README.md](
../tree/HEAD/README.md) (on whichever branch you're using).

* well-formed pattern.

  If you can write a [pattern](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns) that would match it,
  try adding it to the `patterns.txt` file.

  Patterns are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your lines.

  Note that patterns can't match multiline strings.

</details>

author_association: NONE
user: login: taooceros
created_at: 2025-01-09T04:07:25Z
body: @jjw24 is there anything else left on this pr?
author_association: MEMBER
user: login: github-actions[bot]
created_at: 2025-01-18T10:07:04Z
body: 
# @check-spelling-bot Report
## :red_circle: Please review
### See the [:open_file_folder: files](https://github.com/Flow-Launcher/Flow.Launcher/pull/2448/files/) view, the [:scroll:action log](https://github.com/Flow-Launcher/Flow.Launcher/actions/runs/12843064273/job/35814648692#step:4:1),  or [:memo: job summary](https://github.com/Flow-Launcher/Flow.Launcher/actions/runs/12843064273/attempts/1#summary-35814648692) for details.

[:x: Errors](https://docs.check-spelling.dev/Event-descriptions) | Count
-|-
[:x: forbidden-pattern](https://docs.check-spelling.dev/Event-descriptions#forbidden-pattern) | 22
[:warning: ignored-expect-variant](https://docs.check-spelling.dev/Event-descriptions#ignored-expect-variant) | 1
[:warning: non-alpha-in-dictionary](https://docs.check-spelling.dev/Event-descriptions#non-alpha-in-dictionary) | 19

See [:x: Event descriptions](https://docs.check-spelling.dev/Event-descriptions) for more information.

<!-- Because only_check_changed_files is active, checking for obsolete items cannot be performed-->

<!-- See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-advice --> <!-- markdownlint-disable MD033 MD041 -->
<details><summary>If the flagged items are :exploding_head: false positives</summary>

If items relate to a ...
* binary file (or some other file you wouldn't want to check at all).

  Please add a file path to the `excludes.txt` file matching the containing file.

  File paths are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your files.

  `^` refers to the file's path from the root of the repository, so `^README\.md$` would exclude [README.md](
../tree/HEAD/README.md) (on whichever branch you're using).

* well-formed pattern.

  If you can write a [pattern](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns) that would match it,
  try adding it to the `patterns.txt` file.

  Patterns are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your lines.

  Note that patterns can't match multiline strings.

</details>

author_association: NONE
user: login: github-actions[bot]
created_at: 2025-01-18T10:16:54Z
body: 
# @check-spelling-bot Report
## :red_circle: Please review
### See the [:open_file_folder: files](https://github.com/Flow-Launcher/Flow.Launcher/pull/2448/files/) view, the [:scroll:action log](https://github.com/Flow-Launcher/Flow.Launcher/actions/runs/12843133162/job/35814793891#step:4:1),  or [:memo: job summary](https://github.com/Flow-Launcher/Flow.Launcher/actions/runs/12843133162/attempts/1#summary-35814793891) for details.

[:x: Errors](https://docs.check-spelling.dev/Event-descriptions) | Count
-|-
[:x: forbidden-pattern](https://docs.check-spelling.dev/Event-descriptions#forbidden-pattern) | 22
[:warning: ignored-expect-variant](https://docs.check-spelling.dev/Event-descriptions#ignored-expect-variant) | 1
[:warning: non-alpha-in-dictionary](https://docs.check-spelling.dev/Event-descriptions#non-alpha-in-dictionary) | 19

See [:x: Event descriptions](https://docs.check-spelling.dev/Event-descriptions) for more information.

<!-- Because only_check_changed_files is active, checking for obsolete items cannot be performed-->

<!-- See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-advice --> <!-- markdownlint-disable MD033 MD041 -->
<details><summary>If the flagged items are :exploding_head: false positives</summary>

If items relate to a ...
* binary file (or some other file you wouldn't want to check at all).

  Please add a file path to the `excludes.txt` file matching the containing file.

  File paths are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your files.

  `^` refers to the file's path from the root of the repository, so `^README\.md$` would exclude [README.md](
../tree/HEAD/README.md) (on whichever branch you're using).

* well-formed pattern.

  If you can write a [pattern](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns) that would match it,
  try adding it to the `patterns.txt` file.

  Patterns are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your lines.

  Note that patterns can't match multiline strings.

</details>

author_association: NONE
user: login: github-actions[bot]
created_at: 2025-01-18T10:27:05Z
body: 
# @check-spelling-bot Report
## :red_circle: Please review
### See the [:open_file_folder: files](https://github.com/Flow-Launcher/Flow.Launcher/pull/2448/files/) view, the [:scroll:action log](https://github.com/Flow-Launcher/Flow.Launcher/actions/runs/12843205072/job/35814926994#step:4:1),  or [:memo: job summary](https://github.com/Flow-Launcher/Flow.Launcher/actions/runs/12843205072/attempts/1#summary-35814926994) for details.

[:x: Errors](https://docs.check-spelling.dev/Event-descriptions) | Count
-|-
[:x: forbidden-pattern](https://docs.check-spelling.dev/Event-descriptions#forbidden-pattern) | 22
[:warning: ignored-expect-variant](https://docs.check-spelling.dev/Event-descriptions#ignored-expect-variant) | 1
[:warning: non-alpha-in-dictionary](https://docs.check-spelling.dev/Event-descriptions#non-alpha-in-dictionary) | 19

See [:x: Event descriptions](https://docs.check-spelling.dev/Event-descriptions) for more information.

<!-- Because only_check_changed_files is active, checking for obsolete items cannot be performed-->

<!-- See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-advice --> <!-- markdownlint-disable MD033 MD041 -->
<details><summary>If the flagged items are :exploding_head: false positives</summary>

If items relate to a ...
* binary file (or some other file you wouldn't want to check at all).

  Please add a file path to the `excludes.txt` file matching the containing file.

  File paths are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your files.

  `^` refers to the file's path from the root of the repository, so `^README\.md$` would exclude [README.md](
../tree/HEAD/README.md) (on whichever branch you're using).

* well-formed pattern.

  If you can write a [pattern](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns) that would match it,
  try adding it to the `patterns.txt` file.

  Patterns are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your lines.

  Note that patterns can't match multiline strings.

</details>

author_association: NONE
user: login: jjw24
created_at: 2025-01-18T11:10:42Z
body: > @jjw24 is there anything else left on this pr?

Getting there but it's not ready yet.
author_association: MEMBER
user: login: github-actions[bot]
created_at: 2025-01-18T13:25:03Z
body: 
# @check-spelling-bot Report
## :red_circle: Please review
### See the [:open_file_folder: files](https://github.com/Flow-Launcher/Flow.Launcher/pull/2448/files/) view, the [:scroll:action log](https://github.com/Flow-Launcher/Flow.Launcher/actions/runs/12844410656/job/35817367960#step:4:1),  or [:memo: job summary](https://github.com/Flow-Launcher/Flow.Launcher/actions/runs/12844410656/attempts/1#summary-35817367960) for details.

[:x: Errors](https://docs.check-spelling.dev/Event-descriptions) | Count
-|-
[:x: forbidden-pattern](https://docs.check-spelling.dev/Event-descriptions#forbidden-pattern) | 22
[:warning: ignored-expect-variant](https://docs.check-spelling.dev/Event-descriptions#ignored-expect-variant) | 1
[:warning: non-alpha-in-dictionary](https://docs.check-spelling.dev/Event-descriptions#non-alpha-in-dictionary) | 19

See [:x: Event descriptions](https://docs.check-spelling.dev/Event-descriptions) for more information.

<!-- Because only_check_changed_files is active, checking for obsolete items cannot be performed-->

<!-- See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-advice --> <!-- markdownlint-disable MD033 MD041 -->
<details><summary>If the flagged items are :exploding_head: false positives</summary>

If items relate to a ...
* binary file (or some other file you wouldn't want to check at all).

  Please add a file path to the `excludes.txt` file matching the containing file.

  File paths are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your files.

  `^` refers to the file's path from the root of the repository, so `^README\.md$` would exclude [README.md](
../tree/HEAD/README.md) (on whichever branch you're using).

* well-formed pattern.

  If you can write a [pattern](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns) that would match it,
  try adding it to the `patterns.txt` file.

  Patterns are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your lines.

  Note that patterns can't match multiline strings.

</details>

author_association: NONE
user: login: github-actions[bot]
created_at: 2025-03-12T10:25:10Z
body: 
# @check-spelling-bot Report
## :red_circle: Please review
### See the [:open_file_folder: files](https://github.com/Flow-Launcher/Flow.Launcher/pull/2448/files/) view, the [:scroll:action log](https://github.com/Flow-Launcher/Flow.Launcher/actions/runs/13809059586/job/38626219537#step:4:1),  or [:memo: job summary](https://github.com/Flow-Launcher/Flow.Launcher/actions/runs/13809059586/attempts/1#summary-38626219537) for details.

[:x: Errors](https://docs.check-spelling.dev/Event-descriptions) | Count
-|-
[:x: forbidden-pattern](https://docs.check-spelling.dev/Event-descriptions#forbidden-pattern) | 22
[:warning: ignored-expect-variant](https://docs.check-spelling.dev/Event-descriptions#ignored-expect-variant) | 1
[:warning: non-alpha-in-dictionary](https://docs.check-spelling.dev/Event-descriptions#non-alpha-in-dictionary) | 19

See [:x: Event descriptions](https://docs.check-spelling.dev/Event-descriptions) for more information.

<!-- Because only_check_changed_files is active, checking for obsolete items cannot be performed-->
Using `only_check_changed_files` is incompatible with `expect.txt`.
To accept the items listed, you should add them to `allow.txt`.

<!-- See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-advice --> <!-- markdownlint-disable MD033 MD041 -->
<details><summary>If the flagged items are :exploding_head: false positives</summary>

If items relate to a ...
* binary file (or some other file you wouldn't want to check at all).

  Please add a file path to the `excludes.txt` file matching the containing file.

  File paths are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your files.

  `^` refers to the file's path from the root of the repository, so `^README\.md$` would exclude [README.md](
../tree/HEAD/README.md) (on whichever branch you're using).

* well-formed pattern.

  If you can write a [pattern](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns) that would match it,
  try adding it to the `patterns.txt` file.

  Patterns are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your lines.

  Note that patterns can't match multiline strings.

</details>

author_association: NONE
user: login: github-actions[bot]
created_at: 2025-03-12T10:40:23Z
body: 
# @check-spelling-bot Report
## :red_circle: Please review
### See the [:open_file_folder: files](https://github.com/Flow-Launcher/Flow.Launcher/pull/2448/files/) view, the [:scroll:action log](https://github.com/Flow-Launcher/Flow.Launcher/actions/runs/13809353383/job/38627139990#step:4:1),  or [:memo: job summary](https://github.com/Flow-Launcher/Flow.Launcher/actions/runs/13809353383/attempts/1#summary-38627139990) for details.

[:x: Errors](https://docs.check-spelling.dev/Event-descriptions) | Count
-|-
[:x: forbidden-pattern](https://docs.check-spelling.dev/Event-descriptions#forbidden-pattern) | 22
[:warning: ignored-expect-variant](https://docs.check-spelling.dev/Event-descriptions#ignored-expect-variant) | 1
[:warning: non-alpha-in-dictionary](https://docs.check-spelling.dev/Event-descriptions#non-alpha-in-dictionary) | 19

See [:x: Event descriptions](https://docs.check-spelling.dev/Event-descriptions) for more information.

<!-- Because only_check_changed_files is active, checking for obsolete items cannot be performed-->
Using `only_check_changed_files` is incompatible with `expect.txt`.
To accept the items listed, you should add them to `allow.txt`.

<!-- See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-advice --> <!-- markdownlint-disable MD033 MD041 -->
<details><summary>If the flagged items are :exploding_head: false positives</summary>

If items relate to a ...
* binary file (or some other file you wouldn't want to check at all).

  Please add a file path to the `excludes.txt` file matching the containing file.

  File paths are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your files.

  `^` refers to the file's path from the root of the repository, so `^README\.md$` would exclude [README.md](
../tree/HEAD/README.md) (on whichever branch you're using).

* well-formed pattern.

  If you can write a [pattern](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns) that would match it,
  try adding it to the `patterns.txt` file.

  Patterns are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your lines.

  Note that patterns can't match multiline strings.

</details>

author_association: NONE
user: login: github-actions[bot]
created_at: 2025-03-12T10:46:58Z
body: 
# @check-spelling-bot Report
## :red_circle: Please review
### See the [:open_file_folder: files](https://github.com/Flow-Launcher/Flow.Launcher/pull/2448/files/) view, the [:scroll:action log](https://github.com/Flow-Launcher/Flow.Launcher/actions/runs/13809479810/job/38627537118#step:4:1),  or [:memo: job summary](https://github.com/Flow-Launcher/Flow.Launcher/actions/runs/13809479810/attempts/1#summary-38627537118) for details.

[:x: Errors](https://docs.check-spelling.dev/Event-descriptions) | Count
-|-
[:x: forbidden-pattern](https://docs.check-spelling.dev/Event-descriptions#forbidden-pattern) | 22
[:warning: ignored-expect-variant](https://docs.check-spelling.dev/Event-descriptions#ignored-expect-variant) | 1
[:warning: non-alpha-in-dictionary](https://docs.check-spelling.dev/Event-descriptions#non-alpha-in-dictionary) | 19

See [:x: Event descriptions](https://docs.check-spelling.dev/Event-descriptions) for more information.

<!-- Because only_check_changed_files is active, checking for obsolete items cannot be performed-->
Using `only_check_changed_files` is incompatible with `expect.txt`.
To accept the items listed, you should add them to `allow.txt`.

<!-- See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-advice --> <!-- markdownlint-disable MD033 MD041 -->
<details><summary>If the flagged items are :exploding_head: false positives</summary>

If items relate to a ...
* binary file (or some other file you wouldn't want to check at all).

  Please add a file path to the `excludes.txt` file matching the containing file.

  File paths are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your files.

  `^` refers to the file's path from the root of the repository, so `^README\.md$` would exclude [README.md](
../tree/HEAD/README.md) (on whichever branch you're using).

* well-formed pattern.

  If you can write a [pattern](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns) that would match it,
  try adding it to the `patterns.txt` file.

  Patterns are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your lines.

  Note that patterns can't match multiline strings.

</details>

author_association: NONE
user: login: github-actions[bot]
created_at: 2025-03-12T10:55:39Z
body: 
# @check-spelling-bot Report
## :red_circle: Please review
### See the [:open_file_folder: files](https://github.com/Flow-Launcher/Flow.Launcher/pull/2448/files/) view, the [:scroll:action log](https://github.com/Flow-Launcher/Flow.Launcher/actions/runs/13809635601/job/38628052425#step:4:1),  or [:memo: job summary](https://github.com/Flow-Launcher/Flow.Launcher/actions/runs/13809635601/attempts/1#summary-38628052425) for details.

[:x: Errors](https://docs.check-spelling.dev/Event-descriptions) | Count
-|-
[:x: forbidden-pattern](https://docs.check-spelling.dev/Event-descriptions#forbidden-pattern) | 22
[:warning: ignored-expect-variant](https://docs.check-spelling.dev/Event-descriptions#ignored-expect-variant) | 1
[:warning: non-alpha-in-dictionary](https://docs.check-spelling.dev/Event-descriptions#non-alpha-in-dictionary) | 19

See [:x: Event descriptions](https://docs.check-spelling.dev/Event-descriptions) for more information.

<!-- Because only_check_changed_files is active, checking for obsolete items cannot be performed-->
Using `only_check_changed_files` is incompatible with `expect.txt`.
To accept the items listed, you should add them to `allow.txt`.

<!-- See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-advice --> <!-- markdownlint-disable MD033 MD041 -->
<details><summary>If the flagged items are :exploding_head: false positives</summary>

If items relate to a ...
* binary file (or some other file you wouldn't want to check at all).

  Please add a file path to the `excludes.txt` file matching the containing file.

  File paths are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your files.

  `^` refers to the file's path from the root of the repository, so `^README\.md$` would exclude [README.md](
../tree/HEAD/README.md) (on whichever branch you're using).

* well-formed pattern.

  If you can write a [pattern](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns) that would match it,
  try adding it to the `patterns.txt` file.

  Patterns are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your lines.

  Note that patterns can't match multiline strings.

</details>

author_association: NONE
user: login: github-actions[bot]
created_at: 2025-03-12T13:51:50Z
body: 
# @check-spelling-bot Report
## :red_circle: Please review
### See the [:open_file_folder: files](https://github.com/Flow-Launcher/Flow.Launcher/pull/2448/files/) view, the [:scroll:action log](https://github.com/Flow-Launcher/Flow.Launcher/actions/runs/13813025826/job/38638940655#step:4:1),  or [:memo: job summary](https://github.com/Flow-Launcher/Flow.Launcher/actions/runs/13813025826/attempts/1#summary-38638940655) for details.

[:x: Errors](https://docs.check-spelling.dev/Event-descriptions) | Count
-|-
[:x: forbidden-pattern](https://docs.check-spelling.dev/Event-descriptions#forbidden-pattern) | 22
[:warning: ignored-expect-variant](https://docs.check-spelling.dev/Event-descriptions#ignored-expect-variant) | 1
[:warning: non-alpha-in-dictionary](https://docs.check-spelling.dev/Event-descriptions#non-alpha-in-dictionary) | 19

See [:x: Event descriptions](https://docs.check-spelling.dev/Event-descriptions) for more information.

<!-- Because only_check_changed_files is active, checking for obsolete items cannot be performed-->
Using `only_check_changed_files` is incompatible with `expect.txt`.
To accept the items listed, you should add them to `allow.txt`.

<!-- See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-advice --> <!-- markdownlint-disable MD033 MD041 -->
<details><summary>If the flagged items are :exploding_head: false positives</summary>

If items relate to a ...
* binary file (or some other file you wouldn't want to check at all).

  Please add a file path to the `excludes.txt` file matching the containing file.

  File paths are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your files.

  `^` refers to the file's path from the root of the repository, so `^README\.md$` would exclude [README.md](
../tree/HEAD/README.md) (on whichever branch you're using).

* well-formed pattern.

  If you can write a [pattern](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns) that would match it,
  try adding it to the `patterns.txt` file.

  Patterns are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your lines.

  Note that patterns can't match multiline strings.

</details>

author_association: NONE
user: login: github-actions[bot]
created_at: 2025-03-12T14:26:21Z
body: 
# @check-spelling-bot Report
## :red_circle: Please review
### See the [:open_file_folder: files](https://github.com/Flow-Launcher/Flow.Launcher/pull/2448/files/) view, the [:scroll:action log](https://github.com/Flow-Launcher/Flow.Launcher/actions/runs/13813789819/job/38641609139#step:4:1),  or [:memo: job summary](https://github.com/Flow-Launcher/Flow.Launcher/actions/runs/13813789819/attempts/1#summary-38641609139) for details.

[:x: Errors](https://docs.check-spelling.dev/Event-descriptions) | Count
-|-
[:x: forbidden-pattern](https://docs.check-spelling.dev/Event-descriptions#forbidden-pattern) | 22
[:warning: ignored-expect-variant](https://docs.check-spelling.dev/Event-descriptions#ignored-expect-variant) | 1
[:warning: non-alpha-in-dictionary](https://docs.check-spelling.dev/Event-descriptions#non-alpha-in-dictionary) | 19

See [:x: Event descriptions](https://docs.check-spelling.dev/Event-descriptions) for more information.

<!-- Because only_check_changed_files is active, checking for obsolete items cannot be performed-->
Using `only_check_changed_files` is incompatible with `expect.txt`.
To accept the items listed, you should add them to `allow.txt`.

<!-- See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-advice --> <!-- markdownlint-disable MD033 MD041 -->
<details><summary>If the flagged items are :exploding_head: false positives</summary>

If items relate to a ...
* binary file (or some other file you wouldn't want to check at all).

  Please add a file path to the `excludes.txt` file matching the containing file.

  File paths are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your files.

  `^` refers to the file's path from the root of the repository, so `^README\.md$` would exclude [README.md](
../tree/HEAD/README.md) (on whichever branch you're using).

* well-formed pattern.

  If you can write a [pattern](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns) that would match it,
  try adding it to the `patterns.txt` file.

  Patterns are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your lines.

  Note that patterns can't match multiline strings.

</details>

author_association: NONE
user: login: Jack251970
created_at: 2025-03-12T14:32:16Z
body: @Odotocodot Hi, I have changed theme selection part and here is the final result.

Firstly, I use codes from `SettingsPaneThemeViewModel.cs` to fix possible drop effect issue.

Secondly, every time you change the theme, the window will not be closed but re-query the results because users may not feel good about the theme they changed so they possibly want to change theme for many times.

Thirdly, I add light/dark theme tooltip into results as their subtitles.

https://github.com/user-attachments/assets/d2031fab-e3aa-40a9-93ed-e63813dc38f3

This works fine for me and I have approved those changes. Please check if this works for you, and feel free to merge if you think it is ready.

Lastly, thanks for your idea and your patience!
author_association: MEMBER
user: login: github-actions[bot]
created_at: 2025-03-12T16:10:59Z
body: 
# @check-spelling-bot Report
## :red_circle: Please review
### See the [:open_file_folder: files](https://github.com/Flow-Launcher/Flow.Launcher/pull/2448/files/) view, the [:scroll:action log](https://github.com/Flow-Launcher/Flow.Launcher/actions/runs/13816145012/job/38649724875#step:4:1),  or [:memo: job summary](https://github.com/Flow-Launcher/Flow.Launcher/actions/runs/13816145012/attempts/1#summary-38649724875) for details.

[:x: Errors](https://docs.check-spelling.dev/Event-descriptions) | Count
-|-
[:x: forbidden-pattern](https://docs.check-spelling.dev/Event-descriptions#forbidden-pattern) | 22
[:warning: ignored-expect-variant](https://docs.check-spelling.dev/Event-descriptions#ignored-expect-variant) | 1
[:warning: non-alpha-in-dictionary](https://docs.check-spelling.dev/Event-descriptions#non-alpha-in-dictionary) | 19

See [:x: Event descriptions](https://docs.check-spelling.dev/Event-descriptions) for more information.

<!-- Because only_check_changed_files is active, checking for obsolete items cannot be performed-->
Using `only_check_changed_files` is incompatible with `expect.txt`.
To accept the items listed, you should add them to `allow.txt`.

<!-- See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-advice --> <!-- markdownlint-disable MD033 MD041 -->
<details><summary>If the flagged items are :exploding_head: false positives</summary>

If items relate to a ...
* binary file (or some other file you wouldn't want to check at all).

  Please add a file path to the `excludes.txt` file matching the containing file.

  File paths are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your files.

  `^` refers to the file's path from the root of the repository, so `^README\.md$` would exclude [README.md](
../tree/HEAD/README.md) (on whichever branch you're using).

* well-formed pattern.

  If you can write a [pattern](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns) that would match it,
  try adding it to the `patterns.txt` file.

  Patterns are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your lines.

  Note that patterns can't match multiline strings.

</details>

author_association: NONE
user: login: Odotocodot
created_at: 2025-03-12T16:16:23Z
body: @Jack251970 Nice!
Those are very good additions 😃, definitely makes sense to keep the window open and update the theme.

I fixed a null exception and removed some extra code (In `ThemeSelector` `DropShadowEffect` was only ever set to false so removed the logic when setting to true).

If eveything is in order I am happy to merge!

author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-03-12T16:27:33Z
body: 
# @check-spelling-bot Report
## :red_circle: Please review
### See the [:open_file_folder: files](https://github.com/Flow-Launcher/Flow.Launcher/pull/2448/files/) view, the [:scroll:action log](https://github.com/Flow-Launcher/Flow.Launcher/actions/runs/13816499583/job/38650947461#step:4:1),  or [:memo: job summary](https://github.com/Flow-Launcher/Flow.Launcher/actions/runs/13816499583/attempts/1#summary-38650947461) for details.

[:x: Errors](https://docs.check-spelling.dev/Event-descriptions) | Count
-|-
[:x: forbidden-pattern](https://docs.check-spelling.dev/Event-descriptions#forbidden-pattern) | 22
[:warning: ignored-expect-variant](https://docs.check-spelling.dev/Event-descriptions#ignored-expect-variant) | 1
[:warning: non-alpha-in-dictionary](https://docs.check-spelling.dev/Event-descriptions#non-alpha-in-dictionary) | 19

See [:x: Event descriptions](https://docs.check-spelling.dev/Event-descriptions) for more information.

<!-- Because only_check_changed_files is active, checking for obsolete items cannot be performed-->
Using `only_check_changed_files` is incompatible with `expect.txt`.
To accept the items listed, you should add them to `allow.txt`.

<!-- See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-advice --> <!-- markdownlint-disable MD033 MD041 -->
<details><summary>If the flagged items are :exploding_head: false positives</summary>

If items relate to a ...
* binary file (or some other file you wouldn't want to check at all).

  Please add a file path to the `excludes.txt` file matching the containing file.

  File paths are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your files.

  `^` refers to the file's path from the root of the repository, so `^README\.md$` would exclude [README.md](
../tree/HEAD/README.md) (on whichever branch you're using).

* well-formed pattern.

  If you can write a [pattern](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns) that would match it,
  try adding it to the `patterns.txt` file.

  Patterns are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your lines.

  Note that patterns can't match multiline strings.

</details>

author_association: NONE
user: login: Odotocodot
created_at: 2025-03-12T17:40:36Z
body: > If eveything is in order I am happy to merge!

*happy for a merge! 
I do not have the permissions to merge :wink:
author_association: CONTRIBUTOR