diff --git a/Flow.Launcher.Core/Flow.Launcher.Core.csproj b/Flow.Launcher.Core/Flow.Launcher.Core.csproj
index e9f199d00..d10a0313d 100644
--- a/Flow.Launcher.Core/Flow.Launcher.Core.csproj
+++ b/Flow.Launcher.Core/Flow.Launcher.Core.csproj
@@ -57,6 +57,7 @@
+
diff --git a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj
index b91da7114..ae94afdab 100644
--- a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj
+++ b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj
@@ -67,6 +67,7 @@
+
diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj
index 1472813b8..027fac7eb 100644
--- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj
+++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj
@@ -77,6 +77,7 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
+
diff --git a/Flow.Launcher.Test/Flow.Launcher.Test.csproj b/Flow.Launcher.Test/Flow.Launcher.Test.csproj
index 0241a374e..98f25a5f4 100644
--- a/Flow.Launcher.Test/Flow.Launcher.Test.csproj
+++ b/Flow.Launcher.Test/Flow.Launcher.Test.csproj
@@ -55,6 +55,7 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
+
\ No newline at end of file
diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj
index 1e305d3d9..0a792ef72 100644
--- a/Flow.Launcher/Flow.Launcher.csproj
+++ b/Flow.Launcher/Flow.Launcher.csproj
@@ -104,6 +104,7 @@
+
diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs
index 48acf6109..acf863bf0 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs
@@ -3,11 +3,24 @@ using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using Flow.Launcher.Infrastructure.Logger;
+using System;
+using System.Data.SQLite;
+using SkiaSharp;
namespace Flow.Launcher.Plugin.BrowserBookmark;
public abstract class ChromiumBookmarkLoader : IBookmarkLoader
{
+ private readonly string _faviconCacheDir;
+
+ protected ChromiumBookmarkLoader()
+ {
+ _faviconCacheDir = Path.Combine(
+ Path.GetDirectoryName(typeof(ChromiumBookmarkLoader).Assembly.Location),
+ "FaviconCache");
+ Directory.CreateDirectory(_faviconCacheDir);
+ }
+
public abstract List GetBookmarks();
protected List LoadBookmarks(string browserDataPath, string name)
@@ -22,10 +35,30 @@ public abstract class ChromiumBookmarkLoader : IBookmarkLoader
if (!File.Exists(bookmarkPath))
continue;
- Main.RegisterBookmarkFile(bookmarkPath);
+ // Register bookmark file monitoring (direct call to Main.RegisterBookmarkFile)
+ try
+ {
+ if (File.Exists(bookmarkPath))
+ {
+ //Main.RegisterBookmarkFile(bookmarkPath);
+ }
+ }
+ catch (Exception ex)
+ {
+ Log.Exception($"Failed to register bookmark file monitoring: {bookmarkPath}", ex);
+ }
var source = name + (Path.GetFileName(profile) == "Default" ? "" : $" ({Path.GetFileName(profile)})");
- bookmarks.AddRange(LoadBookmarksFromFile(bookmarkPath, source));
+ var profileBookmarks = LoadBookmarksFromFile(bookmarkPath, source);
+
+ // Load favicons after loading bookmarks
+ var faviconDbPath = Path.Combine(profile, "Favicons");
+ if (File.Exists(faviconDbPath))
+ {
+ LoadFaviconsFromDb(faviconDbPath, profileBookmarks);
+ }
+
+ bookmarks.AddRange(profileBookmarks);
}
return bookmarks;
@@ -52,8 +85,7 @@ public abstract class ChromiumBookmarkLoader : IBookmarkLoader
if (folder.Value.ValueKind != JsonValueKind.Object)
continue;
- // Fix for Opera. It stores bookmarks slightly different than chrome. See PR and bug report for this change for details.
- // If various exceptions start to build up here consider splitting this Loader into multiple separate ones.
+ // Fix for Opera. It stores bookmarks slightly different than chrome.
if (folder.Name == "custom_root")
EnumerateRoot(folder.Value, bookmarks, source);
else
@@ -91,4 +123,108 @@ public abstract class ChromiumBookmarkLoader : IBookmarkLoader
}
}
}
+
+ private void LoadFaviconsFromDb(string dbPath, List bookmarks)
+ {
+ try
+ {
+ // Use a copy to avoid lock issues with the original file
+ var tempDbPath = Path.Combine(_faviconCacheDir, $"tempfavicons_{Guid.NewGuid()}.db");
+
+ try
+ {
+ File.Copy(dbPath, tempDbPath, true);
+ }
+ catch (Exception ex)
+ {
+ Log.Exception($"Failed to copy favicon DB: {dbPath}", ex);
+ return;
+ }
+
+ try
+ {
+ using var connection = new SQLiteConnection($"Data Source={tempDbPath};Version=3;Read Only=True;");
+ connection.Open();
+
+ foreach (var bookmark in bookmarks)
+ {
+ try
+ {
+ var url = bookmark.Url;
+ if (string.IsNullOrEmpty(url)) continue;
+
+ // Extract domain from URL
+ if (!Uri.TryCreate(url, UriKind.Absolute, out Uri uri))
+ continue;
+
+ var domain = uri.Host;
+
+ using var cmd = connection.CreateCommand();
+ cmd.CommandText = @"
+ SELECT f.id, b.image_data
+ FROM favicons f
+ JOIN favicon_bitmaps b ON f.id = b.icon_id
+ JOIN icon_mapping m ON f.id = m.icon_id
+ WHERE m.page_url LIKE @url
+ ORDER BY b.width DESC
+ LIMIT 1";
+
+ cmd.Parameters.AddWithValue("@url", $"%{domain}%");
+
+ using var reader = cmd.ExecuteReader();
+ if (reader.Read() && !reader.IsDBNull(1))
+ {
+ var iconId = reader.GetInt64(0).ToString();
+ var imageData = (byte[])reader["image_data"];
+
+ if (imageData != null && imageData.Length > 0)
+ {
+ var faviconPath = Path.Combine(_faviconCacheDir, $"{domain}_{iconId}.png");
+ if (!File.Exists(faviconPath))
+ {
+ SaveBitmapData(imageData, faviconPath);
+ }
+ bookmark.FaviconPath = faviconPath;
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Log.Exception($"Failed to extract bookmark favicon: {bookmark.Url}", ex);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Log.Exception($"Failed to connect to SQLite: {tempDbPath}", ex);
+ }
+
+ // Delete temporary file
+ try { File.Delete(tempDbPath); } catch { /* Ignore */ }
+ }
+ catch (Exception ex)
+ {
+ Log.Exception($"Failed to load favicon DB: {dbPath}", ex);
+ }
+ }
+
+ private void SaveBitmapData(byte[] imageData, string outputPath)
+ {
+ try
+ {
+ using var ms = new MemoryStream(imageData);
+ using var bitmap = SKBitmap.Decode(ms);
+ if (bitmap != null)
+ {
+ using var image = SKImage.FromBitmap(bitmap);
+ using var data = image.Encode(SKEncodedImageFormat.Png, 100);
+ using var fs = File.OpenWrite(outputPath);
+ data.SaveTo(fs);
+ }
+ }
+ catch (Exception ex)
+ {
+ Log.Exception($"Failed to save image: {outputPath}", ex);
+ }
+ }
}
diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs
index 35ad32fb3..84c343707 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs
@@ -4,13 +4,26 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using Flow.Launcher.Infrastructure.Logger;
+using SkiaSharp;
namespace Flow.Launcher.Plugin.BrowserBookmark;
public abstract class FirefoxBookmarkLoaderBase : IBookmarkLoader
{
+ private readonly string _faviconCacheDir;
+
+ protected FirefoxBookmarkLoaderBase()
+ {
+ _faviconCacheDir = Path.Combine(
+ Path.GetDirectoryName(typeof(FirefoxBookmarkLoaderBase).Assembly.Location),
+ "FaviconCache");
+ Directory.CreateDirectory(_faviconCacheDir);
+ }
+
public abstract List GetBookmarks();
+ // Updated query - removed favicon_id column
private const string QueryAllBookmarks = """
SELECT moz_places.url, moz_bookmarks.title
FROM moz_places
@@ -20,36 +33,161 @@ public abstract class FirefoxBookmarkLoaderBase : IBookmarkLoader
ORDER BY moz_places.visit_count DESC
""";
- private const string DbPathFormat = "Data Source ={0}";
+ private const string DbPathFormat = "Data Source={0}";
- protected static List GetBookmarksFromPath(string placesPath)
+ protected List GetBookmarksFromPath(string placesPath)
{
- // Return empty list if the places.sqlite file cannot be found
+ // Variable to store bookmark list
+ var bookmarks = new List();
+
+ // Return empty list if places.sqlite file doesn't exist
if (string.IsNullOrEmpty(placesPath) || !File.Exists(placesPath))
- return new List();
+ return bookmarks;
- Main.RegisterBookmarkFile(placesPath);
+ try
+ {
+ // Try to register file monitoring
+ try
+ {
+ Main.RegisterBookmarkFile(placesPath);
+ }
+ catch (Exception ex)
+ {
+ Log.Exception($"Failed to register Firefox bookmark file monitoring: {placesPath}", ex);
+ }
- // create the connection string and init the connection
- string dbPath = string.Format(DbPathFormat, placesPath);
- using var dbConnection = new SqliteConnection(dbPath);
- // Open connection to the database file and execute the query
- dbConnection.Open();
- var reader = new SqliteCommand(QueryAllBookmarks, dbConnection).ExecuteReader();
+ // Use a copy to avoid lock issues with the original file
+ var tempDbPath = Path.Combine(_faviconCacheDir, $"tempplaces_{Guid.NewGuid()}.sqlite");
+ File.Copy(placesPath, tempDbPath, true);
- // return results in List format
- return reader
- .Select(
- x => new Bookmark(
- x["title"] is DBNull ? string.Empty : x["title"].ToString(),
- x["url"].ToString()
+ // Connect to database and execute query
+ string dbPath = string.Format(DbPathFormat, tempDbPath);
+ using var dbConnection = new SqliteConnection(dbPath);
+ dbConnection.Open();
+ var reader = new SqliteCommand(QueryAllBookmarks, dbConnection).ExecuteReader();
+
+ // Create bookmark list
+ bookmarks = reader
+ .Select(
+ x => new Bookmark(
+ x["title"] is DBNull ? string.Empty : x["title"].ToString(),
+ x["url"].ToString(),
+ "Firefox"
+ )
)
- )
- .ToList();
+ .ToList();
+
+ // Path to favicon database
+ var faviconDbPath = Path.Combine(Path.GetDirectoryName(placesPath), "favicons.sqlite");
+ if (File.Exists(faviconDbPath))
+ {
+ LoadFaviconsFromDb(faviconDbPath, bookmarks);
+ }
+
+ // Delete temporary file
+ try { File.Delete(tempDbPath); } catch { /* Ignore */ }
+ }
+ catch (Exception ex)
+ {
+ Log.Exception($"Failed to load Firefox bookmarks: {placesPath}", ex);
+ }
+
+ return bookmarks;
+ }
+
+ private void LoadFaviconsFromDb(string faviconDbPath, List bookmarks)
+ {
+ try
+ {
+ // Use a copy to avoid lock issues with the original file
+ var tempDbPath = Path.Combine(_faviconCacheDir, $"tempfavicons_{Guid.NewGuid()}.sqlite");
+ File.Copy(faviconDbPath, tempDbPath, true);
+
+ string dbPath = string.Format(DbPathFormat, tempDbPath);
+ using var connection = new SqliteConnection(dbPath);
+ connection.Open();
+
+ // Get favicons based on bookmark URLs
+ foreach (var bookmark in bookmarks)
+ {
+ try
+ {
+ if (string.IsNullOrEmpty(bookmark.Url))
+ continue;
+
+ // Extract domain from URL
+ if (!Uri.TryCreate(bookmark.Url, UriKind.Absolute, out Uri uri))
+ continue;
+
+ var domain = uri.Host;
+
+ // Query for latest Firefox version favicon structure
+ using var cmd = connection.CreateCommand();
+ cmd.CommandText = @"
+ SELECT i.data
+ FROM moz_icons i
+ JOIN moz_icons_to_pages ip ON i.id = ip.icon_id
+ JOIN moz_pages_w_icons p ON ip.page_id = p.id
+ WHERE p.page_url LIKE @url
+ AND i.data IS NOT NULL
+ ORDER BY i.width DESC -- Select largest icon available
+ LIMIT 1";
+
+ cmd.Parameters.AddWithValue("@url", $"%{domain}%");
+
+ using var reader = cmd.ExecuteReader();
+ if (reader.Read() && !reader.IsDBNull(0))
+ {
+ var imageData = (byte[])reader["data"];
+
+ if (imageData != null && imageData.Length > 0)
+ {
+ var faviconPath = Path.Combine(_faviconCacheDir, $"firefox_{domain}.png");
+
+ if (!File.Exists(faviconPath))
+ {
+ SaveBitmapData(imageData, faviconPath);
+ }
+ bookmark.FaviconPath = faviconPath;
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Log.Exception($"Failed to extract Firefox favicon: {bookmark.Url}", ex);
+ }
+ }
+
+ // Delete temporary file
+ try { File.Delete(tempDbPath); } catch { /* Ignore */ }
+ }
+ catch (Exception ex)
+ {
+ Log.Exception($"Failed to load Firefox favicon DB: {faviconDbPath}", ex);
+ }
+ }
+
+ private void SaveBitmapData(byte[] imageData, string outputPath)
+ {
+ try
+ {
+ using var ms = new MemoryStream(imageData);
+ using var bitmap = SKBitmap.Decode(ms);
+ if (bitmap != null)
+ {
+ using var image = SKImage.FromBitmap(bitmap);
+ using var data = image.Encode(SKEncodedImageFormat.Png, 100);
+ using var fs = File.OpenWrite(outputPath);
+ data.SaveTo(fs);
+ }
+ }
+ catch (Exception ex)
+ {
+ Log.Exception($"Failed to save image: {outputPath}", ex);
+ }
}
}
-
public class FirefoxBookmarkLoader : FirefoxBookmarkLoaderBase
{
///
@@ -77,33 +215,6 @@ public class FirefoxBookmarkLoader : FirefoxBookmarkLoaderBase
using var sReader = new StreamReader(profileIni);
var ini = sReader.ReadToEnd();
- /*
- Current profiles.ini structure example as of Firefox version 69.0.1
-
- [Install736426B0AF4A39CB]
- Default=Profiles/7789f565.default-release <== this is the default profile this plugin will get the bookmarks from. When opened Firefox will load the default profile
- Locked=1
-
- [Profile2]
- Name=newblahprofile
- IsRelative=0
- Path=C:\t6h2yuq8.newblahprofile <== Note this is a custom location path for the profile user can set, we need to cater for this in code.
-
- [Profile1]
- Name=default
- IsRelative=1
- Path=Profiles/cydum7q4.default
- Default=1
-
- [Profile0]
- Name=default-release
- IsRelative=1
- Path=Profiles/7789f565.default-release
-
- [General]
- StartWithLastProfile=1
- Version=2
- */
var lines = ini.Split("\r\n").ToList();
var defaultProfileFolderNameRaw = lines.FirstOrDefault(x => x.Contains("Default=") && x != "Default=1") ?? string.Empty;
diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj
index b4e42fbcd..4ac1e68b6 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj
@@ -96,6 +96,9 @@
+
+
+
diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs
index a48d70f2d..9d9b9e505 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs
@@ -68,7 +68,9 @@ public class Main : ISettingProvider, IPlugin, IReloadable, IPluginI18n, IContex
{
Title = c.Name,
SubTitle = c.Url,
- IcoPath = @"Images\bookmark.png",
+ IcoPath = !string.IsNullOrEmpty(c.FaviconPath) && File.Exists(c.FaviconPath)
+ ? c.FaviconPath
+ : @"Images\bookmark.png",
Score = BookmarkLoader.MatchProgram(c, param).Score,
Action = _ =>
{
@@ -90,7 +92,9 @@ public class Main : ISettingProvider, IPlugin, IReloadable, IPluginI18n, IContex
{
Title = c.Name,
SubTitle = c.Url,
- IcoPath = @"Images\bookmark.png",
+ IcoPath = !string.IsNullOrEmpty(c.FaviconPath) && File.Exists(c.FaviconPath)
+ ? c.FaviconPath
+ : @"Images\bookmark.png",
Score = 5,
Action = _ =>
{
diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Models/Bookmark.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Models/Bookmark.cs
index c738da389..caab16b65 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Models/Bookmark.cs
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Models/Bookmark.cs
@@ -18,4 +18,5 @@ public record Bookmark(string Name, string Url, string Source = "")
}
public List CustomBrowsers { get; set; } = new();
+ public string FaviconPath { get; set; } = string.Empty;
}
diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj b/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj
index 1b985acf9..0bac1040e 100644
--- a/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj
+++ b/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj
@@ -63,6 +63,7 @@
+
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj
index 549217027..5b326b524 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj
@@ -46,6 +46,7 @@
+
diff --git a/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj b/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj
index 21d964c11..bb23eeed8 100644
--- a/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj
+++ b/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj
@@ -56,5 +56,9 @@
PreserveNewest
+
+
+
+
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Flow.Launcher.Plugin.PluginsManager.csproj b/Plugins/Flow.Launcher.Plugin.PluginsManager/Flow.Launcher.Plugin.PluginsManager.csproj
index b438305d6..f3f5f268b 100644
--- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Flow.Launcher.Plugin.PluginsManager.csproj
+++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Flow.Launcher.Plugin.PluginsManager.csproj
@@ -37,4 +37,8 @@
PreserveNewest
+
+
+
+
diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj
index 4e216b7b2..628e349c4 100644
--- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj
+++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj
@@ -55,6 +55,7 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
+
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj b/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj
index 99c1a12e9..ff538828a 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj
+++ b/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj
@@ -69,6 +69,7 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
+
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj b/Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj
index 8f443214b..deef21944 100644
--- a/Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj
+++ b/Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj
@@ -60,6 +60,7 @@
+
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 266c24170..f59d596c8 100644
--- a/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj
+++ b/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj
@@ -64,5 +64,6 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
+
diff --git a/Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj b/Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj
index 6d338733e..ec0d3c1ca 100644
--- a/Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj
+++ b/Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj
@@ -56,4 +56,8 @@
+
+
+
+
diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj b/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj
index 55d69d526..5500096cd 100644
--- a/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj
+++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj
@@ -56,4 +56,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.WindowsSettings/Flow.Launcher.Plugin.WindowsSettings.csproj b/Plugins/Flow.Launcher.Plugin.WindowsSettings/Flow.Launcher.Plugin.WindowsSettings.csproj
index 73fcd9f83..512665753 100644
--- a/Plugins/Flow.Launcher.Plugin.WindowsSettings/Flow.Launcher.Plugin.WindowsSettings.csproj
+++ b/Plugins/Flow.Launcher.Plugin.WindowsSettings/Flow.Launcher.Plugin.WindowsSettings.csproj
@@ -68,5 +68,8 @@
+
+
+